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 namespace KIO
00049 {
00050 enum DeleteJobState {
00051 DELETEJOB_STATE_STATING,
00052 DELETEJOB_STATE_DELETING_FILES,
00053 DELETEJOB_STATE_DELETING_DIRS
00054 };
00055
00056 class DeleteJobPrivate: public KIO::JobPrivate
00057 {
00058 public:
00059 DeleteJobPrivate(const KUrl::List& src)
00060 : state( DELETEJOB_STATE_STATING )
00061 , m_totalSize( 0 )
00062 , m_processedSize( 0 )
00063 , m_fileProcessedSize( 0 )
00064 , m_processedFiles( 0 )
00065 , m_processedDirs( 0 )
00066 , m_totalFilesDirs( 0 )
00067 , m_srcList( src )
00068 , m_currentStat( m_srcList.begin() )
00069 , m_reportTimer( 0 )
00070 {
00071 }
00072 DeleteJobState state;
00073 KIO::filesize_t m_totalSize;
00074 KIO::filesize_t m_processedSize;
00075 KIO::filesize_t m_fileProcessedSize;
00076 int m_processedFiles;
00077 int m_processedDirs;
00078 int m_totalFilesDirs;
00079 KUrl m_currentURL;
00080 KUrl::List files;
00081 KUrl::List symlinks;
00082 KUrl::List dirs;
00083 KUrl::List m_srcList;
00084 KUrl::List::iterator m_currentStat;
00085 QSet<QString> m_parentDirs;
00086 QTimer *m_reportTimer;
00087
00088 void statNextSrc();
00089 void currentSourceStated(bool isDir, bool isLink);
00090 void finishedStatPhase();
00091 void deleteNextFile();
00092 void deleteNextDir();
00096 void slotProcessedSize( KJob*, qulonglong data_size );
00097 void slotReport();
00098 void slotStart();
00099 void slotEntries( KIO::Job*, const KIO::UDSEntryList& list );
00100
00101 Q_DECLARE_PUBLIC(DeleteJob)
00102
00103 static inline DeleteJob *newJob(const KUrl::List &src, JobFlags flags)
00104 {
00105 DeleteJob *job = new DeleteJob(*new DeleteJobPrivate(src));
00106 job->setUiDelegate(new JobUiDelegate);
00107 if (!(flags & HideProgressInfo))
00108 KIO::getJobTracker()->registerJob(job);
00109 return job;
00110 }
00111 };
00112
00113 }
00114
00115 using namespace KIO;
00116
00117 DeleteJob::DeleteJob(DeleteJobPrivate &dd)
00118 : Job(dd)
00119 {
00120 d_func()->m_reportTimer = new QTimer(this);
00121 connect(d_func()->m_reportTimer,SIGNAL(timeout()),this,SLOT(slotReport()));
00122
00123 d_func()->m_reportTimer->start( 200 );
00124
00125 QTimer::singleShot(0, this, SLOT(slotStart()));
00126 }
00127
00128 DeleteJob::~DeleteJob()
00129 {
00130 }
00131
00132 KUrl::List DeleteJob::urls() const
00133 {
00134 return d_func()->m_srcList;
00135 }
00136
00137 void DeleteJobPrivate::slotStart()
00138 {
00139 statNextSrc();
00140 }
00141
00142 void DeleteJobPrivate::slotReport()
00143 {
00144 Q_Q(DeleteJob);
00145 emit q->deleting( q, m_currentURL );
00146
00147
00148 JobPrivate::emitDeleting( q, m_currentURL);
00149
00150 switch( state ) {
00151 case DELETEJOB_STATE_STATING:
00152 q->setTotalAmount(KJob::Bytes, m_totalSize);
00153 q->setTotalAmount(KJob::Files, files.count());
00154 q->setTotalAmount(KJob::Directories, dirs.count());
00155 break;
00156 case DELETEJOB_STATE_DELETING_DIRS:
00157 q->setProcessedAmount(KJob::Directories, m_processedDirs);
00158 q->emitPercent( m_processedFiles + m_processedDirs, m_totalFilesDirs );
00159 break;
00160 case DELETEJOB_STATE_DELETING_FILES:
00161 q->setProcessedAmount(KJob::Files, m_processedFiles);
00162 q->emitPercent( m_processedFiles, m_totalFilesDirs );
00163 break;
00164 }
00165 }
00166
00167
00168 void DeleteJobPrivate::slotEntries(KIO::Job* job, const UDSEntryList& list)
00169 {
00170 UDSEntryList::ConstIterator it = list.begin();
00171 const UDSEntryList::ConstIterator end = list.end();
00172 for (; it != end; ++it)
00173 {
00174 const UDSEntry& entry = *it;
00175 const QString displayName = entry.stringValue( KIO::UDSEntry::UDS_NAME );
00176
00177 assert(!displayName.isEmpty());
00178 if (displayName != ".." && displayName != ".")
00179 {
00180 KUrl url;
00181 const QString urlStr = entry.stringValue( KIO::UDSEntry::UDS_URL );
00182 if ( !urlStr.isEmpty() )
00183 url = urlStr;
00184 else {
00185 url = ((SimpleJob *)job)->url();
00186 url.addPath( displayName );
00187 }
00188
00189 m_totalSize += (KIO::filesize_t)entry.numberValue( KIO::UDSEntry::UDS_SIZE, 0 );
00190
00191
00192 if ( entry.isLink() )
00193 symlinks.append( url );
00194 else if ( entry.isDir() )
00195 dirs.append( url );
00196 else
00197 files.append( url );
00198 }
00199 }
00200 }
00201
00202
00203 void DeleteJobPrivate::statNextSrc()
00204 {
00205 Q_Q(DeleteJob);
00206
00207 if (m_currentStat != m_srcList.end()) {
00208 m_currentURL = (*m_currentStat);
00209
00210
00211 if (!KProtocolManager::supportsDeleting(m_currentURL)) {
00212 QPointer<DeleteJob> that = q;
00213 ++m_currentStat;
00214 emit q->warning( q, buildErrorString(ERR_CANNOT_DELETE, m_currentURL.prettyUrl()) );
00215 if (that)
00216 statNextSrc();
00217 return;
00218 }
00219
00220 state = DELETEJOB_STATE_STATING;
00221
00222
00223 while(m_currentStat != m_srcList.end()) {
00224 m_currentURL = (*m_currentStat);
00225 KFileItem cachedItem = KDirLister::cachedItemForUrl(m_currentURL);
00226 if (cachedItem.isNull())
00227 break;
00228
00229 currentSourceStated(cachedItem.isDir(), cachedItem.isLink());
00230 ++m_currentStat;
00231 }
00232
00233
00234 extern bool kio_resolve_local_urls;
00235 if (!kio_resolve_local_urls) {
00236
00237
00238
00239 while(m_currentStat != m_srcList.end() && (*m_currentStat).isLocalFile()) {
00240 m_currentURL = (*m_currentStat);
00241 QFileInfo fileInfo(m_currentURL.path());
00242 currentSourceStated(fileInfo.isDir(), fileInfo.isSymLink());
00243 ++m_currentStat;
00244 }
00245 }
00246 if (m_currentStat == m_srcList.end()) {
00247
00248 statNextSrc();
00249 } else {
00250 KIO::SimpleJob * job = KIO::stat( m_currentURL, StatJob::SourceSide, 0, KIO::HideProgressInfo );
00251 Scheduler::scheduleJob(job);
00252
00253 q->addSubjob(job);
00254 }
00255 } else {
00256 if (!q->hasSubjobs())
00257 finishedStatPhase();
00258 }
00259 }
00260
00261 void DeleteJobPrivate::finishedStatPhase()
00262 {
00263 m_totalFilesDirs = files.count() + symlinks.count() + dirs.count();
00264 slotReport();
00265
00266
00267
00268
00269 for ( QSet<QString>::const_iterator it = m_parentDirs.constBegin() ; it != m_parentDirs.constEnd() ; ++it )
00270 KDirWatch::self()->stopDirScan( *it );
00271 state = DELETEJOB_STATE_DELETING_FILES;
00272 deleteNextFile();
00273 }
00274
00275 void DeleteJobPrivate::deleteNextFile()
00276 {
00277 Q_Q(DeleteJob);
00278
00279 if ( !files.isEmpty() || !symlinks.isEmpty() )
00280 {
00281 SimpleJob *job;
00282 do {
00283
00284 KUrl::List::iterator it = files.begin();
00285 bool isLink = false;
00286 if ( it == files.end() )
00287 {
00288 it = symlinks.begin();
00289 isLink = true;
00290 }
00291
00292
00293 if ( (*it).isLocalFile() && unlink( QFile::encodeName((*it).path()) ) == 0 ) {
00294
00295 job = 0;
00296 m_processedFiles++;
00297 if ( m_processedFiles % 300 == 1 || m_totalFilesDirs < 300) {
00298 m_currentURL = *it;
00299 slotReport();
00300 }
00301 } else
00302 {
00303
00304 job = KIO::file_delete( *it, KIO::HideProgressInfo );
00305 Scheduler::scheduleJob(job);
00306 m_currentURL=(*it);
00307 }
00308 if ( isLink )
00309 symlinks.erase(it);
00310 else
00311 files.erase(it);
00312 if ( job ) {
00313 q->addSubjob(job);
00314 return;
00315 }
00316
00317 } while (!job && (!files.isEmpty() || !symlinks.isEmpty()));
00318 }
00319 state = DELETEJOB_STATE_DELETING_DIRS;
00320 deleteNextDir();
00321 }
00322
00323 void DeleteJobPrivate::deleteNextDir()
00324 {
00325 Q_Q(DeleteJob);
00326 if ( !dirs.isEmpty() )
00327 {
00328 do {
00329
00330 KUrl::List::iterator it = --dirs.end();
00331
00332 if ( (*it).isLocalFile() && ::rmdir( QFile::encodeName((*it).path()) ) == 0 ) {
00333
00334 m_processedDirs++;
00335 if ( m_processedDirs % 100 == 1 ) {
00336 m_currentURL = *it;
00337 slotReport();
00338 }
00339 } else {
00340 SimpleJob* job;
00341 if ( KProtocolManager::canDeleteRecursive( *it ) ) {
00342
00343
00344 job = KIO::file_delete( *it, KIO::HideProgressInfo );
00345 } else {
00346 job = KIO::rmdir( *it );
00347 }
00348 Scheduler::scheduleJob(job);
00349 dirs.erase(it);
00350 q->addSubjob( job );
00351 return;
00352 }
00353 dirs.erase(it);
00354 } while ( !dirs.isEmpty() );
00355 }
00356
00357
00358 for (QSet<QString>::const_iterator it = m_parentDirs.constBegin() ; it != m_parentDirs.constEnd() ; ++it)
00359 KDirWatch::self()->restartDirScan( *it );
00360
00361
00362 if ( !m_srcList.isEmpty() )
00363 {
00364
00365 org::kde::KDirNotify::emitFilesRemoved( m_srcList.toStringList() );
00366 }
00367 if (m_reportTimer!=0)
00368 m_reportTimer->stop();
00369 q->emitResult();
00370 }
00371
00372
00373 void DeleteJobPrivate::slotProcessedSize( KJob*, qulonglong data_size )
00374 {
00375 Q_Q(DeleteJob);
00376
00377
00378
00379
00380 m_fileProcessedSize = data_size;
00381 q->setProcessedAmount(KJob::Bytes, m_processedSize + m_fileProcessedSize);
00382
00383
00384
00385 q->setProcessedAmount(KJob::Bytes, m_processedSize + m_fileProcessedSize);
00386
00387
00388 if ( m_totalSize == 0 )
00389 q->setPercent( 100 );
00390 else
00391 q->setPercent( (unsigned long)(( (float)(m_processedSize + m_fileProcessedSize) / (float)m_totalSize ) * 100.0) );
00392 }
00393
00394 void DeleteJobPrivate::currentSourceStated(bool isDir, bool isLink)
00395 {
00396 Q_Q(DeleteJob);
00397 const KUrl url = (*m_currentStat);
00398 if (isDir && !isLink) {
00399
00400 dirs.append( url );
00401 if (url.isLocalFile()) {
00402 const QString parentDir = url.path(KUrl::RemoveTrailingSlash);
00403 m_parentDirs.insert(parentDir);
00404 }
00405 if (!KProtocolManager::canDeleteRecursive(url)) {
00406
00407 ListJob *newjob = KIO::listRecursive(url, KIO::HideProgressInfo);
00408 newjob->setUnrestricted(true);
00409 Scheduler::scheduleJob(newjob);
00410 QObject::connect(newjob, SIGNAL(entries(KIO::Job*, const KIO::UDSEntryList&)),
00411 q, SLOT(slotEntries(KIO::Job*,const KIO::UDSEntryList&)));
00412 q->addSubjob(newjob);
00413
00414 }
00415 } else {
00416 if (isLink) {
00417
00418 symlinks.append(url);
00419 } else {
00420
00421 files.append(url);
00422 }
00423 if (url.isLocalFile()) {
00424 const QString parentDir = url.directory(KUrl::ObeyTrailingSlash);
00425 m_parentDirs.insert(parentDir);
00426 }
00427 }
00428 }
00429
00430 void DeleteJob::slotResult( KJob *job )
00431 {
00432 Q_D(DeleteJob);
00433 switch ( d->state )
00434 {
00435 case DELETEJOB_STATE_STATING:
00436 removeSubjob( job );
00437
00438
00439 if (StatJob* statJob = qobject_cast<StatJob*>(job)) {
00440
00441 if (job->error()) {
00442
00443 Job::slotResult(job);
00444 return;
00445 }
00446
00447 const UDSEntry entry = statJob->statResult();
00448
00449 const bool isLink = entry.isLink();
00450 const bool isDir = entry.isDir();
00451 d->currentSourceStated(isDir, isLink);
00452
00453 ++d->m_currentStat;
00454 d->statNextSrc();
00455 } else {
00456 if (job->error()) {
00457
00458 }
00459 if (!hasSubjobs())
00460 d->finishedStatPhase();
00461 }
00462 break;
00463 case DELETEJOB_STATE_DELETING_FILES:
00464 if ( job->error() )
00465 {
00466 Job::slotResult( job );
00467 return;
00468 }
00469 removeSubjob( job );
00470 assert( !hasSubjobs() );
00471 d->m_processedFiles++;
00472
00473 d->deleteNextFile();
00474 break;
00475 case DELETEJOB_STATE_DELETING_DIRS:
00476 if ( job->error() )
00477 {
00478 Job::slotResult( job );
00479 return;
00480 }
00481 removeSubjob( job );
00482 assert( !hasSubjobs() );
00483 d->m_processedDirs++;
00484
00485
00486
00487 d->deleteNextDir();
00488 break;
00489 default:
00490 assert(0);
00491 }
00492 }
00493
00494 DeleteJob *KIO::del( const KUrl& src, JobFlags flags )
00495 {
00496 KUrl::List srcList;
00497 srcList.append( src );
00498 return DeleteJobPrivate::newJob(srcList, flags);
00499 }
00500
00501 DeleteJob *KIO::del( const KUrl::List& src, JobFlags flags )
00502 {
00503 return DeleteJobPrivate::newJob(src, flags);
00504 }
00505
00506 #include "deletejob.moc"