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