• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.14.38 API Reference
  • KDE Home
  • Contact Us
 

KIO

  • kio
  • kio
copyjob.cpp
Go to the documentation of this file.
1/* This file is part of the KDE libraries
2 Copyright 2000 Stephan Kulow <coolo@kde.org>
3 Copyright 2000-2006 David Faure <faure@kde.org>
4 Copyright 2000 Waldo Bastian <bastian@kde.org>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public
8 License as published by the Free Software Foundation; either
9 version 2 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20*/
21
22#include "copyjob.h"
23#include <errno.h>
24#include "kdirlister.h"
25#include "kfileitem.h"
26#include "deletejob.h"
27#include "clipboardupdater_p.h"
28
29#include <klocale.h>
30#include <kdesktopfile.h>
31#include <kdebug.h>
32#include <kde_file.h>
33
34#include "slave.h"
35#include "scheduler.h"
36#include "kdirwatch.h"
37#include "kprotocolmanager.h"
38
39#include "jobuidelegate.h"
40
41#include <kdirnotify.h>
42#include <ktemporaryfile.h>
43
44#ifdef Q_OS_UNIX
45#include <utime.h>
46#endif
47#include <assert.h>
48
49#include <QtCore/QTimer>
50#include <QtCore/QFile>
51#include <sys/stat.h> // mode_t
52#include <QPointer>
53
54#include "job_p.h"
55#include <kdiskfreespaceinfo.h>
56
57using namespace KIO;
58
59//this will update the report dialog with 5 Hz, I think this is fast enough, aleXXX
60#define REPORT_TIMEOUT 200
61
62enum DestinationState {
63 DEST_NOT_STATED,
64 DEST_IS_DIR,
65 DEST_IS_FILE,
66 DEST_DOESNT_EXIST
67};
68
85enum CopyJobState {
86 STATE_STATING,
87 STATE_RENAMING,
88 STATE_LISTING,
89 STATE_CREATING_DIRS,
90 STATE_CONFLICT_CREATING_DIRS,
91 STATE_COPYING_FILES,
92 STATE_CONFLICT_COPYING_FILES,
93 STATE_DELETING_DIRS,
94 STATE_SETTING_DIR_ATTRIBUTES
95};
96
98class KIO::CopyJobPrivate: public KIO::JobPrivate
99{
100public:
101 CopyJobPrivate(const KUrl::List& src, const KUrl& dest,
102 CopyJob::CopyMode mode, bool asMethod)
103 : m_globalDest(dest)
104 , m_globalDestinationState(DEST_NOT_STATED)
105 , m_defaultPermissions(false)
106 , m_bURLDirty(false)
107 , m_mode(mode)
108 , m_asMethod(asMethod)
109 , destinationState(DEST_NOT_STATED)
110 , state(STATE_STATING)
111 , m_freeSpace(-1)
112 , m_totalSize(0)
113 , m_processedSize(0)
114 , m_fileProcessedSize(0)
115 , m_processedFiles(0)
116 , m_processedDirs(0)
117 , m_srcList(src)
118 , m_currentStatSrc(m_srcList.constBegin())
119 , m_bCurrentOperationIsLink(false)
120 , m_bSingleFileCopy(false)
121 , m_bOnlyRenames(mode==CopyJob::Move)
122 , m_dest(dest)
123 , m_bAutoRenameFiles(false)
124 , m_bAutoRenameDirs(false)
125 , m_bAutoSkipFiles( false )
126 , m_bAutoSkipDirs( false )
127 , m_bOverwriteAllFiles( false )
128 , m_bOverwriteAllDirs( false )
129 , m_conflictError(0)
130 , m_reportTimer(0)
131 {
132 }
133
134 // This is the dest URL that was initially given to CopyJob
135 // It is copied into m_dest, which can be changed for a given src URL
136 // (when using the RENAME dialog in slotResult),
137 // and which will be reset for the next src URL.
138 KUrl m_globalDest;
139 // The state info about that global dest
140 DestinationState m_globalDestinationState;
141 // See setDefaultPermissions
142 bool m_defaultPermissions;
143 // Whether URLs changed (and need to be emitted by the next slotReport call)
144 bool m_bURLDirty;
145 // Used after copying all the files into the dirs, to set mtime (TODO: and permissions?)
146 // after the copy is done
147 QLinkedList<CopyInfo> m_directoriesCopied;
148 QLinkedList<CopyInfo>::const_iterator m_directoriesCopiedIterator;
149
150 CopyJob::CopyMode m_mode;
151 bool m_asMethod;
152 DestinationState destinationState;
153 CopyJobState state;
154
155 KIO::filesize_t m_freeSpace;
156
157 KIO::filesize_t m_totalSize;
158 KIO::filesize_t m_processedSize;
159 KIO::filesize_t m_fileProcessedSize;
160 int m_processedFiles;
161 int m_processedDirs;
162 QList<CopyInfo> files;
163 QList<CopyInfo> dirs;
164 KUrl::List dirsToRemove;
165 KUrl::List m_srcList;
166 KUrl::List m_successSrcList; // Entries in m_srcList that have successfully been moved
167 KUrl::List::const_iterator m_currentStatSrc;
168 bool m_bCurrentSrcIsDir;
169 bool m_bCurrentOperationIsLink;
170 bool m_bSingleFileCopy;
171 bool m_bOnlyRenames;
172 KUrl m_dest;
173 KUrl m_currentDest; // set during listing, used by slotEntries
174 //
175 QStringList m_skipList;
176 QSet<QString> m_overwriteList;
177 bool m_bAutoRenameFiles;
178 bool m_bAutoRenameDirs;
179 bool m_bAutoSkipFiles;
180 bool m_bAutoSkipDirs;
181 bool m_bOverwriteAllFiles;
182 bool m_bOverwriteAllDirs;
183 int m_conflictError;
184
185 QTimer *m_reportTimer;
186
187 // The current src url being stat'ed or copied
188 // During the stat phase, this is initially equal to *m_currentStatSrc but it can be resolved to a local file equivalent (#188903).
189 KUrl m_currentSrcURL;
190 KUrl m_currentDestURL;
191
192 QSet<QString> m_parentDirs;
193
194 void statCurrentSrc();
195 void statNextSrc();
196
197 // Those aren't slots but submethods for slotResult.
198 void slotResultStating( KJob * job );
199 void startListing( const KUrl & src );
200 void slotResultCreatingDirs( KJob * job );
201 void slotResultConflictCreatingDirs( KJob * job );
202 void createNextDir();
203 void slotResultCopyingFiles( KJob * job );
204 void slotResultConflictCopyingFiles( KJob * job );
205// KIO::Job* linkNextFile( const KUrl& uSource, const KUrl& uDest, bool overwrite );
206 KIO::Job* linkNextFile( const KUrl& uSource, const KUrl& uDest, JobFlags flags );
207 void copyNextFile();
208 void slotResultDeletingDirs( KJob * job );
209 void deleteNextDir();
210 void sourceStated(const UDSEntry& entry, const KUrl& sourceUrl);
211 void skip(const KUrl & sourceURL, bool isDir);
212 void slotResultRenaming( KJob * job );
213 void slotResultSettingDirAttributes( KJob * job );
214 void setNextDirAttribute();
215
216 void startRenameJob(const KUrl &slave_url);
217 bool shouldOverwriteDir( const QString& path ) const;
218 bool shouldOverwriteFile( const QString& path ) const;
219 bool shouldSkip( const QString& path ) const;
220 void skipSrc(bool isDir);
221
222 void slotStart();
223 void slotEntries( KIO::Job*, const KIO::UDSEntryList& list );
224 void slotSubError(KIO::ListJob* job, KIO::ListJob *subJob);
225 void addCopyInfoFromUDSEntry(const UDSEntry& entry, const KUrl& srcUrl, bool srcIsDir, const KUrl& currentDest);
229 void slotProcessedSize( KJob*, qulonglong data_size );
234 void slotTotalSize( KJob*, qulonglong size );
235
236 void slotReport();
237
238 Q_DECLARE_PUBLIC(CopyJob)
239
240 static inline CopyJob *newJob(const KUrl::List& src, const KUrl& dest,
241 CopyJob::CopyMode mode, bool asMethod, JobFlags flags)
242 {
243 CopyJob *job = new CopyJob(*new CopyJobPrivate(src,dest,mode,asMethod));
244 job->setUiDelegate(new JobUiDelegate);
245 if (!(flags & HideProgressInfo))
246 KIO::getJobTracker()->registerJob(job);
247 if (flags & KIO::Overwrite) {
248 job->d_func()->m_bOverwriteAllDirs = true;
249 job->d_func()->m_bOverwriteAllFiles = true;
250 }
251 return job;
252 }
253};
254
255CopyJob::CopyJob(CopyJobPrivate &dd)
256 : Job(dd)
257{
258 setProperty("destUrl", d_func()->m_dest.url());
259 QTimer::singleShot(0, this, SLOT(slotStart()));
260 qRegisterMetaType<KIO::UDSEntry>("KIO::UDSEntry");
261}
262
263CopyJob::~CopyJob()
264{
265}
266
267KUrl::List CopyJob::srcUrls() const
268{
269 return d_func()->m_srcList;
270}
271
272KUrl CopyJob::destUrl() const
273{
274 return d_func()->m_dest;
275}
276
277void CopyJobPrivate::slotStart()
278{
279 Q_Q(CopyJob);
285 m_reportTimer = new QTimer(q);
286
287 q->connect(m_reportTimer,SIGNAL(timeout()),q,SLOT(slotReport()));
288 m_reportTimer->start(REPORT_TIMEOUT);
289
290 // Stat the dest
291 KIO::Job * job = KIO::stat( m_dest, StatJob::DestinationSide, 2, KIO::HideProgressInfo );
292 //kDebug(7007) << "CopyJob:stating the dest " << m_dest;
293 q->addSubjob(job);
294}
295
296// For unit test purposes
297KIO_EXPORT bool kio_resolve_local_urls = true;
298
299void CopyJobPrivate::slotResultStating( KJob *job )
300{
301 Q_Q(CopyJob);
302 //kDebug(7007);
303 // Was there an error while stating the src ?
304 if (job->error() && destinationState != DEST_NOT_STATED )
305 {
306 const KUrl srcurl = static_cast<SimpleJob*>(job)->url();
307 if ( !srcurl.isLocalFile() )
308 {
309 // Probably : src doesn't exist. Well, over some protocols (e.g. FTP)
310 // this info isn't really reliable (thanks to MS FTP servers).
311 // We'll assume a file, and try to download anyway.
312 kDebug(7007) << "Error while stating source. Activating hack";
313 q->removeSubjob( job );
314 assert ( !q->hasSubjobs() ); // We should have only one job at a time ...
315 struct CopyInfo info;
316 info.permissions = (mode_t) -1;
317 info.mtime = (time_t) -1;
318 info.ctime = (time_t) -1;
319 info.size = (KIO::filesize_t)-1;
320 info.uSource = srcurl;
321 info.uDest = m_dest;
322 // Append filename or dirname to destination URL, if allowed
323 if ( destinationState == DEST_IS_DIR && !m_asMethod )
324 info.uDest.addPath( srcurl.fileName() );
325
326 files.append( info );
327 statNextSrc();
328 return;
329 }
330 // Local file. If stat fails, the file definitely doesn't exist.
331 // yes, q->Job::, because we don't want to call our override
332 q->Job::slotResult( job ); // will set the error and emit result(this)
333 return;
334 }
335
336 // Keep copy of the stat result
337 const UDSEntry entry = static_cast<StatJob*>(job)->statResult();
338
339 if ( destinationState == DEST_NOT_STATED ) {
340 if ( m_dest.isLocalFile() ) { //works for dirs as well
341 QString path(m_dest.toLocalFile());
342 QFileInfo fileInfo(path);
343 if (m_asMethod || !fileInfo.exists()) {
344 // In copy-as mode, we want to check the directory to which we're
345 // copying. The target file or directory does not exist yet, which
346 // might confuse KDiskFreeSpaceInfo.
347 path = fileInfo.absolutePath();
348 }
349 KDiskFreeSpaceInfo freeSpaceInfo = KDiskFreeSpaceInfo::freeSpaceInfo(path);
350 if (freeSpaceInfo.isValid()) {
351 m_freeSpace = freeSpaceInfo.available();
352 } else {
353 kDebug(7007) << "Couldn't determine free space information for" << path;
354 }
355 //TODO actually preliminary check is even more valuable for slow NFS/SMB mounts,
356 //but we need to find a way to report connection errors to user
357 }
358
359 const bool isGlobalDest = m_dest == m_globalDest;
360 const bool isDir = entry.isDir();
361 // we were stating the dest
362 if (job->error()) {
363 destinationState = DEST_DOESNT_EXIST;
364 //kDebug(7007) << "dest does not exist";
365 } else {
366 // Treat symlinks to dirs as dirs here, so no test on isLink
367 destinationState = isDir ? DEST_IS_DIR : DEST_IS_FILE;
368 //kDebug(7007) << "dest is dir:" << isDir;
369
370 const QString sLocalPath = entry.stringValue( KIO::UDSEntry::UDS_LOCAL_PATH );
371 if ( !sLocalPath.isEmpty() && kio_resolve_local_urls && destinationState != DEST_DOESNT_EXIST ) {
372 m_dest = KUrl();
373 m_dest.setPath(sLocalPath);
374 if ( isGlobalDest )
375 m_globalDest = m_dest;
376 }
377 }
378 if ( isGlobalDest )
379 m_globalDestinationState = destinationState;
380
381 q->removeSubjob( job );
382 assert ( !q->hasSubjobs() );
383
384 // After knowing what the dest is, we can start stat'ing the first src.
385 statCurrentSrc();
386 } else {
387 sourceStated(entry, static_cast<SimpleJob*>(job)->url());
388 q->removeSubjob( job );
389 }
390}
391
392void CopyJobPrivate::sourceStated(const UDSEntry& entry, const KUrl& sourceUrl)
393{
394 const QString sLocalPath = entry.stringValue( KIO::UDSEntry::UDS_LOCAL_PATH );
395 const bool isDir = entry.isDir();
396
397 // We were stating the current source URL
398 // Is it a file or a dir ?
399
400 // There 6 cases, and all end up calling addCopyInfoFromUDSEntry first :
401 // 1 - src is a dir, destination is a directory,
402 // slotEntries will append the source-dir-name to the destination
403 // 2 - src is a dir, destination is a file -- will offer to overwrite, later on.
404 // 3 - src is a dir, destination doesn't exist, then it's the destination dirname,
405 // so slotEntries will use it as destination.
406
407 // 4 - src is a file, destination is a directory,
408 // slotEntries will append the filename to the destination.
409 // 5 - src is a file, destination is a file, m_dest is the exact destination name
410 // 6 - src is a file, destination doesn't exist, m_dest is the exact destination name
411
412 KUrl srcurl;
413 if (!sLocalPath.isEmpty() && destinationState != DEST_DOESNT_EXIST) {
414 kDebug() << "Using sLocalPath. destinationState=" << destinationState;
415 // Prefer the local path -- but only if we were able to stat() the dest.
416 // Otherwise, renaming a desktop:/ url would copy from src=file to dest=desktop (#218719)
417 srcurl.setPath(sLocalPath);
418 } else {
419 srcurl = sourceUrl;
420 }
421 addCopyInfoFromUDSEntry(entry, srcurl, false, m_dest);
422
423 m_currentDest = m_dest;
424 m_bCurrentSrcIsDir = false;
425
426 if ( isDir
427 // treat symlinks as files (no recursion)
428 && !entry.isLink()
429 && m_mode != CopyJob::Link ) // No recursion in Link mode either.
430 {
431 //kDebug(7007) << "Source is a directory";
432
433 if (srcurl.isLocalFile()) {
434 const QString parentDir = srcurl.toLocalFile(KUrl::RemoveTrailingSlash);
435 m_parentDirs.insert(parentDir);
436 }
437
438 m_bCurrentSrcIsDir = true; // used by slotEntries
439 if ( destinationState == DEST_IS_DIR ) // (case 1)
440 {
441 if ( !m_asMethod )
442 {
443 // Use <desturl>/<directory_copied> as destination, from now on
444 QString directory = srcurl.fileName();
445 const QString sName = entry.stringValue( KIO::UDSEntry::UDS_NAME );
446 KProtocolInfo::FileNameUsedForCopying fnu = KProtocolManager::fileNameUsedForCopying(srcurl);
447 if (fnu == KProtocolInfo::Name) {
448 if (!sName.isEmpty())
449 directory = sName;
450 } else if (fnu == KProtocolInfo::DisplayName) {
451 const QString dispName = entry.stringValue( KIO::UDSEntry::UDS_DISPLAY_NAME );
452 if (!dispName.isEmpty())
453 directory = dispName;
454 else if (!sName.isEmpty())
455 directory = sName;
456 }
457 m_currentDest.addPath( directory );
458 }
459 }
460 else // (case 3)
461 {
462 // otherwise dest is new name for toplevel dir
463 // so the destination exists, in fact, from now on.
464 // (This even works with other src urls in the list, since the
465 // dir has effectively been created)
466 destinationState = DEST_IS_DIR;
467 if ( m_dest == m_globalDest )
468 m_globalDestinationState = destinationState;
469 }
470
471 startListing( srcurl );
472 }
473 else
474 {
475 //kDebug(7007) << "Source is a file (or a symlink), or we are linking -> no recursive listing";
476
477 if (srcurl.isLocalFile()) {
478 const QString parentDir = srcurl.directory(KUrl::ObeyTrailingSlash);
479 m_parentDirs.insert(parentDir);
480 }
481
482 statNextSrc();
483 }
484}
485
486bool CopyJob::doSuspend()
487{
488 Q_D(CopyJob);
489 d->slotReport();
490 return Job::doSuspend();
491}
492
493void CopyJobPrivate::slotReport()
494{
495 Q_Q(CopyJob);
496 if ( q->isSuspended() )
497 return;
498 // If showProgressInfo was set, progressId() is > 0.
499 switch (state) {
500 case STATE_RENAMING:
501 q->setTotalAmount(KJob::Files, m_srcList.count());
502 // fall-through intended
503 case STATE_COPYING_FILES:
504 q->setProcessedAmount( KJob::Files, m_processedFiles );
505 if (m_bURLDirty)
506 {
507 // Only emit urls when they changed. This saves time, and fixes #66281
508 m_bURLDirty = false;
509 if (m_mode==CopyJob::Move)
510 {
511 emitMoving(q, m_currentSrcURL, m_currentDestURL);
512 emit q->moving( q, m_currentSrcURL, m_currentDestURL);
513 }
514 else if (m_mode==CopyJob::Link)
515 {
516 emitCopying( q, m_currentSrcURL, m_currentDestURL ); // we don't have a delegate->linking
517 emit q->linking( q, m_currentSrcURL.path(), m_currentDestURL );
518 }
519 else
520 {
521 emitCopying( q, m_currentSrcURL, m_currentDestURL );
522 emit q->copying( q, m_currentSrcURL, m_currentDestURL );
523 }
524 }
525 break;
526
527 case STATE_CREATING_DIRS:
528 q->setProcessedAmount( KJob::Directories, m_processedDirs );
529 if (m_bURLDirty)
530 {
531 m_bURLDirty = false;
532 emit q->creatingDir( q, m_currentDestURL );
533 emitCreatingDir( q, m_currentDestURL );
534 }
535 break;
536
537 case STATE_STATING:
538 case STATE_LISTING:
539 if (m_bURLDirty)
540 {
541 m_bURLDirty = false;
542 if (m_mode==CopyJob::Move)
543 {
544 emitMoving( q, m_currentSrcURL, m_currentDestURL );
545 }
546 else
547 {
548 emitCopying( q, m_currentSrcURL, m_currentDestURL );
549 }
550 }
551 q->setTotalAmount(KJob::Bytes, m_totalSize);
552 q->setTotalAmount(KJob::Files, files.count());
553 q->setTotalAmount(KJob::Directories, dirs.count());
554 break;
555
556 default:
557 break;
558 }
559}
560
561void CopyJobPrivate::slotEntries(KIO::Job* job, const UDSEntryList& list)
562{
563 //Q_Q(CopyJob);
564 UDSEntryList::ConstIterator it = list.constBegin();
565 UDSEntryList::ConstIterator end = list.constEnd();
566 for (; it != end; ++it) {
567 const UDSEntry& entry = *it;
568 addCopyInfoFromUDSEntry(entry, static_cast<SimpleJob *>(job)->url(), m_bCurrentSrcIsDir, m_currentDest);
569 }
570}
571
572void CopyJobPrivate::slotSubError(ListJob* job, ListJob* subJob)
573{
574 const KUrl url = subJob->url();
575 kWarning() << url << subJob->errorString();
576
577 Q_Q(CopyJob);
578
579 emit q->warning(job, subJob->errorString(), QString());
580 skip(url, true);
581}
582
583
584void CopyJobPrivate::addCopyInfoFromUDSEntry(const UDSEntry& entry, const KUrl& srcUrl, bool srcIsDir, const KUrl& currentDest)
585{
586 struct CopyInfo info;
587 info.permissions = entry.numberValue(KIO::UDSEntry::UDS_ACCESS, -1);
588 info.mtime = (time_t) entry.numberValue(KIO::UDSEntry::UDS_MODIFICATION_TIME, -1);
589 info.ctime = (time_t) entry.numberValue(KIO::UDSEntry::UDS_CREATION_TIME, -1);
590 info.size = (KIO::filesize_t) entry.numberValue(KIO::UDSEntry::UDS_SIZE, -1);
591 if (info.size != (KIO::filesize_t) -1)
592 m_totalSize += info.size;
593
594 // recursive listing, displayName can be a/b/c/d
595 const QString fileName = entry.stringValue(KIO::UDSEntry::UDS_NAME);
596 const QString urlStr = entry.stringValue(KIO::UDSEntry::UDS_URL);
597 KUrl url;
598 if (!urlStr.isEmpty())
599 url = urlStr;
600 QString localPath = entry.stringValue(KIO::UDSEntry::UDS_LOCAL_PATH);
601 const bool isDir = entry.isDir();
602 info.linkDest = entry.stringValue(KIO::UDSEntry::UDS_LINK_DEST);
603
604 if (fileName != QLatin1String("..") && fileName != QLatin1String(".")) {
605 const bool hasCustomURL = !url.isEmpty() || !localPath.isEmpty();
606 if (!hasCustomURL) {
607 // Make URL from displayName
608 url = srcUrl;
609 if (srcIsDir) { // Only if src is a directory. Otherwise uSource is fine as is
610 //kDebug(7007) << "adding path" << displayName;
611 url.addPath(fileName);
612 }
613 }
614 //kDebug(7007) << "displayName=" << displayName << "url=" << url;
615 if (!localPath.isEmpty() && kio_resolve_local_urls && destinationState != DEST_DOESNT_EXIST) {
616 url = KUrl(localPath);
617 }
618
619 info.uSource = url;
620 info.uDest = currentDest;
621 //kDebug(7007) << "uSource=" << info.uSource << "uDest(1)=" << info.uDest;
622 // Append filename or dirname to destination URL, if allowed
623 if (destinationState == DEST_IS_DIR &&
624 // "copy/move as <foo>" means 'foo' is the dest for the base srcurl
625 // (passed here during stating) but not its children (during listing)
626 (! (m_asMethod && state == STATE_STATING)))
627 {
628 QString destFileName;
629 KProtocolInfo::FileNameUsedForCopying fnu = KProtocolManager::fileNameUsedForCopying(url);
630 if (hasCustomURL &&
631 fnu == KProtocolInfo::FromUrl) {
632 //destFileName = url.fileName(); // Doesn't work for recursive listing
633 // Count the number of prefixes used by the recursive listjob
634 int numberOfSlashes = fileName.count('/'); // don't make this a find()!
635 QString path = url.path();
636 int pos = 0;
637 for (int n = 0; n < numberOfSlashes + 1; ++n) {
638 pos = path.lastIndexOf('/', pos - 1);
639 if (pos == -1) { // error
640 kWarning(7007) << "kioslave bug: not enough slashes in UDS_URL" << path << "- looking for" << numberOfSlashes << "slashes";
641 break;
642 }
643 }
644 if (pos >= 0) {
645 destFileName = path.mid(pos + 1);
646 }
647
648 } else if ( fnu == KProtocolInfo::Name ) { // destination filename taken from UDS_NAME
649 destFileName = fileName;
650 } else { // from display name (with fallback to name)
651 const QString displayName = entry.stringValue(KIO::UDSEntry::UDS_DISPLAY_NAME);
652 destFileName = displayName.isEmpty() ? fileName : displayName;
653 }
654
655 // Here we _really_ have to add some filename to the dest.
656 // Otherwise, we end up with e.g. dest=..../Desktop/ itself.
657 // (This can happen when dropping a link to a webpage with no path)
658 if (destFileName.isEmpty()) {
659 destFileName = KIO::encodeFileName(info.uSource.prettyUrl());
660 }
661
662 //kDebug(7007) << " adding destFileName=" << destFileName;
663 info.uDest.addPath(destFileName);
664 }
665 //kDebug(7007) << " uDest(2)=" << info.uDest;
666 //kDebug(7007) << " " << info.uSource << "->" << info.uDest;
667 if (info.linkDest.isEmpty() && isDir && m_mode != CopyJob::Link) { // Dir
668 dirs.append(info); // Directories
669 if (m_mode == CopyJob::Move) {
670 dirsToRemove.append(info.uSource);
671 }
672 } else {
673 files.append(info); // Files and any symlinks
674 }
675 }
676}
677
678void CopyJobPrivate::skipSrc(bool isDir)
679{
680 m_dest = m_globalDest;
681 destinationState = m_globalDestinationState;
682 skip(*m_currentStatSrc, isDir);
683 ++m_currentStatSrc;
684 statCurrentSrc();
685}
686
687void CopyJobPrivate::statNextSrc()
688{
689 /* Revert to the global destination, the one that applies to all source urls.
690 * Imagine you copy the items a b and c into /d, but /d/b exists so the user uses "Rename" to put it in /foo/b instead.
691 * d->m_dest is /foo/b for b, but we have to revert to /d for item c and following.
692 */
693 m_dest = m_globalDest;
694 destinationState = m_globalDestinationState;
695 ++m_currentStatSrc;
696 statCurrentSrc();
697}
698
699void CopyJobPrivate::statCurrentSrc()
700{
701 Q_Q(CopyJob);
702 if (m_currentStatSrc != m_srcList.constEnd()) {
703 m_currentSrcURL = (*m_currentStatSrc);
704 m_bURLDirty = true;
705 if (m_mode == CopyJob::Link) {
706 // Skip the "stating the source" stage, we don't need it for linking
707 m_currentDest = m_dest;
708 struct CopyInfo info;
709 info.permissions = -1;
710 info.mtime = (time_t) -1;
711 info.ctime = (time_t) -1;
712 info.size = (KIO::filesize_t)-1;
713 info.uSource = m_currentSrcURL;
714 info.uDest = m_currentDest;
715 // Append filename or dirname to destination URL, if allowed
716 if (destinationState == DEST_IS_DIR && !m_asMethod) {
717 if (
718 (m_currentSrcURL.protocol() == info.uDest.protocol()) &&
719 (m_currentSrcURL.host() == info.uDest.host()) &&
720 (m_currentSrcURL.port() == info.uDest.port()) &&
721 (m_currentSrcURL.user() == info.uDest.user()) &&
722 (m_currentSrcURL.pass() == info.uDest.pass()) ) {
723 // This is the case of creating a real symlink
724 info.uDest.addPath( m_currentSrcURL.fileName() );
725 } else {
726 // Different protocols, we'll create a .desktop file
727 // We have to change the extension anyway, so while we're at it,
728 // name the file like the URL
729 info.uDest.addPath(KIO::encodeFileName(m_currentSrcURL.prettyUrl()) + ".desktop");
730 }
731 }
732 files.append( info ); // Files and any symlinks
733 statNextSrc(); // we could use a loop instead of a recursive call :)
734 return;
735 }
736
737 // Let's see if we can skip stat'ing, for the case where a directory view has the info already
738 const KFileItem cachedItem = KDirLister::cachedItemForUrl(m_currentSrcURL);
739 KIO::UDSEntry entry;
740 if (!cachedItem.isNull()) {
741 entry = cachedItem.entry();
742 if (destinationState != DEST_DOESNT_EXIST) { // only resolve src if we could resolve dest (#218719)
743 bool dummyIsLocal;
744 m_currentSrcURL = cachedItem.mostLocalUrl(dummyIsLocal); // #183585
745 }
746 }
747
748 if (m_mode == CopyJob::Move && (
749 // Don't go renaming right away if we need a stat() to find out the destination filename
750 KProtocolManager::fileNameUsedForCopying(m_currentSrcURL) == KProtocolInfo::FromUrl ||
751 destinationState != DEST_IS_DIR || m_asMethod)
752 ) {
753 // If moving, before going for the full stat+[list+]copy+del thing, try to rename
754 // The logic is pretty similar to FileCopyJobPrivate::slotStart()
755 if ( (m_currentSrcURL.protocol() == m_dest.protocol()) &&
756 (m_currentSrcURL.host() == m_dest.host()) &&
757 (m_currentSrcURL.port() == m_dest.port()) &&
758 (m_currentSrcURL.user() == m_dest.user()) &&
759 (m_currentSrcURL.pass() == m_dest.pass()) )
760 {
761 startRenameJob( m_currentSrcURL );
762 return;
763 }
764 else if ( m_currentSrcURL.isLocalFile() && KProtocolManager::canRenameFromFile( m_dest ) )
765 {
766 startRenameJob( m_dest );
767 return;
768 }
769 else if ( m_dest.isLocalFile() && KProtocolManager::canRenameToFile( m_currentSrcURL ) )
770 {
771 startRenameJob( m_currentSrcURL );
772 return;
773 }
774 }
775
776 // if the file system doesn't support deleting, we do not even stat
777 if (m_mode == CopyJob::Move && !KProtocolManager::supportsDeleting(m_currentSrcURL)) {
778 QPointer<CopyJob> that = q;
779 emit q->warning( q, buildErrorString(ERR_CANNOT_DELETE, m_currentSrcURL.prettyUrl()) );
780 if (that)
781 statNextSrc(); // we could use a loop instead of a recursive call :)
782 return;
783 }
784
785 m_bOnlyRenames = false;
786
787 // Testing for entry.count()>0 here is not good enough; KFileItem inserts
788 // entries for UDS_USER and UDS_GROUP even on initially empty UDSEntries (#192185)
789 if (entry.contains(KIO::UDSEntry::UDS_NAME)) {
790 kDebug(7007) << "fast path! found info about" << m_currentSrcURL << "in KDirLister";
791 // sourceStated(entry, m_currentSrcURL); // don't recurse, see #319747, use queued invokeMethod instead
792 QMetaObject::invokeMethod(q, "sourceStated", Qt::QueuedConnection, Q_ARG(KIO::UDSEntry, entry), Q_ARG(KUrl, m_currentSrcURL));
793 return;
794 }
795
796 // Stat the next src url
797 Job * job = KIO::stat( m_currentSrcURL, StatJob::SourceSide, 2, KIO::HideProgressInfo );
798 //kDebug(7007) << "KIO::stat on" << m_currentSrcURL;
799 state = STATE_STATING;
800 q->addSubjob(job);
801 m_currentDestURL = m_dest;
802 m_bURLDirty = true;
803 }
804 else
805 {
806 // Finished the stat'ing phase
807 // First make sure that the totals were correctly emitted
808 state = STATE_STATING;
809 m_bURLDirty = true;
810 slotReport();
811
812 kDebug(7007)<<"Stating finished. To copy:"<<m_totalSize<<", available:"<<m_freeSpace;
813 //TODO warn user beforehand if space is not enough
814
815 if (!dirs.isEmpty())
816 emit q->aboutToCreate( q, dirs );
817 if (!files.isEmpty())
818 emit q->aboutToCreate( q, files );
819 // Check if we are copying a single file
820 m_bSingleFileCopy = ( files.count() == 1 && dirs.isEmpty() );
821 // Then start copying things
822 state = STATE_CREATING_DIRS;
823 createNextDir();
824 }
825}
826
827void CopyJobPrivate::startRenameJob( const KUrl& slave_url )
828{
829 Q_Q(CopyJob);
830
831 // Silence KDirWatch notifications, otherwise performance is horrible
832 if (m_currentSrcURL.isLocalFile()) {
833 const QString parentDir = m_currentSrcURL.directory(KUrl::ObeyTrailingSlash);
834 if (!m_parentDirs.contains(parentDir)) {
835 KDirWatch::self()->stopDirScan(parentDir);
836 m_parentDirs.insert(parentDir);
837 }
838 }
839
840 KUrl dest = m_dest;
841 // Append filename or dirname to destination URL, if allowed
842 if ( destinationState == DEST_IS_DIR && !m_asMethod )
843 dest.addPath( m_currentSrcURL.fileName() );
844 m_currentDestURL = dest;
845 kDebug(7007) << m_currentSrcURL << "->" << dest << "trying direct rename first";
846 state = STATE_RENAMING;
847
848 struct CopyInfo info;
849 info.permissions = -1;
850 info.mtime = (time_t) -1;
851 info.ctime = (time_t) -1;
852 info.size = (KIO::filesize_t)-1;
853 info.uSource = m_currentSrcURL;
854 info.uDest = dest;
855 QList<CopyInfo> files;
856 files.append(info);
857 emit q->aboutToCreate( q, files );
858
859 KIO_ARGS << m_currentSrcURL << dest << (qint8) false /*no overwrite*/;
860 SimpleJob * newJob = SimpleJobPrivate::newJobNoUi(slave_url, CMD_RENAME, packedArgs);
861 Scheduler::setJobPriority(newJob, 1);
862 q->addSubjob( newJob );
863 if ( m_currentSrcURL.directory() != dest.directory() ) // For the user, moving isn't renaming. Only renaming is.
864 m_bOnlyRenames = false;
865}
866
867void CopyJobPrivate::startListing( const KUrl & src )
868{
869 Q_Q(CopyJob);
870 state = STATE_LISTING;
871 m_bURLDirty = true;
872 ListJob * newjob = listRecursive(src, KIO::HideProgressInfo);
873 newjob->setUnrestricted(true);
874 q->connect(newjob, SIGNAL(entries(KIO::Job*,KIO::UDSEntryList)),
875 SLOT(slotEntries(KIO::Job*,KIO::UDSEntryList)));
876 q->connect(newjob, SIGNAL(subError(KIO::ListJob*,KIO::ListJob*)),
877 SLOT(slotSubError(KIO::ListJob*,KIO::ListJob*)));
878 q->addSubjob( newjob );
879}
880
881void CopyJobPrivate::skip(const KUrl & sourceUrl, bool isDir)
882{
883 KUrl dir = sourceUrl;
884 if (!isDir) {
885 // Skipping a file: make sure not to delete the parent dir (#208418)
886 dir.setPath(dir.directory());
887 }
888 while (dirsToRemove.removeAll(dir) > 0) {
889 // Do not rely on rmdir() on the parent directories aborting.
890 // Exclude the parent dirs explicitly.
891 dir.setPath(dir.directory());
892 }
893}
894
895bool CopyJobPrivate::shouldOverwriteDir( const QString& path ) const
896{
897 if ( m_bOverwriteAllDirs )
898 return true;
899 return m_overwriteList.contains(path);
900}
901
902bool CopyJobPrivate::shouldOverwriteFile( const QString& path ) const
903{
904 if ( m_bOverwriteAllFiles )
905 return true;
906 return m_overwriteList.contains(path);
907}
908
909bool CopyJobPrivate::shouldSkip( const QString& path ) const
910{
911 Q_FOREACH(const QString& skipPath, m_skipList) {
912 if ( path.startsWith(skipPath) )
913 return true;
914 }
915 return false;
916}
917
918void CopyJobPrivate::slotResultCreatingDirs( KJob * job )
919{
920 Q_Q(CopyJob);
921 // The dir we are trying to create:
922 QList<CopyInfo>::Iterator it = dirs.begin();
923 // Was there an error creating a dir ?
924 if ( job->error() )
925 {
926 m_conflictError = job->error();
927 if ( (m_conflictError == ERR_DIR_ALREADY_EXIST)
928 || (m_conflictError == ERR_FILE_ALREADY_EXIST) ) // can't happen?
929 {
930 KUrl oldURL = ((SimpleJob*)job)->url();
931 // Should we skip automatically ?
932 if ( m_bAutoSkipDirs ) {
933 // We don't want to copy files in this directory, so we put it on the skip list
934 m_skipList.append( oldURL.path( KUrl::AddTrailingSlash ) );
935 skip(oldURL, true);
936 dirs.erase( it ); // Move on to next dir
937 } else {
938 // Did the user choose to overwrite already?
939 const QString destDir = (*it).uDest.path();
940 if ( shouldOverwriteDir( destDir ) ) { // overwrite => just skip
941 emit q->copyingDone( q, (*it).uSource, (*it).uDest, (*it).mtime, true /* directory */, false /* renamed */ );
942 dirs.erase( it ); // Move on to next dir
943 } else {
944 if (m_bAutoRenameDirs) {
945 QString oldPath = (*it).uDest.path(KUrl::AddTrailingSlash);
946
947 KUrl destDirectory((*it).uDest);
948 destDirectory.setPath(destDirectory.directory());
949 QString newName = KIO::RenameDialog::suggestName(destDirectory, (*it).uDest.fileName());
950
951 KUrl newUrl((*it).uDest);
952 newUrl.setFileName(newName);
953
954 emit q->renamed(q, (*it).uDest, newUrl); // for e.g. kpropsdlg
955
956 // Change the current one and strip the trailing '/'
957 (*it).uDest.setPath(newUrl.path(KUrl::RemoveTrailingSlash));
958
959 QString newPath = newUrl.path(KUrl::AddTrailingSlash); // With trailing slash
960 QList<CopyInfo>::Iterator renamedirit = it;
961 ++renamedirit;
962 // Change the name of subdirectories inside the directory
963 for(; renamedirit != dirs.end() ; ++renamedirit) {
964 QString path = (*renamedirit).uDest.path();
965 if (path.startsWith(oldPath)) {
966 QString n = path;
967 n.replace(0, oldPath.length(), newPath);
968 kDebug(7007) << "dirs list:" << (*renamedirit).uSource.path()
969 << "was going to be" << path
970 << ", changed into" << n;
971 (*renamedirit).uDest.setPath(n);
972 }
973 }
974 // Change filenames inside the directory
975 QList<CopyInfo>::Iterator renamefileit = files.begin();
976 for(; renamefileit != files.end() ; ++renamefileit) {
977 QString path = (*renamefileit).uDest.path();
978 if (path.startsWith(oldPath)) {
979 QString n = path;
980 n.replace(0, oldPath.length(), newPath);
981 kDebug(7007) << "files list:" << (*renamefileit).uSource.path()
982 << "was going to be" << path
983 << ", changed into" << n;
984 (*renamefileit).uDest.setPath(n);
985 }
986 }
987 if (!dirs.isEmpty()) {
988 emit q->aboutToCreate(q, dirs);
989 }
990 if (!files.isEmpty()) {
991 emit q->aboutToCreate(q, files);
992 }
993
994 }
995 else {
996 if (!q->isInteractive()) {
997 q->Job::slotResult(job); // will set the error and emit result(this)
998 return;
999 }
1000
1001 assert(((SimpleJob*)job)->url().url() == (*it).uDest.url());
1002 q->removeSubjob(job);
1003 assert (!q->hasSubjobs()); // We should have only one job at a time ...
1004
1005 // We need to stat the existing dir, to get its last-modification time
1006 KUrl existingDest((*it).uDest);
1007 SimpleJob * newJob = KIO::stat(existingDest, StatJob::DestinationSide, 2, KIO::HideProgressInfo);
1008 Scheduler::setJobPriority(newJob, 1);
1009 kDebug(7007) << "KIO::stat for resolving conflict on " << existingDest;
1010 state = STATE_CONFLICT_CREATING_DIRS;
1011 q->addSubjob(newJob);
1012 return; // Don't move to next dir yet !
1013 }
1014 }
1015 }
1016 }
1017 else
1018 {
1019 // Severe error, abort
1020 q->Job::slotResult( job ); // will set the error and emit result(this)
1021 return;
1022 }
1023 }
1024 else // no error : remove from list, to move on to next dir
1025 {
1026 //this is required for the undo feature
1027 emit q->copyingDone( q, (*it).uSource, (*it).uDest, (*it).mtime, true, false );
1028 m_directoriesCopied.append( *it );
1029 dirs.erase( it );
1030 }
1031
1032 m_processedDirs++;
1033 //emit processedAmount( this, KJob::Directories, m_processedDirs );
1034 q->removeSubjob( job );
1035 assert( !q->hasSubjobs() ); // We should have only one job at a time ...
1036 createNextDir();
1037}
1038
1039void CopyJobPrivate::slotResultConflictCreatingDirs( KJob * job )
1040{
1041 Q_Q(CopyJob);
1042 // We come here after a conflict has been detected and we've stated the existing dir
1043
1044 // The dir we were trying to create:
1045 QList<CopyInfo>::Iterator it = dirs.begin();
1046
1047 const UDSEntry entry = ((KIO::StatJob*)job)->statResult();
1048
1049 // Its modification time:
1050 const time_t destmtime = (time_t) entry.numberValue( KIO::UDSEntry::UDS_MODIFICATION_TIME, -1 );
1051 const time_t destctime = (time_t) entry.numberValue( KIO::UDSEntry::UDS_CREATION_TIME, -1 );
1052
1053 const KIO::filesize_t destsize = entry.numberValue( KIO::UDSEntry::UDS_SIZE );
1054 const QString linkDest = entry.stringValue( KIO::UDSEntry::UDS_LINK_DEST );
1055
1056 q->removeSubjob( job );
1057 assert ( !q->hasSubjobs() ); // We should have only one job at a time ...
1058
1059 // Always multi and skip (since there are files after that)
1060 RenameDialog_Mode mode = (RenameDialog_Mode)( M_MULTI | M_SKIP | M_ISDIR );
1061 // Overwrite only if the existing thing is a dir (no chance with a file)
1062 if ( m_conflictError == ERR_DIR_ALREADY_EXIST )
1063 {
1064 if( (*it).uSource == (*it).uDest ||
1065 ((*it).uSource.protocol() == (*it).uDest.protocol() &&
1066 (*it).uSource.path( KUrl::RemoveTrailingSlash ) == linkDest) )
1067 mode = (RenameDialog_Mode)( mode | M_OVERWRITE_ITSELF);
1068 else
1069 mode = (RenameDialog_Mode)( mode | M_OVERWRITE );
1070 }
1071
1072 QString existingDest = (*it).uDest.path();
1073 QString newPath;
1074 if (m_reportTimer)
1075 m_reportTimer->stop();
1076 RenameDialog_Result r = q->ui()->askFileRename( q, i18n("Folder Already Exists"),
1077 (*it).uSource.url(),
1078 (*it).uDest.url(),
1079 mode, newPath,
1080 (*it).size, destsize,
1081 (*it).ctime, destctime,
1082 (*it).mtime, destmtime );
1083 if (m_reportTimer)
1084 m_reportTimer->start(REPORT_TIMEOUT);
1085 switch ( r ) {
1086 case R_CANCEL:
1087 q->setError( ERR_USER_CANCELED );
1088 q->emitResult();
1089 return;
1090 case R_AUTO_RENAME:
1091 m_bAutoRenameDirs = true;
1092 // fall through
1093 case R_RENAME:
1094 {
1095 QString oldPath = (*it).uDest.path( KUrl::AddTrailingSlash );
1096 KUrl newUrl( (*it).uDest );
1097 newUrl.setPath( newPath );
1098 emit q->renamed( q, (*it).uDest, newUrl ); // for e.g. kpropsdlg
1099
1100 // Change the current one and strip the trailing '/'
1101 (*it).uDest.setPath( newUrl.path( KUrl::RemoveTrailingSlash ) );
1102 newPath = newUrl.path( KUrl::AddTrailingSlash ); // With trailing slash
1103 QList<CopyInfo>::Iterator renamedirit = it;
1104 ++renamedirit;
1105 // Change the name of subdirectories inside the directory
1106 for( ; renamedirit != dirs.end() ; ++renamedirit )
1107 {
1108 QString path = (*renamedirit).uDest.path();
1109 if ( path.startsWith( oldPath ) ) {
1110 QString n = path;
1111 n.replace( 0, oldPath.length(), newPath );
1112 kDebug(7007) << "dirs list:" << (*renamedirit).uSource.path()
1113 << "was going to be" << path
1114 << ", changed into" << n;
1115 (*renamedirit).uDest.setPath( n );
1116 }
1117 }
1118 // Change filenames inside the directory
1119 QList<CopyInfo>::Iterator renamefileit = files.begin();
1120 for( ; renamefileit != files.end() ; ++renamefileit )
1121 {
1122 QString path = (*renamefileit).uDest.path();
1123 if ( path.startsWith( oldPath ) ) {
1124 QString n = path;
1125 n.replace( 0, oldPath.length(), newPath );
1126 kDebug(7007) << "files list:" << (*renamefileit).uSource.path()
1127 << "was going to be" << path
1128 << ", changed into" << n;
1129 (*renamefileit).uDest.setPath( n );
1130 }
1131 }
1132 if (!dirs.isEmpty())
1133 emit q->aboutToCreate( q, dirs );
1134 if (!files.isEmpty())
1135 emit q->aboutToCreate( q, files );
1136 }
1137 break;
1138 case R_AUTO_SKIP:
1139 m_bAutoSkipDirs = true;
1140 // fall through
1141 case R_SKIP:
1142 m_skipList.append( existingDest );
1143 skip((*it).uSource, true);
1144 // Move on to next dir
1145 dirs.erase( it );
1146 m_processedDirs++;
1147 break;
1148 case R_OVERWRITE:
1149 m_overwriteList.insert( existingDest );
1150 emit q->copyingDone( q, (*it).uSource, (*it).uDest, (*it).mtime, true /* directory */, false /* renamed */ );
1151 // Move on to next dir
1152 dirs.erase( it );
1153 m_processedDirs++;
1154 break;
1155 case R_OVERWRITE_ALL:
1156 m_bOverwriteAllDirs = true;
1157 emit q->copyingDone( q, (*it).uSource, (*it).uDest, (*it).mtime, true /* directory */, false /* renamed */ );
1158 // Move on to next dir
1159 dirs.erase( it );
1160 m_processedDirs++;
1161 break;
1162 default:
1163 assert( 0 );
1164 }
1165 state = STATE_CREATING_DIRS;
1166 //emit processedAmount( this, KJob::Directories, m_processedDirs );
1167 createNextDir();
1168}
1169
1170void CopyJobPrivate::createNextDir()
1171{
1172 Q_Q(CopyJob);
1173 KUrl udir;
1174 if ( !dirs.isEmpty() )
1175 {
1176 // Take first dir to create out of list
1177 QList<CopyInfo>::Iterator it = dirs.begin();
1178 // Is this URL on the skip list or the overwrite list ?
1179 while( it != dirs.end() && udir.isEmpty() )
1180 {
1181 const QString dir = (*it).uDest.path();
1182 if ( shouldSkip( dir ) ) {
1183 dirs.erase( it );
1184 it = dirs.begin();
1185 } else
1186 udir = (*it).uDest;
1187 }
1188 }
1189 if ( !udir.isEmpty() ) // any dir to create, finally ?
1190 {
1191 // Create the directory - with default permissions so that we can put files into it
1192 // TODO : change permissions once all is finished; but for stuff coming from CDROM it sucks...
1193 KIO::SimpleJob *newjob = KIO::mkdir( udir, -1 );
1194 Scheduler::setJobPriority(newjob, 1);
1195 if (shouldOverwriteFile(udir.path())) { // if we are overwriting an existing file or symlink
1196 newjob->addMetaData("overwrite", "true");
1197 }
1198
1199 m_currentDestURL = udir;
1200 m_bURLDirty = true;
1201
1202 q->addSubjob(newjob);
1203 return;
1204 }
1205 else // we have finished creating dirs
1206 {
1207 q->setProcessedAmount( KJob::Directories, m_processedDirs ); // make sure final number appears
1208
1209 if (m_mode == CopyJob::Move) {
1210 // Now we know which dirs hold the files we're going to delete.
1211 // To speed things up and prevent double-notification, we disable KDirWatch
1212 // on those dirs temporarily (using KDirWatch::self, that's the instanced
1213 // used by e.g. kdirlister).
1214 for ( QSet<QString>::const_iterator it = m_parentDirs.constBegin() ; it != m_parentDirs.constEnd() ; ++it )
1215 KDirWatch::self()->stopDirScan( *it );
1216 }
1217
1218 state = STATE_COPYING_FILES;
1219 m_processedFiles++; // Ralf wants it to start at 1, not 0
1220 copyNextFile();
1221 }
1222}
1223
1224void CopyJobPrivate::slotResultCopyingFiles( KJob * job )
1225{
1226 Q_Q(CopyJob);
1227 // The file we were trying to copy:
1228 QList<CopyInfo>::Iterator it = files.begin();
1229 if ( job->error() )
1230 {
1231 // Should we skip automatically ?
1232 if ( m_bAutoSkipFiles )
1233 {
1234 skip((*it).uSource, false);
1235 m_fileProcessedSize = (*it).size;
1236 files.erase( it ); // Move on to next file
1237 }
1238 else
1239 {
1240 m_conflictError = job->error(); // save for later
1241 // Existing dest ?
1242 if ( ( m_conflictError == ERR_FILE_ALREADY_EXIST )
1243 || ( m_conflictError == ERR_DIR_ALREADY_EXIST )
1244 || ( m_conflictError == ERR_IDENTICAL_FILES ) )
1245 {
1246 if (m_bAutoRenameFiles) {
1247 KUrl destDirectory((*it).uDest);
1248 destDirectory.setPath(destDirectory.directory());
1249 const QString newName = KIO::RenameDialog::suggestName(destDirectory, (*it).uDest.fileName());
1250
1251 KUrl newUrl((*it).uDest);
1252 newUrl.setFileName(newName);
1253
1254 emit q->renamed(q, (*it).uDest, newUrl); // for e.g. kpropsdlg
1255 (*it).uDest = newUrl;
1256
1257 QList<CopyInfo> files;
1258 files.append(*it);
1259 emit q->aboutToCreate(q, files);
1260 }
1261 else {
1262 if ( !q->isInteractive() ) {
1263 q->Job::slotResult( job ); // will set the error and emit result(this)
1264 return;
1265 }
1266
1267 q->removeSubjob(job);
1268 assert (!q->hasSubjobs());
1269 // We need to stat the existing file, to get its last-modification time
1270 KUrl existingFile((*it).uDest);
1271 SimpleJob * newJob = KIO::stat(existingFile, StatJob::DestinationSide, 2, KIO::HideProgressInfo);
1272 Scheduler::setJobPriority(newJob, 1);
1273 kDebug(7007) << "KIO::stat for resolving conflict on " << existingFile;
1274 state = STATE_CONFLICT_COPYING_FILES;
1275 q->addSubjob(newJob);
1276 return; // Don't move to next file yet !
1277 }
1278 }
1279 else
1280 {
1281 if ( m_bCurrentOperationIsLink && qobject_cast<KIO::DeleteJob*>( job ) )
1282 {
1283 // Very special case, see a few lines below
1284 // We are deleting the source of a symlink we successfully moved... ignore error
1285 m_fileProcessedSize = (*it).size;
1286 files.erase( it );
1287 } else {
1288 if ( !q->isInteractive() ) {
1289 q->Job::slotResult( job ); // will set the error and emit result(this)
1290 return;
1291 }
1292
1293 // Go directly to the conflict resolution, there is nothing to stat
1294 slotResultConflictCopyingFiles( job );
1295 return;
1296 }
1297 }
1298 }
1299 } else // no error
1300 {
1301 // Special case for moving links. That operation needs two jobs, unlike others.
1302 if ( m_bCurrentOperationIsLink && m_mode == CopyJob::Move
1303 && !qobject_cast<KIO::DeleteJob *>( job ) // Deleting source not already done
1304 )
1305 {
1306 q->removeSubjob( job );
1307 assert ( !q->hasSubjobs() );
1308 // The only problem with this trick is that the error handling for this del operation
1309 // is not going to be right... see 'Very special case' above.
1310 KIO::Job * newjob = KIO::del( (*it).uSource, HideProgressInfo );
1311 q->addSubjob( newjob );
1312 return; // Don't move to next file yet !
1313 }
1314
1315 if ( m_bCurrentOperationIsLink )
1316 {
1317 QString target = ( m_mode == CopyJob::Link ? (*it).uSource.path() : (*it).linkDest );
1318 //required for the undo feature
1319 emit q->copyingLinkDone( q, (*it).uSource, target, (*it).uDest );
1320 }
1321 else {
1322 //required for the undo feature
1323 emit q->copyingDone( q, (*it).uSource, (*it).uDest, (*it).mtime, false, false );
1324 if (m_mode == CopyJob::Move)
1325 {
1326 org::kde::KDirNotify::emitFileMoved( (*it).uSource.url(), (*it).uDest.url() );
1327 }
1328 m_successSrcList.append((*it).uSource);
1329 if (m_freeSpace != (KIO::filesize_t)-1 && (*it).size != (KIO::filesize_t)-1) {
1330 m_freeSpace -= (*it).size;
1331 }
1332
1333 }
1334 // remove from list, to move on to next file
1335 files.erase( it );
1336 }
1337 m_processedFiles++;
1338
1339 // clear processed size for last file and add it to overall processed size
1340 m_processedSize += m_fileProcessedSize;
1341 m_fileProcessedSize = 0;
1342
1343 //kDebug(7007) << files.count() << "files remaining";
1344
1345 // Merge metadata from subjob
1346 KIO::Job* kiojob = dynamic_cast<KIO::Job*>(job);
1347 Q_ASSERT(kiojob);
1348 m_incomingMetaData += kiojob->metaData();
1349 q->removeSubjob( job );
1350 assert( !q->hasSubjobs() ); // We should have only one job at a time ...
1351 copyNextFile();
1352}
1353
1354void CopyJobPrivate::slotResultConflictCopyingFiles( KJob * job )
1355{
1356 Q_Q(CopyJob);
1357 // We come here after a conflict has been detected and we've stated the existing file
1358 // The file we were trying to create:
1359 QList<CopyInfo>::Iterator it = files.begin();
1360
1361 RenameDialog_Result res;
1362 QString newPath;
1363
1364 if (m_reportTimer)
1365 m_reportTimer->stop();
1366
1367 if ( ( m_conflictError == ERR_FILE_ALREADY_EXIST )
1368 || ( m_conflictError == ERR_DIR_ALREADY_EXIST )
1369 || ( m_conflictError == ERR_IDENTICAL_FILES ) )
1370 {
1371 // Its modification time:
1372 const UDSEntry entry = ((KIO::StatJob*)job)->statResult();
1373
1374 const time_t destmtime = (time_t) entry.numberValue( KIO::UDSEntry::UDS_MODIFICATION_TIME, -1 );
1375 const time_t destctime = (time_t) entry.numberValue( KIO::UDSEntry::UDS_CREATION_TIME, -1 );
1376 const KIO::filesize_t destsize = entry.numberValue( KIO::UDSEntry::UDS_SIZE );
1377 const QString linkDest = entry.stringValue( KIO::UDSEntry::UDS_LINK_DEST );
1378
1379 // Offer overwrite only if the existing thing is a file
1380 // If src==dest, use "overwrite-itself"
1381 RenameDialog_Mode mode;
1382 bool isDir = true;
1383
1384 if( m_conflictError == ERR_DIR_ALREADY_EXIST )
1385 mode = M_ISDIR;
1386 else
1387 {
1388 if ( (*it).uSource == (*it).uDest ||
1389 ((*it).uSource.protocol() == (*it).uDest.protocol() &&
1390 (*it).uSource.path( KUrl::RemoveTrailingSlash ) == linkDest) )
1391 mode = M_OVERWRITE_ITSELF;
1392 else
1393 mode = M_OVERWRITE;
1394 isDir = false;
1395 }
1396
1397 if ( !m_bSingleFileCopy )
1398 mode = (RenameDialog_Mode) ( mode | M_MULTI | M_SKIP );
1399
1400 res = q->ui()->askFileRename( q, !isDir ?
1401 i18n("File Already Exists") : i18n("Already Exists as Folder"),
1402 (*it).uSource.url(),
1403 (*it).uDest.url(),
1404 mode, newPath,
1405 (*it).size, destsize,
1406 (*it).ctime, destctime,
1407 (*it).mtime, destmtime );
1408
1409 }
1410 else
1411 {
1412 if ( job->error() == ERR_USER_CANCELED )
1413 res = R_CANCEL;
1414 else if ( !q->isInteractive() ) {
1415 q->Job::slotResult( job ); // will set the error and emit result(this)
1416 return;
1417 }
1418 else
1419 {
1420 SkipDialog_Result skipResult = q->ui()->askSkip( q, files.count() > 1,
1421 job->errorString() );
1422
1423 // Convert the return code from SkipDialog into a RenameDialog code
1424 res = ( skipResult == S_SKIP ) ? R_SKIP :
1425 ( skipResult == S_AUTO_SKIP ) ? R_AUTO_SKIP :
1426 R_CANCEL;
1427 }
1428 }
1429
1430 if (m_reportTimer)
1431 m_reportTimer->start(REPORT_TIMEOUT);
1432
1433 q->removeSubjob( job );
1434 assert ( !q->hasSubjobs() );
1435 switch ( res ) {
1436 case R_CANCEL:
1437 q->setError( ERR_USER_CANCELED );
1438 q->emitResult();
1439 return;
1440 case R_AUTO_RENAME:
1441 m_bAutoRenameFiles = true;
1442 // fall through
1443 case R_RENAME:
1444 {
1445 KUrl newUrl( (*it).uDest );
1446 newUrl.setPath( newPath );
1447 emit q->renamed( q, (*it).uDest, newUrl ); // for e.g. kpropsdlg
1448 (*it).uDest = newUrl;
1449
1450 QList<CopyInfo> files;
1451 files.append(*it);
1452 emit q->aboutToCreate( q, files );
1453 }
1454 break;
1455 case R_AUTO_SKIP:
1456 m_bAutoSkipFiles = true;
1457 // fall through
1458 case R_SKIP:
1459 // Move on to next file
1460 skip((*it).uSource, false);
1461 m_processedSize += (*it).size;
1462 files.erase( it );
1463 m_processedFiles++;
1464 break;
1465 case R_OVERWRITE_ALL:
1466 m_bOverwriteAllFiles = true;
1467 break;
1468 case R_OVERWRITE:
1469 // Add to overwrite list, so that copyNextFile knows to overwrite
1470 m_overwriteList.insert( (*it).uDest.path() );
1471 break;
1472 default:
1473 assert( 0 );
1474 }
1475 state = STATE_COPYING_FILES;
1476 copyNextFile();
1477}
1478
1479KIO::Job* CopyJobPrivate::linkNextFile( const KUrl& uSource, const KUrl& uDest, JobFlags flags )
1480{
1481 //kDebug(7007) << "Linking";
1482 if (
1483 (uSource.protocol() == uDest.protocol()) &&
1484 (uSource.host() == uDest.host()) &&
1485 (uSource.port() == uDest.port()) &&
1486 (uSource.user() == uDest.user()) &&
1487 (uSource.pass() == uDest.pass()) )
1488 {
1489 // This is the case of creating a real symlink
1490 KIO::SimpleJob *newJob = KIO::symlink( uSource.path(), uDest, flags|HideProgressInfo /*no GUI*/ );
1491 Scheduler::setJobPriority(newJob, 1);
1492 //kDebug(7007) << "Linking target=" << uSource.path() << "link=" << uDest;
1493 //emit linking( this, uSource.path(), uDest );
1494 m_bCurrentOperationIsLink = true;
1495 m_currentSrcURL=uSource;
1496 m_currentDestURL=uDest;
1497 m_bURLDirty = true;
1498 //Observer::self()->slotCopying( this, uSource, uDest ); // should be slotLinking perhaps
1499 return newJob;
1500 } else {
1501 Q_Q(CopyJob);
1502 //kDebug(7007) << "Linking URL=" << uSource << "link=" << uDest;
1503 if ( uDest.isLocalFile() ) {
1504 // if the source is a devices url, handle it a littlebit special
1505
1506 QString path = uDest.toLocalFile();
1507 //kDebug(7007) << "path=" << path;
1508 QFile f( path );
1509 if ( f.open( QIODevice::ReadWrite ) )
1510 {
1511 f.close();
1512 KDesktopFile desktopFile( path );
1513 KConfigGroup config = desktopFile.desktopGroup();
1514 KUrl url = uSource;
1515 url.setPass( "" );
1516 config.writePathEntry( "URL", url.url() );
1517 config.writeEntry( "Name", url.url() );
1518 config.writeEntry( "Type", QString::fromLatin1("Link") );
1519 QString protocol = uSource.protocol();
1520 if ( protocol == QLatin1String("ftp") )
1521 config.writeEntry( "Icon", QString::fromLatin1("folder-remote") );
1522 else if ( protocol == QLatin1String("http") )
1523 config.writeEntry( "Icon", QString::fromLatin1("text-html") );
1524 else if ( protocol == QLatin1String("info") )
1525 config.writeEntry( "Icon", QString::fromLatin1("text-x-texinfo") );
1526 else if ( protocol == QLatin1String("mailto") ) // sven:
1527 config.writeEntry( "Icon", QString::fromLatin1("internet-mail") ); // added mailto: support
1528 else
1529 config.writeEntry( "Icon", QString::fromLatin1("unknown") );
1530 config.sync();
1531 files.erase( files.begin() ); // done with this one, move on
1532 m_processedFiles++;
1533 //emit processedAmount( this, KJob::Files, m_processedFiles );
1534 copyNextFile();
1535 return 0;
1536 }
1537 else
1538 {
1539 kDebug(7007) << "ERR_CANNOT_OPEN_FOR_WRITING";
1540 q->setError( ERR_CANNOT_OPEN_FOR_WRITING );
1541 q->setErrorText( uDest.toLocalFile() );
1542 q->emitResult();
1543 return 0;
1544 }
1545 } else {
1546 // Todo: not show "link" on remote dirs if the src urls are not from the same protocol+host+...
1547 q->setError( ERR_CANNOT_SYMLINK );
1548 q->setErrorText( uDest.prettyUrl() );
1549 q->emitResult();
1550 return 0;
1551 }
1552 }
1553}
1554
1555void CopyJobPrivate::copyNextFile()
1556{
1557 Q_Q(CopyJob);
1558 bool bCopyFile = false;
1559 //kDebug(7007);
1560 // Take the first file in the list
1561 QList<CopyInfo>::Iterator it = files.begin();
1562 // Is this URL on the skip list ?
1563 while (it != files.end() && !bCopyFile)
1564 {
1565 const QString destFile = (*it).uDest.path();
1566 bCopyFile = !shouldSkip( destFile );
1567 if ( !bCopyFile ) {
1568 files.erase( it );
1569 it = files.begin();
1570 }
1571 }
1572
1573 if (bCopyFile) // any file to create, finally ?
1574 {
1575 //kDebug()<<"preparing to copy"<<(*it).uSource<<(*it).size<<m_freeSpace;
1576 if (m_freeSpace != (KIO::filesize_t)-1 && (*it).size != (KIO::filesize_t)-1) {
1577 if (m_freeSpace < (*it).size) {
1578 q->setError( ERR_DISK_FULL );
1579 q->emitResult();
1580 return;
1581 }
1582 //TODO check if dst mount is msdos and (*it).size exceeds it's limits
1583 }
1584
1585 const KUrl& uSource = (*it).uSource;
1586 const KUrl& uDest = (*it).uDest;
1587 // Do we set overwrite ?
1588 bool bOverwrite;
1589 const QString destFile = uDest.path();
1590 // kDebug(7007) << "copying" << destFile;
1591 if ( uDest == uSource )
1592 bOverwrite = false;
1593 else
1594 bOverwrite = shouldOverwriteFile( destFile );
1595
1596 m_bCurrentOperationIsLink = false;
1597 KIO::Job * newjob = 0;
1598 if ( m_mode == CopyJob::Link ) {
1599 // User requested that a symlink be made
1600 const JobFlags flags = bOverwrite ? Overwrite : DefaultFlags;
1601 newjob = linkNextFile(uSource, uDest, flags);
1602 if (!newjob)
1603 return;
1604 } else if ( !(*it).linkDest.isEmpty() &&
1605 (uSource.protocol() == uDest.protocol()) &&
1606 (uSource.host() == uDest.host()) &&
1607 (uSource.port() == uDest.port()) &&
1608 (uSource.user() == uDest.user()) &&
1609 (uSource.pass() == uDest.pass()))
1610 // Copying a symlink - only on the same protocol/host/etc. (#5601, downloading an FTP file through its link),
1611 {
1612 const JobFlags flags = bOverwrite ? Overwrite : DefaultFlags;
1613 KIO::SimpleJob *newJob = KIO::symlink( (*it).linkDest, uDest, flags | HideProgressInfo /*no GUI*/ );
1614 Scheduler::setJobPriority(newJob, 1);
1615 newjob = newJob;
1616 //kDebug(7007) << "Linking target=" << (*it).linkDest << "link=" << uDest;
1617 m_currentSrcURL = KUrl( (*it).linkDest );
1618 m_currentDestURL = uDest;
1619 m_bURLDirty = true;
1620 //emit linking( this, (*it).linkDest, uDest );
1621 //Observer::self()->slotCopying( this, m_currentSrcURL, uDest ); // should be slotLinking perhaps
1622 m_bCurrentOperationIsLink = true;
1623 // NOTE: if we are moving stuff, the deletion of the source will be done in slotResultCopyingFiles
1624 } else if (m_mode == CopyJob::Move) // Moving a file
1625 {
1626 JobFlags flags = bOverwrite ? Overwrite : DefaultFlags;
1627 KIO::FileCopyJob * moveJob = KIO::file_move( uSource, uDest, (*it).permissions, flags | HideProgressInfo/*no GUI*/ );
1628 moveJob->setSourceSize( (*it).size );
1629 if ((*it).mtime != -1) {
1630 moveJob->setModificationTime( QDateTime::fromTime_t( (*it).mtime ) ); // #55804
1631 }
1632 newjob = moveJob;
1633 //kDebug(7007) << "Moving" << uSource << "to" << uDest;
1634 //emit moving( this, uSource, uDest );
1635 m_currentSrcURL=uSource;
1636 m_currentDestURL=uDest;
1637 m_bURLDirty = true;
1638 //Observer::self()->slotMoving( this, uSource, uDest );
1639 }
1640 else // Copying a file
1641 {
1642 // If source isn't local and target is local, we ignore the original permissions
1643 // Otherwise, files downloaded from HTTP end up with -r--r--r--
1644 bool remoteSource = !KProtocolManager::supportsListing(uSource);
1645 int permissions = (*it).permissions;
1646 if ( m_defaultPermissions || ( remoteSource && uDest.isLocalFile() ) )
1647 permissions = -1;
1648 JobFlags flags = bOverwrite ? Overwrite : DefaultFlags;
1649 KIO::FileCopyJob * copyJob = KIO::file_copy( uSource, uDest, permissions, flags | HideProgressInfo/*no GUI*/ );
1650 copyJob->setParentJob( q ); // in case of rename dialog
1651 copyJob->setSourceSize( (*it).size );
1652 if ((*it).mtime != -1) {
1653 copyJob->setModificationTime( QDateTime::fromTime_t( (*it).mtime ) );
1654 }
1655 newjob = copyJob;
1656 //kDebug(7007) << "Copying" << uSource << "to" << uDest;
1657 m_currentSrcURL=uSource;
1658 m_currentDestURL=uDest;
1659 m_bURLDirty = true;
1660 }
1661 q->addSubjob(newjob);
1662 q->connect( newjob, SIGNAL(processedSize(KJob*,qulonglong)),
1663 SLOT(slotProcessedSize(KJob*,qulonglong)) );
1664 q->connect( newjob, SIGNAL(totalSize(KJob*,qulonglong)),
1665 SLOT(slotTotalSize(KJob*,qulonglong)) );
1666 }
1667 else
1668 {
1669 // We're done
1670 //kDebug(7007) << "copyNextFile finished";
1671 deleteNextDir();
1672 }
1673}
1674
1675void CopyJobPrivate::deleteNextDir()
1676{
1677 Q_Q(CopyJob);
1678 if ( m_mode == CopyJob::Move && !dirsToRemove.isEmpty() ) // some dirs to delete ?
1679 {
1680 state = STATE_DELETING_DIRS;
1681 m_bURLDirty = true;
1682 // Take first dir to delete out of list - last ones first !
1683 KUrl::List::Iterator it = --dirsToRemove.end();
1684 SimpleJob *job = KIO::rmdir( *it );
1685 Scheduler::setJobPriority(job, 1);
1686 dirsToRemove.erase(it);
1687 q->addSubjob( job );
1688 }
1689 else
1690 {
1691 // This step is done, move on
1692 state = STATE_SETTING_DIR_ATTRIBUTES;
1693 m_directoriesCopiedIterator = m_directoriesCopied.constBegin();
1694 setNextDirAttribute();
1695 }
1696}
1697
1698void CopyJobPrivate::setNextDirAttribute()
1699{
1700 Q_Q(CopyJob);
1701 while (m_directoriesCopiedIterator != m_directoriesCopied.constEnd() &&
1702 (*m_directoriesCopiedIterator).mtime == -1) {
1703 ++m_directoriesCopiedIterator;
1704 }
1705 if ( m_directoriesCopiedIterator != m_directoriesCopied.constEnd() ) {
1706 const KUrl url = (*m_directoriesCopiedIterator).uDest;
1707 const time_t mtime = (*m_directoriesCopiedIterator).mtime;
1708 const QDateTime dt = QDateTime::fromTime_t(mtime);
1709 ++m_directoriesCopiedIterator;
1710
1711 KIO::SimpleJob *job = KIO::setModificationTime( url, dt );
1712 Scheduler::setJobPriority(job, 1);
1713 q->addSubjob( job );
1714
1715
1716#if 0 // ifdef Q_OS_UNIX
1717 // TODO: can be removed now. Or reintroduced as a fast path for local files
1718 // if launching even more jobs as done above is a performance problem.
1719 //
1720 QLinkedList<CopyInfo>::const_iterator it = m_directoriesCopied.constBegin();
1721 for ( ; it != m_directoriesCopied.constEnd() ; ++it ) {
1722 const KUrl& url = (*it).uDest;
1723 if ( url.isLocalFile() && (*it).mtime != (time_t)-1 ) {
1724 KDE_struct_stat statbuf;
1725 if (KDE::lstat(url.path(), &statbuf) == 0) {
1726 struct utimbuf utbuf;
1727 utbuf.actime = statbuf.st_atime; // access time, unchanged
1728 utbuf.modtime = (*it).mtime; // modification time
1729 utime( path, &utbuf );
1730 }
1731
1732 }
1733 }
1734 m_directoriesCopied.clear();
1735 // but then we need to jump to the else part below. Maybe with a recursive call?
1736#endif
1737 } else {
1738 if (m_reportTimer)
1739 m_reportTimer->stop();
1740 --m_processedFiles; // undo the "start at 1" hack
1741 slotReport(); // display final numbers, important if progress dialog stays up
1742
1743 q->emitResult();
1744 }
1745}
1746
1747void CopyJob::emitResult()
1748{
1749 Q_D(CopyJob);
1750 // Before we go, tell the world about the changes that were made.
1751 // Even if some error made us abort midway, we might still have done
1752 // part of the job so we better update the views! (#118583)
1753 if (!d->m_bOnlyRenames) {
1754 // If only renaming happened, KDirNotify::FileRenamed was emitted by the rename jobs
1755 KUrl url(d->m_globalDest);
1756 if (d->m_globalDestinationState != DEST_IS_DIR || d->m_asMethod)
1757 url.setPath(url.directory());
1758 //kDebug(7007) << "KDirNotify'ing FilesAdded" << url;
1759 org::kde::KDirNotify::emitFilesAdded( url.url() );
1760
1761 if (d->m_mode == CopyJob::Move && !d->m_successSrcList.isEmpty()) {
1762 kDebug(7007) << "KDirNotify'ing FilesRemoved" << d->m_successSrcList.toStringList();
1763 org::kde::KDirNotify::emitFilesRemoved(d->m_successSrcList.toStringList());
1764 }
1765 }
1766
1767 // Re-enable watching on the dirs that held the deleted/moved files
1768 if (d->m_mode == CopyJob::Move) {
1769 for (QSet<QString>::const_iterator it = d->m_parentDirs.constBegin() ; it != d->m_parentDirs.constEnd() ; ++it)
1770 KDirWatch::self()->restartDirScan( *it );
1771 }
1772 Job::emitResult();
1773}
1774
1775void CopyJobPrivate::slotProcessedSize( KJob*, qulonglong data_size )
1776{
1777 Q_Q(CopyJob);
1778 //kDebug(7007) << data_size;
1779 m_fileProcessedSize = data_size;
1780 q->setProcessedAmount(KJob::Bytes, m_processedSize + m_fileProcessedSize);
1781
1782 if ( m_processedSize + m_fileProcessedSize > m_totalSize )
1783 {
1784 // Example: download any attachment from bugs.kde.org
1785 m_totalSize = m_processedSize + m_fileProcessedSize;
1786 //kDebug(7007) << "Adjusting m_totalSize to" << m_totalSize;
1787 q->setTotalAmount(KJob::Bytes, m_totalSize); // safety
1788 }
1789 //kDebug(7007) << "emit processedSize" << (unsigned long) (m_processedSize + m_fileProcessedSize);
1790 q->setProcessedAmount(KJob::Bytes, m_processedSize + m_fileProcessedSize);
1791}
1792
1793void CopyJobPrivate::slotTotalSize( KJob*, qulonglong size )
1794{
1795 Q_Q(CopyJob);
1796 //kDebug(7007) << size;
1797 // Special case for copying a single file
1798 // This is because some protocols don't implement stat properly
1799 // (e.g. HTTP), and don't give us a size in some cases (redirection)
1800 // so we'd rather rely on the size given for the transfer
1801 if ( m_bSingleFileCopy && size != m_totalSize)
1802 {
1803 //kDebug(7007) << "slotTotalSize: updating totalsize to" << size;
1804 m_totalSize = size;
1805 q->setTotalAmount(KJob::Bytes, size);
1806 }
1807}
1808
1809void CopyJobPrivate::slotResultDeletingDirs( KJob * job )
1810{
1811 Q_Q(CopyJob);
1812 if (job->error()) {
1813 // Couldn't remove directory. Well, perhaps it's not empty
1814 // because the user pressed Skip for a given file in it.
1815 // Let's not display "Could not remove dir ..." for each of those dir !
1816 } else {
1817 m_successSrcList.append(static_cast<KIO::SimpleJob*>(job)->url());
1818 }
1819 q->removeSubjob( job );
1820 assert( !q->hasSubjobs() );
1821 deleteNextDir();
1822}
1823
1824void CopyJobPrivate::slotResultSettingDirAttributes( KJob * job )
1825{
1826 Q_Q(CopyJob);
1827 if (job->error())
1828 {
1829 // Couldn't set directory attributes. Ignore the error, it can happen
1830 // with inferior file systems like VFAT.
1831 // Let's not display warnings for each dir like "cp -a" does.
1832 }
1833 q->removeSubjob( job );
1834 assert( !q->hasSubjobs() );
1835 setNextDirAttribute();
1836}
1837
1838// We were trying to do a direct renaming, before even stat'ing
1839void CopyJobPrivate::slotResultRenaming( KJob* job )
1840{
1841 Q_Q(CopyJob);
1842 int err = job->error();
1843 const QString errText = job->errorText();
1844 // Merge metadata from subjob
1845 KIO::Job* kiojob = dynamic_cast<KIO::Job*>(job);
1846 Q_ASSERT(kiojob);
1847 m_incomingMetaData += kiojob->metaData();
1848 q->removeSubjob( job );
1849 assert ( !q->hasSubjobs() );
1850 // Determine dest again
1851 KUrl dest = m_dest;
1852 if ( destinationState == DEST_IS_DIR && !m_asMethod )
1853 dest.addPath( m_currentSrcURL.fileName() );
1854 if ( err )
1855 {
1856 // Direct renaming didn't work. Try renaming to a temp name,
1857 // this can help e.g. when renaming 'a' to 'A' on a VFAT partition.
1858 // In that case it's the _same_ dir, we don't want to copy+del (data loss!)
1859 if ( m_currentSrcURL.isLocalFile() && m_currentSrcURL.url(KUrl::RemoveTrailingSlash) != dest.url(KUrl::RemoveTrailingSlash) &&
1860 m_currentSrcURL.url(KUrl::RemoveTrailingSlash).toLower() == dest.url(KUrl::RemoveTrailingSlash).toLower() &&
1861 ( err == ERR_FILE_ALREADY_EXIST ||
1862 err == ERR_DIR_ALREADY_EXIST ||
1863 err == ERR_IDENTICAL_FILES ) )
1864 {
1865 kDebug(7007) << "Couldn't rename directly, dest already exists. Detected special case of lower/uppercase renaming in same dir, try with 2 rename calls";
1866 const QString _src( m_currentSrcURL.toLocalFile() );
1867 const QString _dest( dest.toLocalFile() );
1868 const QString _tmpPrefix = m_currentSrcURL.directory(KUrl::ObeyTrailingSlash|KUrl::AppendTrailingSlash);
1869 KTemporaryFile tmpFile;
1870 tmpFile.setPrefix(_tmpPrefix);
1871 const bool openOk = tmpFile.open();
1872 if (!openOk) {
1873 kWarning(7007) << "Couldn't open temp file in" << _tmpPrefix;
1874 } else {
1875 const QString _tmp( tmpFile.fileName() );
1876 tmpFile.close();
1877 tmpFile.remove();
1878 kDebug(7007) << "KTemporaryFile using" << _tmp << "as intermediary";
1879 if (KDE::rename( _src, _tmp ) == 0) {
1880 //kDebug(7007) << "Renaming" << _src << "to" << _tmp << "succeeded";
1881 if (!QFile::exists( _dest ) && KDE::rename(_tmp, _dest) == 0) {
1882 err = 0;
1883 org::kde::KDirNotify::emitFileRenamed(m_currentSrcURL.url(), dest.url());
1884 } else {
1885 kDebug(7007) << "Didn't manage to rename" << _tmp << "to" << _dest << ", reverting";
1886 // Revert back to original name!
1887 if (KDE::rename( _tmp, _src ) != 0) {
1888 kError(7007) << "Couldn't rename" << _tmp << "back to" << _src << '!';
1889 // Severe error, abort
1890 q->Job::slotResult(job); // will set the error and emit result(this)
1891 return;
1892 }
1893 }
1894 } else {
1895 kDebug(7007) << "mv" << _src << _tmp << "failed:" << strerror(errno);
1896 }
1897 }
1898 }
1899 }
1900 if ( err )
1901 {
1902 // This code is similar to CopyJobPrivate::slotResultConflictCopyingFiles
1903 // but here it's about the base src url being moved/renamed
1904 // (m_currentSrcURL) and its dest (m_dest), not about a single file.
1905 // It also means we already stated the dest, here.
1906 // On the other hand we haven't stated the src yet (we skipped doing it
1907 // to save time, since it's not necessary to rename directly!)...
1908
1909 // Existing dest?
1910 if ( err == ERR_DIR_ALREADY_EXIST ||
1911 err == ERR_FILE_ALREADY_EXIST ||
1912 err == ERR_IDENTICAL_FILES )
1913 {
1914 // Should we skip automatically ?
1915 bool isDir = (err == ERR_DIR_ALREADY_EXIST); // ## technically, isDir means "source is dir", not "dest is dir" #######
1916 if ((isDir && m_bAutoSkipDirs) || (!isDir && m_bAutoSkipFiles)) {
1917 // Move on to next source url
1918 skipSrc(isDir);
1919 return;
1920 } else if ((isDir && m_bOverwriteAllDirs) || (!isDir && m_bOverwriteAllFiles)) {
1921 ; // nothing to do, stat+copy+del will overwrite
1922 } else if ((isDir && m_bAutoRenameDirs) || (!isDir && m_bAutoRenameFiles)) {
1923 KUrl destDirectory(m_currentDestURL); // dest including filename
1924 destDirectory.setPath(destDirectory.directory());
1925 const QString newName = KIO::RenameDialog::suggestName(destDirectory, m_currentDestURL.fileName());
1926
1927 m_dest.setPath(m_currentDestURL.path());
1928 m_dest.setFileName(newName);
1929 KIO::Job* job = KIO::stat(m_dest, StatJob::DestinationSide, 2, KIO::HideProgressInfo);
1930 state = STATE_STATING;
1931 destinationState = DEST_NOT_STATED;
1932 q->addSubjob(job);
1933 return;
1934 } else if ( q->isInteractive() ) {
1935 QString newPath;
1936 // we lack mtime info for both the src (not stated)
1937 // and the dest (stated but this info wasn't stored)
1938 // Let's do it for local files, at least
1939 KIO::filesize_t sizeSrc = (KIO::filesize_t) -1;
1940 KIO::filesize_t sizeDest = (KIO::filesize_t) -1;
1941 time_t ctimeSrc = (time_t) -1;
1942 time_t ctimeDest = (time_t) -1;
1943 time_t mtimeSrc = (time_t) -1;
1944 time_t mtimeDest = (time_t) -1;
1945
1946 bool destIsDir = err == ERR_DIR_ALREADY_EXIST;
1947
1948 // ## TODO we need to stat the source using KIO::stat
1949 // so that this code is properly network-transparent.
1950
1951 KDE_struct_stat stat_buf;
1952 if ( m_currentSrcURL.isLocalFile() &&
1953 KDE::stat(m_currentSrcURL.toLocalFile(), &stat_buf) == 0 ) {
1954 sizeSrc = stat_buf.st_size;
1955 ctimeSrc = stat_buf.st_ctime;
1956 mtimeSrc = stat_buf.st_mtime;
1957 isDir = S_ISDIR(stat_buf.st_mode);
1958 }
1959 if ( dest.isLocalFile() &&
1960 KDE::stat(dest.toLocalFile(), &stat_buf) == 0 ) {
1961 sizeDest = stat_buf.st_size;
1962 ctimeDest = stat_buf.st_ctime;
1963 mtimeDest = stat_buf.st_mtime;
1964 destIsDir = S_ISDIR(stat_buf.st_mode);
1965 }
1966
1967 // If src==dest, use "overwrite-itself"
1968 RenameDialog_Mode mode = ( m_currentSrcURL == dest ) ? M_OVERWRITE_ITSELF : M_OVERWRITE;
1969 if (!isDir && destIsDir) {
1970 // We can't overwrite a dir with a file.
1971 mode = (RenameDialog_Mode) 0;
1972 }
1973
1974 if ( m_srcList.count() > 1 )
1975 mode = (RenameDialog_Mode) ( mode | M_MULTI | M_SKIP );
1976 if (destIsDir)
1977 mode = (RenameDialog_Mode) ( mode | M_ISDIR );
1978
1979 if (m_reportTimer)
1980 m_reportTimer->stop();
1981
1982 RenameDialog_Result r = q->ui()->askFileRename(
1983 q,
1984 err != ERR_DIR_ALREADY_EXIST ? i18n("File Already Exists") : i18n("Already Exists as Folder"),
1985 m_currentSrcURL.url(),
1986 dest.url(),
1987 mode, newPath,
1988 sizeSrc, sizeDest,
1989 ctimeSrc, ctimeDest,
1990 mtimeSrc, mtimeDest );
1991
1992 if (m_reportTimer)
1993 m_reportTimer->start(REPORT_TIMEOUT);
1994
1995 switch ( r )
1996 {
1997 case R_CANCEL:
1998 {
1999 q->setError( ERR_USER_CANCELED );
2000 q->emitResult();
2001 return;
2002 }
2003 case R_AUTO_RENAME:
2004 if (isDir) {
2005 m_bAutoRenameDirs = true;
2006 }
2007 else {
2008 m_bAutoRenameFiles = true;
2009 }
2010 // fall through
2011 case R_RENAME:
2012 {
2013 // Set m_dest to the chosen destination
2014 // This is only for this src url; the next one will revert to m_globalDest
2015 m_dest.setPath( newPath );
2016 KIO::Job* job = KIO::stat( m_dest, StatJob::DestinationSide, 2, KIO::HideProgressInfo );
2017 state = STATE_STATING;
2018 destinationState = DEST_NOT_STATED;
2019 q->addSubjob(job);
2020 return;
2021 }
2022 case R_AUTO_SKIP:
2023 if (isDir)
2024 m_bAutoSkipDirs = true;
2025 else
2026 m_bAutoSkipFiles = true;
2027 // fall through
2028 case R_SKIP:
2029 // Move on to next url
2030 skipSrc(isDir);
2031 return;
2032 case R_OVERWRITE_ALL:
2033 if (destIsDir)
2034 m_bOverwriteAllDirs = true;
2035 else
2036 m_bOverwriteAllFiles = true;
2037 break;
2038 case R_OVERWRITE:
2039 // Add to overwrite list
2040 // Note that we add dest, not m_dest.
2041 // This ensures that when moving several urls into a dir (m_dest),
2042 // we only overwrite for the current one, not for all.
2043 // When renaming a single file (m_asMethod), it makes no difference.
2044 kDebug(7007) << "adding to overwrite list: " << dest.path();
2045 m_overwriteList.insert( dest.path() );
2046 break;
2047 default:
2048 //assert( 0 );
2049 break;
2050 }
2051 } else if ( err != KIO::ERR_UNSUPPORTED_ACTION ) {
2052 // Dest already exists, and job is not interactive -> abort with error
2053 q->setError( err );
2054 q->setErrorText( errText );
2055 q->emitResult();
2056 return;
2057 }
2058 } else if ( err != KIO::ERR_UNSUPPORTED_ACTION ) {
2059 kDebug(7007) << "Couldn't rename" << m_currentSrcURL << "to" << dest << ", aborting";
2060 q->setError( err );
2061 q->setErrorText( errText );
2062 q->emitResult();
2063 return;
2064 }
2065 kDebug(7007) << "Couldn't rename" << m_currentSrcURL << "to" << dest << ", reverting to normal way, starting with stat";
2066 //kDebug(7007) << "KIO::stat on" << m_currentSrcURL;
2067 KIO::Job* job = KIO::stat( m_currentSrcURL, StatJob::SourceSide, 2, KIO::HideProgressInfo );
2068 state = STATE_STATING;
2069 q->addSubjob(job);
2070 m_bOnlyRenames = false;
2071 }
2072 else
2073 {
2074 kDebug(7007) << "Renaming succeeded, move on";
2075 ++m_processedFiles;
2076 emit q->copyingDone( q, *m_currentStatSrc, dest, -1 /*mtime unknown, and not needed*/, true, true );
2077 m_successSrcList.append(*m_currentStatSrc);
2078 statNextSrc();
2079 }
2080}
2081
2082void CopyJob::slotResult( KJob *job )
2083{
2084 Q_D(CopyJob);
2085 //kDebug(7007) << "d->state=" << (int) d->state;
2086 // In each case, what we have to do is :
2087 // 1 - check for errors and treat them
2088 // 2 - removeSubjob(job);
2089 // 3 - decide what to do next
2090
2091 switch ( d->state ) {
2092 case STATE_STATING: // We were trying to stat a src url or the dest
2093 d->slotResultStating( job );
2094 break;
2095 case STATE_RENAMING: // We were trying to do a direct renaming, before even stat'ing
2096 {
2097 d->slotResultRenaming( job );
2098 break;
2099 }
2100 case STATE_LISTING: // recursive listing finished
2101 //kDebug(7007) << "totalSize:" << (unsigned int) d->m_totalSize << "files:" << d->files.count() << "d->dirs:" << d->dirs.count();
2102 // Was there an error ?
2103 if (job->error())
2104 {
2105 Job::slotResult( job ); // will set the error and emit result(this)
2106 return;
2107 }
2108
2109 removeSubjob( job );
2110 assert ( !hasSubjobs() );
2111
2112 d->statNextSrc();
2113 break;
2114 case STATE_CREATING_DIRS:
2115 d->slotResultCreatingDirs( job );
2116 break;
2117 case STATE_CONFLICT_CREATING_DIRS:
2118 d->slotResultConflictCreatingDirs( job );
2119 break;
2120 case STATE_COPYING_FILES:
2121 d->slotResultCopyingFiles( job );
2122 break;
2123 case STATE_CONFLICT_COPYING_FILES:
2124 d->slotResultConflictCopyingFiles( job );
2125 break;
2126 case STATE_DELETING_DIRS:
2127 d->slotResultDeletingDirs( job );
2128 break;
2129 case STATE_SETTING_DIR_ATTRIBUTES:
2130 d->slotResultSettingDirAttributes( job );
2131 break;
2132 default:
2133 assert( 0 );
2134 }
2135}
2136
2137void KIO::CopyJob::setDefaultPermissions( bool b )
2138{
2139 d_func()->m_defaultPermissions = b;
2140}
2141
2142KIO::CopyJob::CopyMode KIO::CopyJob::operationMode() const
2143{
2144 return d_func()->m_mode;
2145}
2146
2147void KIO::CopyJob::setAutoSkip(bool autoSkip)
2148{
2149 d_func()->m_bAutoSkipFiles = autoSkip;
2150 d_func()->m_bAutoSkipDirs = autoSkip;
2151}
2152
2153void KIO::CopyJob::setAutoRename(bool autoRename)
2154{
2155 d_func()->m_bAutoRenameFiles = autoRename;
2156 d_func()->m_bAutoRenameDirs = autoRename;
2157}
2158
2159void KIO::CopyJob::setWriteIntoExistingDirectories(bool overwriteAll) // #65926
2160{
2161 d_func()->m_bOverwriteAllDirs = overwriteAll;
2162}
2163
2164CopyJob *KIO::copy(const KUrl& src, const KUrl& dest, JobFlags flags)
2165{
2166 //kDebug(7007) << "src=" << src << "dest=" << dest;
2167 KUrl::List srcList;
2168 srcList.append( src );
2169 return CopyJobPrivate::newJob(srcList, dest, CopyJob::Copy, false, flags);
2170}
2171
2172CopyJob *KIO::copyAs(const KUrl& src, const KUrl& dest, JobFlags flags)
2173{
2174 //kDebug(7007) << "src=" << src << "dest=" << dest;
2175 KUrl::List srcList;
2176 srcList.append( src );
2177 return CopyJobPrivate::newJob(srcList, dest, CopyJob::Copy, true, flags);
2178}
2179
2180CopyJob *KIO::copy( const KUrl::List& src, const KUrl& dest, JobFlags flags )
2181{
2182 //kDebug(7007) << src << dest;
2183 return CopyJobPrivate::newJob(src, dest, CopyJob::Copy, false, flags);
2184}
2185
2186CopyJob *KIO::move(const KUrl& src, const KUrl& dest, JobFlags flags)
2187{
2188 //kDebug(7007) << src << dest;
2189 KUrl::List srcList;
2190 srcList.append( src );
2191 CopyJob* job = CopyJobPrivate::newJob(srcList, dest, CopyJob::Move, false, flags);
2192 ClipboardUpdater::create(job, ClipboardUpdater::UpdateContent);
2193 return job;
2194}
2195
2196CopyJob *KIO::moveAs(const KUrl& src, const KUrl& dest, JobFlags flags)
2197{
2198 //kDebug(7007) << src << dest;
2199 KUrl::List srcList;
2200 srcList.append( src );
2201 CopyJob* job = CopyJobPrivate::newJob(srcList, dest, CopyJob::Move, true, flags);
2202 ClipboardUpdater::create(job, ClipboardUpdater::UpdateContent);
2203 return job;
2204}
2205
2206CopyJob *KIO::move( const KUrl::List& src, const KUrl& dest, JobFlags flags)
2207{
2208 //kDebug(7007) << src << dest;
2209 CopyJob* job = CopyJobPrivate::newJob(src, dest, CopyJob::Move, false, flags);
2210 ClipboardUpdater::create(job, ClipboardUpdater::UpdateContent);
2211 return job;
2212}
2213
2214CopyJob *KIO::link(const KUrl& src, const KUrl& destDir, JobFlags flags)
2215{
2216 KUrl::List srcList;
2217 srcList.append( src );
2218 return CopyJobPrivate::newJob(srcList, destDir, CopyJob::Link, false, flags);
2219}
2220
2221CopyJob *KIO::link(const KUrl::List& srcList, const KUrl& destDir, JobFlags flags)
2222{
2223 return CopyJobPrivate::newJob(srcList, destDir, CopyJob::Link, false, flags);
2224}
2225
2226CopyJob *KIO::linkAs(const KUrl& src, const KUrl& destDir, JobFlags flags )
2227{
2228 KUrl::List srcList;
2229 srcList.append( src );
2230 return CopyJobPrivate::newJob(srcList, destDir, CopyJob::Link, false, flags);
2231}
2232
2233CopyJob *KIO::trash(const KUrl& src, JobFlags flags)
2234{
2235 KUrl::List srcList;
2236 srcList.append( src );
2237 return CopyJobPrivate::newJob(srcList, KUrl( "trash:/" ), CopyJob::Move, false, flags);
2238}
2239
2240CopyJob *KIO::trash(const KUrl::List& srcList, JobFlags flags)
2241{
2242 return CopyJobPrivate::newJob(srcList, KUrl( "trash:/" ), CopyJob::Move, false, flags);
2243}
2244
2245#include "copyjob.moc"
KCompositeJob::slotResult
virtual void slotResult(KJob *job)
KCompositeJob::hasSubjobs
bool hasSubjobs()
KConfigGroup
KDesktopFile
KDirLister::cachedItemForUrl
static KFileItem cachedItemForUrl(const KUrl &url)
Return the KFileItem for the given URL, if we listed it recently and it's still in the cache - which ...
Definition: kdirlister.cpp:2788
KDirWatch::stopDirScan
bool stopDirScan(const QString &path)
KDirWatch::self
static KDirWatch * self()
KDirWatch::restartDirScan
bool restartDirScan(const QString &path)
KFileItem
A KFileItem is a generic class to handle a file, local or remote.
Definition: kfileitem.h:46
KFileItem::mostLocalUrl
KUrl mostLocalUrl(bool &local) const
Tries to give a local URL for this file item if possible.
Definition: kfileitem.cpp:1470
KFileItem::isNull
bool isNull() const
Return true if default-constructed.
Definition: kfileitem.cpp:1714
KFileItem::entry
KIO::UDSEntry entry() const
Returns the UDS entry.
Definition: kfileitem.cpp:1672
KIO::ClipboardUpdater::UpdateContent
@ UpdateContent
Definition: clipboardupdater_p.h:55
KIO::ClipboardUpdater::create
static ClipboardUpdater * create(Job *job, Mode mode)
Returns an instance of clipboard updater if QApplication::type() does not return a tty.
Definition: clipboardupdater.cpp:162
KIO::CopyJob
CopyJob is used to move, copy or symlink files and directories.
Definition: copyjob.h:65
KIO::CopyJob::srcUrls
KUrl::List srcUrls() const
Returns the list of source URLs.
Definition: copyjob.cpp:267
KIO::CopyJob::CopyMode
CopyMode
Defines the mode of the operation.
Definition: copyjob.h:73
KIO::CopyJob::Move
@ Move
Definition: copyjob.h:73
KIO::CopyJob::Copy
@ Copy
Definition: copyjob.h:73
KIO::CopyJob::Link
@ Link
Definition: copyjob.h:73
KIO::CopyJob::doSuspend
virtual bool doSuspend()
Reimplemented for internal reasons.
Definition: copyjob.cpp:486
KIO::CopyJob::setDefaultPermissions
void setDefaultPermissions(bool b)
By default the permissions of the copied files will be those of the source files.
Definition: copyjob.cpp:2137
KIO::CopyJob::setWriteIntoExistingDirectories
void setWriteIntoExistingDirectories(bool overwriteAllDirs)
Reuse any directory that already exists, instead of the default behavior (interactive mode: showing a...
Definition: copyjob.cpp:2159
KIO::CopyJob::~CopyJob
virtual ~CopyJob()
Definition: copyjob.cpp:263
KIO::CopyJob::operationMode
CopyMode operationMode() const
Returns the mode of the operation (copy, move, or link), depending on whether KIO::copy(),...
Definition: copyjob.cpp:2142
KIO::CopyJob::CopyJob
CopyJob(CopyJobPrivate &dd)
Definition: copyjob.cpp:255
KIO::CopyJob::setAutoRename
void setAutoRename(bool autoRename)
Rename files automatically when the destination already exists, instead of the default behavior (inte...
Definition: copyjob.cpp:2153
KIO::CopyJob::slotResult
virtual void slotResult(KJob *job)
Definition: copyjob.cpp:2082
KIO::CopyJob::setAutoSkip
void setAutoSkip(bool autoSkip)
Skip copying or moving any file when the destination already exists, instead of the default behavior ...
Definition: copyjob.cpp:2147
KIO::CopyJob::destUrl
KUrl destUrl() const
Returns the destination URL.
Definition: copyjob.cpp:272
KIO::CopyJob::emitResult
void emitResult()
Definition: copyjob.cpp:1747
KIO::FileCopyJob
The FileCopyJob copies data from one place to another.
Definition: jobclasses.h:856
KIO::FileCopyJob::setModificationTime
void setModificationTime(const QDateTime &mtime)
Sets the modification time of the file.
Definition: job.cpp:2076
KIO::FileCopyJob::setSourceSize
void setSourceSize(KIO::filesize_t size)
If you know the size of the source file, call this method to inform this job.
Definition: job.cpp:2068
KIO::JobPrivate
Definition: job_p.h:40
KIO::JobUiDelegate
A UI delegate tuned to be used with KIO Jobs.
Definition: jobuidelegate.h:40
KIO::Job
The base class for all jobs.
Definition: jobclasses.h:94
KIO::Job::setParentJob
void setParentJob(Job *parentJob)
Set the parent Job.
Definition: job.cpp:235
KIO::Job::errorString
QString errorString() const
Converts an error code and a non-i18n error message into an error message in the current language.
Definition: global.cpp:159
KIO::Job::doSuspend
virtual bool doSuspend()
Suspend this job.
Definition: job.cpp:186
KIO::Job::removeSubjob
virtual bool removeSubjob(KJob *job)
Mark a sub job as being done.
Definition: job.cpp:118
KIO::Job::metaData
MetaData metaData() const
Get meta data received from the slave.
Definition: job.cpp:248
KIO::Job::addMetaData
void addMetaData(const QString &key, const QString &value)
Add key/value pair to the meta data that is sent to the slave.
Definition: job.cpp:264
KIO::ListJob
A ListJob is allows you to get the get the content of a directory.
Definition: jobclasses.h:936
KIO::ListJob::setUnrestricted
void setUnrestricted(bool unrestricted)
Do not apply any KIOSK restrictions to this job.
Definition: job.cpp:2745
KIO::RenameDialog::suggestName
static QString suggestName(const KUrl &baseURL, const QString &oldName)
Given a directory path and a filename (which usually exists already), this function returns a suggest...
Definition: renamedialog.cpp:419
KIO::Scheduler::setJobPriority
static void setJobPriority(SimpleJob *job, int priority)
Changes the priority of job; jobs of the same priority run in the order in which they were created.
Definition: scheduler.cpp:805
KIO::SimpleJobPrivate::newJobNoUi
static SimpleJob * newJobNoUi(const KUrl &url, int command, const QByteArray &packedArgs)
Definition: job_p.h:201
KIO::SimpleJob
A simple job (one url and one command).
Definition: jobclasses.h:322
KIO::SimpleJob::url
const KUrl & url() const
Returns the SimpleJob's URL.
Definition: job.cpp:341
KIO::StatJob
A KIO job that retrieves information about a file or directory.
Definition: jobclasses.h:440
KIO::StatJob::DestinationSide
@ DestinationSide
Definition: jobclasses.h:447
KIO::StatJob::SourceSide
@ SourceSide
Definition: jobclasses.h:446
KIO::UDSEntry
Universal Directory Service.
Definition: udsentry.h:59
KIO::UDSEntry::stringValue
QString stringValue(uint field) const
Definition: udsentry.cpp:73
KIO::UDSEntry::numberValue
long long numberValue(uint field, long long defaultValue=0) const
Definition: udsentry.cpp:78
KIO::UDSEntry::isLink
bool isLink() const
Definition: udsentry.cpp:89
KIO::UDSEntry::UDS_CREATION_TIME
@ UDS_CREATION_TIME
The time the file was created.
Definition: udsentry.h:177
KIO::UDSEntry::UDS_URL
@ UDS_URL
An alternative URL (If different from the caption).
Definition: udsentry.h:190
KIO::UDSEntry::UDS_LINK_DEST
@ UDS_LINK_DEST
Name of the file where the link points to Allows to check for a symlink (don't use S_ISLNK !...
Definition: udsentry.h:184
KIO::UDSEntry::UDS_LOCAL_PATH
@ UDS_LOCAL_PATH
A local file path if the ioslave display files sitting on the local filesystem (but in another hierar...
Definition: udsentry.h:166
KIO::UDSEntry::UDS_MODIFICATION_TIME
@ UDS_MODIFICATION_TIME
The last time the file was modified.
Definition: udsentry.h:173
KIO::UDSEntry::UDS_SIZE
@ UDS_SIZE
Size of the file.
Definition: udsentry.h:144
KIO::UDSEntry::UDS_DISPLAY_NAME
@ UDS_DISPLAY_NAME
If set, contains the label to display instead of the 'real name' in UDS_NAME.
Definition: udsentry.h:211
KIO::UDSEntry::UDS_NAME
@ UDS_NAME
Filename - as displayed in directory listings etc.
Definition: udsentry.h:163
KIO::UDSEntry::UDS_ACCESS
@ UDS_ACCESS
Access permissions (part of the mode returned by stat)
Definition: udsentry.h:171
KIO::UDSEntry::contains
bool contains(uint field) const
check existence of a field
Definition: udsentry.cpp:118
KIO::UDSEntry::isDir
bool isDir() const
Definition: udsentry.cpp:84
KJobTrackerInterface::registerJob
virtual void registerJob(KJob *job)
KJob
KJob::Files
Files
KJob::Bytes
Bytes
KJob::Directories
Directories
KJob::errorString
virtual QString errorString() const
KJob::emitResult
void emitResult()
KJob::error
int error() const
KJob::errorText
QString errorText() const
KJob::setUiDelegate
void setUiDelegate(KJobUiDelegate *delegate)
KProtocolInfo::FileNameUsedForCopying
FileNameUsedForCopying
KProtocolManager::supportsDeleting
static bool supportsDeleting(const KUrl &url)
Returns whether the protocol can delete files/objects.
Definition: kprotocolmanager.cpp:1078
KProtocolManager::fileNameUsedForCopying
static KProtocolInfo::FileNameUsedForCopying fileNameUsedForCopying(const KUrl &url)
This setting defines the strategy to use for generating a filename, when copying a file or directory ...
Definition: kprotocolmanager.cpp:1161
KProtocolManager::canRenameToFile
static bool canRenameToFile(const KUrl &url)
Returns whether the protocol can rename (i.e.
Definition: kprotocolmanager.cpp:1143
KProtocolManager::supportsListing
static bool supportsListing(const KUrl &url)
Returns whether the protocol can list files/objects.
Definition: kprotocolmanager.cpp:1033
KProtocolManager::canRenameFromFile
static bool canRenameFromFile(const KUrl &url)
Returns whether the protocol can rename (i.e.
Definition: kprotocolmanager.cpp:1133
KTemporaryFile
KTemporaryFile::setPrefix
void setPrefix(const QString &prefix)
KUrl::List
KUrl
KUrl::prettyUrl
QString prettyUrl(AdjustPathOption trailing=LeaveTrailingSlash) const
KUrl::ObeyTrailingSlash
ObeyTrailingSlash
KUrl::AppendTrailingSlash
AppendTrailingSlash
KUrl::RemoveTrailingSlash
RemoveTrailingSlash
KUrl::AddTrailingSlash
AddTrailingSlash
KUrl::url
QString url(AdjustPathOption trailing=LeaveTrailingSlash) const
KUrl::path
QString path(AdjustPathOption trailing=LeaveTrailingSlash) const
KUrl::setFileName
void setFileName(const QString &_txt)
KUrl::directory
QString directory(const DirectoryOptions &options=IgnoreTrailingSlash) const
KUrl::isLocalFile
bool isLocalFile() const
KUrl::pass
QString pass() const
KUrl::user
QString user() const
KUrl::setPath
void setPath(const QString &path)
KUrl::setPass
void setPass(const QString &pass)
KUrl::fileName
QString fileName(const DirectoryOptions &options=IgnoreTrailingSlash) const
KUrl::protocol
QString protocol() const
KUrl::toLocalFile
QString toLocalFile(AdjustPathOption trailing=LeaveTrailingSlash) const
KUrl::addPath
void addPath(const QString &txt)
OrgKdeKDirNotifyInterface::emitFileRenamed
static void emitFileRenamed(const QString &src, const QString &dst)
Definition: kdirnotify.cpp:37
OrgKdeKDirNotifyInterface::emitFilesAdded
static void emitFilesAdded(const QString &directory)
Definition: kdirnotify.cpp:47
OrgKdeKDirNotifyInterface::emitFilesRemoved
static void emitFilesRemoved(const QStringList &fileList)
Definition: kdirnotify.cpp:57
OrgKdeKDirNotifyInterface::emitFileMoved
static void emitFileMoved(const QString &src, const QString &dst)
Definition: kdirnotify.cpp:42
QList
QSet
clipboardupdater_p.h
kio_resolve_local_urls
bool kio_resolve_local_urls
Definition: copyjob.cpp:297
CopyJobState
CopyJobState
States: STATE_STATING for the dest statCurrentSrc then does, for each src url: STATE_RENAMING if dire...
Definition: copyjob.cpp:85
STATE_SETTING_DIR_ATTRIBUTES
@ STATE_SETTING_DIR_ATTRIBUTES
Definition: copyjob.cpp:94
STATE_DELETING_DIRS
@ STATE_DELETING_DIRS
Definition: copyjob.cpp:93
STATE_STATING
@ STATE_STATING
Definition: copyjob.cpp:86
STATE_LISTING
@ STATE_LISTING
Definition: copyjob.cpp:88
STATE_CONFLICT_CREATING_DIRS
@ STATE_CONFLICT_CREATING_DIRS
Definition: copyjob.cpp:90
STATE_COPYING_FILES
@ STATE_COPYING_FILES
Definition: copyjob.cpp:91
STATE_RENAMING
@ STATE_RENAMING
Definition: copyjob.cpp:87
STATE_CREATING_DIRS
@ STATE_CREATING_DIRS
Definition: copyjob.cpp:89
STATE_CONFLICT_COPYING_FILES
@ STATE_CONFLICT_COPYING_FILES
Definition: copyjob.cpp:92
REPORT_TIMEOUT
#define REPORT_TIMEOUT
Definition: copyjob.cpp:60
DestinationState
DestinationState
Definition: copyjob.cpp:62
DEST_IS_FILE
@ DEST_IS_FILE
Definition: copyjob.cpp:65
DEST_IS_DIR
@ DEST_IS_DIR
Definition: copyjob.cpp:64
DEST_NOT_STATED
@ DEST_NOT_STATED
Definition: copyjob.cpp:63
DEST_DOESNT_EXIST
@ DEST_DOESNT_EXIST
Definition: copyjob.cpp:66
copyjob.h
deletejob.h
f
static quint32 f(DES_KEY *key, quint32 r, char *subkey)
Definition: des.cpp:378
kDebug
#define kDebug
kWarning
#define kWarning
job_p.h
KIO_ARGS
#define KIO_ARGS
Definition: job_p.h:34
jobuidelegate.h
kdebug.h
kdesktopfile.h
kdirlister.h
kdirnotify.h
kdirwatch.h
kdiskfreespaceinfo.h
kfileitem.h
timeout
int timeout
klocale.h
i18n
QString i18n(const char *text)
kprotocolmanager.h
ktemporaryfile.h
KDE::lstat
int lstat(const QString &path, KDE_struct_stat *buf)
utime
int utime(const QString &filename, struct utimbuf *buf)
KDE::stat
int stat(const QString &path, KDE_struct_stat *buf)
KDE::rename
int rename(const QString &in, const QString &out)
dirs
KStandardDirs * dirs()
config
KSharedConfigPtr config()
KIO
A namespace for KIO globals.
Definition: kbookmarkmenu.h:55
KIO::del
DeleteJob * del(const KUrl &src, JobFlags flags=DefaultFlags)
Delete a file or directory.
Definition: deletejob.cpp:492
KIO::SkipDialog_Result
SkipDialog_Result
Definition: skipdialog.h:29
KIO::S_SKIP
@ S_SKIP
Definition: skipdialog.h:29
KIO::S_AUTO_SKIP
@ S_AUTO_SKIP
Definition: skipdialog.h:29
KIO::move
CopyJob * move(const KUrl &src, const KUrl &dest, JobFlags flags=DefaultFlags)
Moves a file or directory src to the given destination dest.
Definition: copyjob.cpp:2186
KIO::trash
CopyJob * trash(const KUrl &src, JobFlags flags=DefaultFlags)
Trash a file or directory.
Definition: copyjob.cpp:2233
KIO::setModificationTime
SimpleJob * setModificationTime(const KUrl &url, const QDateTime &mtime)
Changes the modification time on a file or directory.
Definition: job.cpp:724
KIO::link
CopyJob * link(const KUrl &src, const KUrl &destDir, JobFlags flags=DefaultFlags)
Create a link.
Definition: copyjob.cpp:2214
KIO::moveAs
CopyJob * moveAs(const KUrl &src, const KUrl &dest, JobFlags flags=DefaultFlags)
Moves a file or directory src to the given destination dest.
Definition: copyjob.cpp:2196
KIO::rmdir
SimpleJob * rmdir(const KUrl &url)
Removes a single directory.
Definition: job.cpp:704
KIO::listRecursive
ListJob * listRecursive(const KUrl &url, JobFlags flags=DefaultFlags, bool includeHidden=true)
The same as the previous method, but recurses subdirectories.
Definition: job.cpp:2740
KIO::CMD_RENAME
@ CMD_RENAME
Definition: global.h:165
KIO::buildErrorString
QString buildErrorString(int errorCode, const QString &errorText)
Returns a translated error message for errorCode using the additional error information provided by e...
Definition: global.cpp:164
KIO::linkAs
CopyJob * linkAs(const KUrl &src, const KUrl &dest, JobFlags flags=DefaultFlags)
Create a link.
Definition: copyjob.cpp:2226
KIO::stat
StatJob * stat(const KUrl &url, JobFlags flags=DefaultFlags)
Find all details for one file or directory.
Definition: job.cpp:924
KIO::encodeFileName
QString encodeFileName(const QString &str)
Encodes (from the text displayed to the real filename) This translates '/' into a "unicode fraction s...
Definition: global.cpp:146
KIO::file_copy
FileCopyJob * file_copy(const KUrl &src, const KUrl &dest, int permissions=-1, JobFlags flags=DefaultFlags)
Copy a single file.
Definition: job.cpp:2473
KIO::copy
CopyJob * copy(const KUrl &src, const KUrl &dest, JobFlags flags=DefaultFlags)
Copy a file or directory src into the destination dest, which can be a file (including the final file...
Definition: copyjob.cpp:2164
KIO::file_move
FileCopyJob * file_move(const KUrl &src, const KUrl &dest, int permissions=-1, JobFlags flags=DefaultFlags)
Move a single file.
Definition: job.cpp:2479
KIO::mkdir
SimpleJob * mkdir(const KUrl &url, int permissions=-1)
Creates a single directory.
Definition: job.cpp:697
KIO::RenameDialog_Result
RenameDialog_Result
The result of open_RenameDialog().
Definition: renamedialog.h:61
KIO::R_RENAME
@ R_RENAME
Definition: renamedialog.h:61
KIO::R_AUTO_SKIP
@ R_AUTO_SKIP
Definition: renamedialog.h:61
KIO::R_OVERWRITE_ALL
@ R_OVERWRITE_ALL
Definition: renamedialog.h:61
KIO::R_OVERWRITE
@ R_OVERWRITE
Definition: renamedialog.h:61
KIO::R_AUTO_RENAME
@ R_AUTO_RENAME
Definition: renamedialog.h:61
KIO::R_SKIP
@ R_SKIP
Definition: renamedialog.h:61
KIO::R_CANCEL
@ R_CANCEL
Definition: renamedialog.h:61
KIO::RenameDialog_Mode
RenameDialog_Mode
M_OVERWRITE: We have an existing dest, show details about it and offer to overwrite it.
Definition: renamedialog.h:56
KIO::M_OVERWRITE_ITSELF
@ M_OVERWRITE_ITSELF
Definition: renamedialog.h:56
KIO::M_MULTI
@ M_MULTI
Definition: renamedialog.h:56
KIO::M_SKIP
@ M_SKIP
Definition: renamedialog.h:56
KIO::M_ISDIR
@ M_ISDIR
Definition: renamedialog.h:56
KIO::M_OVERWRITE
@ M_OVERWRITE
Definition: renamedialog.h:56
KIO::DefaultFlags
@ DefaultFlags
Show the progress info GUI, no Resume and no Overwrite.
Definition: jobclasses.h:46
KIO::HideProgressInfo
@ HideProgressInfo
Hide progress information dialog, i.e.
Definition: jobclasses.h:51
KIO::Overwrite
@ Overwrite
When set, automatically overwrite the destination if it exists already.
Definition: jobclasses.h:67
KIO::filesize_t
qulonglong filesize_t
64-bit file size
Definition: global.h:57
KIO::copyAs
CopyJob * copyAs(const KUrl &src, const KUrl &dest, JobFlags flags=DefaultFlags)
Copy a file or directory src into the destination dest, which is the destination name in any case,...
Definition: copyjob.cpp:2172
KIO::ERR_IDENTICAL_FILES
@ ERR_IDENTICAL_FILES
Definition: global.h:257
KIO::ERR_DIR_ALREADY_EXIST
@ ERR_DIR_ALREADY_EXIST
Definition: global.h:207
KIO::ERR_FILE_ALREADY_EXIST
@ ERR_FILE_ALREADY_EXIST
Definition: global.h:206
KIO::ERR_CANNOT_SYMLINK
@ ERR_CANNOT_SYMLINK
Definition: global.h:254
KIO::ERR_DISK_FULL
@ ERR_DISK_FULL
Definition: global.h:256
KIO::ERR_CANNOT_DELETE
@ ERR_CANNOT_DELETE
Definition: global.h:235
KIO::ERR_UNSUPPORTED_ACTION
@ ERR_UNSUPPORTED_ACTION
Definition: global.h:202
KIO::ERR_USER_CANCELED
@ ERR_USER_CANCELED
Definition: global.h:214
KIO::ERR_CANNOT_OPEN_FOR_WRITING
@ ERR_CANNOT_OPEN_FOR_WRITING
Definition: global.h:196
KIO::getJobTracker
KJobTrackerInterface * getJobTracker()
Definition: global.cpp:1246
KIO::symlink
SimpleJob * symlink(const QString &target, const KUrl &dest, JobFlags flags=DefaultFlags)
Create or move a symlink.
Definition: job.cpp:738
KRecentDirs::dir
QString dir(const QString &fileClass)
Returns the most recently used directory accociated with this file-class.
Definition: krecentdirs.cpp:68
KRecentDirs::list
QStringList list(const QString &fileClass)
Returns a list of directories associated with this file-class.
Definition: krecentdirs.cpp:60
end
const KShortcut & end()
scheduler.h
slave.h
KIO::CopyInfo
Definition: copyjob.h:42
KIO::CopyInfo::uDest
KUrl uDest
Definition: copyjob.h:44
KIO::CopyInfo::permissions
int permissions
Definition: copyjob.h:46
KIO::CopyInfo::mtime
time_t mtime
Definition: copyjob.h:48
KIO::CopyInfo::size
KIO::filesize_t size
Definition: copyjob.h:49
KIO::CopyInfo::ctime
time_t ctime
Definition: copyjob.h:47
KIO::CopyInfo::uSource
KUrl uSource
Definition: copyjob.h:43
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Feb 20 2023 00:00:00 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KIO

Skip menu "KIO"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs-4.14.38 API Reference

Skip menu "kdelibs-4.14.38 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal