• Skip to content
  • Skip to link menu
KDE 4.2 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • 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-2006  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 "kmimetype.h"
00026 #include "scheduler.h"
00027 #include "kdirwatch.h"
00028 #include "kprotocolmanager.h"
00029 #include "jobuidelegate.h"
00030 #include <kdirnotify.h>
00031 #include <kuiserverjobtracker.h>
00032 
00033 #include <kauthorized.h>
00034 #include <klocale.h>
00035 #include <kdebug.h>
00036 #include <kde_file.h>
00037 
00038 #include <assert.h>
00039 #include <stdlib.h>
00040 #include <time.h>
00041 
00042 #include <QtCore/QTimer>
00043 #include <QtCore/QFile>
00044 #include <QPointer>
00045 
00046 #include "job_p.h"
00047 
00048 extern bool kio_resolve_local_urls; // from copyjob.cpp, abused here to save a symbol.
00049 
00050 namespace KIO
00051 {
00052     enum DeleteJobState {
00053         DELETEJOB_STATE_STATING,
00054         DELETEJOB_STATE_DELETING_FILES,
00055         DELETEJOB_STATE_DELETING_DIRS
00056     };
00057 
00058     class DeleteJobPrivate: public KIO::JobPrivate
00059     {
00060     public:
00061         DeleteJobPrivate(const KUrl::List& src)
00062             : state( DELETEJOB_STATE_STATING )
00063             , m_totalSize( 0 )
00064             , m_processedSize( 0 )
00065             , m_fileProcessedSize( 0 )
00066             , m_processedFiles( 0 )
00067             , m_processedDirs( 0 )
00068             , m_totalFilesDirs( 0 )
00069             , m_srcList( src )
00070             , m_currentStat( m_srcList.begin() )
00071             , m_reportTimer( 0 )
00072         {
00073         }
00074         DeleteJobState state;
00075         KIO::filesize_t m_totalSize;
00076         KIO::filesize_t m_processedSize;
00077         KIO::filesize_t m_fileProcessedSize;
00078         int m_processedFiles;
00079         int m_processedDirs;
00080         int m_totalFilesDirs;
00081         KUrl m_currentURL;
00082         KUrl::List files;
00083         KUrl::List symlinks;
00084         KUrl::List dirs;
00085         KUrl::List m_srcList;
00086         KUrl::List::iterator m_currentStat;
00087     QSet<QString> m_parentDirs;
00088         QTimer *m_reportTimer;
00089 
00090         void statNextSrc();
00091         void currentSourceStated(bool isDir, bool isLink);
00092         void finishedStatPhase();
00093         void deleteNextFile();
00094         void deleteNextDir();
00098         void slotProcessedSize( KJob*, qulonglong data_size );
00099         void slotReport();
00100         void slotStart();
00101         void slotEntries( KIO::Job*, const KIO::UDSEntryList& list );
00102 
00103         Q_DECLARE_PUBLIC(DeleteJob)
00104 
00105         static inline DeleteJob *newJob(const KUrl::List &src, JobFlags flags)
00106         {
00107             DeleteJob *job = new DeleteJob(*new DeleteJobPrivate(src));
00108             job->setUiDelegate(new JobUiDelegate);
00109             if (!(flags & HideProgressInfo))
00110                 KIO::getJobTracker()->registerJob(job);
00111             return job;
00112         }
00113     };
00114 
00115 } // namespace KIO
00116 
00117 using namespace KIO;
00118 
00119 DeleteJob::DeleteJob(DeleteJobPrivate &dd)
00120     : Job(dd)
00121 {
00122     d_func()->m_reportTimer = new QTimer(this);
00123     connect(d_func()->m_reportTimer,SIGNAL(timeout()),this,SLOT(slotReport()));
00124     //this will update the report dialog with 5 Hz, I think this is fast enough, aleXXX
00125     d_func()->m_reportTimer->start( 200 );
00126 
00127     QTimer::singleShot(0, this, SLOT(slotStart()));
00128 }
00129 
00130 DeleteJob::~DeleteJob()
00131 {
00132 }
00133 
00134 KUrl::List DeleteJob::urls() const
00135 {
00136     return d_func()->m_srcList;
00137 }
00138 
00139 void DeleteJobPrivate::slotStart()
00140 {
00141     statNextSrc();
00142 }
00143 
00144 void DeleteJobPrivate::slotReport()
00145 {
00146    Q_Q(DeleteJob);
00147    emit q->deleting( q, m_currentURL );
00148 
00149    // TODO: maybe we could skip everything else when (flags & HideProgressInfo) ?
00150    JobPrivate::emitDeleting( q, m_currentURL);
00151 
00152    switch( state ) {
00153         case DELETEJOB_STATE_STATING:
00154             q->setTotalAmount(KJob::Bytes, m_totalSize);
00155             q->setTotalAmount(KJob::Files, files.count());
00156             q->setTotalAmount(KJob::Directories, dirs.count());
00157             break;
00158         case DELETEJOB_STATE_DELETING_DIRS:
00159             q->setProcessedAmount(KJob::Directories, m_processedDirs);
00160             q->emitPercent( m_processedFiles + m_processedDirs, m_totalFilesDirs );
00161             break;
00162         case DELETEJOB_STATE_DELETING_FILES:
00163             q->setProcessedAmount(KJob::Files, m_processedFiles);
00164             q->emitPercent( m_processedFiles, m_totalFilesDirs );
00165             break;
00166    }
00167 }
00168 
00169 
00170 void DeleteJobPrivate::slotEntries(KIO::Job* job, const UDSEntryList& list)
00171 {
00172     UDSEntryList::ConstIterator it = list.begin();
00173     const UDSEntryList::ConstIterator end = list.end();
00174     for (; it != end; ++it)
00175     {
00176         const UDSEntry& entry = *it;
00177         const QString displayName = entry.stringValue( KIO::UDSEntry::UDS_NAME );
00178 
00179         assert(!displayName.isEmpty());
00180         if (displayName != ".." && displayName != ".")
00181         {
00182             KUrl url;
00183             const QString urlStr = entry.stringValue( KIO::UDSEntry::UDS_URL );
00184             if ( !urlStr.isEmpty() )
00185                 url = urlStr;
00186             else {
00187                 url = ((SimpleJob *)job)->url(); // assumed to be a dir
00188                 url.addPath( displayName );
00189             }
00190 
00191             m_totalSize += (KIO::filesize_t)entry.numberValue( KIO::UDSEntry::UDS_SIZE, 0 );
00192 
00193             //kDebug(7007) << displayName << "(" << url << ")";
00194             if ( entry.isLink() )
00195                 symlinks.append( url );
00196             else if ( entry.isDir() )
00197                 dirs.append( url );
00198             else
00199                 files.append( url );
00200         }
00201     }
00202 }
00203 
00204 
00205 void DeleteJobPrivate::statNextSrc()
00206 {
00207     Q_Q(DeleteJob);
00208     //kDebug(7007);
00209     if (m_currentStat != m_srcList.end()) {
00210         m_currentURL = (*m_currentStat);
00211 
00212         // if the file system doesn't support deleting, we do not even stat
00213         if (!KProtocolManager::supportsDeleting(m_currentURL)) {
00214             QPointer<DeleteJob> that = q;
00215             ++m_currentStat;
00216             emit q->warning( q, buildErrorString(ERR_CANNOT_DELETE, m_currentURL.prettyUrl()) );
00217             if (that)
00218                 statNextSrc();
00219             return;
00220         }
00221         // Stat it
00222         state = DELETEJOB_STATE_STATING;
00223 
00224         // Fast path for KFileItems in directory views
00225         while(m_currentStat != m_srcList.end()) {
00226             m_currentURL = (*m_currentStat);
00227             KFileItem cachedItem = KDirLister::cachedItemForUrl(m_currentURL);
00228             if (cachedItem.isNull())
00229                 break;
00230             //kDebug(7007) << "Found cached info about" << m_currentURL << "isDir=" << cachedItem.isDir() << "isLink=" << cachedItem.isLink();
00231             currentSourceStated(cachedItem.isDir(), cachedItem.isLink());
00232             ++m_currentStat;
00233         }
00234 
00235         // Hook for unit test to disable the fast path.
00236         if (!kio_resolve_local_urls) {
00237 
00238             // Fast path for local files
00239             // (using a loop, instead of a huge recursion)
00240             while(m_currentStat != m_srcList.end() && (*m_currentStat).isLocalFile()) {
00241                 m_currentURL = (*m_currentStat);
00242                 QFileInfo fileInfo(m_currentURL.path());
00243                 currentSourceStated(fileInfo.isDir(), fileInfo.isSymLink());
00244                 ++m_currentStat;
00245             }
00246         }
00247         if (m_currentStat == m_srcList.end()) {
00248             // Done, jump to the last else of this method
00249             statNextSrc();
00250         } else {
00251             KIO::SimpleJob * job = KIO::stat( m_currentURL, StatJob::SourceSide, 0, KIO::HideProgressInfo );
00252             Scheduler::scheduleJob(job);
00253             //kDebug(7007) << "stat'ing" << m_currentURL;
00254             q->addSubjob(job);
00255         }
00256     } else {
00257         if (!q->hasSubjobs()) // don't go there yet if we're still listing some subdirs
00258             finishedStatPhase();
00259     }
00260 }
00261 
00262 void DeleteJobPrivate::finishedStatPhase()
00263 {
00264     m_totalFilesDirs = files.count() + symlinks.count() + dirs.count();
00265     slotReport();
00266     // Now we know which dirs hold the files we're going to delete.
00267     // To speed things up and prevent double-notification, we disable KDirWatch
00268     // on those dirs temporarily (using KDirWatch::self, that's the instanced
00269     // used by e.g. kdirlister).
00270     for ( QSet<QString>::const_iterator it = m_parentDirs.constBegin() ; it != m_parentDirs.constEnd() ; ++it )
00271         KDirWatch::self()->stopDirScan( *it );
00272     state = DELETEJOB_STATE_DELETING_FILES;
00273     deleteNextFile();
00274 }
00275 
00276 void DeleteJobPrivate::deleteNextFile()
00277 {
00278     Q_Q(DeleteJob);
00279     //kDebug(7007);
00280     if ( !files.isEmpty() || !symlinks.isEmpty() )
00281     {
00282         SimpleJob *job;
00283         do {
00284             // Take first file to delete out of list
00285             KUrl::List::iterator it = files.begin();
00286             bool isLink = false;
00287             if ( it == files.end() ) // No more files
00288             {
00289                 it = symlinks.begin(); // Pick up a symlink to delete
00290                 isLink = true;
00291             }
00292             // Normal deletion
00293             // If local file, try do it directly
00294             if ( (*it).isLocalFile() && unlink( QFile::encodeName((*it).path()) ) == 0 ) {
00295                 //kdDebug(7007) << "DeleteJob deleted" << (*it).path();
00296                 job = 0;
00297                 m_processedFiles++;
00298                 if ( m_processedFiles % 300 == 1 || m_totalFilesDirs < 300) { // update progress info every 300 files
00299                     m_currentURL = *it;
00300                     slotReport();
00301                 }
00302             } else
00303             { // if remote - or if unlink() failed (we'll use the job's error handling in that case)
00304                 //kDebug(7007) << "calling file_delete on" << *it;
00305                 job = KIO::file_delete( *it, KIO::HideProgressInfo );
00306                 Scheduler::scheduleJob(job);
00307                 m_currentURL=(*it);
00308             }
00309             if ( isLink )
00310                 symlinks.erase(it);
00311             else
00312                 files.erase(it);
00313             if ( job ) {
00314                 q->addSubjob(job);
00315                 return;
00316             }
00317             // loop only if direct deletion worked (job=0) and there is something else to delete
00318         } while (!job && (!files.isEmpty() || !symlinks.isEmpty()));
00319     }
00320     state = DELETEJOB_STATE_DELETING_DIRS;
00321     deleteNextDir();
00322 }
00323 
00324 void DeleteJobPrivate::deleteNextDir()
00325 {
00326     Q_Q(DeleteJob);
00327     if ( !dirs.isEmpty() ) // some dirs to delete ?
00328     {
00329         do {
00330             // Take first dir to delete out of list - last ones first !
00331             KUrl::List::iterator it = --dirs.end();
00332             // If local dir, try to rmdir it directly
00333             if ( (*it).isLocalFile() && ::rmdir( QFile::encodeName((*it).path()) ) == 0 ) {
00334 
00335                 m_processedDirs++;
00336                 if ( m_processedDirs % 100 == 1 ) { // update progress info every 100 dirs
00337                     m_currentURL = *it;
00338                     slotReport();
00339                 }
00340             } else {
00341                 SimpleJob* job;
00342                 if ( KProtocolManager::canDeleteRecursive( *it ) ) {
00343                     // If the ioslave supports recursive deletion of a directory, then
00344                     // we only need to send a single CMD_DEL command, so we use file_delete :)
00345                     job = KIO::file_delete( *it, KIO::HideProgressInfo );
00346                 } else {
00347                     job = KIO::rmdir( *it );
00348                 }
00349                 Scheduler::scheduleJob(job);
00350                 dirs.erase(it);
00351                 q->addSubjob( job );
00352                 return;
00353             }
00354             dirs.erase(it);
00355         } while ( !dirs.isEmpty() );
00356     }
00357 
00358     // Re-enable watching on the dirs that held the deleted files
00359     for (QSet<QString>::const_iterator it = m_parentDirs.constBegin() ; it != m_parentDirs.constEnd() ; ++it)
00360         KDirWatch::self()->restartDirScan( *it );
00361 
00362     // Finished - tell the world
00363     if ( !m_srcList.isEmpty() )
00364     {
00365         //kDebug(7007) << "KDirNotify'ing FilesRemoved " << m_srcList.toStringList();
00366         org::kde::KDirNotify::emitFilesRemoved( m_srcList.toStringList() );
00367     }
00368     if (m_reportTimer!=0)
00369        m_reportTimer->stop();
00370     q->emitResult();
00371 }
00372 
00373 // Note: I don't think this slot is connected to anywhere! -thiago
00374 void DeleteJobPrivate::slotProcessedSize( KJob*, qulonglong data_size )
00375 {
00376    Q_Q(DeleteJob);
00377    // Note: this is the same implementation as CopyJob::slotProcessedSize but
00378    // it's different from FileCopyJob::slotProcessedSize - which is why this
00379    // is not in Job.
00380 
00381    m_fileProcessedSize = data_size;
00382    q->setProcessedAmount(KJob::Bytes, m_processedSize + m_fileProcessedSize);
00383 
00384    //kDebug(7007) << (unsigned int) (m_processedSize + m_fileProcessedSize);
00385 
00386    q->setProcessedAmount(KJob::Bytes, m_processedSize + m_fileProcessedSize);
00387 
00388    // calculate percents
00389    if ( m_totalSize == 0 )
00390       q->setPercent( 100 );
00391    else
00392       q->setPercent( (unsigned long)(( (float)(m_processedSize + m_fileProcessedSize) / (float)m_totalSize ) * 100.0) );
00393 }
00394 
00395 void DeleteJobPrivate::currentSourceStated(bool isDir, bool isLink)
00396 {
00397     Q_Q(DeleteJob);
00398     const KUrl url = (*m_currentStat);
00399     if (isDir && !isLink) {
00400         // Add toplevel dir in list of dirs
00401         dirs.append( url );
00402         if (url.isLocalFile()) {
00403             const QString parentDir = url.path(KUrl::RemoveTrailingSlash);
00404             m_parentDirs.insert(parentDir);
00405         }
00406         if (!KProtocolManager::canDeleteRecursive(url)) {
00407             //kDebug(7007) << url << "is a directory, let's list it";
00408             ListJob *newjob = KIO::listRecursive(url, KIO::HideProgressInfo);
00409             newjob->setUnrestricted(true); // No KIOSK restrictions
00410             Scheduler::scheduleJob(newjob);
00411             QObject::connect(newjob, SIGNAL(entries(KIO::Job*, const KIO::UDSEntryList&)),
00412                              q, SLOT(slotEntries(KIO::Job*,const KIO::UDSEntryList&)));
00413             q->addSubjob(newjob);
00414             // Note that this listing job will happen in parallel with other stat jobs.
00415         }
00416     } else {
00417         if (isLink) {
00418             //kDebug(7007) << "Target is a symlink";
00419             symlinks.append(url);
00420         } else {
00421             //kDebug(7007) << "Target is a file";
00422             files.append(url);
00423         }
00424         if (url.isLocalFile()) {
00425             const QString parentDir = url.directory(KUrl::ObeyTrailingSlash);
00426             m_parentDirs.insert(parentDir);
00427         }
00428     }
00429 }
00430 
00431 void DeleteJob::slotResult( KJob *job )
00432 {
00433     Q_D(DeleteJob);
00434     switch ( d->state )
00435     {
00436     case DELETEJOB_STATE_STATING:
00437         removeSubjob( job );
00438 
00439         // Was this a stat job or a list job? We do both in parallel.
00440         if (StatJob* statJob = qobject_cast<StatJob*>(job)) {
00441             // Was there an error while stating ?
00442             if (job->error()) {
00443                 // Probably : doesn't exist
00444                 Job::slotResult(job); // will set the error and emit result(this)
00445                 return;
00446             }
00447 
00448             const UDSEntry entry = statJob->statResult();
00449             // Is it a file or a dir ?
00450             const bool isLink = entry.isLink();
00451             const bool isDir = entry.isDir();
00452             d->currentSourceStated(isDir, isLink);
00453 
00454             ++d->m_currentStat;
00455             d->statNextSrc();
00456         } else {
00457             if (job->error()) {
00458                 // Try deleting nonetheless, it may be empty (and non-listable)
00459             }
00460             if (!hasSubjobs())
00461                 d->finishedStatPhase();
00462         }
00463         break;
00464     case DELETEJOB_STATE_DELETING_FILES:
00465     // Propagate the subjob's metadata (a SimpleJob) to the real DeleteJob
00466     // FIXME: setMetaData() in the KIO API only allows access to outgoing metadata,
00467     // but we need to alter the incoming one
00468     d->m_incomingMetaData = dynamic_cast<KIO::Job*>(job)->metaData();
00469 
00470         if ( job->error() )
00471         {
00472             Job::slotResult( job ); // will set the error and emit result(this)
00473             return;
00474         }
00475         removeSubjob( job );
00476         assert( !hasSubjobs() );
00477         d->m_processedFiles++;
00478 
00479         d->deleteNextFile();
00480         break;
00481     case DELETEJOB_STATE_DELETING_DIRS:
00482         if ( job->error() )
00483         {
00484             Job::slotResult( job ); // will set the error and emit result(this)
00485             return;
00486         }
00487         removeSubjob( job );
00488         assert( !hasSubjobs() );
00489         d->m_processedDirs++;
00490         //emit processedAmount( this, KJob::Directories, d->m_processedDirs );
00491         //emitPercent( d->m_processedFiles + d->m_processedDirs, d->m_totalFilesDirs );
00492 
00493         d->deleteNextDir();
00494         break;
00495     default:
00496         assert(0);
00497     }
00498 }
00499 
00500 DeleteJob *KIO::del( const KUrl& src, JobFlags flags )
00501 {
00502     KUrl::List srcList;
00503     srcList.append( src );
00504     return DeleteJobPrivate::newJob(srcList, flags);
00505 }
00506 
00507 DeleteJob *KIO::del( const KUrl::List& src, JobFlags flags )
00508 {
00509     return DeleteJobPrivate::newJob(src, flags);
00510 }
00511 
00512 #include "deletejob.moc"

KIO

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
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