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

KIO

  • kio
  • kio
job.cpp
Go to the documentation of this file.
1 /* This file is part of the KDE libraries
2  Copyright (C) 2000 Stephan Kulow <coolo@kde.org>
3  2000-2009 David Faure <faure@kde.org>
4  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 "job.h"
23 #include "job_p.h"
24 #include "clipboardupdater_p.h"
25 
26 #include <config.h>
27 
28 #include <sys/types.h>
29 #include <sys/wait.h>
30 #include <sys/stat.h>
31 
32 #include <signal.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <time.h>
36 #include <unistd.h>
37 extern "C" {
38 #include <pwd.h>
39 #include <grp.h>
40 }
41 #include <QtCore/QTimer>
42 #include <QtCore/QFile>
43 
44 #include <kauthorized.h>
45 #include <klocale.h>
46 #include <kconfig.h>
47 #include <kdebug.h>
48 #include <kde_file.h>
49 
50 #include <errno.h>
51 
52 #include "jobuidelegate.h"
53 #include "kmimetype.h"
54 #include "slave.h"
55 #include "scheduler.h"
56 #include "kdirwatch.h"
57 #include "kprotocolinfo.h"
58 #include "kprotocolmanager.h"
59 #include "filejob.h"
60 
61 #include <kdirnotify.h>
62 #include <ktemporaryfile.h>
63 
64 using namespace KIO;
65 
66 #define MAX_READ_BUF_SIZE (64 * 1024) // 64 KB at a time seems reasonable...
67 
68 static inline Slave *jobSlave(SimpleJob *job)
69 {
70  return SimpleJobPrivate::get(job)->m_slave;
71 }
72 
73 //this will update the report dialog with 5 Hz, I think this is fast enough, aleXXX
74 #define REPORT_TIMEOUT 200
75 
76 Job::Job() : KCompositeJob(*new JobPrivate, 0)
77 {
78  setCapabilities( KJob::Killable | KJob::Suspendable );
79 }
80 
81 Job::Job(JobPrivate &dd) : KCompositeJob(dd, 0)
82 {
83  setCapabilities( KJob::Killable | KJob::Suspendable );
84 }
85 
86 Job::~Job()
87 {
88 }
89 
90 JobUiDelegate *Job::ui() const
91 {
92  return static_cast<JobUiDelegate*>( uiDelegate() );
93 }
94 
95 bool Job::addSubjob(KJob *jobBase)
96 {
97  //kDebug(7007) << "addSubjob(" << jobBase << ") this=" << this;
98 
99  bool ok = KCompositeJob::addSubjob( jobBase );
100  KIO::Job *job = dynamic_cast<KIO::Job*>( jobBase );
101  if (ok && job) {
102  // Copy metadata into subjob (e.g. window-id, user-timestamp etc.)
103  Q_D(Job);
104  job->mergeMetaData(d->m_outgoingMetaData);
105 
106  // Forward information from that subjob.
107  connect(job, SIGNAL(speed(KJob*,ulong)),
108  SLOT(slotSpeed(KJob*,ulong)));
109 
110  if (ui() && job->ui()) {
111  job->ui()->setWindow( ui()->window() );
112  job->ui()->updateUserTimestamp( ui()->userTimestamp() );
113  }
114  }
115  return ok;
116 }
117 
118 bool Job::removeSubjob( KJob *jobBase )
119 {
120  //kDebug(7007) << "removeSubjob(" << jobBase << ") this=" << this << "subjobs=" << subjobs().count();
121  return KCompositeJob::removeSubjob( jobBase );
122 }
123 
124 void JobPrivate::emitMoving(KIO::Job * job, const KUrl &src, const KUrl &dest)
125 {
126  emit job->description(job, i18nc("@title job","Moving"),
127  qMakePair(i18nc("The source of a file operation", "Source"), src.pathOrUrl()),
128  qMakePair(i18nc("The destination of a file operation", "Destination"), dest.pathOrUrl()));
129 }
130 
131 void JobPrivate::emitCopying(KIO::Job * job, const KUrl &src, const KUrl &dest)
132 {
133  emit job->description(job, i18nc("@title job","Copying"),
134  qMakePair(i18nc("The source of a file operation", "Source"), src.pathOrUrl()),
135  qMakePair(i18nc("The destination of a file operation", "Destination"), dest.pathOrUrl()));
136 }
137 
138 void JobPrivate::emitCreatingDir(KIO::Job * job, const KUrl &dir)
139 {
140  emit job->description(job, i18nc("@title job","Creating directory"),
141  qMakePair(i18n("Directory"), dir.pathOrUrl()));
142 }
143 
144 void JobPrivate::emitDeleting(KIO::Job *job, const KUrl &url)
145 {
146  emit job->description(job, i18nc("@title job","Deleting"),
147  qMakePair(i18n("File"), url.pathOrUrl()));
148 }
149 
150 void JobPrivate::emitStating(KIO::Job *job, const KUrl &url)
151 {
152  emit job->description(job, i18nc("@title job","Examining"),
153  qMakePair(i18n("File"), url.pathOrUrl()));
154 }
155 
156 void JobPrivate::emitTransferring(KIO::Job *job, const KUrl &url)
157 {
158  emit job->description(job, i18nc("@title job","Transferring"),
159  qMakePair(i18nc("The source of a file operation", "Source"), url.pathOrUrl()));
160 }
161 
162 void JobPrivate::emitMounting(KIO::Job * job, const QString &dev, const QString &point)
163 {
164  emit job->description(job, i18nc("@title job","Mounting"),
165  qMakePair(i18n("Device"), dev),
166  qMakePair(i18n("Mountpoint"), point));
167 }
168 
169 void JobPrivate::emitUnmounting(KIO::Job * job, const QString &point)
170 {
171  emit job->description(job, i18nc("@title job","Unmounting"),
172  qMakePair(i18n("Mountpoint"), point));
173 }
174 
175 bool Job::doKill()
176 {
177  // kill all subjobs, without triggering their result slot
178  Q_FOREACH( KJob* it, subjobs()) {
179  it->kill( KJob::Quietly );
180  }
181  clearSubjobs();
182 
183  return true;
184 }
185 
186 bool Job::doSuspend()
187 {
188  Q_FOREACH(KJob* it, subjobs()) {
189  if (!it->suspend())
190  return false;
191  }
192 
193  return true;
194 }
195 
196 bool Job::doResume()
197 {
198  Q_FOREACH ( KJob* it, subjobs() )
199  {
200  if (!it->resume())
201  return false;
202  }
203 
204  return true;
205 }
206 
207 void JobPrivate::slotSpeed( KJob*, unsigned long speed )
208 {
209  //kDebug(7007) << speed;
210  q_func()->emitSpeed( speed );
211 }
212 
213 //Job::errorString is implemented in global.cpp
214 
215 #ifndef KDE_NO_DEPRECATED
216 void Job::showErrorDialog( QWidget *parent )
217 {
218  if ( ui() )
219  {
220  ui()->setWindow( parent );
221  ui()->showErrorMessage();
222  }
223  else
224  {
225  kError() << errorString();
226  }
227 }
228 #endif
229 
230 bool Job::isInteractive() const
231 {
232  return uiDelegate() != 0;
233 }
234 
235 void Job::setParentJob(Job* job)
236 {
237  Q_D(Job);
238  Q_ASSERT(d->m_parentJob == 0L);
239  Q_ASSERT(job);
240  d->m_parentJob = job;
241 }
242 
243 Job* Job::parentJob() const
244 {
245  return d_func()->m_parentJob;
246 }
247 
248 MetaData Job::metaData() const
249 {
250  return d_func()->m_incomingMetaData;
251 }
252 
253 QString Job::queryMetaData(const QString &key)
254 {
255  return d_func()->m_incomingMetaData.value(key, QString());
256 }
257 
258 void Job::setMetaData( const KIO::MetaData &_metaData)
259 {
260  Q_D(Job);
261  d->m_outgoingMetaData = _metaData;
262 }
263 
264 void Job::addMetaData( const QString &key, const QString &value)
265 {
266  d_func()->m_outgoingMetaData.insert(key, value);
267 }
268 
269 void Job::addMetaData( const QMap<QString,QString> &values)
270 {
271  Q_D(Job);
272  QMap<QString,QString>::const_iterator it = values.begin();
273  for(;it != values.end(); ++it)
274  d->m_outgoingMetaData.insert(it.key(), it.value());
275 }
276 
277 void Job::mergeMetaData( const QMap<QString,QString> &values)
278 {
279  Q_D(Job);
280  QMap<QString,QString>::const_iterator it = values.begin();
281  for(;it != values.end(); ++it)
282  // there's probably a faster way
283  if ( !d->m_outgoingMetaData.contains( it.key() ) )
284  d->m_outgoingMetaData.insert( it.key(), it.value() );
285 }
286 
287 MetaData Job::outgoingMetaData() const
288 {
289  return d_func()->m_outgoingMetaData;
290 }
291 
292 SimpleJob::SimpleJob(SimpleJobPrivate &dd)
293  : Job(dd)
294 {
295  d_func()->simpleJobInit();
296 }
297 
298 void SimpleJobPrivate::simpleJobInit()
299 {
300  Q_Q(SimpleJob);
301  if (!m_url.isValid())
302  {
303  q->setError( ERR_MALFORMED_URL );
304  q->setErrorText( m_url.url() );
305  QTimer::singleShot(0, q, SLOT(slotFinished()) );
306  return;
307  }
308 
309  Scheduler::doJob(q);
310 }
311 
312 
313 bool SimpleJob::doKill()
314 {
315  Q_D(SimpleJob);
316  if ((d->m_extraFlags & JobPrivate::EF_KillCalled) == 0) {
317  d->m_extraFlags |= JobPrivate::EF_KillCalled;
318  Scheduler::cancelJob(this); // deletes the slave if not 0
319  } else {
320  kWarning(7007) << this << "This is overkill.";
321  }
322  return Job::doKill();
323 }
324 
325 bool SimpleJob::doSuspend()
326 {
327  Q_D(SimpleJob);
328  if ( d->m_slave )
329  d->m_slave->suspend();
330  return Job::doSuspend();
331 }
332 
333 bool SimpleJob::doResume()
334 {
335  Q_D(SimpleJob);
336  if ( d->m_slave )
337  d->m_slave->resume();
338  return Job::doResume();
339 }
340 
341 const KUrl& SimpleJob::url() const
342 {
343  return d_func()->m_url;
344 }
345 
346 void SimpleJob::putOnHold()
347 {
348  Q_D(SimpleJob);
349  Q_ASSERT( d->m_slave );
350  if ( d->m_slave )
351  {
352  Scheduler::putSlaveOnHold(this, d->m_url);
353  }
354  // we should now be disassociated from the slave
355  Q_ASSERT(!d->m_slave);
356  kill( Quietly );
357 }
358 
359 void SimpleJob::removeOnHold()
360 {
361  Scheduler::removeSlaveOnHold();
362 }
363 
364 bool SimpleJob::isRedirectionHandlingEnabled() const
365 {
366  return d_func()->m_redirectionHandlingEnabled;
367 }
368 
369 void SimpleJob::setRedirectionHandlingEnabled(bool handle)
370 {
371  Q_D(SimpleJob);
372  d->m_redirectionHandlingEnabled = handle;
373 }
374 
375 SimpleJob::~SimpleJob()
376 {
377  Q_D(SimpleJob);
378  // last chance to remove this job from the scheduler!
379  if (d->m_schedSerial) {
380  kDebug(7007) << "Killing job" << this << "in destructor!" << kBacktrace();
381  Scheduler::cancelJob(this);
382  }
383 }
384 
385 void SimpleJobPrivate::start(Slave *slave)
386 {
387  Q_Q(SimpleJob);
388  m_slave = slave;
389 
390  // Slave::setJob can send us SSL metadata if there is a persistent connection
391  q->connect( slave, SIGNAL(metaData(KIO::MetaData)),
392  SLOT(slotMetaData(KIO::MetaData)) );
393 
394  slave->setJob(q);
395 
396  q->connect( slave, SIGNAL(error(int,QString)),
397  SLOT(slotError(int,QString)) );
398 
399  q->connect( slave, SIGNAL(warning(QString)),
400  SLOT(slotWarning(QString)) );
401 
402  q->connect( slave, SIGNAL(infoMessage(QString)),
403  SLOT(_k_slotSlaveInfoMessage(QString)) );
404 
405  q->connect( slave, SIGNAL(connected()),
406  SLOT(slotConnected()));
407 
408  q->connect( slave, SIGNAL(finished()),
409  SLOT(slotFinished()) );
410 
411  if ((m_extraFlags & EF_TransferJobDataSent) == 0) // this is a "get" job
412  {
413  q->connect( slave, SIGNAL(totalSize(KIO::filesize_t)),
414  SLOT(slotTotalSize(KIO::filesize_t)) );
415 
416  q->connect( slave, SIGNAL(processedSize(KIO::filesize_t)),
417  SLOT(slotProcessedSize(KIO::filesize_t)) );
418 
419  q->connect( slave, SIGNAL(speed(ulong)),
420  SLOT(slotSpeed(ulong)) );
421  }
422 
423  if (ui() && ui()->window())
424  {
425  m_outgoingMetaData.insert("window-id", QString::number((qptrdiff)ui()->window()->winId()));
426  }
427 
428  if (ui() && ui()->userTimestamp())
429  {
430  m_outgoingMetaData.insert("user-timestamp", QString::number(ui()->userTimestamp()));
431  }
432 
433  if (ui() == 0) // not interactive
434  {
435  m_outgoingMetaData.insert("no-auth-prompt", "true");
436  }
437 
438  if (!m_outgoingMetaData.isEmpty())
439  {
440  KIO_ARGS << m_outgoingMetaData;
441  slave->send( CMD_META_DATA, packedArgs );
442  }
443 
444  if (!m_subUrl.isEmpty())
445  {
446  KIO_ARGS << m_subUrl;
447  slave->send( CMD_SUBURL, packedArgs );
448  }
449 
450  slave->send( m_command, m_packedArgs );
451 }
452 
453 void SimpleJobPrivate::slaveDone()
454 {
455  Q_Q(SimpleJob);
456  if (m_slave) {
457  if (m_command == CMD_OPEN) {
458  m_slave->send(CMD_CLOSE);
459  }
460  q->disconnect(m_slave); // Remove all signals between slave and job
461  }
462  // only finish a job once; Scheduler::jobFinished() resets schedSerial to zero.
463  if (m_schedSerial) {
464  Scheduler::jobFinished(q, m_slave);
465  }
466 }
467 
468 void SimpleJob::slotFinished( )
469 {
470  Q_D(SimpleJob);
471  // Return slave to the scheduler
472  d->slaveDone();
473 
474  if (!hasSubjobs())
475  {
476  if ( !error() && (d->m_command == CMD_MKDIR || d->m_command == CMD_RENAME ) )
477  {
478  if ( d->m_command == CMD_MKDIR )
479  {
480  KUrl urlDir( url() );
481  urlDir.setPath( urlDir.directory() );
482  org::kde::KDirNotify::emitFilesAdded( urlDir.url() );
483  }
484  else /*if ( m_command == CMD_RENAME )*/
485  {
486  KUrl src, dst;
487  QDataStream str( d->m_packedArgs );
488  str >> src >> dst;
489  if( src.directory() == dst.directory() ) // For the user, moving isn't renaming. Only renaming is.
490  org::kde::KDirNotify::emitFileRenamed( src.url(), dst.url() );
491 
492  org::kde::KDirNotify::emitFileMoved( src.url(), dst.url() );
493  ClipboardUpdater::update(src, dst);
494  }
495  }
496  emitResult();
497  }
498 }
499 
500 void SimpleJob::slotError( int err, const QString & errorText )
501 {
502  Q_D(SimpleJob);
503  setError( err );
504  setErrorText( errorText );
505  if ((error() == ERR_UNKNOWN_HOST) && d->m_url.host().isEmpty())
506  setErrorText( QString() );
507  // error terminates the job
508  slotFinished();
509 }
510 
511 void SimpleJob::slotWarning( const QString & errorText )
512 {
513  emit warning( this, errorText );
514 }
515 
516 void SimpleJobPrivate::_k_slotSlaveInfoMessage( const QString & msg )
517 {
518  emit q_func()->infoMessage( q_func(), msg );
519 }
520 
521 void SimpleJobPrivate::slotConnected()
522 {
523  emit q_func()->connected( q_func() );
524 }
525 
526 void SimpleJobPrivate::slotTotalSize( KIO::filesize_t size )
527 {
528  Q_Q(SimpleJob);
529  if (size != q->totalAmount(KJob::Bytes))
530  {
531  q->setTotalAmount(KJob::Bytes, size);
532  }
533 }
534 
535 void SimpleJobPrivate::slotProcessedSize( KIO::filesize_t size )
536 {
537  Q_Q(SimpleJob);
538  //kDebug(7007) << KIO::number(size);
539  q->setProcessedAmount(KJob::Bytes, size);
540 }
541 
542 void SimpleJobPrivate::slotSpeed( unsigned long speed )
543 {
544  //kDebug(7007) << speed;
545  q_func()->emitSpeed( speed );
546 }
547 
548 void SimpleJobPrivate::restartAfterRedirection(KUrl *redirectionUrl)
549 {
550  Q_Q(SimpleJob);
551  // Return slave to the scheduler while we still have the old URL in place; the scheduler
552  // requires a job URL to stay invariant while the job is running.
553  slaveDone();
554 
555  m_url = *redirectionUrl;
556  redirectionUrl->clear();
557  if ((m_extraFlags & EF_KillCalled) == 0) {
558  Scheduler::doJob(q);
559  }
560 }
561 
562 int SimpleJobPrivate::requestMessageBox(int _type, const QString& text, const QString& caption,
563  const QString& buttonYes, const QString& buttonNo,
564  const QString& iconYes, const QString& iconNo,
565  const QString& dontAskAgainName,
566  const KIO::MetaData& sslMetaData)
567 {
568  JobUiDelegate* delegate = ui();
569  if (delegate) {
570  const JobUiDelegate::MessageBoxType type = static_cast<JobUiDelegate::MessageBoxType>(_type);
571  return delegate->requestMessageBox(type, text, caption, buttonYes, buttonNo,
572  iconYes, iconNo, dontAskAgainName, sslMetaData);
573  }
574  kWarning(7007) << "JobUiDelegate not set! Returing -1";
575  return -1;
576 }
577 
578 void SimpleJob::slotMetaData( const KIO::MetaData &_metaData )
579 {
580  Q_D(SimpleJob);
581  QMapIterator<QString,QString> it (_metaData);
582  while (it.hasNext()) {
583  it.next();
584  if (it.key().startsWith(QLatin1String("{internal~"), Qt::CaseInsensitive))
585  d->m_internalMetaData.insert(it.key(), it.value());
586  else
587  d->m_incomingMetaData.insert(it.key(), it.value());
588  }
589 
590  // Update the internal meta-data values as soon as possible. Waiting until
591  // the ioslave is finished has unintended consequences if the client starts
592  // a new connection without waiting for the ioslave to finish.
593  if (!d->m_internalMetaData.isEmpty()) {
594  Scheduler::updateInternalMetaData(this);
595  }
596 }
597 
598 void SimpleJob::storeSSLSessionFromJob(const KUrl &redirectionURL)
599 {
600  Q_UNUSED(redirectionURL);
601 }
602 
603 
605 class KIO::MkdirJobPrivate: public SimpleJobPrivate
606 {
607 public:
608  MkdirJobPrivate(const KUrl& url, int command, const QByteArray &packedArgs)
609  : SimpleJobPrivate(url, command, packedArgs)
610  { }
611  KUrl m_redirectionURL;
612  void slotRedirection(const KUrl &url);
613 
620  virtual void start( Slave *slave );
621 
622  Q_DECLARE_PUBLIC(MkdirJob)
623 
624  static inline MkdirJob *newJob(const KUrl& url, int command, const QByteArray &packedArgs)
625  {
626  MkdirJob *job = new MkdirJob(*new MkdirJobPrivate(url, command, packedArgs));
627  job->setUiDelegate(new JobUiDelegate);
628  return job;
629  }
630 };
631 
632 MkdirJob::MkdirJob(MkdirJobPrivate &dd)
633  : SimpleJob(dd)
634 {
635 }
636 
637 MkdirJob::~MkdirJob()
638 {
639 }
640 
641 void MkdirJobPrivate::start(Slave *slave)
642 {
643  Q_Q(MkdirJob);
644  q->connect( slave, SIGNAL(redirection(KUrl)),
645  SLOT(slotRedirection(KUrl)) );
646 
647  SimpleJobPrivate::start(slave);
648 }
649 
650 // Slave got a redirection request
651 void MkdirJobPrivate::slotRedirection( const KUrl &url)
652 {
653  Q_Q(MkdirJob);
654  kDebug(7007) << url;
655  if (!KAuthorized::authorizeUrlAction("redirect", m_url, url))
656  {
657  kWarning(7007) << "Redirection from" << m_url << "to" << url << "REJECTED!";
658  q->setError( ERR_ACCESS_DENIED );
659  q->setErrorText( url.pathOrUrl() );
660  return;
661  }
662  m_redirectionURL = url; // We'll remember that when the job finishes
663  // Tell the user that we haven't finished yet
664  emit q->redirection(q, m_redirectionURL);
665 }
666 
667 void MkdirJob::slotFinished()
668 {
669  Q_D(MkdirJob);
670 
671  if ( !d->m_redirectionURL.isEmpty() && d->m_redirectionURL.isValid() )
672  {
673  //kDebug(7007) << "MkdirJob: Redirection to " << m_redirectionURL;
674  if (queryMetaData("permanent-redirect")=="true")
675  emit permanentRedirection(this, d->m_url, d->m_redirectionURL);
676 
677  if ( d->m_redirectionHandlingEnabled )
678  {
679  KUrl dummyUrl;
680  int permissions;
681  QDataStream istream( d->m_packedArgs );
682  istream >> dummyUrl >> permissions;
683 
684  d->m_packedArgs.truncate(0);
685  QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly );
686  stream << d->m_redirectionURL << permissions;
687 
688  d->restartAfterRedirection(&d->m_redirectionURL);
689  return;
690  }
691  }
692 
693  // Return slave to the scheduler
694  SimpleJob::slotFinished();
695 }
696 
697 SimpleJob *KIO::mkdir( const KUrl& url, int permissions )
698 {
699  //kDebug(7007) << "mkdir " << url;
700  KIO_ARGS << url << permissions;
701  return MkdirJobPrivate::newJob(url, CMD_MKDIR, packedArgs);
702 }
703 
704 SimpleJob *KIO::rmdir( const KUrl& url )
705 {
706  //kDebug(7007) << "rmdir " << url;
707  KIO_ARGS << url << qint8(false); // isFile is false
708  return SimpleJobPrivate::newJob(url, CMD_DEL, packedArgs);
709 }
710 
711 SimpleJob *KIO::chmod( const KUrl& url, int permissions )
712 {
713  //kDebug(7007) << "chmod " << url;
714  KIO_ARGS << url << permissions;
715  return SimpleJobPrivate::newJob(url, CMD_CHMOD, packedArgs);
716 }
717 
718 SimpleJob *KIO::chown( const KUrl& url, const QString& owner, const QString& group )
719 {
720  KIO_ARGS << url << owner << group;
721  return SimpleJobPrivate::newJob(url, CMD_CHOWN, packedArgs);
722 }
723 
724 SimpleJob *KIO::setModificationTime( const KUrl& url, const QDateTime& mtime )
725 {
726  //kDebug(7007) << "setModificationTime " << url << " " << mtime;
727  KIO_ARGS << url << mtime;
728  return SimpleJobPrivate::newJobNoUi(url, CMD_SETMODIFICATIONTIME, packedArgs);
729 }
730 
731 SimpleJob *KIO::rename( const KUrl& src, const KUrl & dest, JobFlags flags )
732 {
733  //kDebug(7007) << "rename " << src << " " << dest;
734  KIO_ARGS << src << dest << (qint8) (flags & Overwrite);
735  return SimpleJobPrivate::newJob(src, CMD_RENAME, packedArgs);
736 }
737 
738 SimpleJob *KIO::symlink( const QString& target, const KUrl & dest, JobFlags flags )
739 {
740  //kDebug(7007) << "symlink target=" << target << " " << dest;
741  KIO_ARGS << target << dest << (qint8) (flags & Overwrite);
742  return SimpleJobPrivate::newJob(dest, CMD_SYMLINK, packedArgs, flags);
743 }
744 
745 SimpleJob *KIO::special(const KUrl& url, const QByteArray & data, JobFlags flags)
746 {
747  //kDebug(7007) << "special " << url;
748  return SimpleJobPrivate::newJob(url, CMD_SPECIAL, data, flags);
749 }
750 
751 SimpleJob *KIO::mount( bool ro, const QByteArray& fstype, const QString& dev, const QString& point, JobFlags flags )
752 {
753  KIO_ARGS << int(1) << qint8( ro ? 1 : 0 )
754  << QString::fromLatin1(fstype) << dev << point;
755  SimpleJob *job = special( KUrl("file:/"), packedArgs, flags );
756  if (!(flags & HideProgressInfo)) {
757  KIO::JobPrivate::emitMounting(job, dev, point);
758  }
759  return job;
760 }
761 
762 SimpleJob *KIO::unmount( const QString& point, JobFlags flags )
763 {
764  KIO_ARGS << int(2) << point;
765  SimpleJob *job = special( KUrl("file:/"), packedArgs, flags );
766  if (!(flags & HideProgressInfo)) {
767  KIO::JobPrivate::emitUnmounting(job, point);
768  }
769  return job;
770 }
771 
772 
773 
775 
776 class KIO::StatJobPrivate: public SimpleJobPrivate
777 {
778 public:
779  inline StatJobPrivate(const KUrl& url, int command, const QByteArray &packedArgs)
780  : SimpleJobPrivate(url, command, packedArgs), m_bSource(true), m_details(2)
781  {}
782 
783  UDSEntry m_statResult;
784  KUrl m_redirectionURL;
785  bool m_bSource;
786  short int m_details;
787  void slotStatEntry( const KIO::UDSEntry & entry );
788  void slotRedirection( const KUrl &url);
789 
796  virtual void start( Slave *slave );
797 
798  Q_DECLARE_PUBLIC(StatJob)
799 
800  static inline StatJob *newJob(const KUrl& url, int command, const QByteArray &packedArgs,
801  JobFlags flags )
802  {
803  StatJob *job = new StatJob(*new StatJobPrivate(url, command, packedArgs));
804  job->setUiDelegate(new JobUiDelegate);
805  if (!(flags & HideProgressInfo)) {
806  KIO::getJobTracker()->registerJob(job);
807  emitStating(job, url);
808  }
809  return job;
810  }
811 };
812 
813 StatJob::StatJob(StatJobPrivate &dd)
814  : SimpleJob(dd)
815 {
816 }
817 
818 StatJob::~StatJob()
819 {
820 }
821 
822 #ifndef KDE_NO_DEPRECATED
823 void StatJob::setSide( bool source )
824 {
825  d_func()->m_bSource = source;
826 }
827 #endif
828 
829 void StatJob::setSide( StatSide side )
830 {
831  d_func()->m_bSource = side == SourceSide;
832 }
833 
834 void StatJob::setDetails( short int details )
835 {
836  d_func()->m_details = details;
837 }
838 
839 const UDSEntry & StatJob::statResult() const
840 {
841  return d_func()->m_statResult;
842 }
843 
844 KUrl StatJob::mostLocalUrl() const
845 {
846  if (!url().isLocalFile()) {
847  const UDSEntry& udsEntry = d_func()->m_statResult;
848  const QString path = udsEntry.stringValue( KIO::UDSEntry::UDS_LOCAL_PATH );
849  if (!path.isEmpty())
850  return KUrl(path);
851  }
852  return url();
853 }
854 
855 void StatJobPrivate::start(Slave *slave)
856 {
857  Q_Q(StatJob);
858  m_outgoingMetaData.insert( "statSide", m_bSource ? "source" : "dest" );
859  m_outgoingMetaData.insert( "details", QString::number(m_details) );
860 
861  q->connect( slave, SIGNAL(statEntry(KIO::UDSEntry)),
862  SLOT(slotStatEntry(KIO::UDSEntry)) );
863  q->connect( slave, SIGNAL(redirection(KUrl)),
864  SLOT(slotRedirection(KUrl)) );
865 
866  SimpleJobPrivate::start(slave);
867 }
868 
869 void StatJobPrivate::slotStatEntry( const KIO::UDSEntry & entry )
870 {
871  //kDebug(7007);
872  m_statResult = entry;
873 }
874 
875 // Slave got a redirection request
876 void StatJobPrivate::slotRedirection( const KUrl &url)
877 {
878  Q_Q(StatJob);
879  kDebug(7007) << m_url << "->" << url;
880  if (!KAuthorized::authorizeUrlAction("redirect", m_url, url))
881  {
882  kWarning(7007) << "Redirection from " << m_url << " to " << url << " REJECTED!";
883  q->setError( ERR_ACCESS_DENIED );
884  q->setErrorText( url.pathOrUrl() );
885  return;
886  }
887  m_redirectionURL = url; // We'll remember that when the job finishes
888  // Tell the user that we haven't finished yet
889  emit q->redirection(q, m_redirectionURL);
890 }
891 
892 void StatJob::slotFinished()
893 {
894  Q_D(StatJob);
895 
896  if ( !d->m_redirectionURL.isEmpty() && d->m_redirectionURL.isValid() )
897  {
898  //kDebug(7007) << "StatJob: Redirection to " << m_redirectionURL;
899  if (queryMetaData("permanent-redirect")=="true")
900  emit permanentRedirection(this, d->m_url, d->m_redirectionURL);
901 
902  if ( d->m_redirectionHandlingEnabled )
903  {
904  d->m_packedArgs.truncate(0);
905  QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly );
906  stream << d->m_redirectionURL;
907 
908  d->restartAfterRedirection(&d->m_redirectionURL);
909  return;
910  }
911  }
912 
913  // Return slave to the scheduler
914  SimpleJob::slotFinished();
915 }
916 
917 void StatJob::slotMetaData( const KIO::MetaData &_metaData)
918 {
919  Q_D(StatJob);
920  SimpleJob::slotMetaData(_metaData);
921  storeSSLSessionFromJob(d->m_redirectionURL);
922 }
923 
924 StatJob *KIO::stat(const KUrl& url, JobFlags flags)
925 {
926  // Assume sideIsSource. Gets are more common than puts.
927  return stat( url, StatJob::SourceSide, 2, flags );
928 }
929 
930 StatJob *KIO::mostLocalUrl(const KUrl& url, JobFlags flags)
931 {
932  StatJob* job = stat( url, StatJob::SourceSide, 2, flags );
933  if (url.isLocalFile()) {
934  QTimer::singleShot(0, job, SLOT(slotFinished()));
935  Scheduler::cancelJob(job); // deletes the slave if not 0
936  }
937  return job;
938 }
939 
940 #ifndef KDE_NO_DEPRECATED
941 StatJob *KIO::stat(const KUrl& url, bool sideIsSource, short int details, JobFlags flags )
942 {
943  //kDebug(7007) << "stat" << url;
944  KIO_ARGS << url;
945  StatJob * job = StatJobPrivate::newJob(url, CMD_STAT, packedArgs, flags);
946  job->setSide( sideIsSource ? StatJob::SourceSide : StatJob::DestinationSide );
947  job->setDetails( details );
948  return job;
949 }
950 #endif
951 
952 StatJob *KIO::stat(const KUrl& url, KIO::StatJob::StatSide side, short int details, JobFlags flags )
953 {
954  //kDebug(7007) << "stat" << url;
955  KIO_ARGS << url;
956  StatJob * job = StatJobPrivate::newJob(url, CMD_STAT, packedArgs, flags);
957  job->setSide( side );
958  job->setDetails( details );
959  return job;
960 }
961 
962 SimpleJob *KIO::http_update_cache( const KUrl& url, bool no_cache, time_t expireDate)
963 {
964  Q_ASSERT(url.protocol() == "http" || url.protocol() == "https");
965  // Send http update_cache command (2)
966  KIO_ARGS << (int)2 << url << no_cache << qlonglong(expireDate);
967  SimpleJob * job = SimpleJobPrivate::newJob(url, CMD_SPECIAL, packedArgs);
968  Scheduler::setJobPriority(job, 1);
969  return job;
970 }
971 
973 
974 TransferJob::TransferJob(TransferJobPrivate &dd)
975  : SimpleJob(dd)
976 {
977  Q_D(TransferJob);
978  if (d->m_command == CMD_PUT) {
979  d->m_extraFlags |= JobPrivate::EF_TransferJobDataSent;
980  }
981 }
982 
983 TransferJob::~TransferJob()
984 {
985 }
986 
987 // Slave sends data
988 void TransferJob::slotData( const QByteArray &_data)
989 {
990  Q_D(TransferJob);
991  if (d->m_command == CMD_GET && !d->m_isMimetypeEmitted) {
992  kWarning(7007) << "mimeType() not emitted when sending first data!; job URL ="
993  << d->m_url << "data size =" << _data.size();
994  }
995  // shut up the warning, HACK: downside is that it changes the meaning of the variable
996  d->m_isMimetypeEmitted = true;
997 
998  if (d->m_redirectionURL.isEmpty() || !d->m_redirectionURL.isValid() || error()) {
999  emit data(this, _data);
1000  }
1001 }
1002 
1003 void KIO::TransferJob::setTotalSize(KIO::filesize_t bytes)
1004 {
1005  setTotalAmount(KJob::Bytes, bytes);
1006 }
1007 
1008 // Slave got a redirection request
1009 void TransferJob::slotRedirection( const KUrl &url)
1010 {
1011  Q_D(TransferJob);
1012  kDebug(7007) << url;
1013  if (!KAuthorized::authorizeUrlAction("redirect", d->m_url, url))
1014  {
1015  kWarning(7007) << "Redirection from " << d->m_url << " to " << url << " REJECTED!";
1016  return;
1017  }
1018 
1019  // Some websites keep redirecting to themselves where each redirection
1020  // acts as the stage in a state-machine. We define "endless redirections"
1021  // as 5 redirections to the same URL.
1022  if (d->m_redirectionList.count(url) > 5)
1023  {
1024  kDebug(7007) << "CYCLIC REDIRECTION!";
1025  setError( ERR_CYCLIC_LINK );
1026  setErrorText( d->m_url.pathOrUrl() );
1027  }
1028  else
1029  {
1030  d->m_redirectionURL = url; // We'll remember that when the job finishes
1031  d->m_redirectionList.append(url);
1032  d->m_outgoingMetaData["ssl_was_in_use"] = d->m_incomingMetaData["ssl_in_use"];
1033  // Tell the user that we haven't finished yet
1034  emit redirection(this, d->m_redirectionURL);
1035  }
1036 }
1037 
1038 void TransferJob::slotFinished()
1039 {
1040  Q_D(TransferJob);
1041 
1042  kDebug(7007) << d->m_url;
1043  if (!d->m_redirectionURL.isEmpty() && d->m_redirectionURL.isValid()) {
1044 
1045  //kDebug(7007) << "Redirection to" << m_redirectionURL;
1046  if (queryMetaData("permanent-redirect")=="true")
1047  emit permanentRedirection(this, d->m_url, d->m_redirectionURL);
1048 
1049  if (d->m_redirectionHandlingEnabled) {
1050  // Honour the redirection
1051  // We take the approach of "redirecting this same job"
1052  // Another solution would be to create a subjob, but the same problem
1053  // happens (unpacking+repacking)
1054  d->staticData.truncate(0);
1055  d->m_incomingMetaData.clear();
1056  if (queryMetaData("cache") != "reload")
1057  addMetaData("cache","refresh");
1058  d->m_internalSuspended = false;
1059  // The very tricky part is the packed arguments business
1060  QString dummyStr;
1061  KUrl dummyUrl;
1062  QDataStream istream( d->m_packedArgs );
1063  switch( d->m_command ) {
1064  case CMD_GET: {
1065  d->m_packedArgs.truncate(0);
1066  QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly );
1067  stream << d->m_redirectionURL;
1068  break;
1069  }
1070  case CMD_PUT: {
1071  int permissions;
1072  qint8 iOverwrite, iResume;
1073  istream >> dummyUrl >> iOverwrite >> iResume >> permissions;
1074  d->m_packedArgs.truncate(0);
1075  QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly );
1076  stream << d->m_redirectionURL << iOverwrite << iResume << permissions;
1077  break;
1078  }
1079  case CMD_SPECIAL: {
1080  int specialcmd;
1081  istream >> specialcmd;
1082  if (specialcmd == 1) // HTTP POST
1083  {
1084  d->m_outgoingMetaData.remove(QLatin1String("content-type"));
1085  addMetaData("cache","reload");
1086  d->m_packedArgs.truncate(0);
1087  QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly );
1088  stream << d->m_redirectionURL;
1089  d->m_command = CMD_GET;
1090  }
1091  break;
1092  }
1093  }
1094  d->restartAfterRedirection(&d->m_redirectionURL);
1095  return;
1096  }
1097  }
1098 
1099  SimpleJob::slotFinished();
1100 }
1101 
1102 void TransferJob::setAsyncDataEnabled(bool enabled)
1103 {
1104  Q_D(TransferJob);
1105  if (enabled)
1106  d->m_extraFlags |= JobPrivate::EF_TransferJobAsync;
1107  else
1108  d->m_extraFlags &= ~JobPrivate::EF_TransferJobAsync;
1109 }
1110 
1111 void TransferJob::sendAsyncData(const QByteArray &dataForSlave)
1112 {
1113  Q_D(TransferJob);
1114  if (d->m_extraFlags & JobPrivate::EF_TransferJobNeedData)
1115  {
1116  d->m_slave->send( MSG_DATA, dataForSlave );
1117  if (d->m_extraFlags & JobPrivate::EF_TransferJobDataSent) // put job -> emit progress
1118  {
1119  KIO::filesize_t size = processedAmount(KJob::Bytes)+dataForSlave.size();
1120  setProcessedAmount(KJob::Bytes, size);
1121  }
1122  }
1123 
1124  d->m_extraFlags &= ~JobPrivate::EF_TransferJobNeedData;
1125 }
1126 
1127 #ifndef KDE_NO_DEPRECATED
1128 void TransferJob::setReportDataSent(bool enabled)
1129 {
1130  Q_D(TransferJob);
1131  if (enabled)
1132  d->m_extraFlags |= JobPrivate::EF_TransferJobDataSent;
1133  else
1134  d->m_extraFlags &= ~JobPrivate::EF_TransferJobDataSent;
1135 }
1136 #endif
1137 
1138 #ifndef KDE_NO_DEPRECATED
1139 bool TransferJob::reportDataSent() const
1140 {
1141  return (d_func()->m_extraFlags & JobPrivate::EF_TransferJobDataSent);
1142 }
1143 #endif
1144 
1145 QString TransferJob::mimetype() const
1146 {
1147  return d_func()->m_mimetype;
1148 }
1149 
1150 // Slave requests data
1151 void TransferJob::slotDataReq()
1152 {
1153  Q_D(TransferJob);
1154  QByteArray dataForSlave;
1155 
1156  d->m_extraFlags |= JobPrivate::EF_TransferJobNeedData;
1157 
1158  if (!d->staticData.isEmpty())
1159  {
1160  dataForSlave = d->staticData;
1161  d->staticData.clear();
1162  }
1163  else
1164  {
1165  emit dataReq( this, dataForSlave);
1166 
1167  if (d->m_extraFlags & JobPrivate::EF_TransferJobAsync)
1168  return;
1169  }
1170 
1171  static const int max_size = 14 * 1024 * 1024;
1172  if (dataForSlave.size() > max_size)
1173  {
1174  kDebug(7007) << "send " << dataForSlave.size() / 1024 / 1024 << "MB of data in TransferJob::dataReq. This needs to be splitted, which requires a copy. Fix the application.\n";
1175  d->staticData = QByteArray(dataForSlave.data() + max_size , dataForSlave.size() - max_size);
1176  dataForSlave.truncate(max_size);
1177  }
1178 
1179  sendAsyncData(dataForSlave);
1180 
1181  if (d->m_subJob)
1182  {
1183  // Bitburger protocol in action
1184  d->internalSuspend(); // Wait for more data from subJob.
1185  d->m_subJob->d_func()->internalResume(); // Ask for more!
1186  }
1187 }
1188 
1189 void TransferJob::slotMimetype( const QString& type )
1190 {
1191  Q_D(TransferJob);
1192  d->m_mimetype = type;
1193  if (d->m_command == CMD_GET && d->m_isMimetypeEmitted) {
1194  kWarning(7007) << "mimetype() emitted again, or after sending first data!; job URL ="
1195  << d->m_url;
1196  }
1197  d->m_isMimetypeEmitted = true;
1198  emit mimetype( this, type );
1199 }
1200 
1201 
1202 void TransferJobPrivate::internalSuspend()
1203 {
1204  m_internalSuspended = true;
1205  if (m_slave)
1206  m_slave->suspend();
1207 }
1208 
1209 void TransferJobPrivate::internalResume()
1210 {
1211  m_internalSuspended = false;
1212  if ( m_slave && !suspended )
1213  m_slave->resume();
1214 }
1215 
1216 bool TransferJob::doResume()
1217 {
1218  Q_D(TransferJob);
1219  if ( !SimpleJob::doResume() )
1220  return false;
1221  if ( d->m_internalSuspended )
1222  d->internalSuspend();
1223  return true;
1224 }
1225 
1226 bool TransferJob::isErrorPage() const
1227 {
1228  return d_func()->m_errorPage;
1229 }
1230 
1231 void TransferJobPrivate::start(Slave *slave)
1232 {
1233  Q_Q(TransferJob);
1234  Q_ASSERT(slave);
1235  JobPrivate::emitTransferring(q, m_url);
1236  q->connect( slave, SIGNAL(data(QByteArray)),
1237  SLOT(slotData(QByteArray)) );
1238 
1239  if (m_outgoingDataSource)
1240  q->connect( slave, SIGNAL(dataReq()),
1241  SLOT(slotDataReqFromDevice()) );
1242  else
1243  q->connect( slave, SIGNAL(dataReq()),
1244  SLOT(slotDataReq()) );
1245 
1246  q->connect( slave, SIGNAL(redirection(KUrl)),
1247  SLOT(slotRedirection(KUrl)) );
1248 
1249  q->connect( slave, SIGNAL(mimeType(QString)),
1250  SLOT(slotMimetype(QString)) );
1251 
1252  q->connect( slave, SIGNAL(errorPage()),
1253  SLOT(slotErrorPage()) );
1254 
1255  q->connect( slave, SIGNAL(needSubUrlData()),
1256  SLOT(slotNeedSubUrlData()) );
1257 
1258  q->connect( slave, SIGNAL(canResume(KIO::filesize_t)),
1259  SLOT(slotCanResume(KIO::filesize_t)) );
1260 
1261  if (slave->suspended())
1262  {
1263  m_mimetype = "unknown";
1264  // WABA: The slave was put on hold. Resume operation.
1265  slave->resume();
1266  }
1267 
1268  SimpleJobPrivate::start(slave);
1269  if (m_internalSuspended)
1270  slave->suspend();
1271 }
1272 
1273 void TransferJobPrivate::slotNeedSubUrlData()
1274 {
1275  Q_Q(TransferJob);
1276  // Job needs data from subURL.
1277  m_subJob = KIO::get( m_subUrl, NoReload, HideProgressInfo);
1278  internalSuspend(); // Put job on hold until we have some data.
1279  q->connect(m_subJob, SIGNAL(data(KIO::Job*,QByteArray)),
1280  SLOT(slotSubUrlData(KIO::Job*,QByteArray)));
1281  q->addSubjob(m_subJob);
1282 }
1283 
1284 void TransferJobPrivate::slotSubUrlData(KIO::Job*, const QByteArray &data)
1285 {
1286  // The Alternating Bitburg protocol in action again.
1287  staticData = data;
1288  m_subJob->d_func()->internalSuspend(); // Put job on hold until we have delivered the data.
1289  internalResume(); // Activate ourselves again.
1290 }
1291 
1292 void TransferJob::slotMetaData( const KIO::MetaData &_metaData)
1293 {
1294  Q_D(TransferJob);
1295  SimpleJob::slotMetaData(_metaData);
1296  storeSSLSessionFromJob(d->m_redirectionURL);
1297 }
1298 
1299 void TransferJobPrivate::slotErrorPage()
1300 {
1301  m_errorPage = true;
1302 }
1303 
1304 void TransferJobPrivate::slotCanResume( KIO::filesize_t offset )
1305 {
1306  Q_Q(TransferJob);
1307  emit q->canResume(q, offset);
1308 }
1309 
1310 void TransferJobPrivate::slotDataReqFromDevice()
1311 {
1312  Q_Q(TransferJob);
1313 
1314  QByteArray dataForSlave;
1315 
1316  m_extraFlags |= JobPrivate::EF_TransferJobNeedData;
1317 
1318  if (m_outgoingDataSource)
1319  dataForSlave = m_outgoingDataSource.data()->read(MAX_READ_BUF_SIZE);
1320 
1321  if (dataForSlave.isEmpty())
1322  {
1323  emit q->dataReq(q, dataForSlave);
1324  if (m_extraFlags & JobPrivate::EF_TransferJobAsync)
1325  return;
1326  }
1327 
1328  q->sendAsyncData(dataForSlave);
1329 
1330  if (m_subJob)
1331  {
1332  // Bitburger protocol in action
1333  internalSuspend(); // Wait for more data from subJob.
1334  m_subJob->d_func()->internalResume(); // Ask for more!
1335  }
1336 }
1337 
1338 void TransferJob::slotResult( KJob *job)
1339 {
1340  Q_D(TransferJob);
1341  // This can only be our suburl.
1342  Q_ASSERT(job == d->m_subJob);
1343 
1344  SimpleJob::slotResult( job );
1345 
1346  if (!error() && job == d->m_subJob)
1347  {
1348  d->m_subJob = 0; // No action required
1349  d->internalResume(); // Make sure we get the remaining data.
1350  }
1351 }
1352 
1353 void TransferJob::setModificationTime( const QDateTime& mtime )
1354 {
1355  addMetaData( "modified", mtime.toString( Qt::ISODate ) );
1356 }
1357 
1358 TransferJob *KIO::get( const KUrl& url, LoadType reload, JobFlags flags )
1359 {
1360  // Send decoded path and encoded query
1361  KIO_ARGS << url;
1362  TransferJob * job = TransferJobPrivate::newJob(url, CMD_GET, packedArgs,
1363  QByteArray(), flags);
1364  if (reload == Reload)
1365  job->addMetaData("cache", "reload");
1366  return job;
1367 }
1368 
1369 class KIO::StoredTransferJobPrivate: public TransferJobPrivate
1370 {
1371 public:
1372  StoredTransferJobPrivate(const KUrl& url, int command,
1373  const QByteArray &packedArgs,
1374  const QByteArray &_staticData)
1375  : TransferJobPrivate(url, command, packedArgs, _staticData),
1376  m_uploadOffset( 0 )
1377  {}
1378  StoredTransferJobPrivate(const KUrl& url, int command,
1379  const QByteArray &packedArgs,
1380  QIODevice* ioDevice)
1381  : TransferJobPrivate(url, command, packedArgs, ioDevice),
1382  m_uploadOffset( 0 )
1383  {}
1384 
1385  QByteArray m_data;
1386  int m_uploadOffset;
1387 
1388  void slotStoredData( KIO::Job *job, const QByteArray &data );
1389  void slotStoredDataReq( KIO::Job *job, QByteArray &data );
1390 
1391  Q_DECLARE_PUBLIC(StoredTransferJob)
1392 
1393  static inline StoredTransferJob *newJob(const KUrl &url, int command,
1394  const QByteArray &packedArgs,
1395  const QByteArray &staticData, JobFlags flags)
1396  {
1397  StoredTransferJob *job = new StoredTransferJob(
1398  *new StoredTransferJobPrivate(url, command, packedArgs, staticData));
1399  job->setUiDelegate(new JobUiDelegate);
1400  if (!(flags & HideProgressInfo))
1401  KIO::getJobTracker()->registerJob(job);
1402  return job;
1403  }
1404 
1405  static inline StoredTransferJob *newJob(const KUrl &url, int command,
1406  const QByteArray &packedArgs,
1407  QIODevice* ioDevice, JobFlags flags)
1408  {
1409  StoredTransferJob *job = new StoredTransferJob(
1410  *new StoredTransferJobPrivate(url, command, packedArgs, ioDevice));
1411  job->setUiDelegate(new JobUiDelegate);
1412  if (!(flags & HideProgressInfo))
1413  KIO::getJobTracker()->registerJob(job);
1414  return job;
1415  }
1416 };
1417 
1418 namespace KIO {
1419  class PostErrorJob : public StoredTransferJob
1420  {
1421  public:
1422 
1423  PostErrorJob(int _error, const QString& url, const QByteArray &packedArgs, const QByteArray &postData)
1424  : StoredTransferJob(*new StoredTransferJobPrivate(KUrl(), CMD_SPECIAL, packedArgs, postData))
1425  {
1426  setError( _error );
1427  setErrorText( url );
1428  }
1429 
1430  PostErrorJob(int _error, const QString& url, const QByteArray &packedArgs, QIODevice* ioDevice)
1431  : StoredTransferJob(*new StoredTransferJobPrivate(KUrl(), CMD_SPECIAL, packedArgs, ioDevice))
1432  {
1433  setError( _error );
1434  setErrorText( url );
1435  }
1436  };
1437 }
1438 
1439 static int isUrlPortBad(const KUrl& url)
1440 {
1441  int _error = 0;
1442 
1443  // filter out some malicious ports
1444  static const int bad_ports[] = {
1445  1, // tcpmux
1446  7, // echo
1447  9, // discard
1448  11, // systat
1449  13, // daytime
1450  15, // netstat
1451  17, // qotd
1452  19, // chargen
1453  20, // ftp-data
1454  21, // ftp-cntl
1455  22, // ssh
1456  23, // telnet
1457  25, // smtp
1458  37, // time
1459  42, // name
1460  43, // nicname
1461  53, // domain
1462  77, // priv-rjs
1463  79, // finger
1464  87, // ttylink
1465  95, // supdup
1466  101, // hostriame
1467  102, // iso-tsap
1468  103, // gppitnp
1469  104, // acr-nema
1470  109, // pop2
1471  110, // pop3
1472  111, // sunrpc
1473  113, // auth
1474  115, // sftp
1475  117, // uucp-path
1476  119, // nntp
1477  123, // NTP
1478  135, // loc-srv / epmap
1479  139, // netbios
1480  143, // imap2
1481  179, // BGP
1482  389, // ldap
1483  512, // print / exec
1484  513, // login
1485  514, // shell
1486  515, // printer
1487  526, // tempo
1488  530, // courier
1489  531, // Chat
1490  532, // netnews
1491  540, // uucp
1492  556, // remotefs
1493  587, // sendmail
1494  601, //
1495  989, // ftps data
1496  990, // ftps
1497  992, // telnets
1498  993, // imap/SSL
1499  995, // pop3/SSL
1500  1080, // SOCKS
1501  2049, // nfs
1502  4045, // lockd
1503  6000, // x11
1504  6667, // irc
1505  0};
1506  if (url.port() != 80)
1507  {
1508  const int port = url.port();
1509  for (int cnt=0; bad_ports[cnt] && bad_ports[cnt] <= port; ++cnt)
1510  if (port == bad_ports[cnt])
1511  {
1512  _error = KIO::ERR_POST_DENIED;
1513  break;
1514  }
1515  }
1516 
1517  if ( _error )
1518  {
1519  static bool override_loaded = false;
1520  static QList< int >* overriden_ports = NULL;
1521  if( !override_loaded ) {
1522  KConfig cfg( "kio_httprc" );
1523  overriden_ports = new QList< int >;
1524  *overriden_ports = cfg.group(QString()).readEntry( "OverriddenPorts", QList<int>() );
1525  override_loaded = true;
1526  }
1527  for( QList< int >::ConstIterator it = overriden_ports->constBegin();
1528  it != overriden_ports->constEnd();
1529  ++it ) {
1530  if( overriden_ports->contains( url.port())) {
1531  _error = 0;
1532  }
1533  }
1534  }
1535 
1536  // filter out non https? protocols
1537  if ((url.protocol() != "http") && (url.protocol() != "https" ))
1538  _error = KIO::ERR_POST_DENIED;
1539 
1540  if (!_error && !KAuthorized::authorizeUrlAction("open", KUrl(), url))
1541  _error = KIO::ERR_ACCESS_DENIED;
1542 
1543  return _error;
1544 }
1545 
1546 static KIO::PostErrorJob* precheckHttpPost( const KUrl& url, QIODevice* ioDevice, JobFlags flags )
1547 {
1548  // if request is not valid, return an invalid transfer job
1549  const int _error = isUrlPortBad(url);
1550 
1551  if (_error)
1552  {
1553  KIO_ARGS << (int)1 << url;
1554  PostErrorJob * job = new PostErrorJob(_error, url.pathOrUrl(), packedArgs, ioDevice);
1555  job->setUiDelegate(new JobUiDelegate());
1556  if (!(flags & HideProgressInfo)) {
1557  KIO::getJobTracker()->registerJob(job);
1558  }
1559  return job;
1560  }
1561 
1562  // all is ok, return 0
1563  return 0;
1564 }
1565 
1566 static KIO::PostErrorJob* precheckHttpPost( const KUrl& url, const QByteArray& postData, JobFlags flags )
1567 {
1568  // if request is not valid, return an invalid transfer job
1569  const int _error = isUrlPortBad(url);
1570 
1571  if (_error)
1572  {
1573  KIO_ARGS << (int)1 << url;
1574  PostErrorJob * job = new PostErrorJob(_error, url.pathOrUrl(), packedArgs, postData);
1575  job->setUiDelegate(new JobUiDelegate());
1576  if (!(flags & HideProgressInfo)) {
1577  KIO::getJobTracker()->registerJob(job);
1578  }
1579  return job;
1580  }
1581 
1582  // all is ok, return 0
1583  return 0;
1584 }
1585 
1586 TransferJob *KIO::http_post( const KUrl& url, const QByteArray &postData, JobFlags flags )
1587 {
1588  bool redirection = false;
1589  KUrl _url(url);
1590  if (_url.path().isEmpty())
1591  {
1592  redirection = true;
1593  _url.setPath("/");
1594  }
1595 
1596  TransferJob* job = precheckHttpPost(_url, postData, flags);
1597  if (job)
1598  return job;
1599 
1600  // Send http post command (1), decoded path and encoded query
1601  KIO_ARGS << (int)1 << _url << static_cast<qint64>(postData.size());
1602  job = TransferJobPrivate::newJob(_url, CMD_SPECIAL, packedArgs, postData, flags);
1603 
1604  if (redirection)
1605  QTimer::singleShot(0, job, SLOT(slotPostRedirection()) );
1606 
1607  return job;
1608 }
1609 
1610 TransferJob *KIO::http_post( const KUrl& url, QIODevice* ioDevice, qint64 size, JobFlags flags )
1611 {
1612  bool redirection = false;
1613  KUrl _url(url);
1614  if (_url.path().isEmpty())
1615  {
1616  redirection = true;
1617  _url.setPath("/");
1618  }
1619 
1620  TransferJob* job = precheckHttpPost(_url, ioDevice, flags);
1621  if (job)
1622  return job;
1623 
1624  // If no size is specified and the QIODevice is not a sequential one,
1625  // attempt to obtain the size information from it.
1626  Q_ASSERT(ioDevice);
1627  if (size < 0)
1628  size = ((ioDevice && !ioDevice->isSequential()) ? ioDevice->size() : -1);
1629 
1630  // Send http post command (1), decoded path and encoded query
1631  KIO_ARGS << (int)1 << _url << size;
1632  job = TransferJobPrivate::newJob(_url, CMD_SPECIAL, packedArgs, ioDevice, flags);
1633 
1634  if (redirection)
1635  QTimer::singleShot(0, job, SLOT(slotPostRedirection()) );
1636 
1637  return job;
1638 }
1639 
1640 TransferJob* KIO::http_delete(const KUrl& url, JobFlags flags)
1641 {
1642  // Send decoded path and encoded query
1643  KIO_ARGS << url;
1644  TransferJob * job = TransferJobPrivate::newJob(url, CMD_DEL, packedArgs,
1645  QByteArray(), flags);
1646  return job;
1647 }
1648 
1649 StoredTransferJob *KIO::storedHttpPost( const QByteArray& postData, const KUrl& url, JobFlags flags )
1650 {
1651  KUrl _url(url);
1652  if (_url.path().isEmpty())
1653  {
1654  _url.setPath("/");
1655  }
1656 
1657  StoredTransferJob* job = precheckHttpPost(_url, postData, flags);
1658  if (job)
1659  return job;
1660 
1661  // Send http post command (1), decoded path and encoded query
1662  KIO_ARGS << (int)1 << _url << static_cast<qint64>(postData.size());
1663  job = StoredTransferJobPrivate::newJob(_url, CMD_SPECIAL, packedArgs, postData, flags );
1664  return job;
1665 }
1666 
1667 StoredTransferJob *KIO::storedHttpPost( QIODevice* ioDevice, const KUrl& url, qint64 size, JobFlags flags )
1668 {
1669  KUrl _url(url);
1670  if (_url.path().isEmpty())
1671  {
1672  _url.setPath("/");
1673  }
1674 
1675  StoredTransferJob* job = precheckHttpPost(_url, ioDevice, flags);
1676  if (job)
1677  return job;
1678 
1679  // If no size is specified and the QIODevice is not a sequential one,
1680  // attempt to obtain the size information from it.
1681  Q_ASSERT(ioDevice);
1682  if (size < 0)
1683  size = ((ioDevice && !ioDevice->isSequential()) ? ioDevice->size() : -1);
1684 
1685  // Send http post command (1), decoded path and encoded query
1686  KIO_ARGS << (int)1 << _url << size;
1687  job = StoredTransferJobPrivate::newJob(_url, CMD_SPECIAL, packedArgs, ioDevice, flags );
1688  return job;
1689 }
1690 
1691 // http post got redirected from http://host to http://host/ by TransferJob
1692 // We must do this redirection ourselves because redirections by the
1693 // slave change post jobs into get jobs.
1694 void TransferJobPrivate::slotPostRedirection()
1695 {
1696  Q_Q(TransferJob);
1697  kDebug(7007) << "TransferJob::slotPostRedirection(" << m_url << ")";
1698  // Tell the user about the new url.
1699  emit q->redirection(q, m_url);
1700 }
1701 
1702 
1703 TransferJob *KIO::put( const KUrl& url, int permissions, JobFlags flags )
1704 {
1705  KIO_ARGS << url << qint8( (flags & Overwrite) ? 1 : 0 ) << qint8( (flags & Resume) ? 1 : 0 ) << permissions;
1706  return TransferJobPrivate::newJob(url, CMD_PUT, packedArgs, QByteArray(), flags);
1707 }
1708 
1710 
1711 StoredTransferJob::StoredTransferJob(StoredTransferJobPrivate &dd)
1712  : TransferJob(dd)
1713 {
1714  connect( this, SIGNAL(data(KIO::Job*,QByteArray)),
1715  SLOT(slotStoredData(KIO::Job*,QByteArray)) );
1716  connect( this, SIGNAL(dataReq(KIO::Job*,QByteArray&)),
1717  SLOT(slotStoredDataReq(KIO::Job*,QByteArray&)) );
1718 }
1719 
1720 StoredTransferJob::~StoredTransferJob()
1721 {
1722 }
1723 
1724 void StoredTransferJob::setData( const QByteArray& arr )
1725 {
1726  Q_D(StoredTransferJob);
1727  Q_ASSERT( d->m_data.isNull() ); // check that we're only called once
1728  Q_ASSERT( d->m_uploadOffset == 0 ); // no upload started yet
1729  d->m_data = arr;
1730  setTotalSize( d->m_data.size() );
1731 }
1732 
1733 QByteArray StoredTransferJob::data() const
1734 {
1735  return d_func()->m_data;
1736 }
1737 
1738 void StoredTransferJobPrivate::slotStoredData( KIO::Job *, const QByteArray &data )
1739 {
1740  // check for end-of-data marker:
1741  if ( data.size() == 0 )
1742  return;
1743  unsigned int oldSize = m_data.size();
1744  m_data.resize( oldSize + data.size() );
1745  memcpy( m_data.data() + oldSize, data.data(), data.size() );
1746 }
1747 
1748 void StoredTransferJobPrivate::slotStoredDataReq( KIO::Job *, QByteArray &data )
1749 {
1750  // Inspired from kmail's KMKernel::byteArrayToRemoteFile
1751  // send the data in 64 KB chunks
1752  const int MAX_CHUNK_SIZE = 64*1024;
1753  int remainingBytes = m_data.size() - m_uploadOffset;
1754  if( remainingBytes > MAX_CHUNK_SIZE ) {
1755  // send MAX_CHUNK_SIZE bytes to the receiver (deep copy)
1756  data = QByteArray( m_data.data() + m_uploadOffset, MAX_CHUNK_SIZE );
1757  m_uploadOffset += MAX_CHUNK_SIZE;
1758  //kDebug() << "Sending " << MAX_CHUNK_SIZE << " bytes ("
1759  // << remainingBytes - MAX_CHUNK_SIZE << " bytes remain)\n";
1760  } else {
1761  // send the remaining bytes to the receiver (deep copy)
1762  data = QByteArray( m_data.data() + m_uploadOffset, remainingBytes );
1763  m_data = QByteArray();
1764  m_uploadOffset = 0;
1765  //kDebug() << "Sending " << remainingBytes << " bytes\n";
1766  }
1767 }
1768 
1769 StoredTransferJob *KIO::storedGet( const KUrl& url, LoadType reload, JobFlags flags )
1770 {
1771  // Send decoded path and encoded query
1772  KIO_ARGS << url;
1773  StoredTransferJob * job = StoredTransferJobPrivate::newJob(url, CMD_GET, packedArgs, QByteArray(), flags);
1774  if (reload == Reload)
1775  job->addMetaData("cache", "reload");
1776  return job;
1777 }
1778 
1779 StoredTransferJob *KIO::storedPut( const QByteArray& arr, const KUrl& url, int permissions,
1780  JobFlags flags )
1781 {
1782  KIO_ARGS << url << qint8( (flags & Overwrite) ? 1 : 0 ) << qint8( (flags & Resume) ? 1 : 0 ) << permissions;
1783  StoredTransferJob * job = StoredTransferJobPrivate::newJob(url, CMD_PUT, packedArgs, QByteArray(), flags );
1784  job->setData( arr );
1785  return job;
1786 }
1787 
1789 
1790 class KIO::MimetypeJobPrivate: public KIO::TransferJobPrivate
1791 {
1792 public:
1793  MimetypeJobPrivate(const KUrl& url, int command, const QByteArray &packedArgs)
1794  : TransferJobPrivate(url, command, packedArgs, QByteArray())
1795  {}
1796 
1797  Q_DECLARE_PUBLIC(MimetypeJob)
1798 
1799  static inline MimetypeJob *newJob(const KUrl& url, int command, const QByteArray &packedArgs,
1800  JobFlags flags)
1801  {
1802  MimetypeJob *job = new MimetypeJob(*new MimetypeJobPrivate(url, command, packedArgs));
1803  job->setUiDelegate(new JobUiDelegate);
1804  if (!(flags & HideProgressInfo)) {
1805  KIO::getJobTracker()->registerJob(job);
1806  emitStating(job, url);
1807  }
1808  return job;
1809  }
1810 };
1811 
1812 MimetypeJob::MimetypeJob(MimetypeJobPrivate &dd)
1813  : TransferJob(dd)
1814 {
1815 }
1816 
1817 MimetypeJob::~MimetypeJob()
1818 {
1819 }
1820 
1821 void MimetypeJob::slotFinished( )
1822 {
1823  Q_D(MimetypeJob);
1824  //kDebug(7007);
1825  if ( error() == KIO::ERR_IS_DIRECTORY )
1826  {
1827  // It is in fact a directory. This happens when HTTP redirects to FTP.
1828  // Due to the "protocol doesn't support listing" code in KRun, we
1829  // assumed it was a file.
1830  kDebug(7007) << "It is in fact a directory!";
1831  d->m_mimetype = QString::fromLatin1("inode/directory");
1832  emit TransferJob::mimetype( this, d->m_mimetype );
1833  setError( 0 );
1834  }
1835 
1836  if ( !d->m_redirectionURL.isEmpty() && d->m_redirectionURL.isValid() && !error() )
1837  {
1838  //kDebug(7007) << "Redirection to " << m_redirectionURL;
1839  if (queryMetaData("permanent-redirect")=="true")
1840  emit permanentRedirection(this, d->m_url, d->m_redirectionURL);
1841 
1842  if (d->m_redirectionHandlingEnabled)
1843  {
1844  d->staticData.truncate(0);
1845  d->m_internalSuspended = false;
1846  d->m_packedArgs.truncate(0);
1847  QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly );
1848  stream << d->m_redirectionURL;
1849 
1850  d->restartAfterRedirection(&d->m_redirectionURL);
1851  return;
1852  }
1853  }
1854 
1855  // Return slave to the scheduler
1856  TransferJob::slotFinished();
1857 }
1858 
1859 MimetypeJob *KIO::mimetype(const KUrl& url, JobFlags flags)
1860 {
1861  KIO_ARGS << url;
1862  return MimetypeJobPrivate::newJob(url, CMD_MIMETYPE, packedArgs, flags);
1863 }
1864 
1866 
1867 class KIO::DirectCopyJobPrivate: public KIO::SimpleJobPrivate
1868 {
1869 public:
1870  DirectCopyJobPrivate(const KUrl& url, int command, const QByteArray &packedArgs)
1871  : SimpleJobPrivate(url, command, packedArgs)
1872  {}
1873 
1880  virtual void start(Slave *slave);
1881 
1882  Q_DECLARE_PUBLIC(DirectCopyJob)
1883 };
1884 
1885 DirectCopyJob::DirectCopyJob(const KUrl &url, const QByteArray &packedArgs)
1886  : SimpleJob(*new DirectCopyJobPrivate(url, CMD_COPY, packedArgs))
1887 {
1888  setUiDelegate(new JobUiDelegate);
1889 }
1890 
1891 DirectCopyJob::~DirectCopyJob()
1892 {
1893 }
1894 
1895 void DirectCopyJobPrivate::start( Slave* slave )
1896 {
1897  Q_Q(DirectCopyJob);
1898  q->connect( slave, SIGNAL(canResume(KIO::filesize_t)),
1899  SLOT(slotCanResume(KIO::filesize_t)) );
1900  SimpleJobPrivate::start(slave);
1901 }
1902 
1903 void DirectCopyJob::slotCanResume( KIO::filesize_t offset )
1904 {
1905  emit canResume(this, offset);
1906 }
1907 
1909 
1911 class KIO::FileCopyJobPrivate: public KIO::JobPrivate
1912 {
1913 public:
1914  FileCopyJobPrivate(const KUrl& src, const KUrl& dest, int permissions,
1915  bool move, JobFlags flags)
1916  : m_sourceSize(filesize_t(-1)), m_src(src), m_dest(dest), m_moveJob(0), m_copyJob(0), m_delJob(0),
1917  m_chmodJob(0), m_getJob(0), m_putJob(0), m_permissions(permissions),
1918  m_move(move), m_mustChmod(0), m_flags(flags)
1919  {
1920  }
1921  KIO::filesize_t m_sourceSize;
1922  QDateTime m_modificationTime;
1923  KUrl m_src;
1924  KUrl m_dest;
1925  QByteArray m_buffer;
1926  SimpleJob *m_moveJob;
1927  SimpleJob *m_copyJob;
1928  SimpleJob *m_delJob;
1929  SimpleJob *m_chmodJob;
1930  TransferJob *m_getJob;
1931  TransferJob *m_putJob;
1932  int m_permissions;
1933  bool m_move:1;
1934  bool m_canResume:1;
1935  bool m_resumeAnswerSent:1;
1936  bool m_mustChmod:1;
1937  JobFlags m_flags;
1938 
1939  void startBestCopyMethod();
1940  void startCopyJob();
1941  void startCopyJob(const KUrl &slave_url);
1942  void startRenameJob(const KUrl &slave_url);
1943  void startDataPump();
1944  void connectSubjob( SimpleJob * job );
1945 
1946  void slotStart();
1947  void slotData( KIO::Job *, const QByteArray &data);
1948  void slotDataReq( KIO::Job *, QByteArray &data);
1949  void slotMimetype( KIO::Job*, const QString& type );
1955  void slotProcessedSize( KJob *job, qulonglong size );
1961  void slotTotalSize( KJob *job, qulonglong size );
1967  void slotPercent( KJob *job, unsigned long pct );
1973  void slotCanResume( KIO::Job *job, KIO::filesize_t offset );
1974 
1975  Q_DECLARE_PUBLIC(FileCopyJob)
1976 
1977  static inline FileCopyJob* newJob(const KUrl& src, const KUrl& dest, int permissions, bool move,
1978  JobFlags flags)
1979  {
1980  //kDebug(7007) << src << "->" << dest;
1981  FileCopyJob *job = new FileCopyJob(
1982  *new FileCopyJobPrivate(src, dest, permissions, move, flags));
1983  job->setProperty("destUrl", dest.url());
1984  job->setUiDelegate(new JobUiDelegate);
1985  if (!(flags & HideProgressInfo))
1986  KIO::getJobTracker()->registerJob(job);
1987  return job;
1988  }
1989 };
1990 
1991 /*
1992  * The FileCopyJob works according to the famous Bavarian
1993  * 'Alternating Bitburger Protocol': we either drink a beer or we
1994  * we order a beer, but never both at the same time.
1995  * Translated to io-slaves: We alternate between receiving a block of data
1996  * and sending it away.
1997  */
1998 FileCopyJob::FileCopyJob(FileCopyJobPrivate &dd)
1999  : Job(dd)
2000 {
2001  //kDebug(7007);
2002  QTimer::singleShot(0, this, SLOT(slotStart()));
2003 }
2004 
2005 void FileCopyJobPrivate::slotStart()
2006 {
2007  Q_Q(FileCopyJob);
2008  if (!m_move)
2009  JobPrivate::emitCopying( q, m_src, m_dest );
2010  else
2011  JobPrivate::emitMoving( q, m_src, m_dest );
2012 
2013  if ( m_move )
2014  {
2015  // The if() below must be the same as the one in startBestCopyMethod
2016  if ((m_src.protocol() == m_dest.protocol()) &&
2017  (m_src.host() == m_dest.host()) &&
2018  (m_src.port() == m_dest.port()) &&
2019  (m_src.user() == m_dest.user()) &&
2020  (m_src.pass() == m_dest.pass()) &&
2021  !m_src.hasSubUrl() && !m_dest.hasSubUrl())
2022  {
2023  startRenameJob(m_src);
2024  return;
2025  }
2026  else if (m_src.isLocalFile() && KProtocolManager::canRenameFromFile(m_dest))
2027  {
2028  startRenameJob(m_dest);
2029  return;
2030  }
2031  else if (m_dest.isLocalFile() && KProtocolManager::canRenameToFile(m_src))
2032  {
2033  startRenameJob(m_src);
2034  return;
2035  }
2036  // No fast-move available, use copy + del.
2037  }
2038  startBestCopyMethod();
2039 }
2040 
2041 void FileCopyJobPrivate::startBestCopyMethod()
2042 {
2043  if ((m_src.protocol() == m_dest.protocol()) &&
2044  (m_src.host() == m_dest.host()) &&
2045  (m_src.port() == m_dest.port()) &&
2046  (m_src.user() == m_dest.user()) &&
2047  (m_src.pass() == m_dest.pass()) &&
2048  !m_src.hasSubUrl() && !m_dest.hasSubUrl())
2049  {
2050  startCopyJob();
2051  }
2052  else if (m_src.isLocalFile() && KProtocolManager::canCopyFromFile(m_dest))
2053  {
2054  startCopyJob(m_dest);
2055  }
2056  else if (m_dest.isLocalFile() && KProtocolManager::canCopyToFile(m_src) &&
2057  !KIO::Scheduler::isSlaveOnHoldFor(m_src))
2058  {
2059  startCopyJob(m_src);
2060  }
2061  else
2062  {
2063  startDataPump();
2064  }
2065 }
2066 
2067 FileCopyJob::~FileCopyJob()
2068 {
2069 }
2070 
2071 void FileCopyJob::setSourceSize( KIO::filesize_t size )
2072 {
2073  Q_D(FileCopyJob);
2074  d->m_sourceSize = size;
2075  if (size != (KIO::filesize_t) -1)
2076  setTotalAmount(KJob::Bytes, size);
2077 }
2078 
2079 void FileCopyJob::setModificationTime( const QDateTime& mtime )
2080 {
2081  Q_D(FileCopyJob);
2082  d->m_modificationTime = mtime;
2083 }
2084 
2085 KUrl FileCopyJob::srcUrl() const
2086 {
2087  return d_func()->m_src;
2088 }
2089 
2090 KUrl FileCopyJob::destUrl() const
2091 {
2092  return d_func()->m_dest;
2093 }
2094 
2095 void FileCopyJobPrivate::startCopyJob()
2096 {
2097  startCopyJob(m_src);
2098 }
2099 
2100 void FileCopyJobPrivate::startCopyJob(const KUrl &slave_url)
2101 {
2102  Q_Q(FileCopyJob);
2103  //kDebug(7007);
2104  KIO_ARGS << m_src << m_dest << m_permissions << (qint8) (m_flags & Overwrite);
2105  m_copyJob = new DirectCopyJob(slave_url, packedArgs);
2106  if (m_modificationTime.isValid()) {
2107  m_copyJob->addMetaData( "modified", m_modificationTime.toString( Qt::ISODate ) ); // #55804
2108  }
2109  q->addSubjob( m_copyJob );
2110  connectSubjob( m_copyJob );
2111  q->connect( m_copyJob, SIGNAL(canResume(KIO::Job*,KIO::filesize_t)),
2112  SLOT(slotCanResume(KIO::Job*,KIO::filesize_t)));
2113 }
2114 
2115 void FileCopyJobPrivate::startRenameJob(const KUrl &slave_url)
2116 {
2117  Q_Q(FileCopyJob);
2118  m_mustChmod = true; // CMD_RENAME by itself doesn't change permissions
2119  KIO_ARGS << m_src << m_dest << (qint8) (m_flags & Overwrite);
2120  m_moveJob = SimpleJobPrivate::newJobNoUi(slave_url, CMD_RENAME, packedArgs);
2121  if (m_modificationTime.isValid()) {
2122  m_moveJob->addMetaData( "modified", m_modificationTime.toString( Qt::ISODate ) ); // #55804
2123  }
2124  q->addSubjob( m_moveJob );
2125  connectSubjob( m_moveJob );
2126 }
2127 
2128 void FileCopyJobPrivate::connectSubjob( SimpleJob * job )
2129 {
2130  Q_Q(FileCopyJob);
2131  q->connect( job, SIGNAL(totalSize(KJob*,qulonglong)),
2132  SLOT(slotTotalSize(KJob*,qulonglong)) );
2133 
2134  q->connect( job, SIGNAL(processedSize(KJob*,qulonglong)),
2135  SLOT(slotProcessedSize(KJob*,qulonglong)) );
2136 
2137  q->connect( job, SIGNAL(percent(KJob*,ulong)),
2138  SLOT(slotPercent(KJob*,ulong)) );
2139 
2140 }
2141 
2142 bool FileCopyJob::doSuspend()
2143 {
2144  Q_D(FileCopyJob);
2145  if (d->m_moveJob)
2146  d->m_moveJob->suspend();
2147 
2148  if (d->m_copyJob)
2149  d->m_copyJob->suspend();
2150 
2151  if (d->m_getJob)
2152  d->m_getJob->suspend();
2153 
2154  if (d->m_putJob)
2155  d->m_putJob->suspend();
2156 
2157  Job::doSuspend();
2158  return true;
2159 }
2160 
2161 bool FileCopyJob::doResume()
2162 {
2163  Q_D(FileCopyJob);
2164  if (d->m_moveJob)
2165  d->m_moveJob->resume();
2166 
2167  if (d->m_copyJob)
2168  d->m_copyJob->resume();
2169 
2170  if (d->m_getJob)
2171  d->m_getJob->resume();
2172 
2173  if (d->m_putJob)
2174  d->m_putJob->resume();
2175 
2176  Job::doResume();
2177  return true;
2178 }
2179 
2180 void FileCopyJobPrivate::slotProcessedSize( KJob *, qulonglong size )
2181 {
2182  Q_Q(FileCopyJob);
2183  q->setProcessedAmount(KJob::Bytes, size);
2184 }
2185 
2186 void FileCopyJobPrivate::slotTotalSize( KJob*, qulonglong size )
2187 {
2188  Q_Q(FileCopyJob);
2189  if (size != q->totalAmount(KJob::Bytes))
2190  {
2191  q->setTotalAmount(KJob::Bytes, size);
2192  }
2193 }
2194 
2195 void FileCopyJobPrivate::slotPercent( KJob*, unsigned long pct )
2196 {
2197  Q_Q(FileCopyJob);
2198  if ( pct > q->percent() ) {
2199  q->setPercent( pct );
2200  }
2201 }
2202 
2203 void FileCopyJobPrivate::startDataPump()
2204 {
2205  Q_Q(FileCopyJob);
2206  //kDebug(7007);
2207 
2208  m_canResume = false;
2209  m_resumeAnswerSent = false;
2210  m_getJob = 0L; // for now
2211  m_putJob = put( m_dest, m_permissions, (m_flags | HideProgressInfo) /* no GUI */);
2212  //kDebug(7007) << "m_putJob=" << m_putJob << "m_dest=" << m_dest;
2213  if ( m_modificationTime.isValid() ) {
2214  m_putJob->setModificationTime( m_modificationTime );
2215  }
2216 
2217  // The first thing the put job will tell us is whether we can
2218  // resume or not (this is always emitted)
2219  q->connect( m_putJob, SIGNAL(canResume(KIO::Job*,KIO::filesize_t)),
2220  SLOT(slotCanResume(KIO::Job*,KIO::filesize_t)));
2221  q->connect( m_putJob, SIGNAL(dataReq(KIO::Job*,QByteArray&)),
2222  SLOT(slotDataReq(KIO::Job*,QByteArray&)));
2223  q->addSubjob( m_putJob );
2224 }
2225 
2226 void FileCopyJobPrivate::slotCanResume( KIO::Job* job, KIO::filesize_t offset )
2227 {
2228  Q_Q(FileCopyJob);
2229  if ( job == m_putJob || job == m_copyJob )
2230  {
2231  //kDebug(7007) << "'can resume' from PUT job. offset=" << KIO::number(offset);
2232  if (offset)
2233  {
2234  RenameDialog_Result res = R_RESUME;
2235 
2236  if (!KProtocolManager::autoResume() && !(m_flags & Overwrite))
2237  {
2238  QString newPath;
2239  KIO::Job* job = ( q->parentJob() ) ? q->parentJob() : q;
2240  // Ask confirmation about resuming previous transfer
2241  res = ui()->askFileRename(
2242  job, i18n("File Already Exists"),
2243  m_src.url(),
2244  m_dest.url(),
2245  (RenameDialog_Mode) (M_OVERWRITE | M_RESUME | M_NORENAME), newPath,
2246  m_sourceSize, offset );
2247  }
2248 
2249  if ( res == R_OVERWRITE || (m_flags & Overwrite) )
2250  offset = 0;
2251  else if ( res == R_CANCEL )
2252  {
2253  if ( job == m_putJob ) {
2254  m_putJob->kill( FileCopyJob::Quietly );
2255  q->removeSubjob(m_putJob);
2256  m_putJob = 0;
2257  } else {
2258  m_copyJob->kill( FileCopyJob::Quietly );
2259  q->removeSubjob(m_copyJob);
2260  m_copyJob = 0;
2261  }
2262  q->setError( ERR_USER_CANCELED );
2263  q->emitResult();
2264  return;
2265  }
2266  }
2267  else
2268  m_resumeAnswerSent = true; // No need for an answer
2269 
2270  if ( job == m_putJob )
2271  {
2272  m_getJob = KIO::get( m_src, NoReload, HideProgressInfo /* no GUI */ );
2273  //kDebug(7007) << "m_getJob=" << m_getJob << m_src;
2274  m_getJob->addMetaData( "errorPage", "false" );
2275  m_getJob->addMetaData( "AllowCompressedPage", "false" );
2276  // Set size in subjob. This helps if the slave doesn't emit totalSize.
2277  if ( m_sourceSize != (KIO::filesize_t)-1 )
2278  m_getJob->setTotalAmount(KJob::Bytes, m_sourceSize);
2279  if (offset)
2280  {
2281  //kDebug(7007) << "Setting metadata for resume to" << (unsigned long) offset;
2282  // TODO KDE4: rename to seek or offset and document it
2283  // This isn't used only for resuming, but potentially also for extracting (#72302).
2284  m_getJob->addMetaData( "resume", KIO::number(offset) );
2285 
2286  // Might or might not get emitted
2287  q->connect( m_getJob, SIGNAL(canResume(KIO::Job*,KIO::filesize_t)),
2288  SLOT(slotCanResume(KIO::Job*,KIO::filesize_t)));
2289  }
2290  jobSlave(m_putJob)->setOffset( offset );
2291 
2292  m_putJob->d_func()->internalSuspend();
2293  q->addSubjob( m_getJob );
2294  connectSubjob( m_getJob ); // Progress info depends on get
2295  m_getJob->d_func()->internalResume(); // Order a beer
2296 
2297  q->connect( m_getJob, SIGNAL(data(KIO::Job*,QByteArray)),
2298  SLOT(slotData(KIO::Job*,QByteArray)) );
2299  q->connect( m_getJob, SIGNAL(mimetype(KIO::Job*,QString)),
2300  SLOT(slotMimetype(KIO::Job*,QString)) );
2301  }
2302  else // copyjob
2303  {
2304  jobSlave(m_copyJob)->sendResumeAnswer( offset != 0 );
2305  }
2306  }
2307  else if ( job == m_getJob )
2308  {
2309  // Cool, the get job said ok, we can resume
2310  m_canResume = true;
2311  //kDebug(7007) << "'can resume' from the GET job -> we can resume";
2312 
2313  jobSlave(m_getJob)->setOffset( jobSlave(m_putJob)->offset() );
2314  }
2315  else
2316  kWarning(7007) << "unknown job=" << job
2317  << "m_getJob=" << m_getJob << "m_putJob=" << m_putJob;
2318 }
2319 
2320 void FileCopyJobPrivate::slotData( KIO::Job * , const QByteArray &data)
2321 {
2322  //kDebug(7007) << "data size:" << data.size();
2323  Q_ASSERT(m_putJob);
2324  if (!m_putJob) return; // Don't crash
2325  m_getJob->d_func()->internalSuspend();
2326  m_putJob->d_func()->internalResume(); // Drink the beer
2327  m_buffer += data;
2328 
2329  // On the first set of data incoming, we tell the "put" slave about our
2330  // decision about resuming
2331  if (!m_resumeAnswerSent)
2332  {
2333  m_resumeAnswerSent = true;
2334  //kDebug(7007) << "(first time) -> send resume answer " << m_canResume;
2335  jobSlave(m_putJob)->sendResumeAnswer( m_canResume );
2336  }
2337 }
2338 
2339 void FileCopyJobPrivate::slotDataReq( KIO::Job * , QByteArray &data)
2340 {
2341  Q_Q(FileCopyJob);
2342  //kDebug(7007);
2343  if (!m_resumeAnswerSent && !m_getJob) {
2344  // This can't happen
2345  q->setError( ERR_INTERNAL );
2346  q->setErrorText( "'Put' job did not send canResume or 'Get' job did not send data!" );
2347  m_putJob->kill( FileCopyJob::Quietly );
2348  q->removeSubjob(m_putJob);
2349  m_putJob = 0;
2350  q->emitResult();
2351  return;
2352  }
2353  if (m_getJob)
2354  {
2355  m_getJob->d_func()->internalResume(); // Order more beer
2356  m_putJob->d_func()->internalSuspend();
2357  }
2358  data = m_buffer;
2359  m_buffer = QByteArray();
2360 }
2361 
2362 void FileCopyJobPrivate::slotMimetype( KIO::Job*, const QString& type )
2363 {
2364  Q_Q(FileCopyJob);
2365  emit q->mimetype( q, type );
2366 }
2367 
2368 void FileCopyJob::slotResult( KJob *job)
2369 {
2370  Q_D(FileCopyJob);
2371  //kDebug(7007) << "this=" << this << "job=" << job;
2372  removeSubjob(job);
2373  // Did job have an error ?
2374  if ( job->error() )
2375  {
2376  if ((job == d->m_moveJob) && (job->error() == ERR_UNSUPPORTED_ACTION))
2377  {
2378  d->m_moveJob = 0;
2379  d->startBestCopyMethod();
2380  return;
2381  }
2382  else if ((job == d->m_copyJob) && (job->error() == ERR_UNSUPPORTED_ACTION))
2383  {
2384  d->m_copyJob = 0;
2385  d->startDataPump();
2386  return;
2387  }
2388  else if (job == d->m_getJob)
2389  {
2390  d->m_getJob = 0L;
2391  if (d->m_putJob)
2392  {
2393  d->m_putJob->kill( Quietly );
2394  removeSubjob( d->m_putJob );
2395  }
2396  }
2397  else if (job == d->m_putJob)
2398  {
2399  d->m_putJob = 0L;
2400  if (d->m_getJob)
2401  {
2402  d->m_getJob->kill( Quietly );
2403  removeSubjob( d->m_getJob );
2404  }
2405  }
2406  setError( job->error() );
2407  setErrorText( job->errorText() );
2408  emitResult();
2409  return;
2410  }
2411 
2412  if (d->m_mustChmod)
2413  {
2414  // If d->m_permissions == -1, keep the default permissions
2415  if (d->m_permissions != -1)
2416  {
2417  d->m_chmodJob = chmod(d->m_dest, d->m_permissions);
2418  }
2419  d->m_mustChmod = false;
2420  }
2421 
2422  if (job == d->m_moveJob)
2423  {
2424  d->m_moveJob = 0; // Finished
2425  }
2426 
2427  if (job == d->m_copyJob)
2428  {
2429  d->m_copyJob = 0;
2430  if (d->m_move)
2431  {
2432  d->m_delJob = file_delete( d->m_src, HideProgressInfo/*no GUI*/ ); // Delete source
2433  addSubjob(d->m_delJob);
2434  }
2435  }
2436 
2437  if (job == d->m_getJob)
2438  {
2439  //kDebug(7007) << "m_getJob finished";
2440  d->m_getJob = 0; // No action required
2441  if (d->m_putJob)
2442  d->m_putJob->d_func()->internalResume();
2443  }
2444 
2445  if (job == d->m_putJob)
2446  {
2447  //kDebug(7007) << "m_putJob finished";
2448  d->m_putJob = 0;
2449  if (d->m_getJob)
2450  {
2451  // The get job is still running, probably after emitting data(QByteArray())
2452  // and before we receive its finished().
2453  d->m_getJob->d_func()->internalResume();
2454  }
2455  if (d->m_move)
2456  {
2457  d->m_delJob = file_delete( d->m_src, HideProgressInfo/*no GUI*/ ); // Delete source
2458  addSubjob(d->m_delJob);
2459  }
2460  }
2461 
2462  if (job == d->m_delJob)
2463  {
2464  d->m_delJob = 0; // Finished
2465  }
2466 
2467  if (job == d->m_chmodJob)
2468  {
2469  d->m_chmodJob = 0; // Finished
2470  }
2471 
2472  if ( !hasSubjobs() )
2473  emitResult();
2474 }
2475 
2476 FileCopyJob *KIO::file_copy( const KUrl& src, const KUrl& dest, int permissions,
2477  JobFlags flags )
2478 {
2479  return FileCopyJobPrivate::newJob(src, dest, permissions, false, flags);
2480 }
2481 
2482 FileCopyJob *KIO::file_move( const KUrl& src, const KUrl& dest, int permissions,
2483  JobFlags flags )
2484 {
2485  FileCopyJob* job = FileCopyJobPrivate::newJob(src, dest, permissions, true, flags);
2486  ClipboardUpdater::create(job, ClipboardUpdater::UpdateContent);
2487  return job;
2488 }
2489 
2490 SimpleJob *KIO::file_delete( const KUrl& src, JobFlags flags )
2491 {
2492  KIO_ARGS << src << qint8(true); // isFile
2493  SimpleJob* job = SimpleJobPrivate::newJob(src, CMD_DEL, packedArgs, flags);
2494  ClipboardUpdater::create(job, ClipboardUpdater::RemoveContent);
2495  return job;
2496 }
2497 
2499 
2500 class KIO::ListJobPrivate: public KIO::SimpleJobPrivate
2501 {
2502 public:
2503  ListJobPrivate(const KUrl& url, bool _recursive,
2504  const QString &prefix, const QString &displayPrefix,
2505  bool _includeHidden)
2506  : SimpleJobPrivate(url, CMD_LISTDIR, QByteArray()),
2507  recursive(_recursive), includeHidden(_includeHidden),
2508  m_prefix(prefix), m_displayPrefix(displayPrefix), m_processedEntries(0)
2509  {}
2510  bool recursive;
2511  bool includeHidden;
2512  QString m_prefix;
2513  QString m_displayPrefix;
2514  unsigned long m_processedEntries;
2515  KUrl m_redirectionURL;
2516 
2523  virtual void start( Slave *slave );
2524 
2525  void slotListEntries( const KIO::UDSEntryList& list );
2526  void slotRedirection( const KUrl &url );
2527  void gotEntries( KIO::Job * subjob, const KIO::UDSEntryList& list );
2528 
2529  Q_DECLARE_PUBLIC(ListJob)
2530 
2531  static inline ListJob *newJob(const KUrl& u, bool _recursive,
2532  const QString &prefix, const QString &displayPrefix,
2533  bool _includeHidden, JobFlags flags = HideProgressInfo)
2534  {
2535  ListJob *job = new ListJob(*new ListJobPrivate(u, _recursive, prefix, displayPrefix, _includeHidden));
2536  job->setUiDelegate(new JobUiDelegate);
2537  if (!(flags & HideProgressInfo))
2538  KIO::getJobTracker()->registerJob(job);
2539  return job;
2540  }
2541  static inline ListJob *newJobNoUi(const KUrl& u, bool _recursive,
2542  const QString &prefix, const QString &displayPrefix,
2543  bool _includeHidden)
2544  {
2545  return new ListJob(*new ListJobPrivate(u, _recursive, prefix, displayPrefix, _includeHidden));
2546  }
2547 };
2548 
2549 ListJob::ListJob(ListJobPrivate &dd)
2550  : SimpleJob(dd)
2551 {
2552  Q_D(ListJob);
2553  // We couldn't set the args when calling the parent constructor,
2554  // so do it now.
2555  QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly );
2556  stream << d->m_url;
2557 }
2558 
2559 ListJob::~ListJob()
2560 {
2561 }
2562 
2563 void ListJobPrivate::slotListEntries( const KIO::UDSEntryList& list )
2564 {
2565  Q_Q(ListJob);
2566  // Emit progress info (takes care of emit processedSize and percent)
2567  m_processedEntries += list.count();
2568  slotProcessedSize( m_processedEntries );
2569 
2570  if (recursive) {
2571  UDSEntryList::ConstIterator it = list.begin();
2572  const UDSEntryList::ConstIterator end = list.end();
2573 
2574  for (; it != end; ++it) {
2575 
2576  const UDSEntry& entry = *it;
2577 
2578  KUrl itemURL;
2579  // const UDSEntry::ConstIterator end2 = entry.end();
2580  // UDSEntry::ConstIterator it2 = entry.find( KIO::UDSEntry::UDS_URL );
2581  // if ( it2 != end2 )
2582  if (entry.contains(KIO::UDSEntry::UDS_URL))
2583  // itemURL = it2.value().toString();
2584  itemURL = entry.stringValue(KIO::UDSEntry::UDS_URL);
2585  else { // no URL, use the name
2586  itemURL = q->url();
2587  const QString fileName = entry.stringValue(KIO::UDSEntry::UDS_NAME);
2588  Q_ASSERT(!fileName.isEmpty()); // we'll recurse forever otherwise :)
2589  itemURL.addPath(fileName);
2590  }
2591 
2592  if (entry.isDir() && !entry.isLink()) {
2593  const QString filename = itemURL.fileName();
2594  QString displayName = entry.stringValue(KIO::UDSEntry::UDS_DISPLAY_NAME);
2595  if (displayName.isEmpty())
2596  displayName = filename;
2597  // skip hidden dirs when listing if requested
2598  if (filename != ".." && filename != "." && (includeHidden || filename[0] != '.')) {
2599  ListJob *job = ListJobPrivate::newJobNoUi(itemURL,
2600  true /*recursive*/,
2601  m_prefix + filename + '/',
2602  m_displayPrefix + displayName + '/',
2603  includeHidden);
2604  Scheduler::setJobPriority(job, 1);
2605  q->connect(job, SIGNAL(entries(KIO::Job*,KIO::UDSEntryList)),
2606  SLOT(gotEntries(KIO::Job*,KIO::UDSEntryList)));
2607  q->addSubjob(job);
2608  }
2609  }
2610  }
2611  }
2612 
2613  // Not recursive, or top-level of recursive listing : return now (send . and .. as well)
2614  // exclusion of hidden files also requires the full sweep, but the case for full-listing
2615  // a single dir is probably common enough to justify the shortcut
2616  if (m_prefix.isNull() && includeHidden) {
2617  emit q->entries(q, list);
2618  } else {
2619  // cull the unwanted hidden dirs and/or parent dir references from the listing, then emit that
2620  UDSEntryList newlist;
2621 
2622  UDSEntryList::const_iterator it = list.begin();
2623  const UDSEntryList::const_iterator end = list.end();
2624  for (; it != end; ++it) {
2625 
2626  // Modify the name in the UDSEntry
2627  UDSEntry newone = *it;
2628  const QString filename = newone.stringValue( KIO::UDSEntry::UDS_NAME );
2629  QString displayName = newone.stringValue(KIO::UDSEntry::UDS_DISPLAY_NAME);
2630  if (displayName.isEmpty())
2631  displayName = filename;
2632  // Avoid returning entries like subdir/. and subdir/.., but include . and .. for
2633  // the toplevel dir, and skip hidden files/dirs if that was requested
2634  if ( (m_prefix.isNull() || (filename != ".." && filename != ".") )
2635  && (includeHidden || (filename[0] != '.') ) )
2636  {
2637  // ## Didn't find a way to use the iterator instead of re-doing a key lookup
2638  newone.insert( KIO::UDSEntry::UDS_NAME, m_prefix + filename );
2639  newone.insert(KIO::UDSEntry::UDS_DISPLAY_NAME, m_displayPrefix + displayName);
2640  newlist.append(newone);
2641  }
2642  }
2643 
2644  emit q->entries(q, newlist);
2645  }
2646 }
2647 
2648 void ListJobPrivate::gotEntries(KIO::Job *, const KIO::UDSEntryList& list )
2649 {
2650  // Forward entries received by subjob - faking we received them ourselves
2651  Q_Q(ListJob);
2652  emit q->entries(q, list);
2653 }
2654 
2655 void ListJob::slotResult( KJob * job )
2656 {
2657  if (job->error()) {
2658  // If we can't list a subdir, the result is still ok
2659  // This is why we override KCompositeJob::slotResult - to not set
2660  // an error on parent job.
2661  // Let's emit a signal about this though
2662  emit subError(this, static_cast<KIO::ListJob*>(job));
2663  }
2664  removeSubjob(job);
2665  if (!hasSubjobs())
2666  emitResult();
2667 }
2668 
2669 void ListJobPrivate::slotRedirection( const KUrl & url )
2670 {
2671  Q_Q(ListJob);
2672  if (!KAuthorized::authorizeUrlAction("redirect", m_url, url))
2673  {
2674  kWarning(7007) << "ListJob: Redirection from " << m_url << " to " << url << " REJECTED!";
2675  return;
2676  }
2677  m_redirectionURL = url; // We'll remember that when the job finishes
2678  emit q->redirection( q, m_redirectionURL );
2679 }
2680 
2681 void ListJob::slotFinished()
2682 {
2683  Q_D(ListJob);
2684 
2685  // Support for listing archives as directories
2686  if ( error() == KIO::ERR_IS_FILE && d->m_url.isLocalFile() ) {
2687  KMimeType::Ptr ptr = KMimeType::findByUrl( d->m_url, 0, true, true );
2688  if ( ptr ) {
2689  QString proto = ptr->property("X-KDE-LocalProtocol").toString();
2690  if ( !proto.isEmpty() && KProtocolInfo::isKnownProtocol( proto) ) {
2691  d->m_redirectionURL = d->m_url;
2692  d->m_redirectionURL.setProtocol( proto );
2693  setError( 0 );
2694  emit redirection(this,d->m_redirectionURL);
2695  }
2696  }
2697  }
2698 
2699  if ( !d->m_redirectionURL.isEmpty() && d->m_redirectionURL.isValid() && !error() ) {
2700 
2701  //kDebug(7007) << "Redirection to " << d->m_redirectionURL;
2702  if (queryMetaData("permanent-redirect")=="true")
2703  emit permanentRedirection(this, d->m_url, d->m_redirectionURL);
2704 
2705  if ( d->m_redirectionHandlingEnabled ) {
2706  d->m_packedArgs.truncate(0);
2707  QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly );
2708  stream << d->m_redirectionURL;
2709 
2710  d->restartAfterRedirection(&d->m_redirectionURL);
2711  return;
2712  }
2713  }
2714 
2715  // Return slave to the scheduler
2716  SimpleJob::slotFinished();
2717 }
2718 
2719 void ListJob::slotMetaData( const KIO::MetaData &_metaData)
2720 {
2721  Q_D(ListJob);
2722  SimpleJob::slotMetaData(_metaData);
2723  storeSSLSessionFromJob(d->m_redirectionURL);
2724 }
2725 
2726 ListJob *KIO::listDir( const KUrl& url, JobFlags flags, bool includeHidden )
2727 {
2728  return ListJobPrivate::newJob(url, false, QString(), QString(), includeHidden, flags);
2729 }
2730 
2731 ListJob *KIO::listRecursive( const KUrl& url, JobFlags flags, bool includeHidden )
2732 {
2733  return ListJobPrivate::newJob(url, true, QString(), QString(), includeHidden, flags);
2734 }
2735 
2736 void ListJob::setUnrestricted(bool unrestricted)
2737 {
2738  Q_D(ListJob);
2739  if (unrestricted)
2740  d->m_extraFlags |= JobPrivate::EF_ListJobUnrestricted;
2741  else
2742  d->m_extraFlags &= ~JobPrivate::EF_ListJobUnrestricted;
2743 }
2744 
2745 void ListJobPrivate::start(Slave *slave)
2746 {
2747  Q_Q(ListJob);
2748  if (!KAuthorized::authorizeUrlAction("list", m_url, m_url) &&
2749  !(m_extraFlags & EF_ListJobUnrestricted))
2750  {
2751  q->setError( ERR_ACCESS_DENIED );
2752  q->setErrorText( m_url.url() );
2753  QTimer::singleShot(0, q, SLOT(slotFinished()) );
2754  return;
2755  }
2756  q->connect( slave, SIGNAL(listEntries(KIO::UDSEntryList)),
2757  SLOT(slotListEntries(KIO::UDSEntryList)));
2758  q->connect( slave, SIGNAL(totalSize(KIO::filesize_t)),
2759  SLOT(slotTotalSize(KIO::filesize_t)) );
2760  q->connect( slave, SIGNAL(redirection(KUrl)),
2761  SLOT(slotRedirection(KUrl)) );
2762 
2763  SimpleJobPrivate::start(slave);
2764 }
2765 
2766 const KUrl& ListJob::redirectionUrl() const
2767 {
2768  return d_func()->m_redirectionURL;
2769 }
2770 
2772 
2773 class KIO::MultiGetJobPrivate: public KIO::TransferJobPrivate
2774 {
2775 public:
2776  MultiGetJobPrivate(const KUrl& url)
2777  : TransferJobPrivate(url, 0, QByteArray(), QByteArray()),
2778  m_currentEntry( 0, KUrl(), MetaData() )
2779  {}
2780  struct GetRequest {
2781  GetRequest(long _id, const KUrl &_url, const MetaData &_metaData)
2782  : id(_id), url(_url), metaData(_metaData) { }
2783  long id;
2784  KUrl url;
2785  MetaData metaData;
2786 
2787  inline bool operator==( const GetRequest& req ) const
2788  { return req.id == id; }
2789  };
2790  typedef QLinkedList<GetRequest> RequestQueue;
2791 
2792  RequestQueue m_waitQueue;
2793  RequestQueue m_activeQueue;
2794  GetRequest m_currentEntry;
2795  bool b_multiGetActive;
2796 
2803  virtual void start(Slave *slave);
2804 
2805  bool findCurrentEntry();
2806  void flushQueue(QLinkedList<GetRequest> &queue);
2807 
2808  Q_DECLARE_PUBLIC(MultiGetJob)
2809 
2810  static inline MultiGetJob *newJob(const KUrl &url)
2811  {
2812  MultiGetJob *job = new MultiGetJob(*new MultiGetJobPrivate(url));
2813  job->setUiDelegate(new JobUiDelegate);
2814  return job;
2815  }
2816 };
2817 
2818 MultiGetJob::MultiGetJob(MultiGetJobPrivate &dd)
2819  : TransferJob(dd)
2820 {
2821 }
2822 
2823 MultiGetJob::~MultiGetJob()
2824 {
2825 }
2826 
2827 void MultiGetJob::get(long id, const KUrl &url, const MetaData &metaData)
2828 {
2829  Q_D(MultiGetJob);
2830  MultiGetJobPrivate::GetRequest entry(id, url, metaData);
2831  entry.metaData["request-id"] = QString::number(id);
2832  d->m_waitQueue.append(entry);
2833 }
2834 
2835 void MultiGetJobPrivate::flushQueue(RequestQueue &queue)
2836 {
2837  // Use multi-get
2838  // Scan all jobs in m_waitQueue
2839  RequestQueue::iterator wqit = m_waitQueue.begin();
2840  const RequestQueue::iterator wqend = m_waitQueue.end();
2841  while ( wqit != wqend )
2842  {
2843  const GetRequest& entry = *wqit;
2844  if ((m_url.protocol() == entry.url.protocol()) &&
2845  (m_url.host() == entry.url.host()) &&
2846  (m_url.port() == entry.url.port()) &&
2847  (m_url.user() == entry.url.user()))
2848  {
2849  queue.append( entry );
2850  wqit = m_waitQueue.erase( wqit );
2851  }
2852  else
2853  {
2854  ++wqit;
2855  }
2856  }
2857  // Send number of URLs, (URL, metadata)*
2858  KIO_ARGS << (qint32) queue.count();
2859  RequestQueue::const_iterator qit = queue.begin();
2860  const RequestQueue::const_iterator qend = queue.end();
2861  for( ; qit != qend; ++qit )
2862  {
2863  stream << (*qit).url << (*qit).metaData;
2864  }
2865  m_packedArgs = packedArgs;
2866  m_command = CMD_MULTI_GET;
2867  m_outgoingMetaData.clear();
2868 }
2869 
2870 void MultiGetJobPrivate::start(Slave *slave)
2871 {
2872  // Add first job from m_waitQueue and add it to m_activeQueue
2873  GetRequest entry = m_waitQueue.takeFirst();
2874  m_activeQueue.append(entry);
2875 
2876  m_url = entry.url;
2877 
2878  if (!entry.url.protocol().startsWith(QLatin1String("http")))
2879  {
2880  // Use normal get
2881  KIO_ARGS << entry.url;
2882  m_packedArgs = packedArgs;
2883  m_outgoingMetaData = entry.metaData;
2884  m_command = CMD_GET;
2885  b_multiGetActive = false;
2886  }
2887  else
2888  {
2889  flushQueue(m_activeQueue);
2890  b_multiGetActive = true;
2891  }
2892 
2893  TransferJobPrivate::start(slave); // Anything else to do??
2894 }
2895 
2896 bool MultiGetJobPrivate::findCurrentEntry()
2897 {
2898  if (b_multiGetActive)
2899  {
2900  long id = m_incomingMetaData["request-id"].toLong();
2901  RequestQueue::const_iterator qit = m_activeQueue.begin();
2902  const RequestQueue::const_iterator qend = m_activeQueue.end();
2903  for( ; qit != qend; ++qit )
2904  {
2905  if ((*qit).id == id)
2906  {
2907  m_currentEntry = *qit;
2908  return true;
2909  }
2910  }
2911  m_currentEntry.id = 0;
2912  return false;
2913  }
2914  else
2915  {
2916  if ( m_activeQueue.isEmpty() )
2917  return false;
2918  m_currentEntry = m_activeQueue.first();
2919  return true;
2920  }
2921 }
2922 
2923 void MultiGetJob::slotRedirection( const KUrl &url)
2924 {
2925  Q_D(MultiGetJob);
2926  if (!d->findCurrentEntry()) return; // Error
2927  if (!KAuthorized::authorizeUrlAction("redirect", d->m_url, url))
2928  {
2929  kWarning(7007) << "MultiGetJob: Redirection from " << d->m_currentEntry.url << " to " << url << " REJECTED!";
2930  return;
2931  }
2932  d->m_redirectionURL = url;
2933  get(d->m_currentEntry.id, d->m_redirectionURL, d->m_currentEntry.metaData); // Try again
2934 }
2935 
2936 
2937 void MultiGetJob::slotFinished()
2938 {
2939  Q_D(MultiGetJob);
2940  if (!d->findCurrentEntry()) return;
2941  if (d->m_redirectionURL.isEmpty())
2942  {
2943  // No redirection, tell the world that we are finished.
2944  emit result(d->m_currentEntry.id);
2945  }
2946  d->m_redirectionURL = KUrl();
2947  setError( 0 );
2948  d->m_incomingMetaData.clear();
2949  d->m_activeQueue.removeAll(d->m_currentEntry);
2950  if (d->m_activeQueue.count() == 0)
2951  {
2952  if (d->m_waitQueue.count() == 0)
2953  {
2954  // All done
2955  TransferJob::slotFinished();
2956  }
2957  else
2958  {
2959  // return slave to pool
2960  // fetch new slave for first entry in d->m_waitQueue and call start
2961  // again.
2962  d->slaveDone();
2963 
2964  d->m_url = d->m_waitQueue.first().url;
2965  if ((d->m_extraFlags & JobPrivate::EF_KillCalled) == 0) {
2966  Scheduler::doJob(this);
2967  }
2968  }
2969  }
2970 }
2971 
2972 void MultiGetJob::slotData( const QByteArray &_data)
2973 {
2974  Q_D(MultiGetJob);
2975  if(d->m_redirectionURL.isEmpty() || !d->m_redirectionURL.isValid() || error())
2976  emit data(d->m_currentEntry.id, _data);
2977 }
2978 
2979 void MultiGetJob::slotMimetype( const QString &_mimetype )
2980 {
2981  Q_D(MultiGetJob);
2982  if (d->b_multiGetActive)
2983  {
2984  MultiGetJobPrivate::RequestQueue newQueue;
2985  d->flushQueue(newQueue);
2986  if (!newQueue.isEmpty())
2987  {
2988  d->m_activeQueue += newQueue;
2989  d->m_slave->send( d->m_command, d->m_packedArgs );
2990  }
2991  }
2992  if (!d->findCurrentEntry()) return; // Error, unknown request!
2993  emit mimetype(d->m_currentEntry.id, _mimetype);
2994 }
2995 
2996 MultiGetJob *KIO::multi_get(long id, const KUrl &url, const MetaData &metaData)
2997 {
2998  MultiGetJob * job = MultiGetJobPrivate::newJob(url);
2999  job->get(id, url, metaData);
3000  return job;
3001 }
3002 
3003 class KIO::SpecialJobPrivate: public TransferJobPrivate
3004 {
3005  SpecialJobPrivate(const KUrl& url, int command,
3006  const QByteArray &packedArgs,
3007  const QByteArray &_staticData)
3008  : TransferJobPrivate(url, command, packedArgs, _staticData)
3009  {}
3010 };
3011 
3012 SpecialJob::SpecialJob(const KUrl &url, const QByteArray &packedArgs)
3013  : TransferJob(*new TransferJobPrivate(url, CMD_SPECIAL, packedArgs, QByteArray()))
3014 {
3015 }
3016 
3017 SpecialJob::~SpecialJob()
3018 {
3019 }
3020 
3021 void SpecialJob::setArguments(const QByteArray &data)
3022 {
3023  Q_D(SpecialJob);
3024  d->m_packedArgs = data;
3025 }
3026 
3027 QByteArray SpecialJob::arguments() const
3028 {
3029  return d_func()->m_packedArgs;
3030 }
3031 
3032 // Never defined, never used - what's this code about?
3033 #ifdef CACHE_INFO
3034 CacheInfo::CacheInfo(const KUrl &url)
3035 {
3036  m_url = url;
3037 }
3038 
3039 QString CacheInfo::cachedFileName()
3040 {
3041  const QChar separator = '_';
3042 
3043  QString CEF = m_url.path();
3044 
3045  int p = CEF.find('/');
3046 
3047  while(p != -1)
3048  {
3049  CEF[p] = separator;
3050  p = CEF.find('/', p);
3051  }
3052 
3053  QString host = m_url.host().toLower();
3054  CEF = host + CEF + '_';
3055 
3056  QString dir = KProtocolManager::cacheDir();
3057  if (dir[dir.length()-1] != '/')
3058  dir += '/';
3059 
3060  int l = m_url.host().length();
3061  for(int i = 0; i < l; i++)
3062  {
3063  if (host[i].isLetter() && (host[i] != 'w'))
3064  {
3065  dir += host[i];
3066  break;
3067  }
3068  }
3069  if (dir[dir.length()-1] == '/')
3070  dir += '0';
3071 
3072  unsigned long hash = 0x00000000;
3073  QString u = m_url.url().toLatin1();
3074  for(int i = u.length(); i--;)
3075  {
3076  hash = (hash * 12211 + u[i]) % 2147483563;
3077  }
3078 
3079  QString hashString;
3080  hashString.sprintf("%08lx", hash);
3081 
3082  CEF = CEF + hashString;
3083 
3084  CEF = dir + '/' + CEF;
3085 
3086  return CEF;
3087 }
3088 
3089 QFile *CacheInfo::cachedFile()
3090 {
3091 #ifdef Q_WS_WIN
3092  const char *mode = (readWrite ? "rb+" : "rb");
3093 #else
3094  const char *mode = (readWrite ? "r+" : "r");
3095 #endif
3096 
3097  FILE *fs = KDE::fopen(CEF, mode); // Open for reading and writing
3098  if (!fs)
3099  return 0;
3100 
3101  char buffer[401];
3102  bool ok = true;
3103 
3104  // CacheRevision
3105  if (ok && (!fgets(buffer, 400, fs)))
3106  ok = false;
3107  if (ok && (strcmp(buffer, CACHE_REVISION) != 0))
3108  ok = false;
3109 
3110  time_t date;
3111  time_t currentDate = time(0);
3112 
3113  // URL
3114  if (ok && (!fgets(buffer, 400, fs)))
3115  ok = false;
3116  if (ok)
3117  {
3118  int l = strlen(buffer);
3119  if (l>0)
3120  buffer[l-1] = 0; // Strip newline
3121  if (m_.url.url() != buffer)
3122  {
3123  ok = false; // Hash collision
3124  }
3125  }
3126 
3127  // Creation Date
3128  if (ok && (!fgets(buffer, 400, fs)))
3129  ok = false;
3130  if (ok)
3131  {
3132  date = (time_t) strtoul(buffer, 0, 10);
3133  if (m_maxCacheAge && (difftime(currentDate, date) > m_maxCacheAge))
3134  {
3135  m_bMustRevalidate = true;
3136  m_expireDate = currentDate;
3137  }
3138  }
3139 
3140  // Expiration Date
3141  m_cacheExpireDateOffset = KDE_ftell(fs);
3142  if (ok && (!fgets(buffer, 400, fs)))
3143  ok = false;
3144  if (ok)
3145  {
3146  if (m_request.cache == CC_Verify)
3147  {
3148  date = (time_t) strtoul(buffer, 0, 10);
3149  // After the expire date we need to revalidate.
3150  if (!date || difftime(currentDate, date) >= 0)
3151  m_bMustRevalidate = true;
3152  m_expireDate = date;
3153  }
3154  }
3155 
3156  // ETag
3157  if (ok && (!fgets(buffer, 400, fs)))
3158  ok = false;
3159  if (ok)
3160  {
3161  m_etag = QString(buffer).trimmed();
3162  }
3163 
3164  // Last-Modified
3165  if (ok && (!fgets(buffer, 400, fs)))
3166  ok = false;
3167  if (ok)
3168  {
3169  m_lastModified = QString(buffer).trimmed();
3170  }
3171 
3172  fclose(fs);
3173 
3174  if (ok)
3175  return fs;
3176 
3177  unlink( QFile::encodeName(CEF) );
3178  return 0;
3179 
3180 }
3181 
3182 void CacheInfo::flush()
3183 {
3184  cachedFile().remove();
3185 }
3186 
3187 void CacheInfo::touch()
3188 {
3189 
3190 }
3191 void CacheInfo::setExpireDate(int);
3192 void CacheInfo::setExpireTimeout(int);
3193 
3194 
3195 int CacheInfo::creationDate();
3196 int CacheInfo::expireDate();
3197 int CacheInfo::expireTimeout();
3198 #endif
3199 
3200 #include "jobclasses.moc"
3201 #include "job_p.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Thu Sep 25 2014 04:20:27 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.11.5 API Reference

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