• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.10.2 API Reference
  • KDE Home
  • Contact Us
 

KIO

  • kio
  • kio
deletejob.cpp
Go to the documentation of this file.
1 /* This file is part of the KDE libraries
2  Copyright 2000 Stephan Kulow <coolo@kde.org>
3  Copyright 2000-2009 David Faure <faure@kde.org>
4  Copyright 2000 Waldo Bastian <bastian@kde.org>
5 
6  This library is free software; you can redistribute it and/or
7  modify it under the terms of the GNU Library General Public
8  License as published by the Free Software Foundation; either
9  version 2 of the License, or (at your option) any later version.
10 
11  This library is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  Library General Public License for more details.
15 
16  You should have received a copy of the GNU Library General Public License
17  along with this library; see the file COPYING.LIB. If not, write to
18  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  Boston, MA 02110-1301, USA.
20 */
21 
22 #include "deletejob.h"
23 
24 #include "kdirlister.h"
25 #include "scheduler.h"
26 #include "kdirwatch.h"
27 #include "kprotocolmanager.h"
28 #include "jobuidelegate.h"
29 #include <kdirnotify.h>
30 
31 #include <klocale.h>
32 #include <kdebug.h>
33 #include <kde_file.h>
34 
35 #include <QtCore/QTimer>
36 #include <QtCore/QFile>
37 #include <QPointer>
38 
39 #include "job_p.h"
40 
41 extern bool kio_resolve_local_urls; // from copyjob.cpp, abused here to save a symbol.
42 
43 static bool isHttpProtocol(const QString& protocol)
44 {
45  return (protocol.startsWith(QLatin1String("webdav"), Qt::CaseInsensitive) ||
46  protocol.startsWith(QLatin1String("http"), Qt::CaseInsensitive));
47 }
48 
49 namespace KIO
50 {
51  enum DeleteJobState {
52  DELETEJOB_STATE_STATING,
53  DELETEJOB_STATE_DELETING_FILES,
54  DELETEJOB_STATE_DELETING_DIRS
55  };
56 
57  /*
58  static const char* const s_states[] = {
59  "DELETEJOB_STATE_STATING",
60  "DELETEJOB_STATE_DELETING_FILES",
61  "DELETEJOB_STATE_DELETING_DIRS"
62  };
63  */
64 
65  class DeleteJobPrivate: public KIO::JobPrivate
66  {
67  public:
68  DeleteJobPrivate(const KUrl::List& src)
69  : state( DELETEJOB_STATE_STATING )
70  , m_processedFiles( 0 )
71  , m_processedDirs( 0 )
72  , m_totalFilesDirs( 0 )
73  , m_srcList( src )
74  , m_currentStat( m_srcList.begin() )
75  , m_reportTimer( 0 )
76  {
77  }
78  DeleteJobState state;
79  int m_processedFiles;
80  int m_processedDirs;
81  int m_totalFilesDirs;
82  KUrl m_currentURL;
83  KUrl::List files;
84  KUrl::List symlinks;
85  KUrl::List dirs;
86  KUrl::List m_srcList;
87  KUrl::List::iterator m_currentStat;
88  QSet<QString> m_parentDirs;
89  QTimer *m_reportTimer;
90 
91  void statNextSrc();
92  void currentSourceStated(bool isDir, bool isLink);
93  void finishedStatPhase();
94  void deleteNextFile();
95  void deleteNextDir();
96  void slotReport();
97  void slotStart();
98  void slotEntries( KIO::Job*, const KIO::UDSEntryList& list );
99 
100  Q_DECLARE_PUBLIC(DeleteJob)
101 
102  static inline DeleteJob *newJob(const KUrl::List &src, JobFlags flags)
103  {
104  DeleteJob *job = new DeleteJob(*new DeleteJobPrivate(src));
105  job->setUiDelegate(new JobUiDelegate);
106  if (!(flags & HideProgressInfo))
107  KIO::getJobTracker()->registerJob(job);
108  return job;
109  }
110  };
111 
112 } // namespace KIO
113 
114 using namespace KIO;
115 
116 DeleteJob::DeleteJob(DeleteJobPrivate &dd)
117  : Job(dd)
118 {
119  d_func()->m_reportTimer = new QTimer(this);
120  connect(d_func()->m_reportTimer,SIGNAL(timeout()),this,SLOT(slotReport()));
121  //this will update the report dialog with 5 Hz, I think this is fast enough, aleXXX
122  d_func()->m_reportTimer->start( 200 );
123 
124  QTimer::singleShot(0, this, SLOT(slotStart()));
125 }
126 
127 DeleteJob::~DeleteJob()
128 {
129 }
130 
131 KUrl::List DeleteJob::urls() const
132 {
133  return d_func()->m_srcList;
134 }
135 
136 void DeleteJobPrivate::slotStart()
137 {
138  statNextSrc();
139 }
140 
141 void DeleteJobPrivate::slotReport()
142 {
143  Q_Q(DeleteJob);
144  emit q->deleting( q, m_currentURL );
145 
146  // TODO: maybe we could skip everything else when (flags & HideProgressInfo) ?
147  JobPrivate::emitDeleting( q, m_currentURL);
148 
149  switch( state ) {
150  case DELETEJOB_STATE_STATING:
151  q->setTotalAmount(KJob::Files, files.count());
152  q->setTotalAmount(KJob::Directories, dirs.count());
153  break;
154  case DELETEJOB_STATE_DELETING_DIRS:
155  q->setProcessedAmount(KJob::Directories, m_processedDirs);
156  q->emitPercent( m_processedFiles + m_processedDirs, m_totalFilesDirs );
157  break;
158  case DELETEJOB_STATE_DELETING_FILES:
159  q->setProcessedAmount(KJob::Files, m_processedFiles);
160  q->emitPercent( m_processedFiles, m_totalFilesDirs );
161  break;
162  }
163 }
164 
165 
166 void DeleteJobPrivate::slotEntries(KIO::Job* job, const UDSEntryList& list)
167 {
168  UDSEntryList::ConstIterator it = list.begin();
169  const UDSEntryList::ConstIterator end = list.end();
170  for (; it != end; ++it)
171  {
172  const UDSEntry& entry = *it;
173  const QString displayName = entry.stringValue( KIO::UDSEntry::UDS_NAME );
174 
175  Q_ASSERT(!displayName.isEmpty());
176  if (displayName != ".." && displayName != ".")
177  {
178  KUrl url;
179  const QString urlStr = entry.stringValue( KIO::UDSEntry::UDS_URL );
180  if ( !urlStr.isEmpty() )
181  url = urlStr;
182  else {
183  url = static_cast<SimpleJob *>(job)->url(); // assumed to be a dir
184  url.addPath( displayName );
185  }
186 
187  //kDebug(7007) << displayName << "(" << url << ")";
188  if ( entry.isLink() )
189  symlinks.append( url );
190  else if ( entry.isDir() )
191  dirs.append( url );
192  else
193  files.append( url );
194  }
195  }
196 }
197 
198 
199 void DeleteJobPrivate::statNextSrc()
200 {
201  Q_Q(DeleteJob);
202  //kDebug(7007);
203  if (m_currentStat != m_srcList.end()) {
204  m_currentURL = (*m_currentStat);
205 
206  // if the file system doesn't support deleting, we do not even stat
207  if (!KProtocolManager::supportsDeleting(m_currentURL)) {
208  QPointer<DeleteJob> that = q;
209  ++m_currentStat;
210  emit q->warning( q, buildErrorString(ERR_CANNOT_DELETE, m_currentURL.prettyUrl()) );
211  if (that)
212  statNextSrc();
213  return;
214  }
215  // Stat it
216  state = DELETEJOB_STATE_STATING;
217 
218  // Fast path for KFileItems in directory views
219  while(m_currentStat != m_srcList.end()) {
220  m_currentURL = (*m_currentStat);
221  const KFileItem cachedItem = KDirLister::cachedItemForUrl(m_currentURL);
222  if (cachedItem.isNull())
223  break;
224  //kDebug(7007) << "Found cached info about" << m_currentURL << "isDir=" << cachedItem.isDir() << "isLink=" << cachedItem.isLink();
225  currentSourceStated(cachedItem.isDir(), cachedItem.isLink());
226  ++m_currentStat;
227  }
228 
229  // Hook for unit test to disable the fast path.
230  if (!kio_resolve_local_urls) {
231 
232  // Fast path for local files
233  // (using a loop, instead of a huge recursion)
234  while(m_currentStat != m_srcList.end() && (*m_currentStat).isLocalFile()) {
235  m_currentURL = (*m_currentStat);
236  QFileInfo fileInfo(m_currentURL.toLocalFile());
237  currentSourceStated(fileInfo.isDir(), fileInfo.isSymLink());
238  ++m_currentStat;
239  }
240  }
241  if (m_currentStat == m_srcList.end()) {
242  // Done, jump to the last else of this method
243  statNextSrc();
244  } else {
245  KIO::SimpleJob * job = KIO::stat( m_currentURL, StatJob::SourceSide, 0, KIO::HideProgressInfo );
246  Scheduler::setJobPriority(job, 1);
247  //kDebug(7007) << "stat'ing" << m_currentURL;
248  q->addSubjob(job);
249  }
250  } else {
251  if (!q->hasSubjobs()) // don't go there yet if we're still listing some subdirs
252  finishedStatPhase();
253  }
254 }
255 
256 void DeleteJobPrivate::finishedStatPhase()
257 {
258  m_totalFilesDirs = files.count() + symlinks.count() + dirs.count();
259  slotReport();
260  // Now we know which dirs hold the files we're going to delete.
261  // To speed things up and prevent double-notification, we disable KDirWatch
262  // on those dirs temporarily (using KDirWatch::self, that's the instance
263  // used by e.g. kdirlister).
264  const QSet<QString>::const_iterator itEnd = m_parentDirs.constEnd();
265  for ( QSet<QString>::const_iterator it = m_parentDirs.constBegin() ; it != itEnd ; ++it )
266  KDirWatch::self()->stopDirScan( *it );
267  state = DELETEJOB_STATE_DELETING_FILES;
268  deleteNextFile();
269 }
270 
271 void DeleteJobPrivate::deleteNextFile()
272 {
273  Q_Q(DeleteJob);
274  //kDebug(7007);
275  if ( !files.isEmpty() || !symlinks.isEmpty() )
276  {
277  SimpleJob *job;
278  do {
279  // Take first file to delete out of list
280  KUrl::List::iterator it = files.begin();
281  bool isLink = false;
282  if ( it == files.end() ) // No more files
283  {
284  it = symlinks.begin(); // Pick up a symlink to delete
285  isLink = true;
286  }
287  // Normal deletion
288  // If local file, try do it directly
289 #ifdef Q_WS_WIN
290  if ( (*it).isLocalFile() && DeleteFileW( (LPCWSTR)(*it).toLocalFile().utf16() ) != 0 ) {
291 #else
292  if ( (*it).isLocalFile() && unlink( QFile::encodeName((*it).toLocalFile()) ) == 0 ) {
293 #endif
294  //kdDebug(7007) << "DeleteJob deleted" << (*it).toLocalFile();
295  job = 0;
296  m_processedFiles++;
297  if ( m_processedFiles % 300 == 1 || m_totalFilesDirs < 300) { // update progress info every 300 files
298  m_currentURL = *it;
299  slotReport();
300  }
301  } else
302  { // if remote - or if unlink() failed (we'll use the job's error handling in that case)
303  //kDebug(7007) << "calling file_delete on" << *it;
304  if (isHttpProtocol(it->protocol()))
305  job = KIO::http_delete( *it, KIO::HideProgressInfo );
306  else
307  job = KIO::file_delete( *it, KIO::HideProgressInfo );
308  Scheduler::setJobPriority(job, 1);
309  m_currentURL=(*it);
310  }
311  if ( isLink )
312  symlinks.erase(it);
313  else
314  files.erase(it);
315  if ( job ) {
316  q->addSubjob(job);
317  return;
318  }
319  // loop only if direct deletion worked (job=0) and there is something else to delete
320  } while (!job && (!files.isEmpty() || !symlinks.isEmpty()));
321  }
322  state = DELETEJOB_STATE_DELETING_DIRS;
323  deleteNextDir();
324 }
325 
326 void DeleteJobPrivate::deleteNextDir()
327 {
328  Q_Q(DeleteJob);
329  if ( !dirs.isEmpty() ) // some dirs to delete ?
330  {
331  do {
332  // Take first dir to delete out of list - last ones first !
333  KUrl::List::iterator it = --dirs.end();
334  // If local dir, try to rmdir it directly
335 #ifdef Q_WS_WIN
336  if ( (*it).isLocalFile() && RemoveDirectoryW( (LPCWSTR)(*it).toLocalFile().utf16() ) != 0 ) {
337 #else
338  if ( (*it).isLocalFile() && ::rmdir( QFile::encodeName((*it).toLocalFile()) ) == 0 ) {
339 #endif
340  m_processedDirs++;
341  if ( m_processedDirs % 100 == 1 ) { // update progress info every 100 dirs
342  m_currentURL = *it;
343  slotReport();
344  }
345  } else {
346  // Call rmdir - works for kioslaves with canDeleteRecursive too,
347  // CMD_DEL will trigger the recursive deletion in the slave.
348  SimpleJob* job = KIO::rmdir( *it );
349  job->addMetaData(QString::fromLatin1("recurse"), "true");
350  Scheduler::setJobPriority(job, 1);
351  dirs.erase(it);
352  q->addSubjob( job );
353  return;
354  }
355  dirs.erase(it);
356  } while ( !dirs.isEmpty() );
357  }
358 
359  // Re-enable watching on the dirs that held the deleted files
360  const QSet<QString>::const_iterator itEnd = m_parentDirs.constEnd();
361  for (QSet<QString>::const_iterator it = m_parentDirs.constBegin() ; it != itEnd ; ++it) {
362  KDirWatch::self()->restartDirScan( *it );
363  }
364 
365  // Finished - tell the world
366  if ( !m_srcList.isEmpty() )
367  {
368  //kDebug(7007) << "KDirNotify'ing FilesRemoved " << m_srcList.toStringList();
369  org::kde::KDirNotify::emitFilesRemoved( m_srcList.toStringList() );
370  }
371  if (m_reportTimer!=0)
372  m_reportTimer->stop();
373  q->emitResult();
374 }
375 
376 void DeleteJobPrivate::currentSourceStated(bool isDir, bool isLink)
377 {
378  Q_Q(DeleteJob);
379  const KUrl url = (*m_currentStat);
380  if (isDir && !isLink) {
381  // Add toplevel dir in list of dirs
382  dirs.append( url );
383  if (url.isLocalFile()) {
384  // We are about to delete this dir, no need to watch it
385  // Maybe we should ask kdirwatch to remove all watches recursively?
386  // But then there would be no feedback (things disappearing progressively) during huge deletions
387  KDirWatch::self()->stopDirScan(url.toLocalFile(KUrl::RemoveTrailingSlash));
388  }
389  if (!KProtocolManager::canDeleteRecursive(url)) {
390  //kDebug(7007) << url << "is a directory, let's list it";
391  ListJob *newjob = KIO::listRecursive(url, KIO::HideProgressInfo);
392  newjob->addMetaData("details", "0");
393  newjob->setUnrestricted(true); // No KIOSK restrictions
394  Scheduler::setJobPriority(newjob, 1);
395  QObject::connect(newjob, SIGNAL(entries(KIO::Job*,KIO::UDSEntryList)),
396  q, SLOT(slotEntries(KIO::Job*,KIO::UDSEntryList)));
397  q->addSubjob(newjob);
398  // Note that this listing job will happen in parallel with other stat jobs.
399  }
400  } else {
401  if (isLink) {
402  //kDebug(7007) << "Target is a symlink";
403  symlinks.append(url);
404  } else {
405  //kDebug(7007) << "Target is a file";
406  files.append(url);
407  }
408  }
409  if (url.isLocalFile()) {
410  const QString parentDir = url.directory(KUrl::IgnoreTrailingSlash);
411  m_parentDirs.insert(parentDir);
412  }
413 }
414 
415 void DeleteJob::slotResult( KJob *job )
416 {
417  Q_D(DeleteJob);
418  switch ( d->state )
419  {
420  case DELETEJOB_STATE_STATING:
421  removeSubjob( job );
422 
423  // Was this a stat job or a list job? We do both in parallel.
424  if (StatJob* statJob = qobject_cast<StatJob*>(job)) {
425  // Was there an error while stating ?
426  if (job->error()) {
427  // Probably : doesn't exist
428  Job::slotResult(job); // will set the error and emit result(this)
429  return;
430  }
431 
432  const UDSEntry entry = statJob->statResult();
433  // Is it a file or a dir ?
434  const bool isLink = entry.isLink();
435  const bool isDir = entry.isDir();
436  d->currentSourceStated(isDir, isLink);
437 
438  ++d->m_currentStat;
439  d->statNextSrc();
440  } else {
441  if (job->error()) {
442  // Try deleting nonetheless, it may be empty (and non-listable)
443  }
444  if (!hasSubjobs())
445  d->finishedStatPhase();
446  }
447  break;
448  case DELETEJOB_STATE_DELETING_FILES:
449  // Propagate the subjob's metadata (a SimpleJob) to the real DeleteJob
450  // FIXME: setMetaData() in the KIO API only allows access to outgoing metadata,
451  // but we need to alter the incoming one
452  d->m_incomingMetaData = dynamic_cast<KIO::Job*>(job)->metaData();
453 
454  if ( job->error() )
455  {
456  Job::slotResult( job ); // will set the error and emit result(this)
457  return;
458  }
459  removeSubjob( job );
460  Q_ASSERT( !hasSubjobs() );
461  d->m_processedFiles++;
462 
463  d->deleteNextFile();
464  break;
465  case DELETEJOB_STATE_DELETING_DIRS:
466  if ( job->error() )
467  {
468  Job::slotResult( job ); // will set the error and emit result(this)
469  return;
470  }
471  removeSubjob( job );
472  Q_ASSERT( !hasSubjobs() );
473  d->m_processedDirs++;
474  //emit processedAmount( this, KJob::Directories, d->m_processedDirs );
475  //emitPercent( d->m_processedFiles + d->m_processedDirs, d->m_totalFilesDirs );
476 
477  d->deleteNextDir();
478  break;
479  default:
480  Q_ASSERT(0);
481  }
482 }
483 
484 DeleteJob *KIO::del( const KUrl& src, JobFlags flags )
485 {
486  KUrl::List srcList;
487  srcList.append( src );
488  return DeleteJobPrivate::newJob(srcList, flags);
489 }
490 
491 DeleteJob *KIO::del( const KUrl::List& src, JobFlags flags )
492 {
493  return DeleteJobPrivate::newJob(src, flags);
494 }
495 
496 #include "deletejob.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2013 The KDE developers.
Generated on Tue Apr 16 2013 19:14:49 by doxygen 1.8.3.1 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.10.2 API Reference

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