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
00059
00060
00061
00062
00063
00064
00065
00066 class DeleteJobPrivate: public KIO::JobPrivate
00067 {
00068 public:
00069 DeleteJobPrivate(const KUrl::List& src)
00070 : state( DELETEJOB_STATE_STATING )
00071 , m_processedFiles( 0 )
00072 , m_processedDirs( 0 )
00073 , m_totalFilesDirs( 0 )
00074 , m_srcList( src )
00075 , m_currentStat( m_srcList.begin() )
00076 , m_reportTimer( 0 )
00077 {
00078 }
00079 DeleteJobState state;
00080 int m_processedFiles;
00081 int m_processedDirs;
00082 int m_totalFilesDirs;
00083 KUrl m_currentURL;
00084 KUrl::List files;
00085 KUrl::List symlinks;
00086 KUrl::List dirs;
00087 KUrl::List m_srcList;
00088 KUrl::List::iterator m_currentStat;
00089 QSet<QString> m_parentDirs;
00090 QTimer *m_reportTimer;
00091
00092 void statNextSrc();
00093 void currentSourceStated(bool isDir, bool isLink);
00094 void finishedStatPhase();
00095 void deleteNextFile();
00096 void deleteNextDir();
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::Files, files.count());
00153 q->setTotalAmount(KJob::Directories, dirs.count());
00154 break;
00155 case DELETEJOB_STATE_DELETING_DIRS:
00156 q->setProcessedAmount(KJob::Directories, m_processedDirs);
00157 q->emitPercent( m_processedFiles + m_processedDirs, m_totalFilesDirs );
00158 break;
00159 case DELETEJOB_STATE_DELETING_FILES:
00160 q->setProcessedAmount(KJob::Files, m_processedFiles);
00161 q->emitPercent( m_processedFiles, m_totalFilesDirs );
00162 break;
00163 }
00164 }
00165
00166
00167 void DeleteJobPrivate::slotEntries(KIO::Job* job, const UDSEntryList& list)
00168 {
00169 UDSEntryList::ConstIterator it = list.begin();
00170 const UDSEntryList::ConstIterator end = list.end();
00171 for (; it != end; ++it)
00172 {
00173 const UDSEntry& entry = *it;
00174 const QString displayName = entry.stringValue( KIO::UDSEntry::UDS_NAME );
00175
00176 assert(!displayName.isEmpty());
00177 if (displayName != ".." && displayName != ".")
00178 {
00179 KUrl url;
00180 const QString urlStr = entry.stringValue( KIO::UDSEntry::UDS_URL );
00181 if ( !urlStr.isEmpty() )
00182 url = urlStr;
00183 else {
00184 url = ((SimpleJob *)job)->url();
00185 url.addPath( displayName );
00186 }
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 m_currentURL = (*m_currentStat);
00206
00207
00208 if (!KProtocolManager::supportsDeleting(m_currentURL)) {
00209 QPointer<DeleteJob> that = q;
00210 ++m_currentStat;
00211 emit q->warning( q, buildErrorString(ERR_CANNOT_DELETE, m_currentURL.prettyUrl()) );
00212 if (that)
00213 statNextSrc();
00214 return;
00215 }
00216
00217 state = DELETEJOB_STATE_STATING;
00218
00219
00220 while(m_currentStat != m_srcList.end()) {
00221 m_currentURL = (*m_currentStat);
00222 const KFileItem cachedItem = KDirLister::cachedItemForUrl(m_currentURL);
00223 if (cachedItem.isNull())
00224 break;
00225
00226 currentSourceStated(cachedItem.isDir(), cachedItem.isLink());
00227 ++m_currentStat;
00228 }
00229
00230
00231 if (!kio_resolve_local_urls) {
00232
00233
00234
00235 while(m_currentStat != m_srcList.end() && (*m_currentStat).isLocalFile()) {
00236 m_currentURL = (*m_currentStat);
00237 QFileInfo fileInfo(m_currentURL.toLocalFile());
00238 currentSourceStated(fileInfo.isDir(), fileInfo.isSymLink());
00239 ++m_currentStat;
00240 }
00241 }
00242 if (m_currentStat == m_srcList.end()) {
00243
00244 statNextSrc();
00245 } else {
00246 KIO::SimpleJob * job = KIO::stat( m_currentURL, StatJob::SourceSide, 0, KIO::HideProgressInfo );
00247 Scheduler::scheduleJob(job);
00248
00249 q->addSubjob(job);
00250 }
00251 } else {
00252 if (!q->hasSubjobs())
00253 finishedStatPhase();
00254 }
00255 }
00256
00257 void DeleteJobPrivate::finishedStatPhase()
00258 {
00259 m_totalFilesDirs = files.count() + symlinks.count() + dirs.count();
00260 slotReport();
00261
00262
00263
00264
00265 for ( QSet<QString>::const_iterator it = m_parentDirs.constBegin() ; it != m_parentDirs.constEnd() ; ++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
00275 if ( !files.isEmpty() || !symlinks.isEmpty() )
00276 {
00277 SimpleJob *job;
00278 do {
00279
00280 KUrl::List::iterator it = files.begin();
00281 bool isLink = false;
00282 if ( it == files.end() )
00283 {
00284 it = symlinks.begin();
00285 isLink = true;
00286 }
00287
00288
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
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 #ifdef Q_WS_WIN
00333 if ( (*it).isLocalFile() && RemoveDirectoryW( (LPCWSTR)(*it).toLocalFile().utf16() ) != 0 ) {
00334 #else
00335 if ( (*it).isLocalFile() && ::rmdir( QFile::encodeName((*it).toLocalFile()) ) == 0 ) {
00336 #endif
00337 m_processedDirs++;
00338 if ( m_processedDirs % 100 == 1 ) {
00339 m_currentURL = *it;
00340 slotReport();
00341 }
00342 } else {
00343
00344
00345 SimpleJob* job = KIO::rmdir( *it );
00346 job->addMetaData(QString::fromLatin1("recurse"), "true");
00347 Scheduler::scheduleJob(job);
00348 dirs.erase(it);
00349 q->addSubjob( job );
00350 return;
00351 }
00352 dirs.erase(it);
00353 } while ( !dirs.isEmpty() );
00354 }
00355
00356
00357 for (QSet<QString>::const_iterator it = m_parentDirs.constBegin() ; it != m_parentDirs.constEnd() ; ++it)
00358 KDirWatch::self()->restartDirScan( *it );
00359
00360
00361 if ( !m_srcList.isEmpty() )
00362 {
00363
00364 org::kde::KDirNotify::emitFilesRemoved( m_srcList.toStringList() );
00365 }
00366 if (m_reportTimer!=0)
00367 m_reportTimer->stop();
00368 q->emitResult();
00369 }
00370
00371 void DeleteJobPrivate::currentSourceStated(bool isDir, bool isLink)
00372 {
00373 Q_Q(DeleteJob);
00374 const KUrl url = (*m_currentStat);
00375 if (isDir && !isLink) {
00376
00377 dirs.append( url );
00378 if (url.isLocalFile()) {
00379 const QString parentDir = url.toLocalFile(KUrl::RemoveTrailingSlash);
00380 m_parentDirs.insert(parentDir);
00381 }
00382 if (!KProtocolManager::canDeleteRecursive(url)) {
00383
00384 ListJob *newjob = KIO::listRecursive(url, KIO::HideProgressInfo);
00385 newjob->addMetaData("details", "0");
00386 newjob->setUnrestricted(true);
00387 Scheduler::scheduleJob(newjob);
00388 QObject::connect(newjob, SIGNAL(entries(KIO::Job*, const KIO::UDSEntryList&)),
00389 q, SLOT(slotEntries(KIO::Job*,const KIO::UDSEntryList&)));
00390 q->addSubjob(newjob);
00391
00392 }
00393 } else {
00394 if (isLink) {
00395
00396 symlinks.append(url);
00397 } else {
00398
00399 files.append(url);
00400 }
00401 if (url.isLocalFile()) {
00402 const QString parentDir = url.directory(KUrl::ObeyTrailingSlash);
00403 m_parentDirs.insert(parentDir);
00404 }
00405 }
00406 }
00407
00408 void DeleteJob::slotResult( KJob *job )
00409 {
00410 Q_D(DeleteJob);
00411 switch ( d->state )
00412 {
00413 case DELETEJOB_STATE_STATING:
00414 removeSubjob( job );
00415
00416
00417 if (StatJob* statJob = qobject_cast<StatJob*>(job)) {
00418
00419 if (job->error()) {
00420
00421 Job::slotResult(job);
00422 return;
00423 }
00424
00425 const UDSEntry entry = statJob->statResult();
00426
00427 const bool isLink = entry.isLink();
00428 const bool isDir = entry.isDir();
00429 d->currentSourceStated(isDir, isLink);
00430
00431 ++d->m_currentStat;
00432 d->statNextSrc();
00433 } else {
00434 if (job->error()) {
00435
00436 }
00437 if (!hasSubjobs())
00438 d->finishedStatPhase();
00439 }
00440 break;
00441 case DELETEJOB_STATE_DELETING_FILES:
00442
00443
00444
00445 d->m_incomingMetaData = dynamic_cast<KIO::Job*>(job)->metaData();
00446
00447 if ( job->error() )
00448 {
00449 Job::slotResult( job );
00450 return;
00451 }
00452 removeSubjob( job );
00453 assert( !hasSubjobs() );
00454 d->m_processedFiles++;
00455
00456 d->deleteNextFile();
00457 break;
00458 case DELETEJOB_STATE_DELETING_DIRS:
00459 if ( job->error() )
00460 {
00461 Job::slotResult( job );
00462 return;
00463 }
00464 removeSubjob( job );
00465 assert( !hasSubjobs() );
00466 d->m_processedDirs++;
00467
00468
00469
00470 d->deleteNextDir();
00471 break;
00472 default:
00473 assert(0);
00474 }
00475 }
00476
00477 DeleteJob *KIO::del( const KUrl& src, JobFlags flags )
00478 {
00479 KUrl::List srcList;
00480 srcList.append( src );
00481 return DeleteJobPrivate::newJob(srcList, flags);
00482 }
00483
00484 DeleteJob *KIO::del( const KUrl::List& src, JobFlags flags )
00485 {
00486 return DeleteJobPrivate::newJob(src, flags);
00487 }
00488
00489 #include "deletejob.moc"