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

Skip menu "kdelibs-4.8.3 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