00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
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;
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 }
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
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
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();
00188 url.addPath( displayName );
00189 }
00190
00191 m_totalSize += (KIO::filesize_t)entry.numberValue( KIO::UDSEntry::UDS_SIZE, 0 );
00192
00193
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
00209 if (m_currentStat != m_srcList.end()) {
00210 m_currentURL = (*m_currentStat);
00211
00212
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
00222 state = DELETEJOB_STATE_STATING;
00223
00224
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
00231 currentSourceStated(cachedItem.isDir(), cachedItem.isLink());
00232 ++m_currentStat;
00233 }
00234
00235
00236 if (!kio_resolve_local_urls) {
00237
00238
00239
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
00249 statNextSrc();
00250 } else {
00251 KIO::SimpleJob * job = KIO::stat( m_currentURL, StatJob::SourceSide, 0, KIO::HideProgressInfo );
00252 Scheduler::scheduleJob(job);
00253
00254 q->addSubjob(job);
00255 }
00256 } else {
00257 if (!q->hasSubjobs())
00258 finishedStatPhase();
00259 }
00260 }
00261
00262 void DeleteJobPrivate::finishedStatPhase()
00263 {
00264 m_totalFilesDirs = files.count() + symlinks.count() + dirs.count();
00265 slotReport();
00266
00267
00268
00269
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
00280 if ( !files.isEmpty() || !symlinks.isEmpty() )
00281 {
00282 SimpleJob *job;
00283 do {
00284
00285 KUrl::List::iterator it = files.begin();
00286 bool isLink = false;
00287 if ( it == files.end() )
00288 {
00289 it = symlinks.begin();
00290 isLink = true;
00291 }
00292
00293
00294 if ( (*it).isLocalFile() && unlink( QFile::encodeName((*it).path()) ) == 0 ) {
00295
00296 job = 0;
00297 m_processedFiles++;
00298 if ( m_processedFiles % 300 == 1 || m_totalFilesDirs < 300) {
00299 m_currentURL = *it;
00300 slotReport();
00301 }
00302 } else
00303 {
00304
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
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() )
00328 {
00329 do {
00330
00331 KUrl::List::iterator it = --dirs.end();
00332
00333 if ( (*it).isLocalFile() && ::rmdir( QFile::encodeName((*it).path()) ) == 0 ) {
00334
00335 m_processedDirs++;
00336 if ( m_processedDirs % 100 == 1 ) {
00337 m_currentURL = *it;
00338 slotReport();
00339 }
00340 } else {
00341 SimpleJob* job;
00342 if ( KProtocolManager::canDeleteRecursive( *it ) ) {
00343
00344
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
00359 for (QSet<QString>::const_iterator it = m_parentDirs.constBegin() ; it != m_parentDirs.constEnd() ; ++it)
00360 KDirWatch::self()->restartDirScan( *it );
00361
00362
00363 if ( !m_srcList.isEmpty() )
00364 {
00365
00366 org::kde::KDirNotify::emitFilesRemoved( m_srcList.toStringList() );
00367 }
00368 if (m_reportTimer!=0)
00369 m_reportTimer->stop();
00370 q->emitResult();
00371 }
00372
00373
00374 void DeleteJobPrivate::slotProcessedSize( KJob*, qulonglong data_size )
00375 {
00376 Q_Q(DeleteJob);
00377
00378
00379
00380
00381 m_fileProcessedSize = data_size;
00382 q->setProcessedAmount(KJob::Bytes, m_processedSize + m_fileProcessedSize);
00383
00384
00385
00386 q->setProcessedAmount(KJob::Bytes, m_processedSize + m_fileProcessedSize);
00387
00388
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
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
00408 ListJob *newjob = KIO::listRecursive(url, KIO::HideProgressInfo);
00409 newjob->setUnrestricted(true);
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
00415 }
00416 } else {
00417 if (isLink) {
00418
00419 symlinks.append(url);
00420 } else {
00421
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
00440 if (StatJob* statJob = qobject_cast<StatJob*>(job)) {
00441
00442 if (job->error()) {
00443
00444 Job::slotResult(job);
00445 return;
00446 }
00447
00448 const UDSEntry entry = statJob->statResult();
00449
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
00459 }
00460 if (!hasSubjobs())
00461 d->finishedStatPhase();
00462 }
00463 break;
00464 case DELETEJOB_STATE_DELETING_FILES:
00465
00466
00467
00468 d->m_incomingMetaData = dynamic_cast<KIO::Job*>(job)->metaData();
00469
00470 if ( job->error() )
00471 {
00472 Job::slotResult( job );
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 );
00485 return;
00486 }
00487 removeSubjob( job );
00488 assert( !hasSubjobs() );
00489 d->m_processedDirs++;
00490
00491
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"