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

KIO

deletejob.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries
00002     Copyright 2000       Stephan Kulow <coolo@kde.org>
00003     Copyright 2000-2009  David Faure <faure@kde.org>
00004     Copyright 2000       Waldo Bastian <bastian@kde.org>
00005 
00006     This library is free software; you can redistribute it and/or
00007     modify it under the terms of the GNU Library General Public
00008     License as published by the Free Software Foundation; either
00009     version 2 of the License, or (at your option) any later version.
00010 
00011     This library is distributed in the hope that it will be useful,
00012     but WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014     Library General Public License for more details.
00015 
00016     You should have received a copy of the GNU Library General Public License
00017     along with this library; see the file COPYING.LIB.  If not, write to
00018     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019     Boston, MA 02110-1301, USA.
00020 */
00021 
00022 #include "deletejob.h"
00023 
00024 #include "kdirlister.h"
00025 #include "scheduler.h"
00026 #include "kdirwatch.h"
00027 #include "kprotocolmanager.h"
00028 #include "jobuidelegate.h"
00029 #include <kdirnotify.h>
00030 
00031 #include <klocale.h>
00032 #include <kdebug.h>
00033 #include <kde_file.h>
00034 
00035 #include <assert.h>
00036 #include <stdlib.h>
00037 #include <time.h>
00038 
00039 #include <QtCore/QTimer>
00040 #include <QtCore/QFile>
00041 #include <QPointer>
00042 
00043 #include "job_p.h"
00044 
00045 extern bool kio_resolve_local_urls; // from copyjob.cpp, abused here to save a symbol.
00046 
00047 namespace KIO
00048 {
00049     enum DeleteJobState {
00050         DELETEJOB_STATE_STATING,
00051         DELETEJOB_STATE_DELETING_FILES,
00052         DELETEJOB_STATE_DELETING_DIRS
00053     };
00054 
00055     /*
00056     static const char* const s_states[] = {
00057         "DELETEJOB_STATE_STATING",
00058         "DELETEJOB_STATE_DELETING_FILES",
00059         "DELETEJOB_STATE_DELETING_DIRS"
00060     };
00061     */
00062 
00063     class DeleteJobPrivate: public KIO::JobPrivate
00064     {
00065     public:
00066         DeleteJobPrivate(const KUrl::List& src)
00067             : state( DELETEJOB_STATE_STATING )
00068             , m_processedFiles( 0 )
00069             , m_processedDirs( 0 )
00070             , m_totalFilesDirs( 0 )
00071             , m_srcList( src )
00072             , m_currentStat( m_srcList.begin() )
00073             , m_reportTimer( 0 )
00074         {
00075         }
00076         DeleteJobState state;
00077         int m_processedFiles;
00078         int m_processedDirs;
00079         int m_totalFilesDirs;
00080         KUrl m_currentURL;
00081         KUrl::List files;
00082         KUrl::List symlinks;
00083         KUrl::List dirs;
00084         KUrl::List m_srcList;
00085         KUrl::List::iterator m_currentStat;
00086     QSet<QString> m_parentDirs;
00087         QTimer *m_reportTimer;
00088 
00089         void statNextSrc();
00090         void currentSourceStated(bool isDir, bool isLink);
00091         void finishedStatPhase();
00092         void deleteNextFile();
00093         void deleteNextDir();
00094         void slotReport();
00095         void slotStart();
00096         void slotEntries( KIO::Job*, const KIO::UDSEntryList& list );
00097 
00098         Q_DECLARE_PUBLIC(DeleteJob)
00099 
00100         static inline DeleteJob *newJob(const KUrl::List &src, JobFlags flags)
00101         {
00102             DeleteJob *job = new DeleteJob(*new DeleteJobPrivate(src));
00103             job->setUiDelegate(new JobUiDelegate);
00104             if (!(flags & HideProgressInfo))
00105                 KIO::getJobTracker()->registerJob(job);
00106             return job;
00107         }
00108     };
00109 
00110 } // namespace KIO
00111 
00112 using namespace KIO;
00113 
00114 DeleteJob::DeleteJob(DeleteJobPrivate &dd)
00115     : Job(dd)
00116 {
00117     d_func()->m_reportTimer = new QTimer(this);
00118     connect(d_func()->m_reportTimer,SIGNAL(timeout()),this,SLOT(slotReport()));
00119     //this will update the report dialog with 5 Hz, I think this is fast enough, aleXXX
00120     d_func()->m_reportTimer->start( 200 );
00121 
00122     QTimer::singleShot(0, this, SLOT(slotStart()));
00123 }
00124 
00125 DeleteJob::~DeleteJob()
00126 {
00127 }
00128 
00129 KUrl::List DeleteJob::urls() const
00130 {
00131     return d_func()->m_srcList;
00132 }
00133 
00134 void DeleteJobPrivate::slotStart()
00135 {
00136     statNextSrc();
00137 }
00138 
00139 void DeleteJobPrivate::slotReport()
00140 {
00141    Q_Q(DeleteJob);
00142    emit q->deleting( q, m_currentURL );
00143 
00144    // TODO: maybe we could skip everything else when (flags & HideProgressInfo) ?
00145    JobPrivate::emitDeleting( q, m_currentURL);
00146 
00147    switch( state ) {
00148         case DELETEJOB_STATE_STATING:
00149             q->setTotalAmount(KJob::Files, files.count());
00150             q->setTotalAmount(KJob::Directories, dirs.count());
00151             break;
00152         case DELETEJOB_STATE_DELETING_DIRS:
00153             q->setProcessedAmount(KJob::Directories, m_processedDirs);
00154             q->emitPercent( m_processedFiles + m_processedDirs, m_totalFilesDirs );
00155             break;
00156         case DELETEJOB_STATE_DELETING_FILES:
00157             q->setProcessedAmount(KJob::Files, m_processedFiles);
00158             q->emitPercent( m_processedFiles, m_totalFilesDirs );
00159             break;
00160    }
00161 }
00162 
00163 
00164 void DeleteJobPrivate::slotEntries(KIO::Job* job, const UDSEntryList& list)
00165 {
00166     UDSEntryList::ConstIterator it = list.begin();
00167     const UDSEntryList::ConstIterator end = list.end();
00168     for (; it != end; ++it)
00169     {
00170         const UDSEntry& entry = *it;
00171         const QString displayName = entry.stringValue( KIO::UDSEntry::UDS_NAME );
00172 
00173         assert(!displayName.isEmpty());
00174         if (displayName != ".." && displayName != ".")
00175         {
00176             KUrl url;
00177             const QString urlStr = entry.stringValue( KIO::UDSEntry::UDS_URL );
00178             if ( !urlStr.isEmpty() )
00179                 url = urlStr;
00180             else {
00181                 url = static_cast<SimpleJob *>(job)->url(); // assumed to be a dir
00182                 url.addPath( displayName );
00183             }
00184 
00185             //kDebug(7007) << displayName << "(" << url << ")";
00186             if ( entry.isLink() )
00187                 symlinks.append( url );
00188             else if ( entry.isDir() )
00189                 dirs.append( url );
00190             else
00191                 files.append( url );
00192         }
00193     }
00194 }
00195 
00196 
00197 void DeleteJobPrivate::statNextSrc()
00198 {
00199     Q_Q(DeleteJob);
00200     //kDebug(7007);
00201     if (m_currentStat != m_srcList.end()) {
00202         m_currentURL = (*m_currentStat);
00203 
00204         // if the file system doesn't support deleting, we do not even stat
00205         if (!KProtocolManager::supportsDeleting(m_currentURL)) {
00206             QPointer<DeleteJob> that = q;
00207             ++m_currentStat;
00208             emit q->warning( q, buildErrorString(ERR_CANNOT_DELETE, m_currentURL.prettyUrl()) );
00209             if (that)
00210                 statNextSrc();
00211             return;
00212         }
00213         // Stat it
00214         state = DELETEJOB_STATE_STATING;
00215 
00216         // Fast path for KFileItems in directory views
00217         while(m_currentStat != m_srcList.end()) {
00218             m_currentURL = (*m_currentStat);
00219             const KFileItem cachedItem = KDirLister::cachedItemForUrl(m_currentURL);
00220             if (cachedItem.isNull())
00221                 break;
00222             //kDebug(7007) << "Found cached info about" << m_currentURL << "isDir=" << cachedItem.isDir() << "isLink=" << cachedItem.isLink();
00223             currentSourceStated(cachedItem.isDir(), cachedItem.isLink());
00224             ++m_currentStat;
00225         }
00226 
00227         // Hook for unit test to disable the fast path.
00228         if (!kio_resolve_local_urls) {
00229 
00230             // Fast path for local files
00231             // (using a loop, instead of a huge recursion)
00232             while(m_currentStat != m_srcList.end() && (*m_currentStat).isLocalFile()) {
00233                 m_currentURL = (*m_currentStat);
00234                 QFileInfo fileInfo(m_currentURL.toLocalFile());
00235                 currentSourceStated(fileInfo.isDir(), fileInfo.isSymLink());
00236                 ++m_currentStat;
00237             }
00238         }
00239         if (m_currentStat == m_srcList.end()) {
00240             // Done, jump to the last else of this method
00241             statNextSrc();
00242         } else {
00243             KIO::SimpleJob * job = KIO::stat( m_currentURL, StatJob::SourceSide, 0, KIO::HideProgressInfo );
00244             Scheduler::setJobPriority(job, 1);
00245             //kDebug(7007) << "stat'ing" << m_currentURL;
00246             q->addSubjob(job);
00247         }
00248     } else {
00249         if (!q->hasSubjobs()) // don't go there yet if we're still listing some subdirs
00250             finishedStatPhase();
00251     }
00252 }
00253 
00254 void DeleteJobPrivate::finishedStatPhase()
00255 {
00256     m_totalFilesDirs = files.count() + symlinks.count() + dirs.count();
00257     slotReport();
00258     // Now we know which dirs hold the files we're going to delete.
00259     // To speed things up and prevent double-notification, we disable KDirWatch
00260     // on those dirs temporarily (using KDirWatch::self, that's the instance
00261     // used by e.g. kdirlister).
00262     for ( QSet<QString>::const_iterator it = m_parentDirs.constBegin() ; it != m_parentDirs.constEnd() ; ++it )
00263         KDirWatch::self()->stopDirScan( *it );
00264     state = DELETEJOB_STATE_DELETING_FILES;
00265     deleteNextFile();
00266 }
00267 
00268 void DeleteJobPrivate::deleteNextFile()
00269 {
00270     Q_Q(DeleteJob);
00271     //kDebug(7007);
00272     if ( !files.isEmpty() || !symlinks.isEmpty() )
00273     {
00274         SimpleJob *job;
00275         do {
00276             // Take first file to delete out of list
00277             KUrl::List::iterator it = files.begin();
00278             bool isLink = false;
00279             if ( it == files.end() ) // No more files
00280             {
00281                 it = symlinks.begin(); // Pick up a symlink to delete
00282                 isLink = true;
00283             }
00284             // Normal deletion
00285             // If local file, try do it directly
00286 #ifdef Q_WS_WIN
00287             if ( (*it).isLocalFile() && DeleteFileW( (LPCWSTR)(*it).toLocalFile().utf16() ) == 0 ) {
00288 #else
00289             if ( (*it).isLocalFile() && unlink( QFile::encodeName((*it).toLocalFile()) ) == 0 ) {
00290 #endif
00291                 //kdDebug(7007) << "DeleteJob deleted" << (*it).toLocalFile();
00292                 job = 0;
00293                 m_processedFiles++;
00294                 if ( m_processedFiles % 300 == 1 || m_totalFilesDirs < 300) { // update progress info every 300 files
00295                     m_currentURL = *it;
00296                     slotReport();
00297                 }
00298             } else
00299             { // if remote - or if unlink() failed (we'll use the job's error handling in that case)
00300                 //kDebug(7007) << "calling file_delete on" << *it;
00301                 job = KIO::file_delete( *it, KIO::HideProgressInfo );
00302                 Scheduler::setJobPriority(job, 1);
00303                 m_currentURL=(*it);
00304             }
00305             if ( isLink )
00306                 symlinks.erase(it);
00307             else
00308                 files.erase(it);
00309             if ( job ) {
00310                 q->addSubjob(job);
00311                 return;
00312             }
00313             // loop only if direct deletion worked (job=0) and there is something else to delete
00314         } while (!job && (!files.isEmpty() || !symlinks.isEmpty()));
00315     }
00316     state = DELETEJOB_STATE_DELETING_DIRS;
00317     deleteNextDir();
00318 }
00319 
00320 void DeleteJobPrivate::deleteNextDir()
00321 {
00322     Q_Q(DeleteJob);
00323     if ( !dirs.isEmpty() ) // some dirs to delete ?
00324     {
00325         do {
00326             // Take first dir to delete out of list - last ones first !
00327             KUrl::List::iterator it = --dirs.end();
00328             // If local dir, try to rmdir it directly
00329 #ifdef Q_WS_WIN
00330             if ( (*it).isLocalFile() && RemoveDirectoryW( (LPCWSTR)(*it).toLocalFile().utf16() ) != 0 ) {
00331 #else
00332             if ( (*it).isLocalFile() && ::rmdir( QFile::encodeName((*it).toLocalFile()) ) == 0 ) {
00333 #endif
00334                 m_processedDirs++;
00335                 if ( m_processedDirs % 100 == 1 ) { // update progress info every 100 dirs
00336                     m_currentURL = *it;
00337                     slotReport();
00338                 }
00339             } else {
00340                 // Call rmdir - works for kioslaves with canDeleteRecursive too,
00341                 // CMD_DEL will trigger the recursive deletion in the slave.
00342                 SimpleJob* job = KIO::rmdir( *it );
00343                 job->addMetaData(QString::fromLatin1("recurse"), "true");
00344                 Scheduler::setJobPriority(job, 1);
00345                 dirs.erase(it);
00346                 q->addSubjob( job );
00347                 return;
00348             }
00349             dirs.erase(it);
00350         } while ( !dirs.isEmpty() );
00351     }
00352 
00353     // Re-enable watching on the dirs that held the deleted files
00354     for (QSet<QString>::const_iterator it = m_parentDirs.constBegin() ; it != m_parentDirs.constEnd() ; ++it) {
00355         KDirWatch::self()->restartDirScan( *it );
00356     }
00357 
00358     // Finished - tell the world
00359     if ( !m_srcList.isEmpty() )
00360     {
00361         //kDebug(7007) << "KDirNotify'ing FilesRemoved " << m_srcList.toStringList();
00362         org::kde::KDirNotify::emitFilesRemoved( m_srcList.toStringList() );
00363     }
00364     if (m_reportTimer!=0)
00365        m_reportTimer->stop();
00366     q->emitResult();
00367 }
00368 
00369 void DeleteJobPrivate::currentSourceStated(bool isDir, bool isLink)
00370 {
00371     Q_Q(DeleteJob);
00372     const KUrl url = (*m_currentStat);
00373     if (isDir && !isLink) {
00374         // Add toplevel dir in list of dirs
00375         dirs.append( url );
00376         if (url.isLocalFile()) {
00377             // We are about to delete this dir, no need to watch it
00378             // Maybe we should ask kdirwatch to remove all watches recursively?
00379             // But then there would be no feedback (things disappearing progressively) during huge deletions
00380             KDirWatch::self()->stopDirScan(url.toLocalFile(KUrl::RemoveTrailingSlash));
00381         }
00382         if (!KProtocolManager::canDeleteRecursive(url)) {
00383             //kDebug(7007) << url << "is a directory, let's list it";
00384             ListJob *newjob = KIO::listRecursive(url, KIO::HideProgressInfo);
00385             newjob->addMetaData("details", "0");
00386             newjob->setUnrestricted(true); // No KIOSK restrictions
00387             Scheduler::setJobPriority(newjob, 1);
00388             QObject::connect(newjob, SIGNAL(entries(KIO::Job*, const KIO::UDSEntryList&)),
00389                              q, SLOT(slotEntries(KIO::Job*,const KIO::UDSEntryList&)));
00390             q->addSubjob(newjob);
00391             // Note that this listing job will happen in parallel with other stat jobs.
00392         }
00393     } else {
00394         if (isLink) {
00395             //kDebug(7007) << "Target is a symlink";
00396             symlinks.append(url);
00397         } else {
00398             //kDebug(7007) << "Target is a file";
00399             files.append(url);
00400         }
00401     }
00402     if (url.isLocalFile()) {
00403         const QString parentDir = url.directory(KUrl::IgnoreTrailingSlash);
00404         m_parentDirs.insert(parentDir);
00405     }
00406 }
00407 
00408 void DeleteJob::slotResult( KJob *job )
00409 {
00410     Q_D(DeleteJob);
00411     switch ( d->state )
00412     {
00413     case DELETEJOB_STATE_STATING:
00414         removeSubjob( job );
00415 
00416         // Was this a stat job or a list job? We do both in parallel.
00417         if (StatJob* statJob = qobject_cast<StatJob*>(job)) {
00418             // Was there an error while stating ?
00419             if (job->error()) {
00420                 // Probably : doesn't exist
00421                 Job::slotResult(job); // will set the error and emit result(this)
00422                 return;
00423             }
00424 
00425             const UDSEntry entry = statJob->statResult();
00426             // Is it a file or a dir ?
00427             const bool isLink = entry.isLink();
00428             const bool isDir = entry.isDir();
00429             d->currentSourceStated(isDir, isLink);
00430 
00431             ++d->m_currentStat;
00432             d->statNextSrc();
00433         } else {
00434             if (job->error()) {
00435                 // Try deleting nonetheless, it may be empty (and non-listable)
00436             }
00437             if (!hasSubjobs())
00438                 d->finishedStatPhase();
00439         }
00440         break;
00441     case DELETEJOB_STATE_DELETING_FILES:
00442     // Propagate the subjob's metadata (a SimpleJob) to the real DeleteJob
00443     // FIXME: setMetaData() in the KIO API only allows access to outgoing metadata,
00444     // but we need to alter the incoming one
00445     d->m_incomingMetaData = dynamic_cast<KIO::Job*>(job)->metaData();
00446 
00447         if ( job->error() )
00448         {
00449             Job::slotResult( job ); // will set the error and emit result(this)
00450             return;
00451         }
00452         removeSubjob( job );
00453         assert( !hasSubjobs() );
00454         d->m_processedFiles++;
00455 
00456         d->deleteNextFile();
00457         break;
00458     case DELETEJOB_STATE_DELETING_DIRS:
00459         if ( job->error() )
00460         {
00461             Job::slotResult( job ); // will set the error and emit result(this)
00462             return;
00463         }
00464         removeSubjob( job );
00465         assert( !hasSubjobs() );
00466         d->m_processedDirs++;
00467         //emit processedAmount( this, KJob::Directories, d->m_processedDirs );
00468         //emitPercent( d->m_processedFiles + d->m_processedDirs, d->m_totalFilesDirs );
00469 
00470         d->deleteNextDir();
00471         break;
00472     default:
00473         assert(0);
00474     }
00475 }
00476 
00477 DeleteJob *KIO::del( const KUrl& src, JobFlags flags )
00478 {
00479     KUrl::List srcList;
00480     srcList.append( src );
00481     return DeleteJobPrivate::newJob(srcList, flags);
00482 }
00483 
00484 DeleteJob *KIO::del( const KUrl::List& src, JobFlags flags )
00485 {
00486     return DeleteJobPrivate::newJob(src, flags);
00487 }
00488 
00489 #include "deletejob.moc"

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

Skip menu "kdelibs"
  • 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
Generated for kdelibs by doxygen 1.7.5
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal