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

KDE's Doxygen guidelines are available online.

KIO

Skip menu "KIO"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Related Pages

kdelibs-4.10.5 API Reference

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