KIO
job.cpp
Go to the documentation of this file.
00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2000 Stephan Kulow <coolo@kde.org> 00003 2000-2009 David Faure <faure@kde.org> 00004 Waldo Bastian <bastian@kde.org> 00005 00006 This library is free software; you can redistribute it and/or 00007 modify it under the terms of the GNU Library General Public 00008 License as published by the Free Software Foundation; either 00009 version 2 of the License, or (at your option) any later version. 00010 00011 This library is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 Library General Public License for more details. 00015 00016 You should have received a copy of the GNU Library General Public License 00017 along with this library; see the file COPYING.LIB. If not, write to 00018 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00019 Boston, MA 02110-1301, USA. 00020 */ 00021 00022 #include "job.h" 00023 #include "job_p.h" 00024 00025 #include <config.h> 00026 00027 #include <sys/types.h> 00028 #include <sys/wait.h> 00029 #include <sys/stat.h> 00030 00031 #include <signal.h> 00032 #include <stdlib.h> 00033 #include <stdio.h> 00034 #include <time.h> 00035 #include <unistd.h> 00036 extern "C" { 00037 #include <pwd.h> 00038 #include <grp.h> 00039 } 00040 #include <QtCore/QTimer> 00041 #include <QtCore/QFile> 00042 00043 #include <kauthorized.h> 00044 #include <klocale.h> 00045 #include <kconfig.h> 00046 #include <kdebug.h> 00047 #include <kde_file.h> 00048 00049 #include <errno.h> 00050 00051 #include "jobuidelegate.h" 00052 #include "kmimetype.h" 00053 #include "slave.h" 00054 #include "scheduler.h" 00055 #include "kdirwatch.h" 00056 #include "kprotocolinfo.h" 00057 #include "kprotocolmanager.h" 00058 #include "filejob.h" 00059 00060 #include <kdirnotify.h> 00061 #include <ktemporaryfile.h> 00062 00063 using namespace KIO; 00064 00065 #define MAX_READ_BUF_SIZE (64 * 1024) // 64 KB at a time seems reasonable... 00066 00067 static inline Slave *jobSlave(SimpleJob *job) 00068 { 00069 return SimpleJobPrivate::get(job)->m_slave; 00070 } 00071 00072 //this will update the report dialog with 5 Hz, I think this is fast enough, aleXXX 00073 #define REPORT_TIMEOUT 200 00074 00075 Job::Job() : KCompositeJob(*new JobPrivate, 0) 00076 { 00077 setCapabilities( KJob::Killable | KJob::Suspendable ); 00078 } 00079 00080 Job::Job(JobPrivate &dd) : KCompositeJob(dd, 0) 00081 { 00082 setCapabilities( KJob::Killable | KJob::Suspendable ); 00083 } 00084 00085 Job::~Job() 00086 { 00087 } 00088 00089 JobUiDelegate *Job::ui() const 00090 { 00091 return static_cast<JobUiDelegate*>( uiDelegate() ); 00092 } 00093 00094 bool Job::addSubjob(KJob *jobBase) 00095 { 00096 //kDebug(7007) << "addSubjob(" << jobBase << ") this=" << this; 00097 00098 bool ok = KCompositeJob::addSubjob( jobBase ); 00099 KIO::Job *job = dynamic_cast<KIO::Job*>( jobBase ); 00100 if (ok && job) { 00101 // Copy metadata into subjob (e.g. window-id, user-timestamp etc.) 00102 Q_D(Job); 00103 job->mergeMetaData(d->m_outgoingMetaData); 00104 00105 // Forward information from that subjob. 00106 connect(job, SIGNAL(speed(KJob*,ulong)), 00107 SLOT(slotSpeed(KJob*,ulong))); 00108 00109 if (ui() && job->ui()) { 00110 job->ui()->setWindow( ui()->window() ); 00111 job->ui()->updateUserTimestamp( ui()->userTimestamp() ); 00112 } 00113 } 00114 return ok; 00115 } 00116 00117 bool Job::removeSubjob( KJob *jobBase ) 00118 { 00119 //kDebug(7007) << "removeSubjob(" << jobBase << ") this=" << this << "subjobs=" << subjobs().count(); 00120 return KCompositeJob::removeSubjob( jobBase ); 00121 } 00122 00123 void JobPrivate::emitMoving(KIO::Job * job, const KUrl &src, const KUrl &dest) 00124 { 00125 emit job->description(job, i18nc("@title job","Moving"), 00126 qMakePair(i18nc("The source of a file operation", "Source"), src.pathOrUrl()), 00127 qMakePair(i18nc("The destination of a file operation", "Destination"), dest.pathOrUrl())); 00128 } 00129 00130 void JobPrivate::emitCopying(KIO::Job * job, const KUrl &src, const KUrl &dest) 00131 { 00132 emit job->description(job, i18nc("@title job","Copying"), 00133 qMakePair(i18nc("The source of a file operation", "Source"), src.pathOrUrl()), 00134 qMakePair(i18nc("The destination of a file operation", "Destination"), dest.pathOrUrl())); 00135 } 00136 00137 void JobPrivate::emitCreatingDir(KIO::Job * job, const KUrl &dir) 00138 { 00139 emit job->description(job, i18nc("@title job","Creating directory"), 00140 qMakePair(i18n("Directory"), dir.pathOrUrl())); 00141 } 00142 00143 void JobPrivate::emitDeleting(KIO::Job *job, const KUrl &url) 00144 { 00145 emit job->description(job, i18nc("@title job","Deleting"), 00146 qMakePair(i18n("File"), url.pathOrUrl())); 00147 } 00148 00149 void JobPrivate::emitStating(KIO::Job *job, const KUrl &url) 00150 { 00151 emit job->description(job, i18nc("@title job","Examining"), 00152 qMakePair(i18n("File"), url.pathOrUrl())); 00153 } 00154 00155 void JobPrivate::emitTransferring(KIO::Job *job, const KUrl &url) 00156 { 00157 emit job->description(job, i18nc("@title job","Transferring"), 00158 qMakePair(i18nc("The source of a file operation", "Source"), url.pathOrUrl())); 00159 } 00160 00161 void JobPrivate::emitMounting(KIO::Job * job, const QString &dev, const QString &point) 00162 { 00163 emit job->description(job, i18nc("@title job","Mounting"), 00164 qMakePair(i18n("Device"), dev), 00165 qMakePair(i18n("Mountpoint"), point)); 00166 } 00167 00168 void JobPrivate::emitUnmounting(KIO::Job * job, const QString &point) 00169 { 00170 emit job->description(job, i18nc("@title job","Unmounting"), 00171 qMakePair(i18n("Mountpoint"), point)); 00172 } 00173 00174 bool Job::doKill() 00175 { 00176 // kill all subjobs, without triggering their result slot 00177 Q_FOREACH( KJob* it, subjobs()) { 00178 it->kill( KJob::Quietly ); 00179 } 00180 clearSubjobs(); 00181 00182 return true; 00183 } 00184 00185 bool Job::doSuspend() 00186 { 00187 Q_FOREACH(KJob* it, subjobs()) { 00188 if (!it->suspend()) 00189 return false; 00190 } 00191 00192 return true; 00193 } 00194 00195 bool Job::doResume() 00196 { 00197 Q_FOREACH ( KJob* it, subjobs() ) 00198 { 00199 if (!it->resume()) 00200 return false; 00201 } 00202 00203 return true; 00204 } 00205 00206 void JobPrivate::slotSpeed( KJob*, unsigned long speed ) 00207 { 00208 //kDebug(7007) << speed; 00209 q_func()->emitSpeed( speed ); 00210 } 00211 00212 //Job::errorString is implemented in global.cpp 00213 00214 #ifndef KDE_NO_DEPRECATED 00215 void Job::showErrorDialog( QWidget *parent ) 00216 { 00217 if ( ui() ) 00218 { 00219 ui()->setWindow( parent ); 00220 ui()->showErrorMessage(); 00221 } 00222 else 00223 { 00224 kError() << errorString(); 00225 } 00226 } 00227 #endif 00228 00229 bool Job::isInteractive() const 00230 { 00231 return uiDelegate() != 0; 00232 } 00233 00234 void Job::setParentJob(Job* job) 00235 { 00236 Q_D(Job); 00237 Q_ASSERT(d->m_parentJob == 0L); 00238 Q_ASSERT(job); 00239 d->m_parentJob = job; 00240 } 00241 00242 Job* Job::parentJob() const 00243 { 00244 return d_func()->m_parentJob; 00245 } 00246 00247 MetaData Job::metaData() const 00248 { 00249 return d_func()->m_incomingMetaData; 00250 } 00251 00252 QString Job::queryMetaData(const QString &key) 00253 { 00254 return d_func()->m_incomingMetaData.value(key, QString()); 00255 } 00256 00257 void Job::setMetaData( const KIO::MetaData &_metaData) 00258 { 00259 Q_D(Job); 00260 d->m_outgoingMetaData = _metaData; 00261 } 00262 00263 void Job::addMetaData( const QString &key, const QString &value) 00264 { 00265 d_func()->m_outgoingMetaData.insert(key, value); 00266 } 00267 00268 void Job::addMetaData( const QMap<QString,QString> &values) 00269 { 00270 Q_D(Job); 00271 QMap<QString,QString>::const_iterator it = values.begin(); 00272 for(;it != values.end(); ++it) 00273 d->m_outgoingMetaData.insert(it.key(), it.value()); 00274 } 00275 00276 void Job::mergeMetaData( const QMap<QString,QString> &values) 00277 { 00278 Q_D(Job); 00279 QMap<QString,QString>::const_iterator it = values.begin(); 00280 for(;it != values.end(); ++it) 00281 // there's probably a faster way 00282 if ( !d->m_outgoingMetaData.contains( it.key() ) ) 00283 d->m_outgoingMetaData.insert( it.key(), it.value() ); 00284 } 00285 00286 MetaData Job::outgoingMetaData() const 00287 { 00288 return d_func()->m_outgoingMetaData; 00289 } 00290 00291 SimpleJob::SimpleJob(SimpleJobPrivate &dd) 00292 : Job(dd) 00293 { 00294 d_func()->simpleJobInit(); 00295 } 00296 00297 void SimpleJobPrivate::simpleJobInit() 00298 { 00299 Q_Q(SimpleJob); 00300 if (!m_url.isValid()) 00301 { 00302 q->setError( ERR_MALFORMED_URL ); 00303 q->setErrorText( m_url.url() ); 00304 QTimer::singleShot(0, q, SLOT(slotFinished()) ); 00305 return; 00306 } 00307 00308 Scheduler::doJob(q); 00309 } 00310 00311 00312 bool SimpleJob::doKill() 00313 { 00314 Q_D(SimpleJob); 00315 if ((d->m_extraFlags & JobPrivate::EF_KillCalled) == 0) { 00316 d->m_extraFlags |= JobPrivate::EF_KillCalled; 00317 Scheduler::cancelJob(this); // deletes the slave if not 0 00318 } else { 00319 kWarning(7007) << this << "This is overkill."; 00320 } 00321 return Job::doKill(); 00322 } 00323 00324 bool SimpleJob::doSuspend() 00325 { 00326 Q_D(SimpleJob); 00327 if ( d->m_slave ) 00328 d->m_slave->suspend(); 00329 return Job::doSuspend(); 00330 } 00331 00332 bool SimpleJob::doResume() 00333 { 00334 Q_D(SimpleJob); 00335 if ( d->m_slave ) 00336 d->m_slave->resume(); 00337 return Job::doResume(); 00338 } 00339 00340 const KUrl& SimpleJob::url() const 00341 { 00342 return d_func()->m_url; 00343 } 00344 00345 void SimpleJob::putOnHold() 00346 { 00347 Q_D(SimpleJob); 00348 Q_ASSERT( d->m_slave ); 00349 if ( d->m_slave ) 00350 { 00351 Scheduler::putSlaveOnHold(this, d->m_url); 00352 } 00353 // we should now be disassociated from the slave 00354 Q_ASSERT(!d->m_slave); 00355 kill( Quietly ); 00356 } 00357 00358 void SimpleJob::removeOnHold() 00359 { 00360 Scheduler::removeSlaveOnHold(); 00361 } 00362 00363 bool SimpleJob::isRedirectionHandlingEnabled() const 00364 { 00365 return d_func()->m_redirectionHandlingEnabled; 00366 } 00367 00368 void SimpleJob::setRedirectionHandlingEnabled(bool handle) 00369 { 00370 Q_D(SimpleJob); 00371 d->m_redirectionHandlingEnabled = handle; 00372 } 00373 00374 SimpleJob::~SimpleJob() 00375 { 00376 Q_D(SimpleJob); 00377 // last chance to remove this job from the scheduler! 00378 if (d->m_schedSerial) { 00379 kDebug(7007) << "Killing job" << this << "in destructor!" << kBacktrace(); 00380 Scheduler::cancelJob(this); 00381 } 00382 } 00383 00384 void SimpleJobPrivate::start(Slave *slave) 00385 { 00386 Q_Q(SimpleJob); 00387 m_slave = slave; 00388 00389 // Slave::setJob can send us SSL metadata if there is a persistent connection 00390 q->connect( slave, SIGNAL(metaData(KIO::MetaData)), 00391 SLOT(slotMetaData(KIO::MetaData)) ); 00392 00393 slave->setJob(q); 00394 00395 q->connect( slave, SIGNAL(error(int,QString)), 00396 SLOT(slotError(int,QString)) ); 00397 00398 q->connect( slave, SIGNAL(warning(QString)), 00399 SLOT(slotWarning(QString)) ); 00400 00401 q->connect( slave, SIGNAL(infoMessage(QString)), 00402 SLOT(_k_slotSlaveInfoMessage(QString)) ); 00403 00404 q->connect( slave, SIGNAL(connected()), 00405 SLOT(slotConnected())); 00406 00407 q->connect( slave, SIGNAL(finished()), 00408 SLOT(slotFinished()) ); 00409 00410 if ((m_extraFlags & EF_TransferJobDataSent) == 0) // this is a "get" job 00411 { 00412 q->connect( slave, SIGNAL(totalSize(KIO::filesize_t)), 00413 SLOT(slotTotalSize(KIO::filesize_t)) ); 00414 00415 q->connect( slave, SIGNAL(processedSize(KIO::filesize_t)), 00416 SLOT(slotProcessedSize(KIO::filesize_t)) ); 00417 00418 q->connect( slave, SIGNAL(speed(ulong)), 00419 SLOT(slotSpeed(ulong)) ); 00420 } 00421 00422 if (ui() && ui()->window()) 00423 { 00424 m_outgoingMetaData.insert("window-id", QString::number((qptrdiff)ui()->window()->winId())); 00425 } 00426 00427 if (ui() && ui()->userTimestamp()) 00428 { 00429 m_outgoingMetaData.insert("user-timestamp", QString::number(ui()->userTimestamp())); 00430 } 00431 00432 if (ui() == 0) // not interactive 00433 { 00434 m_outgoingMetaData.insert("no-auth-prompt", "true"); 00435 } 00436 00437 if (!m_outgoingMetaData.isEmpty()) 00438 { 00439 KIO_ARGS << m_outgoingMetaData; 00440 slave->send( CMD_META_DATA, packedArgs ); 00441 } 00442 00443 if (!m_subUrl.isEmpty()) 00444 { 00445 KIO_ARGS << m_subUrl; 00446 slave->send( CMD_SUBURL, packedArgs ); 00447 } 00448 00449 slave->send( m_command, m_packedArgs ); 00450 } 00451 00452 void SimpleJobPrivate::slaveDone() 00453 { 00454 Q_Q(SimpleJob); 00455 if (m_slave) { 00456 if (m_command == CMD_OPEN) { 00457 m_slave->send(CMD_CLOSE); 00458 } 00459 q->disconnect(m_slave); // Remove all signals between slave and job 00460 } 00461 // only finish a job once; Scheduler::jobFinished() resets schedSerial to zero. 00462 if (m_schedSerial) { 00463 Scheduler::jobFinished(q, m_slave); 00464 } 00465 } 00466 00467 void SimpleJob::slotFinished( ) 00468 { 00469 Q_D(SimpleJob); 00470 // Return slave to the scheduler 00471 d->slaveDone(); 00472 00473 if (!hasSubjobs()) 00474 { 00475 if ( !error() && (d->m_command == CMD_MKDIR || d->m_command == CMD_RENAME ) ) 00476 { 00477 if ( d->m_command == CMD_MKDIR ) 00478 { 00479 KUrl urlDir( url() ); 00480 urlDir.setPath( urlDir.directory() ); 00481 org::kde::KDirNotify::emitFilesAdded( urlDir.url() ); 00482 } 00483 else /*if ( m_command == CMD_RENAME )*/ 00484 { 00485 KUrl src, dst; 00486 QDataStream str( d->m_packedArgs ); 00487 str >> src >> dst; 00488 if( src.directory() == dst.directory() ) // For the user, moving isn't renaming. Only renaming is. 00489 org::kde::KDirNotify::emitFileRenamed( src.url(), dst.url() ); 00490 00491 org::kde::KDirNotify::emitFileMoved( src.url(), dst.url() ); 00492 } 00493 } 00494 emitResult(); 00495 } 00496 } 00497 00498 void SimpleJob::slotError( int err, const QString & errorText ) 00499 { 00500 Q_D(SimpleJob); 00501 setError( err ); 00502 setErrorText( errorText ); 00503 if ((error() == ERR_UNKNOWN_HOST) && d->m_url.host().isEmpty()) 00504 setErrorText( QString() ); 00505 // error terminates the job 00506 slotFinished(); 00507 } 00508 00509 void SimpleJob::slotWarning( const QString & errorText ) 00510 { 00511 emit warning( this, errorText ); 00512 } 00513 00514 void SimpleJobPrivate::_k_slotSlaveInfoMessage( const QString & msg ) 00515 { 00516 emit q_func()->infoMessage( q_func(), msg ); 00517 } 00518 00519 void SimpleJobPrivate::slotConnected() 00520 { 00521 emit q_func()->connected( q_func() ); 00522 } 00523 00524 void SimpleJobPrivate::slotTotalSize( KIO::filesize_t size ) 00525 { 00526 Q_Q(SimpleJob); 00527 if (size != q->totalAmount(KJob::Bytes)) 00528 { 00529 q->setTotalAmount(KJob::Bytes, size); 00530 } 00531 } 00532 00533 void SimpleJobPrivate::slotProcessedSize( KIO::filesize_t size ) 00534 { 00535 Q_Q(SimpleJob); 00536 //kDebug(7007) << KIO::number(size); 00537 q->setProcessedAmount(KJob::Bytes, size); 00538 } 00539 00540 void SimpleJobPrivate::slotSpeed( unsigned long speed ) 00541 { 00542 //kDebug(7007) << speed; 00543 q_func()->emitSpeed( speed ); 00544 } 00545 00546 void SimpleJobPrivate::restartAfterRedirection(KUrl *redirectionUrl) 00547 { 00548 Q_Q(SimpleJob); 00549 // Return slave to the scheduler while we still have the old URL in place; the scheduler 00550 // requires a job URL to stay invariant while the job is running. 00551 slaveDone(); 00552 00553 m_url = *redirectionUrl; 00554 redirectionUrl->clear(); 00555 if ((m_extraFlags & EF_KillCalled) == 0) { 00556 Scheduler::doJob(q); 00557 } 00558 } 00559 00560 void SimpleJob::slotMetaData( const KIO::MetaData &_metaData ) 00561 { 00562 Q_D(SimpleJob); 00563 QMapIterator<QString,QString> it (_metaData); 00564 while (it.hasNext()) { 00565 it.next(); 00566 if (it.key().startsWith(QLatin1String("{internal~"), Qt::CaseInsensitive)) 00567 d->m_internalMetaData.insert(it.key(), it.value()); 00568 else 00569 d->m_incomingMetaData.insert(it.key(), it.value()); 00570 } 00571 00572 // Update the internal meta-data values as soon as possible. Waiting until 00573 // the ioslave is finished has unintended consequences if the client starts 00574 // a new connection without waiting for the ioslave to finish. 00575 if (!d->m_internalMetaData.isEmpty()) { 00576 Scheduler::updateInternalMetaData(this); 00577 } 00578 } 00579 00580 void SimpleJob::storeSSLSessionFromJob(const KUrl &redirectionURL) 00581 { 00582 Q_UNUSED(redirectionURL); 00583 } 00584 00585 00587 class KIO::MkdirJobPrivate: public SimpleJobPrivate 00588 { 00589 public: 00590 MkdirJobPrivate(const KUrl& url, int command, const QByteArray &packedArgs) 00591 : SimpleJobPrivate(url, command, packedArgs) 00592 { } 00593 KUrl m_redirectionURL; 00594 void slotRedirection(const KUrl &url); 00595 00602 virtual void start( Slave *slave ); 00603 00604 Q_DECLARE_PUBLIC(MkdirJob) 00605 00606 static inline MkdirJob *newJob(const KUrl& url, int command, const QByteArray &packedArgs) 00607 { 00608 MkdirJob *job = new MkdirJob(*new MkdirJobPrivate(url, command, packedArgs)); 00609 job->setUiDelegate(new JobUiDelegate); 00610 return job; 00611 } 00612 }; 00613 00614 MkdirJob::MkdirJob(MkdirJobPrivate &dd) 00615 : SimpleJob(dd) 00616 { 00617 } 00618 00619 MkdirJob::~MkdirJob() 00620 { 00621 } 00622 00623 void MkdirJobPrivate::start(Slave *slave) 00624 { 00625 Q_Q(MkdirJob); 00626 q->connect( slave, SIGNAL( redirection(const KUrl &) ), 00627 SLOT( slotRedirection(const KUrl &) ) ); 00628 00629 SimpleJobPrivate::start(slave); 00630 } 00631 00632 // Slave got a redirection request 00633 void MkdirJobPrivate::slotRedirection( const KUrl &url) 00634 { 00635 Q_Q(MkdirJob); 00636 kDebug(7007) << url; 00637 if (!KAuthorized::authorizeUrlAction("redirect", m_url, url)) 00638 { 00639 kWarning(7007) << "Redirection from" << m_url << "to" << url << "REJECTED!"; 00640 q->setError( ERR_ACCESS_DENIED ); 00641 q->setErrorText( url.pathOrUrl() ); 00642 return; 00643 } 00644 m_redirectionURL = url; // We'll remember that when the job finishes 00645 // Tell the user that we haven't finished yet 00646 emit q->redirection(q, m_redirectionURL); 00647 } 00648 00649 void MkdirJob::slotFinished() 00650 { 00651 Q_D(MkdirJob); 00652 00653 if ( !d->m_redirectionURL.isEmpty() && d->m_redirectionURL.isValid() ) 00654 { 00655 //kDebug(7007) << "MkdirJob: Redirection to " << m_redirectionURL; 00656 if (queryMetaData("permanent-redirect")=="true") 00657 emit permanentRedirection(this, d->m_url, d->m_redirectionURL); 00658 00659 if ( d->m_redirectionHandlingEnabled ) 00660 { 00661 KUrl dummyUrl; 00662 int permissions; 00663 QDataStream istream( d->m_packedArgs ); 00664 istream >> dummyUrl >> permissions; 00665 00666 d->m_packedArgs.truncate(0); 00667 QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly ); 00668 stream << d->m_redirectionURL << permissions; 00669 00670 d->restartAfterRedirection(&d->m_redirectionURL); 00671 return; 00672 } 00673 } 00674 00675 // Return slave to the scheduler 00676 SimpleJob::slotFinished(); 00677 } 00678 00679 SimpleJob *KIO::mkdir( const KUrl& url, int permissions ) 00680 { 00681 //kDebug(7007) << "mkdir " << url; 00682 KIO_ARGS << url << permissions; 00683 return MkdirJobPrivate::newJob(url, CMD_MKDIR, packedArgs); 00684 } 00685 00686 SimpleJob *KIO::rmdir( const KUrl& url ) 00687 { 00688 //kDebug(7007) << "rmdir " << url; 00689 KIO_ARGS << url << qint8(false); // isFile is false 00690 return SimpleJobPrivate::newJob(url, CMD_DEL, packedArgs); 00691 } 00692 00693 SimpleJob *KIO::chmod( const KUrl& url, int permissions ) 00694 { 00695 //kDebug(7007) << "chmod " << url; 00696 KIO_ARGS << url << permissions; 00697 return SimpleJobPrivate::newJob(url, CMD_CHMOD, packedArgs); 00698 } 00699 00700 SimpleJob *KIO::chown( const KUrl& url, const QString& owner, const QString& group ) 00701 { 00702 KIO_ARGS << url << owner << group; 00703 return SimpleJobPrivate::newJob(url, CMD_CHOWN, packedArgs); 00704 } 00705 00706 SimpleJob *KIO::setModificationTime( const KUrl& url, const QDateTime& mtime ) 00707 { 00708 //kDebug(7007) << "setModificationTime " << url << " " << mtime; 00709 KIO_ARGS << url << mtime; 00710 return SimpleJobPrivate::newJobNoUi(url, CMD_SETMODIFICATIONTIME, packedArgs); 00711 } 00712 00713 SimpleJob *KIO::rename( const KUrl& src, const KUrl & dest, JobFlags flags ) 00714 { 00715 //kDebug(7007) << "rename " << src << " " << dest; 00716 KIO_ARGS << src << dest << (qint8) (flags & Overwrite); 00717 return SimpleJobPrivate::newJob(src, CMD_RENAME, packedArgs); 00718 } 00719 00720 SimpleJob *KIO::symlink( const QString& target, const KUrl & dest, JobFlags flags ) 00721 { 00722 //kDebug(7007) << "symlink target=" << target << " " << dest; 00723 KIO_ARGS << target << dest << (qint8) (flags & Overwrite); 00724 return SimpleJobPrivate::newJob(dest, CMD_SYMLINK, packedArgs, flags); 00725 } 00726 00727 SimpleJob *KIO::special(const KUrl& url, const QByteArray & data, JobFlags flags) 00728 { 00729 //kDebug(7007) << "special " << url; 00730 return SimpleJobPrivate::newJob(url, CMD_SPECIAL, data, flags); 00731 } 00732 00733 SimpleJob *KIO::mount( bool ro, const QByteArray& fstype, const QString& dev, const QString& point, JobFlags flags ) 00734 { 00735 KIO_ARGS << int(1) << qint8( ro ? 1 : 0 ) 00736 << QString::fromLatin1(fstype) << dev << point; 00737 SimpleJob *job = special( KUrl("file:/"), packedArgs, flags ); 00738 if (!(flags & HideProgressInfo)) { 00739 KIO::JobPrivate::emitMounting(job, dev, point); 00740 } 00741 return job; 00742 } 00743 00744 SimpleJob *KIO::unmount( const QString& point, JobFlags flags ) 00745 { 00746 KIO_ARGS << int(2) << point; 00747 SimpleJob *job = special( KUrl("file:/"), packedArgs, flags ); 00748 if (!(flags & HideProgressInfo)) { 00749 KIO::JobPrivate::emitUnmounting(job, point); 00750 } 00751 return job; 00752 } 00753 00754 00755 00757 00758 class KIO::StatJobPrivate: public SimpleJobPrivate 00759 { 00760 public: 00761 inline StatJobPrivate(const KUrl& url, int command, const QByteArray &packedArgs) 00762 : SimpleJobPrivate(url, command, packedArgs), m_bSource(true), m_details(2) 00763 {} 00764 00765 UDSEntry m_statResult; 00766 KUrl m_redirectionURL; 00767 bool m_bSource; 00768 short int m_details; 00769 void slotStatEntry( const KIO::UDSEntry & entry ); 00770 void slotRedirection( const KUrl &url); 00771 00778 virtual void start( Slave *slave ); 00779 00780 Q_DECLARE_PUBLIC(StatJob) 00781 00782 static inline StatJob *newJob(const KUrl& url, int command, const QByteArray &packedArgs, 00783 JobFlags flags ) 00784 { 00785 StatJob *job = new StatJob(*new StatJobPrivate(url, command, packedArgs)); 00786 job->setUiDelegate(new JobUiDelegate); 00787 if (!(flags & HideProgressInfo)) { 00788 KIO::getJobTracker()->registerJob(job); 00789 emitStating(job, url); 00790 } 00791 return job; 00792 } 00793 }; 00794 00795 StatJob::StatJob(StatJobPrivate &dd) 00796 : SimpleJob(dd) 00797 { 00798 } 00799 00800 StatJob::~StatJob() 00801 { 00802 } 00803 00804 #ifndef KDE_NO_DEPRECATED 00805 void StatJob::setSide( bool source ) 00806 { 00807 d_func()->m_bSource = source; 00808 } 00809 #endif 00810 00811 void StatJob::setSide( StatSide side ) 00812 { 00813 d_func()->m_bSource = side == SourceSide; 00814 } 00815 00816 void StatJob::setDetails( short int details ) 00817 { 00818 d_func()->m_details = details; 00819 } 00820 00821 const UDSEntry & StatJob::statResult() const 00822 { 00823 return d_func()->m_statResult; 00824 } 00825 00826 KUrl StatJob::mostLocalUrl() const 00827 { 00828 if (!url().isLocalFile()) { 00829 const UDSEntry& udsEntry = d_func()->m_statResult; 00830 const QString path = udsEntry.stringValue( KIO::UDSEntry::UDS_LOCAL_PATH ); 00831 if (!path.isEmpty()) 00832 return KUrl(path); 00833 } 00834 return url(); 00835 } 00836 00837 void StatJobPrivate::start(Slave *slave) 00838 { 00839 Q_Q(StatJob); 00840 m_outgoingMetaData.insert( "statSide", m_bSource ? "source" : "dest" ); 00841 m_outgoingMetaData.insert( "details", QString::number(m_details) ); 00842 00843 q->connect( slave, SIGNAL( statEntry( const KIO::UDSEntry& ) ), 00844 SLOT( slotStatEntry( const KIO::UDSEntry & ) ) ); 00845 q->connect( slave, SIGNAL( redirection(const KUrl &) ), 00846 SLOT( slotRedirection(const KUrl &) ) ); 00847 00848 SimpleJobPrivate::start(slave); 00849 } 00850 00851 void StatJobPrivate::slotStatEntry( const KIO::UDSEntry & entry ) 00852 { 00853 //kDebug(7007); 00854 m_statResult = entry; 00855 } 00856 00857 // Slave got a redirection request 00858 void StatJobPrivate::slotRedirection( const KUrl &url) 00859 { 00860 Q_Q(StatJob); 00861 kDebug(7007) << m_url << "->" << url; 00862 if (!KAuthorized::authorizeUrlAction("redirect", m_url, url)) 00863 { 00864 kWarning(7007) << "Redirection from " << m_url << " to " << url << " REJECTED!"; 00865 q->setError( ERR_ACCESS_DENIED ); 00866 q->setErrorText( url.pathOrUrl() ); 00867 return; 00868 } 00869 m_redirectionURL = url; // We'll remember that when the job finishes 00870 // Tell the user that we haven't finished yet 00871 emit q->redirection(q, m_redirectionURL); 00872 } 00873 00874 void StatJob::slotFinished() 00875 { 00876 Q_D(StatJob); 00877 00878 if ( !d->m_redirectionURL.isEmpty() && d->m_redirectionURL.isValid() ) 00879 { 00880 //kDebug(7007) << "StatJob: Redirection to " << m_redirectionURL; 00881 if (queryMetaData("permanent-redirect")=="true") 00882 emit permanentRedirection(this, d->m_url, d->m_redirectionURL); 00883 00884 if ( d->m_redirectionHandlingEnabled ) 00885 { 00886 d->m_packedArgs.truncate(0); 00887 QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly ); 00888 stream << d->m_redirectionURL; 00889 00890 d->restartAfterRedirection(&d->m_redirectionURL); 00891 return; 00892 } 00893 } 00894 00895 // Return slave to the scheduler 00896 SimpleJob::slotFinished(); 00897 } 00898 00899 void StatJob::slotMetaData( const KIO::MetaData &_metaData) 00900 { 00901 Q_D(StatJob); 00902 SimpleJob::slotMetaData(_metaData); 00903 storeSSLSessionFromJob(d->m_redirectionURL); 00904 } 00905 00906 StatJob *KIO::stat(const KUrl& url, JobFlags flags) 00907 { 00908 // Assume sideIsSource. Gets are more common than puts. 00909 return stat( url, StatJob::SourceSide, 2, flags ); 00910 } 00911 00912 StatJob *KIO::mostLocalUrl(const KUrl& url, JobFlags flags) 00913 { 00914 StatJob* job = stat( url, StatJob::SourceSide, 2, flags ); 00915 if (url.isLocalFile()) { 00916 QTimer::singleShot(0, job, SLOT(slotFinished())); 00917 Scheduler::cancelJob(job); // deletes the slave if not 0 00918 } 00919 return job; 00920 } 00921 00922 #ifndef KDE_NO_DEPRECATED 00923 StatJob *KIO::stat(const KUrl& url, bool sideIsSource, short int details, JobFlags flags ) 00924 { 00925 //kDebug(7007) << "stat" << url; 00926 KIO_ARGS << url; 00927 StatJob * job = StatJobPrivate::newJob(url, CMD_STAT, packedArgs, flags); 00928 job->setSide( sideIsSource ? StatJob::SourceSide : StatJob::DestinationSide ); 00929 job->setDetails( details ); 00930 return job; 00931 } 00932 #endif 00933 00934 StatJob *KIO::stat(const KUrl& url, KIO::StatJob::StatSide side, short int details, JobFlags flags ) 00935 { 00936 //kDebug(7007) << "stat" << url; 00937 KIO_ARGS << url; 00938 StatJob * job = StatJobPrivate::newJob(url, CMD_STAT, packedArgs, flags); 00939 job->setSide( side ); 00940 job->setDetails( details ); 00941 return job; 00942 } 00943 00944 SimpleJob *KIO::http_update_cache( const KUrl& url, bool no_cache, time_t expireDate) 00945 { 00946 Q_ASSERT(url.protocol() == "http" || url.protocol() == "https"); 00947 // Send http update_cache command (2) 00948 KIO_ARGS << (int)2 << url << no_cache << qlonglong(expireDate); 00949 SimpleJob * job = SimpleJobPrivate::newJob(url, CMD_SPECIAL, packedArgs); 00950 Scheduler::setJobPriority(job, 1); 00951 return job; 00952 } 00953 00955 00956 TransferJob::TransferJob(TransferJobPrivate &dd) 00957 : SimpleJob(dd) 00958 { 00959 Q_D(TransferJob); 00960 if (d->m_command == CMD_PUT) { 00961 d->m_extraFlags |= JobPrivate::EF_TransferJobDataSent; 00962 } 00963 } 00964 00965 TransferJob::~TransferJob() 00966 { 00967 } 00968 00969 // Slave sends data 00970 void TransferJob::slotData( const QByteArray &_data) 00971 { 00972 Q_D(TransferJob); 00973 if (d->m_command == CMD_GET && !d->m_isMimetypeEmitted) { 00974 kWarning(7007) << "mimeType() not emitted when sending first data!; job URL =" 00975 << d->m_url << "data size =" << _data.size(); 00976 } 00977 // shut up the warning, HACK: downside is that it changes the meaning of the variable 00978 d->m_isMimetypeEmitted = true; 00979 00980 if (d->m_redirectionURL.isEmpty() || !d->m_redirectionURL.isValid() || error()) { 00981 emit data(this, _data); 00982 } 00983 } 00984 00985 void KIO::TransferJob::setTotalSize(KIO::filesize_t bytes) 00986 { 00987 setTotalAmount(KJob::Bytes, bytes); 00988 } 00989 00990 // Slave got a redirection request 00991 void TransferJob::slotRedirection( const KUrl &url) 00992 { 00993 Q_D(TransferJob); 00994 kDebug(7007) << url; 00995 if (!KAuthorized::authorizeUrlAction("redirect", d->m_url, url)) 00996 { 00997 kWarning(7007) << "Redirection from " << d->m_url << " to " << url << " REJECTED!"; 00998 return; 00999 } 01000 01001 // Some websites keep redirecting to themselves where each redirection 01002 // acts as the stage in a state-machine. We define "endless redirections" 01003 // as 5 redirections to the same URL. 01004 if (d->m_redirectionList.count(url) > 5) 01005 { 01006 kDebug(7007) << "CYCLIC REDIRECTION!"; 01007 setError( ERR_CYCLIC_LINK ); 01008 setErrorText( d->m_url.pathOrUrl() ); 01009 } 01010 else 01011 { 01012 d->m_redirectionURL = url; // We'll remember that when the job finishes 01013 d->m_redirectionList.append(url); 01014 d->m_outgoingMetaData["ssl_was_in_use"] = d->m_incomingMetaData["ssl_in_use"]; 01015 // Tell the user that we haven't finished yet 01016 emit redirection(this, d->m_redirectionURL); 01017 } 01018 } 01019 01020 void TransferJob::slotFinished() 01021 { 01022 Q_D(TransferJob); 01023 01024 kDebug(7007) << d->m_url; 01025 if (!d->m_redirectionURL.isEmpty() && d->m_redirectionURL.isValid()) { 01026 01027 //kDebug(7007) << "Redirection to" << m_redirectionURL; 01028 if (queryMetaData("permanent-redirect")=="true") 01029 emit permanentRedirection(this, d->m_url, d->m_redirectionURL); 01030 01031 if (d->m_redirectionHandlingEnabled) { 01032 // Honour the redirection 01033 // We take the approach of "redirecting this same job" 01034 // Another solution would be to create a subjob, but the same problem 01035 // happens (unpacking+repacking) 01036 d->staticData.truncate(0); 01037 d->m_incomingMetaData.clear(); 01038 if (queryMetaData("cache") != "reload") 01039 addMetaData("cache","refresh"); 01040 d->m_internalSuspended = false; 01041 // The very tricky part is the packed arguments business 01042 QString dummyStr; 01043 KUrl dummyUrl; 01044 QDataStream istream( d->m_packedArgs ); 01045 switch( d->m_command ) { 01046 case CMD_GET: { 01047 d->m_packedArgs.truncate(0); 01048 QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly ); 01049 stream << d->m_redirectionURL; 01050 break; 01051 } 01052 case CMD_PUT: { 01053 int permissions; 01054 qint8 iOverwrite, iResume; 01055 istream >> dummyUrl >> iOverwrite >> iResume >> permissions; 01056 d->m_packedArgs.truncate(0); 01057 QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly ); 01058 stream << d->m_redirectionURL << iOverwrite << iResume << permissions; 01059 break; 01060 } 01061 case CMD_SPECIAL: { 01062 int specialcmd; 01063 istream >> specialcmd; 01064 if (specialcmd == 1) // HTTP POST 01065 { 01066 d->m_outgoingMetaData.remove(QLatin1String("content-type")); 01067 addMetaData("cache","reload"); 01068 d->m_packedArgs.truncate(0); 01069 QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly ); 01070 stream << d->m_redirectionURL; 01071 d->m_command = CMD_GET; 01072 } 01073 break; 01074 } 01075 } 01076 d->restartAfterRedirection(&d->m_redirectionURL); 01077 return; 01078 } 01079 } 01080 01081 SimpleJob::slotFinished(); 01082 } 01083 01084 void TransferJob::setAsyncDataEnabled(bool enabled) 01085 { 01086 Q_D(TransferJob); 01087 if (enabled) 01088 d->m_extraFlags |= JobPrivate::EF_TransferJobAsync; 01089 else 01090 d->m_extraFlags &= ~JobPrivate::EF_TransferJobAsync; 01091 } 01092 01093 void TransferJob::sendAsyncData(const QByteArray &dataForSlave) 01094 { 01095 Q_D(TransferJob); 01096 if (d->m_extraFlags & JobPrivate::EF_TransferJobNeedData) 01097 { 01098 d->m_slave->send( MSG_DATA, dataForSlave ); 01099 if (d->m_extraFlags & JobPrivate::EF_TransferJobDataSent) // put job -> emit progress 01100 { 01101 KIO::filesize_t size = processedAmount(KJob::Bytes)+dataForSlave.size(); 01102 setProcessedAmount(KJob::Bytes, size); 01103 } 01104 } 01105 01106 d->m_extraFlags &= ~JobPrivate::EF_TransferJobNeedData; 01107 } 01108 01109 #ifndef KDE_NO_DEPRECATED 01110 void TransferJob::setReportDataSent(bool enabled) 01111 { 01112 Q_D(TransferJob); 01113 if (enabled) 01114 d->m_extraFlags |= JobPrivate::EF_TransferJobDataSent; 01115 else 01116 d->m_extraFlags &= ~JobPrivate::EF_TransferJobDataSent; 01117 } 01118 #endif 01119 01120 #ifndef KDE_NO_DEPRECATED 01121 bool TransferJob::reportDataSent() const 01122 { 01123 return (d_func()->m_extraFlags & JobPrivate::EF_TransferJobDataSent); 01124 } 01125 #endif 01126 01127 QString TransferJob::mimetype() const 01128 { 01129 return d_func()->m_mimetype; 01130 } 01131 01132 // Slave requests data 01133 void TransferJob::slotDataReq() 01134 { 01135 Q_D(TransferJob); 01136 QByteArray dataForSlave; 01137 01138 d->m_extraFlags |= JobPrivate::EF_TransferJobNeedData; 01139 01140 if (!d->staticData.isEmpty()) 01141 { 01142 dataForSlave = d->staticData; 01143 d->staticData.clear(); 01144 } 01145 else 01146 { 01147 emit dataReq( this, dataForSlave); 01148 01149 if (d->m_extraFlags & JobPrivate::EF_TransferJobAsync) 01150 return; 01151 } 01152 01153 static const int max_size = 14 * 1024 * 1024; 01154 if (dataForSlave.size() > max_size) 01155 { 01156 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"; 01157 d->staticData = QByteArray(dataForSlave.data() + max_size , dataForSlave.size() - max_size); 01158 dataForSlave.truncate(max_size); 01159 } 01160 01161 sendAsyncData(dataForSlave); 01162 01163 if (d->m_subJob) 01164 { 01165 // Bitburger protocol in action 01166 d->internalSuspend(); // Wait for more data from subJob. 01167 d->m_subJob->d_func()->internalResume(); // Ask for more! 01168 } 01169 } 01170 01171 void TransferJob::slotMimetype( const QString& type ) 01172 { 01173 Q_D(TransferJob); 01174 d->m_mimetype = type; 01175 if (d->m_command == CMD_GET && d->m_isMimetypeEmitted) { 01176 kWarning(7007) << "mimetype() emitted again, or after sending first data!; job URL =" 01177 << d->m_url; 01178 } 01179 d->m_isMimetypeEmitted = true; 01180 emit mimetype( this, type ); 01181 } 01182 01183 01184 void TransferJobPrivate::internalSuspend() 01185 { 01186 m_internalSuspended = true; 01187 if (m_slave) 01188 m_slave->suspend(); 01189 } 01190 01191 void TransferJobPrivate::internalResume() 01192 { 01193 m_internalSuspended = false; 01194 if ( m_slave && !suspended ) 01195 m_slave->resume(); 01196 } 01197 01198 bool TransferJob::doResume() 01199 { 01200 Q_D(TransferJob); 01201 if ( !SimpleJob::doResume() ) 01202 return false; 01203 if ( d->m_internalSuspended ) 01204 d->internalSuspend(); 01205 return true; 01206 } 01207 01208 bool TransferJob::isErrorPage() const 01209 { 01210 return d_func()->m_errorPage; 01211 } 01212 01213 void TransferJobPrivate::start(Slave *slave) 01214 { 01215 Q_Q(TransferJob); 01216 Q_ASSERT(slave); 01217 JobPrivate::emitTransferring(q, m_url); 01218 q->connect( slave, SIGNAL( data( const QByteArray & ) ), 01219 SLOT( slotData( const QByteArray & ) ) ); 01220 01221 if (m_outgoingDataSource) 01222 q->connect( slave, SIGNAL( dataReq() ), 01223 SLOT( slotDataReqFromDevice() ) ); 01224 else 01225 q->connect( slave, SIGNAL( dataReq() ), 01226 SLOT( slotDataReq() ) ); 01227 01228 q->connect( slave, SIGNAL( redirection(const KUrl &) ), 01229 SLOT( slotRedirection(const KUrl &) ) ); 01230 01231 q->connect( slave, SIGNAL(mimeType( const QString& ) ), 01232 SLOT( slotMimetype( const QString& ) ) ); 01233 01234 q->connect( slave, SIGNAL(errorPage() ), 01235 SLOT( slotErrorPage() ) ); 01236 01237 q->connect( slave, SIGNAL( needSubUrlData() ), 01238 SLOT( slotNeedSubUrlData() ) ); 01239 01240 q->connect( slave, SIGNAL(canResume( KIO::filesize_t ) ), 01241 SLOT( slotCanResume( KIO::filesize_t ) ) ); 01242 01243 if (slave->suspended()) 01244 { 01245 m_mimetype = "unknown"; 01246 // WABA: The slave was put on hold. Resume operation. 01247 slave->resume(); 01248 } 01249 01250 SimpleJobPrivate::start(slave); 01251 if (m_internalSuspended) 01252 slave->suspend(); 01253 } 01254 01255 void TransferJobPrivate::slotNeedSubUrlData() 01256 { 01257 Q_Q(TransferJob); 01258 // Job needs data from subURL. 01259 m_subJob = KIO::get( m_subUrl, NoReload, HideProgressInfo); 01260 internalSuspend(); // Put job on hold until we have some data. 01261 q->connect(m_subJob, SIGNAL( data(KIO::Job*,const QByteArray &)), 01262 SLOT( slotSubUrlData(KIO::Job*,const QByteArray &))); 01263 q->addSubjob(m_subJob); 01264 } 01265 01266 void TransferJobPrivate::slotSubUrlData(KIO::Job*, const QByteArray &data) 01267 { 01268 // The Alternating Bitburg protocol in action again. 01269 staticData = data; 01270 m_subJob->d_func()->internalSuspend(); // Put job on hold until we have delivered the data. 01271 internalResume(); // Activate ourselves again. 01272 } 01273 01274 void TransferJob::slotMetaData( const KIO::MetaData &_metaData) 01275 { 01276 Q_D(TransferJob); 01277 SimpleJob::slotMetaData(_metaData); 01278 storeSSLSessionFromJob(d->m_redirectionURL); 01279 } 01280 01281 void TransferJobPrivate::slotErrorPage() 01282 { 01283 m_errorPage = true; 01284 } 01285 01286 void TransferJobPrivate::slotCanResume( KIO::filesize_t offset ) 01287 { 01288 Q_Q(TransferJob); 01289 emit q->canResume(q, offset); 01290 } 01291 01292 void TransferJobPrivate::slotDataReqFromDevice() 01293 { 01294 Q_Q(TransferJob); 01295 01296 QByteArray dataForSlave; 01297 01298 m_extraFlags |= JobPrivate::EF_TransferJobNeedData; 01299 01300 if (m_outgoingDataSource) 01301 dataForSlave = m_outgoingDataSource.data()->read(MAX_READ_BUF_SIZE); 01302 01303 if (dataForSlave.isEmpty()) 01304 { 01305 emit q->dataReq(q, dataForSlave); 01306 if (m_extraFlags & JobPrivate::EF_TransferJobAsync) 01307 return; 01308 } 01309 01310 q->sendAsyncData(dataForSlave); 01311 01312 if (m_subJob) 01313 { 01314 // Bitburger protocol in action 01315 internalSuspend(); // Wait for more data from subJob. 01316 m_subJob->d_func()->internalResume(); // Ask for more! 01317 } 01318 } 01319 01320 void TransferJob::slotResult( KJob *job) 01321 { 01322 Q_D(TransferJob); 01323 // This can only be our suburl. 01324 Q_ASSERT(job == d->m_subJob); 01325 01326 SimpleJob::slotResult( job ); 01327 01328 if (!error() && job == d->m_subJob) 01329 { 01330 d->m_subJob = 0; // No action required 01331 d->internalResume(); // Make sure we get the remaining data. 01332 } 01333 } 01334 01335 void TransferJob::setModificationTime( const QDateTime& mtime ) 01336 { 01337 addMetaData( "modified", mtime.toString( Qt::ISODate ) ); 01338 } 01339 01340 TransferJob *KIO::get( const KUrl& url, LoadType reload, JobFlags flags ) 01341 { 01342 // Send decoded path and encoded query 01343 KIO_ARGS << url; 01344 TransferJob * job = TransferJobPrivate::newJob(url, CMD_GET, packedArgs, 01345 QByteArray(), flags); 01346 if (reload == Reload) 01347 job->addMetaData("cache", "reload"); 01348 return job; 01349 } 01350 01351 class KIO::StoredTransferJobPrivate: public TransferJobPrivate 01352 { 01353 public: 01354 StoredTransferJobPrivate(const KUrl& url, int command, 01355 const QByteArray &packedArgs, 01356 const QByteArray &_staticData) 01357 : TransferJobPrivate(url, command, packedArgs, _staticData), 01358 m_uploadOffset( 0 ) 01359 {} 01360 StoredTransferJobPrivate(const KUrl& url, int command, 01361 const QByteArray &packedArgs, 01362 QIODevice* ioDevice) 01363 : TransferJobPrivate(url, command, packedArgs, ioDevice), 01364 m_uploadOffset( 0 ) 01365 {} 01366 01367 QByteArray m_data; 01368 int m_uploadOffset; 01369 01370 void slotStoredData( KIO::Job *job, const QByteArray &data ); 01371 void slotStoredDataReq( KIO::Job *job, QByteArray &data ); 01372 01373 Q_DECLARE_PUBLIC(StoredTransferJob) 01374 01375 static inline StoredTransferJob *newJob(const KUrl &url, int command, 01376 const QByteArray &packedArgs, 01377 const QByteArray &staticData, JobFlags flags) 01378 { 01379 StoredTransferJob *job = new StoredTransferJob( 01380 *new StoredTransferJobPrivate(url, command, packedArgs, staticData)); 01381 job->setUiDelegate(new JobUiDelegate); 01382 if (!(flags & HideProgressInfo)) 01383 KIO::getJobTracker()->registerJob(job); 01384 return job; 01385 } 01386 01387 static inline StoredTransferJob *newJob(const KUrl &url, int command, 01388 const QByteArray &packedArgs, 01389 QIODevice* ioDevice, JobFlags flags) 01390 { 01391 StoredTransferJob *job = new StoredTransferJob( 01392 *new StoredTransferJobPrivate(url, command, packedArgs, ioDevice)); 01393 job->setUiDelegate(new JobUiDelegate); 01394 if (!(flags & HideProgressInfo)) 01395 KIO::getJobTracker()->registerJob(job); 01396 return job; 01397 } 01398 }; 01399 01400 namespace KIO { 01401 class PostErrorJob : public StoredTransferJob 01402 { 01403 public: 01404 01405 PostErrorJob(int _error, const QString& url, const QByteArray &packedArgs, const QByteArray &postData) 01406 : StoredTransferJob(*new StoredTransferJobPrivate(KUrl(), CMD_SPECIAL, packedArgs, postData)) 01407 { 01408 setError( _error ); 01409 setErrorText( url ); 01410 } 01411 01412 PostErrorJob(int _error, const QString& url, const QByteArray &packedArgs, QIODevice* ioDevice) 01413 : StoredTransferJob(*new StoredTransferJobPrivate(KUrl(), CMD_SPECIAL, packedArgs, ioDevice)) 01414 { 01415 setError( _error ); 01416 setErrorText( url ); 01417 } 01418 }; 01419 } 01420 01421 static int isUrlPortBad(const KUrl& url) 01422 { 01423 int _error = 0; 01424 01425 // filter out some malicious ports 01426 static const int bad_ports[] = { 01427 1, // tcpmux 01428 7, // echo 01429 9, // discard 01430 11, // systat 01431 13, // daytime 01432 15, // netstat 01433 17, // qotd 01434 19, // chargen 01435 20, // ftp-data 01436 21, // ftp-cntl 01437 22, // ssh 01438 23, // telnet 01439 25, // smtp 01440 37, // time 01441 42, // name 01442 43, // nicname 01443 53, // domain 01444 77, // priv-rjs 01445 79, // finger 01446 87, // ttylink 01447 95, // supdup 01448 101, // hostriame 01449 102, // iso-tsap 01450 103, // gppitnp 01451 104, // acr-nema 01452 109, // pop2 01453 110, // pop3 01454 111, // sunrpc 01455 113, // auth 01456 115, // sftp 01457 117, // uucp-path 01458 119, // nntp 01459 123, // NTP 01460 135, // loc-srv / epmap 01461 139, // netbios 01462 143, // imap2 01463 179, // BGP 01464 389, // ldap 01465 512, // print / exec 01466 513, // login 01467 514, // shell 01468 515, // printer 01469 526, // tempo 01470 530, // courier 01471 531, // Chat 01472 532, // netnews 01473 540, // uucp 01474 556, // remotefs 01475 587, // sendmail 01476 601, // 01477 989, // ftps data 01478 990, // ftps 01479 992, // telnets 01480 993, // imap/SSL 01481 995, // pop3/SSL 01482 1080, // SOCKS 01483 2049, // nfs 01484 4045, // lockd 01485 6000, // x11 01486 6667, // irc 01487 0}; 01488 if (url.port() != 80) 01489 { 01490 const int port = url.port(); 01491 for (int cnt=0; bad_ports[cnt] && bad_ports[cnt] <= port; ++cnt) 01492 if (port == bad_ports[cnt]) 01493 { 01494 _error = KIO::ERR_POST_DENIED; 01495 break; 01496 } 01497 } 01498 01499 if ( _error ) 01500 { 01501 static bool override_loaded = false; 01502 static QList< int >* overriden_ports = NULL; 01503 if( !override_loaded ) { 01504 KConfig cfg( "kio_httprc" ); 01505 overriden_ports = new QList< int >; 01506 *overriden_ports = cfg.group(QString()).readEntry( "OverriddenPorts", QList<int>() ); 01507 override_loaded = true; 01508 } 01509 for( QList< int >::ConstIterator it = overriden_ports->constBegin(); 01510 it != overriden_ports->constEnd(); 01511 ++it ) { 01512 if( overriden_ports->contains( url.port())) { 01513 _error = 0; 01514 } 01515 } 01516 } 01517 01518 // filter out non https? protocols 01519 if ((url.protocol() != "http") && (url.protocol() != "https" )) 01520 _error = KIO::ERR_POST_DENIED; 01521 01522 if (!_error && !KAuthorized::authorizeUrlAction("open", KUrl(), url)) 01523 _error = KIO::ERR_ACCESS_DENIED; 01524 01525 return _error; 01526 } 01527 01528 static KIO::PostErrorJob* precheckHttpPost( const KUrl& url, QIODevice* ioDevice, JobFlags flags ) 01529 { 01530 // if request is not valid, return an invalid transfer job 01531 const int _error = isUrlPortBad(url); 01532 01533 if (_error) 01534 { 01535 KIO_ARGS << (int)1 << url; 01536 PostErrorJob * job = new PostErrorJob(_error, url.pathOrUrl(), packedArgs, ioDevice); 01537 job->setUiDelegate(new JobUiDelegate()); 01538 if (!(flags & HideProgressInfo)) { 01539 KIO::getJobTracker()->registerJob(job); 01540 } 01541 return job; 01542 } 01543 01544 // all is ok, return 0 01545 return 0; 01546 } 01547 01548 static KIO::PostErrorJob* precheckHttpPost( const KUrl& url, const QByteArray& postData, JobFlags flags ) 01549 { 01550 // if request is not valid, return an invalid transfer job 01551 const int _error = isUrlPortBad(url); 01552 01553 if (_error) 01554 { 01555 KIO_ARGS << (int)1 << url; 01556 PostErrorJob * job = new PostErrorJob(_error, url.pathOrUrl(), packedArgs, postData); 01557 job->setUiDelegate(new JobUiDelegate()); 01558 if (!(flags & HideProgressInfo)) { 01559 KIO::getJobTracker()->registerJob(job); 01560 } 01561 return job; 01562 } 01563 01564 // all is ok, return 0 01565 return 0; 01566 } 01567 01568 TransferJob *KIO::http_post( const KUrl& url, const QByteArray &postData, JobFlags flags ) 01569 { 01570 bool redirection = false; 01571 KUrl _url(url); 01572 if (_url.path().isEmpty()) 01573 { 01574 redirection = true; 01575 _url.setPath("/"); 01576 } 01577 01578 TransferJob* job = precheckHttpPost(_url, postData, flags); 01579 if (job) 01580 return job; 01581 01582 // Send http post command (1), decoded path and encoded query 01583 KIO_ARGS << (int)1 << _url << static_cast<qint64>(postData.size()); 01584 job = TransferJobPrivate::newJob(_url, CMD_SPECIAL, packedArgs, postData, flags); 01585 01586 if (redirection) 01587 QTimer::singleShot(0, job, SLOT(slotPostRedirection()) ); 01588 01589 return job; 01590 } 01591 01592 TransferJob *KIO::http_post( const KUrl& url, QIODevice* ioDevice, qint64 size, JobFlags flags ) 01593 { 01594 bool redirection = false; 01595 KUrl _url(url); 01596 if (_url.path().isEmpty()) 01597 { 01598 redirection = true; 01599 _url.setPath("/"); 01600 } 01601 01602 TransferJob* job = precheckHttpPost(_url, ioDevice, flags); 01603 if (job) 01604 return job; 01605 01606 // If no size is specified and the QIODevice is not a sequential one, 01607 // attempt to obtain the size information from it. 01608 Q_ASSERT(ioDevice); 01609 if (size < 0) 01610 size = ((ioDevice && !ioDevice->isSequential()) ? ioDevice->size() : -1); 01611 01612 // Send http post command (1), decoded path and encoded query 01613 KIO_ARGS << (int)1 << _url << size; 01614 job = TransferJobPrivate::newJob(_url, CMD_SPECIAL, packedArgs, ioDevice, flags); 01615 01616 if (redirection) 01617 QTimer::singleShot(0, job, SLOT(slotPostRedirection()) ); 01618 01619 return job; 01620 } 01621 01622 StoredTransferJob *KIO::storedHttpPost( const QByteArray& postData, const KUrl& url, JobFlags flags ) 01623 { 01624 KUrl _url(url); 01625 if (_url.path().isEmpty()) 01626 { 01627 _url.setPath("/"); 01628 } 01629 01630 StoredTransferJob* job = precheckHttpPost(_url, postData, flags); 01631 if (job) 01632 return job; 01633 01634 // Send http post command (1), decoded path and encoded query 01635 KIO_ARGS << (int)1 << _url << static_cast<qint64>(postData.size()); 01636 job = StoredTransferJobPrivate::newJob(_url, CMD_SPECIAL, packedArgs, postData, flags ); 01637 return job; 01638 } 01639 01640 StoredTransferJob *KIO::storedHttpPost( QIODevice* ioDevice, const KUrl& url, qint64 size, JobFlags flags ) 01641 { 01642 KUrl _url(url); 01643 if (_url.path().isEmpty()) 01644 { 01645 _url.setPath("/"); 01646 } 01647 01648 StoredTransferJob* job = precheckHttpPost(_url, ioDevice, flags); 01649 if (job) 01650 return job; 01651 01652 // If no size is specified and the QIODevice is not a sequential one, 01653 // attempt to obtain the size information from it. 01654 Q_ASSERT(ioDevice); 01655 if (size < 0) 01656 size = ((ioDevice && !ioDevice->isSequential()) ? ioDevice->size() : -1); 01657 01658 // Send http post command (1), decoded path and encoded query 01659 KIO_ARGS << (int)1 << _url << size; 01660 job = StoredTransferJobPrivate::newJob(_url, CMD_SPECIAL, packedArgs, ioDevice, flags ); 01661 return job; 01662 } 01663 01664 // http post got redirected from http://host to http://host/ by TransferJob 01665 // We must do this redirection ourselves because redirections by the 01666 // slave change post jobs into get jobs. 01667 void TransferJobPrivate::slotPostRedirection() 01668 { 01669 Q_Q(TransferJob); 01670 kDebug(7007) << "TransferJob::slotPostRedirection(" << m_url << ")"; 01671 // Tell the user about the new url. 01672 emit q->redirection(q, m_url); 01673 } 01674 01675 01676 TransferJob *KIO::put( const KUrl& url, int permissions, JobFlags flags ) 01677 { 01678 KIO_ARGS << url << qint8( (flags & Overwrite) ? 1 : 0 ) << qint8( (flags & Resume) ? 1 : 0 ) << permissions; 01679 return TransferJobPrivate::newJob(url, CMD_PUT, packedArgs, QByteArray(), flags); 01680 } 01681 01683 01684 StoredTransferJob::StoredTransferJob(StoredTransferJobPrivate &dd) 01685 : TransferJob(dd) 01686 { 01687 connect( this, SIGNAL( data( KIO::Job *, const QByteArray & ) ), 01688 SLOT( slotStoredData( KIO::Job *, const QByteArray & ) ) ); 01689 connect( this, SIGNAL( dataReq( KIO::Job *, QByteArray & ) ), 01690 SLOT( slotStoredDataReq( KIO::Job *, QByteArray & ) ) ); 01691 } 01692 01693 StoredTransferJob::~StoredTransferJob() 01694 { 01695 } 01696 01697 void StoredTransferJob::setData( const QByteArray& arr ) 01698 { 01699 Q_D(StoredTransferJob); 01700 Q_ASSERT( d->m_data.isNull() ); // check that we're only called once 01701 Q_ASSERT( d->m_uploadOffset == 0 ); // no upload started yet 01702 d->m_data = arr; 01703 setTotalSize( d->m_data.size() ); 01704 } 01705 01706 QByteArray StoredTransferJob::data() const 01707 { 01708 return d_func()->m_data; 01709 } 01710 01711 void StoredTransferJobPrivate::slotStoredData( KIO::Job *, const QByteArray &data ) 01712 { 01713 // check for end-of-data marker: 01714 if ( data.size() == 0 ) 01715 return; 01716 unsigned int oldSize = m_data.size(); 01717 m_data.resize( oldSize + data.size() ); 01718 memcpy( m_data.data() + oldSize, data.data(), data.size() ); 01719 } 01720 01721 void StoredTransferJobPrivate::slotStoredDataReq( KIO::Job *, QByteArray &data ) 01722 { 01723 // Inspired from kmail's KMKernel::byteArrayToRemoteFile 01724 // send the data in 64 KB chunks 01725 const int MAX_CHUNK_SIZE = 64*1024; 01726 int remainingBytes = m_data.size() - m_uploadOffset; 01727 if( remainingBytes > MAX_CHUNK_SIZE ) { 01728 // send MAX_CHUNK_SIZE bytes to the receiver (deep copy) 01729 data = QByteArray( m_data.data() + m_uploadOffset, MAX_CHUNK_SIZE ); 01730 m_uploadOffset += MAX_CHUNK_SIZE; 01731 //kDebug() << "Sending " << MAX_CHUNK_SIZE << " bytes (" 01732 // << remainingBytes - MAX_CHUNK_SIZE << " bytes remain)\n"; 01733 } else { 01734 // send the remaining bytes to the receiver (deep copy) 01735 data = QByteArray( m_data.data() + m_uploadOffset, remainingBytes ); 01736 m_data = QByteArray(); 01737 m_uploadOffset = 0; 01738 //kDebug() << "Sending " << remainingBytes << " bytes\n"; 01739 } 01740 } 01741 01742 StoredTransferJob *KIO::storedGet( const KUrl& url, LoadType reload, JobFlags flags ) 01743 { 01744 // Send decoded path and encoded query 01745 KIO_ARGS << url; 01746 StoredTransferJob * job = StoredTransferJobPrivate::newJob(url, CMD_GET, packedArgs, QByteArray(), flags); 01747 if (reload == Reload) 01748 job->addMetaData("cache", "reload"); 01749 return job; 01750 } 01751 01752 StoredTransferJob *KIO::storedPut( const QByteArray& arr, const KUrl& url, int permissions, 01753 JobFlags flags ) 01754 { 01755 KIO_ARGS << url << qint8( (flags & Overwrite) ? 1 : 0 ) << qint8( (flags & Resume) ? 1 : 0 ) << permissions; 01756 StoredTransferJob * job = StoredTransferJobPrivate::newJob(url, CMD_PUT, packedArgs, QByteArray(), flags ); 01757 job->setData( arr ); 01758 return job; 01759 } 01760 01762 01763 class KIO::MimetypeJobPrivate: public KIO::TransferJobPrivate 01764 { 01765 public: 01766 MimetypeJobPrivate(const KUrl& url, int command, const QByteArray &packedArgs) 01767 : TransferJobPrivate(url, command, packedArgs, QByteArray()) 01768 {} 01769 01770 Q_DECLARE_PUBLIC(MimetypeJob) 01771 01772 static inline MimetypeJob *newJob(const KUrl& url, int command, const QByteArray &packedArgs, 01773 JobFlags flags) 01774 { 01775 MimetypeJob *job = new MimetypeJob(*new MimetypeJobPrivate(url, command, packedArgs)); 01776 job->setUiDelegate(new JobUiDelegate); 01777 if (!(flags & HideProgressInfo)) { 01778 KIO::getJobTracker()->registerJob(job); 01779 emitStating(job, url); 01780 } 01781 return job; 01782 } 01783 }; 01784 01785 MimetypeJob::MimetypeJob(MimetypeJobPrivate &dd) 01786 : TransferJob(dd) 01787 { 01788 } 01789 01790 MimetypeJob::~MimetypeJob() 01791 { 01792 } 01793 01794 void MimetypeJob::slotFinished( ) 01795 { 01796 Q_D(MimetypeJob); 01797 //kDebug(7007); 01798 if ( error() == KIO::ERR_IS_DIRECTORY ) 01799 { 01800 // It is in fact a directory. This happens when HTTP redirects to FTP. 01801 // Due to the "protocol doesn't support listing" code in KRun, we 01802 // assumed it was a file. 01803 kDebug(7007) << "It is in fact a directory!"; 01804 d->m_mimetype = QString::fromLatin1("inode/directory"); 01805 emit TransferJob::mimetype( this, d->m_mimetype ); 01806 setError( 0 ); 01807 } 01808 01809 if ( !d->m_redirectionURL.isEmpty() && d->m_redirectionURL.isValid() && !error() ) 01810 { 01811 //kDebug(7007) << "Redirection to " << m_redirectionURL; 01812 if (queryMetaData("permanent-redirect")=="true") 01813 emit permanentRedirection(this, d->m_url, d->m_redirectionURL); 01814 01815 if (d->m_redirectionHandlingEnabled) 01816 { 01817 d->staticData.truncate(0); 01818 d->m_internalSuspended = false; 01819 d->m_packedArgs.truncate(0); 01820 QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly ); 01821 stream << d->m_redirectionURL; 01822 01823 d->restartAfterRedirection(&d->m_redirectionURL); 01824 return; 01825 } 01826 } 01827 01828 // Return slave to the scheduler 01829 TransferJob::slotFinished(); 01830 } 01831 01832 MimetypeJob *KIO::mimetype(const KUrl& url, JobFlags flags) 01833 { 01834 KIO_ARGS << url; 01835 return MimetypeJobPrivate::newJob(url, CMD_MIMETYPE, packedArgs, flags); 01836 } 01837 01839 01840 class KIO::DirectCopyJobPrivate: public KIO::SimpleJobPrivate 01841 { 01842 public: 01843 DirectCopyJobPrivate(const KUrl& url, int command, const QByteArray &packedArgs) 01844 : SimpleJobPrivate(url, command, packedArgs) 01845 {} 01846 01853 virtual void start(Slave *slave); 01854 01855 Q_DECLARE_PUBLIC(DirectCopyJob) 01856 }; 01857 01858 DirectCopyJob::DirectCopyJob(const KUrl &url, const QByteArray &packedArgs) 01859 : SimpleJob(*new DirectCopyJobPrivate(url, CMD_COPY, packedArgs)) 01860 { 01861 setUiDelegate(new JobUiDelegate); 01862 } 01863 01864 DirectCopyJob::~DirectCopyJob() 01865 { 01866 } 01867 01868 void DirectCopyJobPrivate::start( Slave* slave ) 01869 { 01870 Q_Q(DirectCopyJob); 01871 q->connect( slave, SIGNAL(canResume( KIO::filesize_t ) ), 01872 SLOT( slotCanResume( KIO::filesize_t ) ) ); 01873 SimpleJobPrivate::start(slave); 01874 } 01875 01876 void DirectCopyJob::slotCanResume( KIO::filesize_t offset ) 01877 { 01878 emit canResume(this, offset); 01879 } 01880 01882 01884 class KIO::FileCopyJobPrivate: public KIO::JobPrivate 01885 { 01886 public: 01887 FileCopyJobPrivate(const KUrl& src, const KUrl& dest, int permissions, 01888 bool move, JobFlags flags) 01889 : m_sourceSize(filesize_t(-1)), m_src(src), m_dest(dest), m_moveJob(0), m_copyJob(0), m_delJob(0), 01890 m_chmodJob(0), m_getJob(0), m_putJob(0), m_permissions(permissions), 01891 m_move(move), m_mustChmod(0), m_flags(flags) 01892 { 01893 } 01894 KIO::filesize_t m_sourceSize; 01895 QDateTime m_modificationTime; 01896 KUrl m_src; 01897 KUrl m_dest; 01898 QByteArray m_buffer; 01899 SimpleJob *m_moveJob; 01900 SimpleJob *m_copyJob; 01901 SimpleJob *m_delJob; 01902 SimpleJob *m_chmodJob; 01903 TransferJob *m_getJob; 01904 TransferJob *m_putJob; 01905 int m_permissions; 01906 bool m_move:1; 01907 bool m_canResume:1; 01908 bool m_resumeAnswerSent:1; 01909 bool m_mustChmod:1; 01910 JobFlags m_flags; 01911 01912 void startBestCopyMethod(); 01913 void startCopyJob(); 01914 void startCopyJob(const KUrl &slave_url); 01915 void startRenameJob(const KUrl &slave_url); 01916 void startDataPump(); 01917 void connectSubjob( SimpleJob * job ); 01918 01919 void slotStart(); 01920 void slotData( KIO::Job *, const QByteArray &data); 01921 void slotDataReq( KIO::Job *, QByteArray &data); 01922 void slotMimetype( KIO::Job*, const QString& type ); 01928 void slotProcessedSize( KJob *job, qulonglong size ); 01934 void slotTotalSize( KJob *job, qulonglong size ); 01940 void slotPercent( KJob *job, unsigned long pct ); 01946 void slotCanResume( KIO::Job *job, KIO::filesize_t offset ); 01947 01948 Q_DECLARE_PUBLIC(FileCopyJob) 01949 01950 static inline FileCopyJob* newJob(const KUrl& src, const KUrl& dest, int permissions, bool move, 01951 JobFlags flags) 01952 { 01953 //kDebug(7007) << src << "->" << dest; 01954 FileCopyJob *job = new FileCopyJob( 01955 *new FileCopyJobPrivate(src, dest, permissions, move, flags)); 01956 job->setProperty("destUrl", dest.url()); 01957 job->setUiDelegate(new JobUiDelegate); 01958 if (!(flags & HideProgressInfo)) 01959 KIO::getJobTracker()->registerJob(job); 01960 return job; 01961 } 01962 }; 01963 01964 /* 01965 * The FileCopyJob works according to the famous Bavarian 01966 * 'Alternating Bitburger Protocol': we either drink a beer or we 01967 * we order a beer, but never both at the same time. 01968 * Translated to io-slaves: We alternate between receiving a block of data 01969 * and sending it away. 01970 */ 01971 FileCopyJob::FileCopyJob(FileCopyJobPrivate &dd) 01972 : Job(dd) 01973 { 01974 //kDebug(7007); 01975 QTimer::singleShot(0, this, SLOT(slotStart())); 01976 } 01977 01978 void FileCopyJobPrivate::slotStart() 01979 { 01980 Q_Q(FileCopyJob); 01981 if (!m_move) 01982 JobPrivate::emitCopying( q, m_src, m_dest ); 01983 else 01984 JobPrivate::emitMoving( q, m_src, m_dest ); 01985 01986 if ( m_move ) 01987 { 01988 // The if() below must be the same as the one in startBestCopyMethod 01989 if ((m_src.protocol() == m_dest.protocol()) && 01990 (m_src.host() == m_dest.host()) && 01991 (m_src.port() == m_dest.port()) && 01992 (m_src.user() == m_dest.user()) && 01993 (m_src.pass() == m_dest.pass()) && 01994 !m_src.hasSubUrl() && !m_dest.hasSubUrl()) 01995 { 01996 startRenameJob(m_src); 01997 return; 01998 } 01999 else if (m_src.isLocalFile() && KProtocolManager::canRenameFromFile(m_dest)) 02000 { 02001 startRenameJob(m_dest); 02002 return; 02003 } 02004 else if (m_dest.isLocalFile() && KProtocolManager::canRenameToFile(m_src)) 02005 { 02006 startRenameJob(m_src); 02007 return; 02008 } 02009 // No fast-move available, use copy + del. 02010 } 02011 startBestCopyMethod(); 02012 } 02013 02014 void FileCopyJobPrivate::startBestCopyMethod() 02015 { 02016 if ((m_src.protocol() == m_dest.protocol()) && 02017 (m_src.host() == m_dest.host()) && 02018 (m_src.port() == m_dest.port()) && 02019 (m_src.user() == m_dest.user()) && 02020 (m_src.pass() == m_dest.pass()) && 02021 !m_src.hasSubUrl() && !m_dest.hasSubUrl()) 02022 { 02023 startCopyJob(); 02024 } 02025 else if (m_src.isLocalFile() && KProtocolManager::canCopyFromFile(m_dest)) 02026 { 02027 startCopyJob(m_dest); 02028 } 02029 else if (m_dest.isLocalFile() && KProtocolManager::canCopyToFile(m_src) && 02030 !KIO::Scheduler::isSlaveOnHoldFor(m_src)) 02031 { 02032 startCopyJob(m_src); 02033 } 02034 else 02035 { 02036 startDataPump(); 02037 } 02038 } 02039 02040 FileCopyJob::~FileCopyJob() 02041 { 02042 } 02043 02044 void FileCopyJob::setSourceSize( KIO::filesize_t size ) 02045 { 02046 Q_D(FileCopyJob); 02047 d->m_sourceSize = size; 02048 if (size != (KIO::filesize_t) -1) 02049 setTotalAmount(KJob::Bytes, size); 02050 } 02051 02052 void FileCopyJob::setModificationTime( const QDateTime& mtime ) 02053 { 02054 Q_D(FileCopyJob); 02055 d->m_modificationTime = mtime; 02056 } 02057 02058 KUrl FileCopyJob::srcUrl() const 02059 { 02060 return d_func()->m_src; 02061 } 02062 02063 KUrl FileCopyJob::destUrl() const 02064 { 02065 return d_func()->m_dest; 02066 } 02067 02068 void FileCopyJobPrivate::startCopyJob() 02069 { 02070 startCopyJob(m_src); 02071 } 02072 02073 void FileCopyJobPrivate::startCopyJob(const KUrl &slave_url) 02074 { 02075 Q_Q(FileCopyJob); 02076 //kDebug(7007); 02077 KIO_ARGS << m_src << m_dest << m_permissions << (qint8) (m_flags & Overwrite); 02078 m_copyJob = new DirectCopyJob(slave_url, packedArgs); 02079 q->addSubjob( m_copyJob ); 02080 connectSubjob( m_copyJob ); 02081 q->connect( m_copyJob, SIGNAL(canResume(KIO::Job *, KIO::filesize_t)), 02082 SLOT(slotCanResume(KIO::Job *, KIO::filesize_t))); 02083 } 02084 02085 void FileCopyJobPrivate::startRenameJob(const KUrl &slave_url) 02086 { 02087 Q_Q(FileCopyJob); 02088 m_mustChmod = true; // CMD_RENAME by itself doesn't change permissions 02089 KIO_ARGS << m_src << m_dest << (qint8) (m_flags & Overwrite); 02090 m_moveJob = SimpleJobPrivate::newJobNoUi(slave_url, CMD_RENAME, packedArgs); 02091 q->addSubjob( m_moveJob ); 02092 connectSubjob( m_moveJob ); 02093 } 02094 02095 void FileCopyJobPrivate::connectSubjob( SimpleJob * job ) 02096 { 02097 Q_Q(FileCopyJob); 02098 q->connect( job, SIGNAL(totalSize( KJob*, qulonglong )), 02099 SLOT( slotTotalSize(KJob*, qulonglong)) ); 02100 02101 q->connect( job, SIGNAL(processedSize( KJob*, qulonglong )), 02102 SLOT( slotProcessedSize(KJob*, qulonglong)) ); 02103 02104 q->connect( job, SIGNAL(percent( KJob*, unsigned long )), 02105 SLOT( slotPercent(KJob*, unsigned long)) ); 02106 02107 } 02108 02109 bool FileCopyJob::doSuspend() 02110 { 02111 Q_D(FileCopyJob); 02112 if (d->m_moveJob) 02113 d->m_moveJob->suspend(); 02114 02115 if (d->m_copyJob) 02116 d->m_copyJob->suspend(); 02117 02118 if (d->m_getJob) 02119 d->m_getJob->suspend(); 02120 02121 if (d->m_putJob) 02122 d->m_putJob->suspend(); 02123 02124 Job::doSuspend(); 02125 return true; 02126 } 02127 02128 bool FileCopyJob::doResume() 02129 { 02130 Q_D(FileCopyJob); 02131 if (d->m_moveJob) 02132 d->m_moveJob->resume(); 02133 02134 if (d->m_copyJob) 02135 d->m_copyJob->resume(); 02136 02137 if (d->m_getJob) 02138 d->m_getJob->resume(); 02139 02140 if (d->m_putJob) 02141 d->m_putJob->resume(); 02142 02143 Job::doResume(); 02144 return true; 02145 } 02146 02147 void FileCopyJobPrivate::slotProcessedSize( KJob *, qulonglong size ) 02148 { 02149 Q_Q(FileCopyJob); 02150 q->setProcessedAmount(KJob::Bytes, size); 02151 } 02152 02153 void FileCopyJobPrivate::slotTotalSize( KJob*, qulonglong size ) 02154 { 02155 Q_Q(FileCopyJob); 02156 if (size != q->totalAmount(KJob::Bytes)) 02157 { 02158 q->setTotalAmount(KJob::Bytes, size); 02159 } 02160 } 02161 02162 void FileCopyJobPrivate::slotPercent( KJob*, unsigned long pct ) 02163 { 02164 Q_Q(FileCopyJob); 02165 if ( pct > q->percent() ) { 02166 q->setPercent( pct ); 02167 } 02168 } 02169 02170 void FileCopyJobPrivate::startDataPump() 02171 { 02172 Q_Q(FileCopyJob); 02173 //kDebug(7007); 02174 02175 m_canResume = false; 02176 m_resumeAnswerSent = false; 02177 m_getJob = 0L; // for now 02178 m_putJob = put( m_dest, m_permissions, (m_flags | HideProgressInfo) /* no GUI */); 02179 //kDebug(7007) << "m_putJob=" << m_putJob << "m_dest=" << m_dest; 02180 if ( m_modificationTime.isValid() ) { 02181 m_putJob->setModificationTime( m_modificationTime ); 02182 } 02183 02184 // The first thing the put job will tell us is whether we can 02185 // resume or not (this is always emitted) 02186 q->connect( m_putJob, SIGNAL(canResume(KIO::Job *, KIO::filesize_t)), 02187 SLOT( slotCanResume(KIO::Job *, KIO::filesize_t))); 02188 q->connect( m_putJob, SIGNAL(dataReq(KIO::Job *, QByteArray&)), 02189 SLOT( slotDataReq(KIO::Job *, QByteArray&))); 02190 q->addSubjob( m_putJob ); 02191 } 02192 02193 void FileCopyJobPrivate::slotCanResume( KIO::Job* job, KIO::filesize_t offset ) 02194 { 02195 Q_Q(FileCopyJob); 02196 if ( job == m_putJob || job == m_copyJob ) 02197 { 02198 //kDebug(7007) << "'can resume' from PUT job. offset=" << KIO::number(offset); 02199 if (offset) 02200 { 02201 RenameDialog_Result res = R_RESUME; 02202 02203 if (!KProtocolManager::autoResume() && !(m_flags & Overwrite)) 02204 { 02205 QString newPath; 02206 KIO::Job* job = ( q->parentJob() ) ? q->parentJob() : q; 02207 // Ask confirmation about resuming previous transfer 02208 res = ui()->askFileRename( 02209 job, i18n("File Already Exists"), 02210 m_src.url(), 02211 m_dest.url(), 02212 (RenameDialog_Mode) (M_OVERWRITE | M_RESUME | M_NORENAME), newPath, 02213 m_sourceSize, offset ); 02214 } 02215 02216 if ( res == R_OVERWRITE || (m_flags & Overwrite) ) 02217 offset = 0; 02218 else if ( res == R_CANCEL ) 02219 { 02220 if ( job == m_putJob ) { 02221 m_putJob->kill( FileCopyJob::Quietly ); 02222 q->removeSubjob(m_putJob); 02223 m_putJob = 0; 02224 } else { 02225 m_copyJob->kill( FileCopyJob::Quietly ); 02226 q->removeSubjob(m_copyJob); 02227 m_copyJob = 0; 02228 } 02229 q->setError( ERR_USER_CANCELED ); 02230 q->emitResult(); 02231 return; 02232 } 02233 } 02234 else 02235 m_resumeAnswerSent = true; // No need for an answer 02236 02237 if ( job == m_putJob ) 02238 { 02239 m_getJob = KIO::get( m_src, NoReload, HideProgressInfo /* no GUI */ ); 02240 //kDebug(7007) << "m_getJob=" << m_getJob << m_src; 02241 m_getJob->addMetaData( "errorPage", "false" ); 02242 m_getJob->addMetaData( "AllowCompressedPage", "false" ); 02243 // Set size in subjob. This helps if the slave doesn't emit totalSize. 02244 if ( m_sourceSize != (KIO::filesize_t)-1 ) 02245 m_getJob->setTotalAmount(KJob::Bytes, m_sourceSize); 02246 if (offset) 02247 { 02248 //kDebug(7007) << "Setting metadata for resume to" << (unsigned long) offset; 02249 // TODO KDE4: rename to seek or offset and document it 02250 // This isn't used only for resuming, but potentially also for extracting (#72302). 02251 m_getJob->addMetaData( "resume", KIO::number(offset) ); 02252 02253 // Might or might not get emitted 02254 q->connect( m_getJob, SIGNAL(canResume(KIO::Job *, KIO::filesize_t)), 02255 SLOT( slotCanResume(KIO::Job *, KIO::filesize_t))); 02256 } 02257 jobSlave(m_putJob)->setOffset( offset ); 02258 02259 m_putJob->d_func()->internalSuspend(); 02260 q->addSubjob( m_getJob ); 02261 connectSubjob( m_getJob ); // Progress info depends on get 02262 m_getJob->d_func()->internalResume(); // Order a beer 02263 02264 q->connect( m_getJob, SIGNAL(data(KIO::Job*,const QByteArray&)), 02265 SLOT( slotData(KIO::Job*,const QByteArray&)) ); 02266 q->connect( m_getJob, SIGNAL(mimetype(KIO::Job*,const QString&) ), 02267 SLOT(slotMimetype(KIO::Job*,const QString&)) ); 02268 } 02269 else // copyjob 02270 { 02271 jobSlave(m_copyJob)->sendResumeAnswer( offset != 0 ); 02272 } 02273 } 02274 else if ( job == m_getJob ) 02275 { 02276 // Cool, the get job said ok, we can resume 02277 m_canResume = true; 02278 //kDebug(7007) << "'can resume' from the GET job -> we can resume"; 02279 02280 jobSlave(m_getJob)->setOffset( jobSlave(m_putJob)->offset() ); 02281 } 02282 else 02283 kWarning(7007) << "unknown job=" << job 02284 << "m_getJob=" << m_getJob << "m_putJob=" << m_putJob; 02285 } 02286 02287 void FileCopyJobPrivate::slotData( KIO::Job * , const QByteArray &data) 02288 { 02289 //kDebug(7007) << "data size:" << data.size(); 02290 Q_ASSERT(m_putJob); 02291 if (!m_putJob) return; // Don't crash 02292 m_getJob->d_func()->internalSuspend(); 02293 m_putJob->d_func()->internalResume(); // Drink the beer 02294 m_buffer += data; 02295 02296 // On the first set of data incoming, we tell the "put" slave about our 02297 // decision about resuming 02298 if (!m_resumeAnswerSent) 02299 { 02300 m_resumeAnswerSent = true; 02301 //kDebug(7007) << "(first time) -> send resume answer " << m_canResume; 02302 jobSlave(m_putJob)->sendResumeAnswer( m_canResume ); 02303 } 02304 } 02305 02306 void FileCopyJobPrivate::slotDataReq( KIO::Job * , QByteArray &data) 02307 { 02308 Q_Q(FileCopyJob); 02309 //kDebug(7007); 02310 if (!m_resumeAnswerSent && !m_getJob) { 02311 // This can't happen 02312 q->setError( ERR_INTERNAL ); 02313 q->setErrorText( "'Put' job did not send canResume or 'Get' job did not send data!" ); 02314 m_putJob->kill( FileCopyJob::Quietly ); 02315 q->removeSubjob(m_putJob); 02316 m_putJob = 0; 02317 q->emitResult(); 02318 return; 02319 } 02320 if (m_getJob) 02321 { 02322 m_getJob->d_func()->internalResume(); // Order more beer 02323 m_putJob->d_func()->internalSuspend(); 02324 } 02325 data = m_buffer; 02326 m_buffer = QByteArray(); 02327 } 02328 02329 void FileCopyJobPrivate::slotMimetype( KIO::Job*, const QString& type ) 02330 { 02331 Q_Q(FileCopyJob); 02332 emit q->mimetype( q, type ); 02333 } 02334 02335 void FileCopyJob::slotResult( KJob *job) 02336 { 02337 Q_D(FileCopyJob); 02338 //kDebug(7007) << "this=" << this << "job=" << job; 02339 removeSubjob(job); 02340 // Did job have an error ? 02341 if ( job->error() ) 02342 { 02343 if ((job == d->m_moveJob) && (job->error() == ERR_UNSUPPORTED_ACTION)) 02344 { 02345 d->m_moveJob = 0; 02346 d->startBestCopyMethod(); 02347 return; 02348 } 02349 else if ((job == d->m_copyJob) && (job->error() == ERR_UNSUPPORTED_ACTION)) 02350 { 02351 d->m_copyJob = 0; 02352 d->startDataPump(); 02353 return; 02354 } 02355 else if (job == d->m_getJob) 02356 { 02357 d->m_getJob = 0L; 02358 if (d->m_putJob) 02359 { 02360 d->m_putJob->kill( Quietly ); 02361 removeSubjob( d->m_putJob ); 02362 } 02363 } 02364 else if (job == d->m_putJob) 02365 { 02366 d->m_putJob = 0L; 02367 if (d->m_getJob) 02368 { 02369 d->m_getJob->kill( Quietly ); 02370 removeSubjob( d->m_getJob ); 02371 } 02372 } 02373 setError( job->error() ); 02374 setErrorText( job->errorText() ); 02375 emitResult(); 02376 return; 02377 } 02378 02379 if (d->m_mustChmod) 02380 { 02381 // If d->m_permissions == -1, keep the default permissions 02382 if (d->m_permissions != -1) 02383 { 02384 d->m_chmodJob = chmod(d->m_dest, d->m_permissions); 02385 } 02386 d->m_mustChmod = false; 02387 } 02388 02389 if (job == d->m_moveJob) 02390 { 02391 d->m_moveJob = 0; // Finished 02392 } 02393 02394 if (job == d->m_copyJob) 02395 { 02396 d->m_copyJob = 0; 02397 if (d->m_move) 02398 { 02399 d->m_delJob = file_delete( d->m_src, HideProgressInfo/*no GUI*/ ); // Delete source 02400 addSubjob(d->m_delJob); 02401 } 02402 } 02403 02404 if (job == d->m_getJob) 02405 { 02406 //kDebug(7007) << "m_getJob finished"; 02407 d->m_getJob = 0; // No action required 02408 if (d->m_putJob) 02409 d->m_putJob->d_func()->internalResume(); 02410 } 02411 02412 if (job == d->m_putJob) 02413 { 02414 //kDebug(7007) << "m_putJob finished"; 02415 d->m_putJob = 0; 02416 if (d->m_getJob) 02417 { 02418 // The get job is still running, probably after emitting data(QByteArray()) 02419 // and before we receive its finished(). 02420 d->m_getJob->d_func()->internalResume(); 02421 } 02422 if (d->m_move) 02423 { 02424 d->m_delJob = file_delete( d->m_src, HideProgressInfo/*no GUI*/ ); // Delete source 02425 addSubjob(d->m_delJob); 02426 } 02427 } 02428 02429 if (job == d->m_delJob) 02430 { 02431 d->m_delJob = 0; // Finished 02432 } 02433 02434 if (job == d->m_chmodJob) 02435 { 02436 d->m_chmodJob = 0; // Finished 02437 } 02438 02439 if ( !hasSubjobs() ) 02440 emitResult(); 02441 } 02442 02443 FileCopyJob *KIO::file_copy( const KUrl& src, const KUrl& dest, int permissions, 02444 JobFlags flags ) 02445 { 02446 return FileCopyJobPrivate::newJob(src, dest, permissions, false, flags); 02447 } 02448 02449 FileCopyJob *KIO::file_move( const KUrl& src, const KUrl& dest, int permissions, 02450 JobFlags flags ) 02451 { 02452 return FileCopyJobPrivate::newJob(src, dest, permissions, true, flags); 02453 } 02454 02455 SimpleJob *KIO::file_delete( const KUrl& src, JobFlags flags ) 02456 { 02457 KIO_ARGS << src << qint8(true); // isFile 02458 return SimpleJobPrivate::newJob(src, CMD_DEL, packedArgs, flags); 02459 } 02460 02462 02463 class KIO::ListJobPrivate: public KIO::SimpleJobPrivate 02464 { 02465 public: 02466 ListJobPrivate(const KUrl& url, bool _recursive, const QString &_prefix, bool _includeHidden) 02467 : SimpleJobPrivate(url, CMD_LISTDIR, QByteArray()), 02468 recursive(_recursive), includeHidden(_includeHidden), 02469 prefix(_prefix), m_processedEntries(0) 02470 {} 02471 bool recursive; 02472 bool includeHidden; 02473 QString prefix; 02474 unsigned long m_processedEntries; 02475 KUrl m_redirectionURL; 02476 02483 virtual void start( Slave *slave ); 02484 02485 void slotListEntries( const KIO::UDSEntryList& list ); 02486 void slotRedirection( const KUrl &url ); 02487 void gotEntries( KIO::Job * subjob, const KIO::UDSEntryList& list ); 02488 02489 Q_DECLARE_PUBLIC(ListJob) 02490 02491 static inline ListJob *newJob(const KUrl& u, bool _recursive, const QString &_prefix, 02492 bool _includeHidden, JobFlags flags = HideProgressInfo) 02493 { 02494 ListJob *job = new ListJob(*new ListJobPrivate(u, _recursive, _prefix, _includeHidden)); 02495 job->setUiDelegate(new JobUiDelegate); 02496 if (!(flags & HideProgressInfo)) 02497 KIO::getJobTracker()->registerJob(job); 02498 return job; 02499 } 02500 static inline ListJob *newJobNoUi(const KUrl& u, bool _recursive, const QString &_prefix, 02501 bool _includeHidden) 02502 { 02503 return new ListJob(*new ListJobPrivate(u, _recursive, _prefix, _includeHidden)); 02504 } 02505 }; 02506 02507 ListJob::ListJob(ListJobPrivate &dd) 02508 : SimpleJob(dd) 02509 { 02510 Q_D(ListJob); 02511 // We couldn't set the args when calling the parent constructor, 02512 // so do it now. 02513 QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly ); 02514 stream << d->m_url; 02515 } 02516 02517 ListJob::~ListJob() 02518 { 02519 } 02520 02521 void ListJobPrivate::slotListEntries( const KIO::UDSEntryList& list ) 02522 { 02523 Q_Q(ListJob); 02524 // Emit progress info (takes care of emit processedSize and percent) 02525 m_processedEntries += list.count(); 02526 slotProcessedSize( m_processedEntries ); 02527 02528 if (recursive) { 02529 UDSEntryList::ConstIterator it = list.begin(); 02530 const UDSEntryList::ConstIterator end = list.end(); 02531 02532 for (; it != end; ++it) { 02533 02534 const UDSEntry& entry = *it; 02535 02536 KUrl itemURL; 02537 // const UDSEntry::ConstIterator end2 = entry.end(); 02538 // UDSEntry::ConstIterator it2 = entry.find( KIO::UDSEntry::UDS_URL ); 02539 // if ( it2 != end2 ) 02540 if (entry.contains(KIO::UDSEntry::UDS_URL)) 02541 // itemURL = it2.value().toString(); 02542 itemURL = entry.stringValue(KIO::UDSEntry::UDS_URL); 02543 else { // no URL, use the name 02544 itemURL = q->url(); 02545 const QString fileName = entry.stringValue(KIO::UDSEntry::UDS_NAME); 02546 Q_ASSERT(!fileName.isEmpty()); // we'll recurse forever otherwise :) 02547 itemURL.addPath(fileName); 02548 } 02549 02550 if (entry.isDir() && !entry.isLink()) { 02551 const QString filename = itemURL.fileName(); 02552 // skip hidden dirs when listing if requested 02553 if (filename != ".." && filename != "." && (includeHidden || filename[0] != '.')) { 02554 ListJob *job = ListJobPrivate::newJobNoUi(itemURL, 02555 true /*recursive*/, 02556 prefix + filename + '/', 02557 includeHidden); 02558 Scheduler::setJobPriority(job, 1); 02559 q->connect(job, SIGNAL(entries( KIO::Job *, const KIO::UDSEntryList& )), 02560 SLOT( gotEntries( KIO::Job*, const KIO::UDSEntryList& ))); 02561 q->addSubjob(job); 02562 } 02563 } 02564 } 02565 } 02566 02567 // Not recursive, or top-level of recursive listing : return now (send . and .. as well) 02568 // exclusion of hidden files also requires the full sweep, but the case for full-listing 02569 // a single dir is probably common enough to justify the shortcut 02570 if (prefix.isNull() && includeHidden) { 02571 emit q->entries(q, list); 02572 } else { 02573 // cull the unwanted hidden dirs and/or parent dir references from the listing, then emit that 02574 UDSEntryList newlist; 02575 02576 UDSEntryList::const_iterator it = list.begin(); 02577 const UDSEntryList::const_iterator end = list.end(); 02578 for (; it != end; ++it) { 02579 02580 // Modify the name in the UDSEntry 02581 UDSEntry newone = *it; 02582 const QString filename = newone.stringValue( KIO::UDSEntry::UDS_NAME ); 02583 // Avoid returning entries like subdir/. and subdir/.., but include . and .. for 02584 // the toplevel dir, and skip hidden files/dirs if that was requested 02585 if ( (prefix.isNull() || (filename != ".." && filename != ".") ) 02586 && (includeHidden || (filename[0] != '.') ) ) 02587 { 02588 // ## Didn't find a way to use the iterator instead of re-doing a key lookup 02589 newone.insert( KIO::UDSEntry::UDS_NAME, prefix + filename ); 02590 newlist.append(newone); 02591 } 02592 } 02593 02594 emit q->entries(q, newlist); 02595 } 02596 } 02597 02598 void ListJobPrivate::gotEntries(KIO::Job *, const KIO::UDSEntryList& list ) 02599 { 02600 // Forward entries received by subjob - faking we received them ourselves 02601 Q_Q(ListJob); 02602 emit q->entries(q, list); 02603 } 02604 02605 void ListJob::slotResult( KJob * job ) 02606 { 02607 // If we can't list a subdir, the result is still ok 02608 // This is why we override Job::slotResult() - to skip error checking 02609 removeSubjob( job ); 02610 if ( !hasSubjobs() ) 02611 emitResult(); 02612 } 02613 02614 void ListJobPrivate::slotRedirection( const KUrl & url ) 02615 { 02616 Q_Q(ListJob); 02617 if (!KAuthorized::authorizeUrlAction("redirect", m_url, url)) 02618 { 02619 kWarning(7007) << "ListJob: Redirection from " << m_url << " to " << url << " REJECTED!"; 02620 return; 02621 } 02622 m_redirectionURL = url; // We'll remember that when the job finishes 02623 emit q->redirection( q, m_redirectionURL ); 02624 } 02625 02626 void ListJob::slotFinished() 02627 { 02628 Q_D(ListJob); 02629 02630 // Support for listing archives as directories 02631 if ( error() == KIO::ERR_IS_FILE && d->m_url.isLocalFile() ) { 02632 KMimeType::Ptr ptr = KMimeType::findByUrl( d->m_url, 0, true, true ); 02633 if ( ptr ) { 02634 QString proto = ptr->property("X-KDE-LocalProtocol").toString(); 02635 if ( !proto.isEmpty() && KProtocolInfo::isKnownProtocol( proto) ) { 02636 d->m_redirectionURL = d->m_url; 02637 d->m_redirectionURL.setProtocol( proto ); 02638 setError( 0 ); 02639 emit redirection(this,d->m_redirectionURL); 02640 } 02641 } 02642 } 02643 02644 if ( !d->m_redirectionURL.isEmpty() && d->m_redirectionURL.isValid() && !error() ) { 02645 02646 //kDebug(7007) << "Redirection to " << d->m_redirectionURL; 02647 if (queryMetaData("permanent-redirect")=="true") 02648 emit permanentRedirection(this, d->m_url, d->m_redirectionURL); 02649 02650 if ( d->m_redirectionHandlingEnabled ) { 02651 d->m_packedArgs.truncate(0); 02652 QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly ); 02653 stream << d->m_redirectionURL; 02654 02655 d->restartAfterRedirection(&d->m_redirectionURL); 02656 return; 02657 } 02658 } 02659 02660 // Return slave to the scheduler 02661 SimpleJob::slotFinished(); 02662 } 02663 02664 void ListJob::slotMetaData( const KIO::MetaData &_metaData) 02665 { 02666 Q_D(ListJob); 02667 SimpleJob::slotMetaData(_metaData); 02668 storeSSLSessionFromJob(d->m_redirectionURL); 02669 } 02670 02671 ListJob *KIO::listDir( const KUrl& url, JobFlags flags, bool includeHidden ) 02672 { 02673 return ListJobPrivate::newJob(url, false, QString(), includeHidden, flags); 02674 } 02675 02676 ListJob *KIO::listRecursive( const KUrl& url, JobFlags flags, bool includeHidden ) 02677 { 02678 return ListJobPrivate::newJob(url, true, QString(), includeHidden, flags); 02679 } 02680 02681 void ListJob::setUnrestricted(bool unrestricted) 02682 { 02683 Q_D(ListJob); 02684 if (unrestricted) 02685 d->m_extraFlags |= JobPrivate::EF_ListJobUnrestricted; 02686 else 02687 d->m_extraFlags &= ~JobPrivate::EF_ListJobUnrestricted; 02688 } 02689 02690 void ListJobPrivate::start(Slave *slave) 02691 { 02692 Q_Q(ListJob); 02693 if (!KAuthorized::authorizeUrlAction("list", m_url, m_url) && 02694 !(m_extraFlags & EF_ListJobUnrestricted)) 02695 { 02696 q->setError( ERR_ACCESS_DENIED ); 02697 q->setErrorText( m_url.url() ); 02698 QTimer::singleShot(0, q, SLOT(slotFinished()) ); 02699 return; 02700 } 02701 q->connect( slave, SIGNAL( listEntries( const KIO::UDSEntryList& )), 02702 SLOT( slotListEntries( const KIO::UDSEntryList& ))); 02703 q->connect( slave, SIGNAL( totalSize( KIO::filesize_t ) ), 02704 SLOT( slotTotalSize( KIO::filesize_t ) ) ); 02705 q->connect( slave, SIGNAL( redirection(const KUrl &) ), 02706 SLOT( slotRedirection(const KUrl &) ) ); 02707 02708 SimpleJobPrivate::start(slave); 02709 } 02710 02711 const KUrl& ListJob::redirectionUrl() const 02712 { 02713 return d_func()->m_redirectionURL; 02714 } 02715 02717 02718 class KIO::MultiGetJobPrivate: public KIO::TransferJobPrivate 02719 { 02720 public: 02721 MultiGetJobPrivate(const KUrl& url) 02722 : TransferJobPrivate(url, 0, QByteArray(), QByteArray()), 02723 m_currentEntry( 0, KUrl(), MetaData() ) 02724 {} 02725 struct GetRequest { 02726 GetRequest(long _id, const KUrl &_url, const MetaData &_metaData) 02727 : id(_id), url(_url), metaData(_metaData) { } 02728 long id; 02729 KUrl url; 02730 MetaData metaData; 02731 02732 inline bool operator==( const GetRequest& req ) const 02733 { return req.id == id; } 02734 }; 02735 typedef QLinkedList<GetRequest> RequestQueue; 02736 02737 RequestQueue m_waitQueue; 02738 RequestQueue m_activeQueue; 02739 GetRequest m_currentEntry; 02740 bool b_multiGetActive; 02741 02748 virtual void start(Slave *slave); 02749 02750 bool findCurrentEntry(); 02751 void flushQueue(QLinkedList<GetRequest> &queue); 02752 02753 Q_DECLARE_PUBLIC(MultiGetJob) 02754 02755 static inline MultiGetJob *newJob(const KUrl &url) 02756 { 02757 MultiGetJob *job = new MultiGetJob(*new MultiGetJobPrivate(url)); 02758 job->setUiDelegate(new JobUiDelegate); 02759 return job; 02760 } 02761 }; 02762 02763 MultiGetJob::MultiGetJob(MultiGetJobPrivate &dd) 02764 : TransferJob(dd) 02765 { 02766 } 02767 02768 MultiGetJob::~MultiGetJob() 02769 { 02770 } 02771 02772 void MultiGetJob::get(long id, const KUrl &url, const MetaData &metaData) 02773 { 02774 Q_D(MultiGetJob); 02775 MultiGetJobPrivate::GetRequest entry(id, url, metaData); 02776 entry.metaData["request-id"] = QString::number(id); 02777 d->m_waitQueue.append(entry); 02778 } 02779 02780 void MultiGetJobPrivate::flushQueue(RequestQueue &queue) 02781 { 02782 // Use multi-get 02783 // Scan all jobs in m_waitQueue 02784 RequestQueue::iterator wqit = m_waitQueue.begin(); 02785 const RequestQueue::iterator wqend = m_waitQueue.end(); 02786 while ( wqit != wqend ) 02787 { 02788 const GetRequest& entry = *wqit; 02789 if ((m_url.protocol() == entry.url.protocol()) && 02790 (m_url.host() == entry.url.host()) && 02791 (m_url.port() == entry.url.port()) && 02792 (m_url.user() == entry.url.user())) 02793 { 02794 queue.append( entry ); 02795 wqit = m_waitQueue.erase( wqit ); 02796 } 02797 else 02798 { 02799 ++wqit; 02800 } 02801 } 02802 // Send number of URLs, (URL, metadata)* 02803 KIO_ARGS << (qint32) queue.count(); 02804 RequestQueue::const_iterator qit = queue.begin(); 02805 const RequestQueue::const_iterator qend = queue.end(); 02806 for( ; qit != qend; ++qit ) 02807 { 02808 stream << (*qit).url << (*qit).metaData; 02809 } 02810 m_packedArgs = packedArgs; 02811 m_command = CMD_MULTI_GET; 02812 m_outgoingMetaData.clear(); 02813 } 02814 02815 void MultiGetJobPrivate::start(Slave *slave) 02816 { 02817 // Add first job from m_waitQueue and add it to m_activeQueue 02818 GetRequest entry = m_waitQueue.takeFirst(); 02819 m_activeQueue.append(entry); 02820 02821 m_url = entry.url; 02822 02823 if (!entry.url.protocol().startsWith(QLatin1String("http"))) 02824 { 02825 // Use normal get 02826 KIO_ARGS << entry.url; 02827 m_packedArgs = packedArgs; 02828 m_outgoingMetaData = entry.metaData; 02829 m_command = CMD_GET; 02830 b_multiGetActive = false; 02831 } 02832 else 02833 { 02834 flushQueue(m_activeQueue); 02835 b_multiGetActive = true; 02836 } 02837 02838 TransferJobPrivate::start(slave); // Anything else to do?? 02839 } 02840 02841 bool MultiGetJobPrivate::findCurrentEntry() 02842 { 02843 if (b_multiGetActive) 02844 { 02845 long id = m_incomingMetaData["request-id"].toLong(); 02846 RequestQueue::const_iterator qit = m_activeQueue.begin(); 02847 const RequestQueue::const_iterator qend = m_activeQueue.end(); 02848 for( ; qit != qend; ++qit ) 02849 { 02850 if ((*qit).id == id) 02851 { 02852 m_currentEntry = *qit; 02853 return true; 02854 } 02855 } 02856 m_currentEntry.id = 0; 02857 return false; 02858 } 02859 else 02860 { 02861 if ( m_activeQueue.isEmpty() ) 02862 return false; 02863 m_currentEntry = m_activeQueue.first(); 02864 return true; 02865 } 02866 } 02867 02868 void MultiGetJob::slotRedirection( const KUrl &url) 02869 { 02870 Q_D(MultiGetJob); 02871 if (!d->findCurrentEntry()) return; // Error 02872 if (!KAuthorized::authorizeUrlAction("redirect", d->m_url, url)) 02873 { 02874 kWarning(7007) << "MultiGetJob: Redirection from " << d->m_currentEntry.url << " to " << url << " REJECTED!"; 02875 return; 02876 } 02877 d->m_redirectionURL = url; 02878 get(d->m_currentEntry.id, d->m_redirectionURL, d->m_currentEntry.metaData); // Try again 02879 } 02880 02881 02882 void MultiGetJob::slotFinished() 02883 { 02884 Q_D(MultiGetJob); 02885 if (!d->findCurrentEntry()) return; 02886 if (d->m_redirectionURL.isEmpty()) 02887 { 02888 // No redirection, tell the world that we are finished. 02889 emit result(d->m_currentEntry.id); 02890 } 02891 d->m_redirectionURL = KUrl(); 02892 setError( 0 ); 02893 d->m_incomingMetaData.clear(); 02894 d->m_activeQueue.removeAll(d->m_currentEntry); 02895 if (d->m_activeQueue.count() == 0) 02896 { 02897 if (d->m_waitQueue.count() == 0) 02898 { 02899 // All done 02900 TransferJob::slotFinished(); 02901 } 02902 else 02903 { 02904 // return slave to pool 02905 // fetch new slave for first entry in d->m_waitQueue and call start 02906 // again. 02907 d->slaveDone(); 02908 02909 d->m_url = d->m_waitQueue.first().url; 02910 if ((d->m_extraFlags & JobPrivate::EF_KillCalled) == 0) { 02911 Scheduler::doJob(this); 02912 } 02913 } 02914 } 02915 } 02916 02917 void MultiGetJob::slotData( const QByteArray &_data) 02918 { 02919 Q_D(MultiGetJob); 02920 if(d->m_redirectionURL.isEmpty() || !d->m_redirectionURL.isValid() || error()) 02921 emit data(d->m_currentEntry.id, _data); 02922 } 02923 02924 void MultiGetJob::slotMimetype( const QString &_mimetype ) 02925 { 02926 Q_D(MultiGetJob); 02927 if (d->b_multiGetActive) 02928 { 02929 MultiGetJobPrivate::RequestQueue newQueue; 02930 d->flushQueue(newQueue); 02931 if (!newQueue.isEmpty()) 02932 { 02933 d->m_activeQueue += newQueue; 02934 d->m_slave->send( d->m_command, d->m_packedArgs ); 02935 } 02936 } 02937 if (!d->findCurrentEntry()) return; // Error, unknown request! 02938 emit mimetype(d->m_currentEntry.id, _mimetype); 02939 } 02940 02941 MultiGetJob *KIO::multi_get(long id, const KUrl &url, const MetaData &metaData) 02942 { 02943 MultiGetJob * job = MultiGetJobPrivate::newJob(url); 02944 job->get(id, url, metaData); 02945 return job; 02946 } 02947 02948 class KIO::SpecialJobPrivate: public TransferJobPrivate 02949 { 02950 SpecialJobPrivate(const KUrl& url, int command, 02951 const QByteArray &packedArgs, 02952 const QByteArray &_staticData) 02953 : TransferJobPrivate(url, command, packedArgs, _staticData) 02954 {} 02955 }; 02956 02957 SpecialJob::SpecialJob(const KUrl &url, const QByteArray &packedArgs) 02958 : TransferJob(*new TransferJobPrivate(url, CMD_SPECIAL, packedArgs, QByteArray())) 02959 { 02960 } 02961 02962 SpecialJob::~SpecialJob() 02963 { 02964 } 02965 02966 void SpecialJob::setArguments(const QByteArray &data) 02967 { 02968 Q_D(SpecialJob); 02969 d->m_packedArgs = data; 02970 } 02971 02972 QByteArray SpecialJob::arguments() const 02973 { 02974 return d_func()->m_packedArgs; 02975 } 02976 02977 // Never defined, never used - what's this code about? 02978 #ifdef CACHE_INFO 02979 CacheInfo::CacheInfo(const KUrl &url) 02980 { 02981 m_url = url; 02982 } 02983 02984 QString CacheInfo::cachedFileName() 02985 { 02986 const QChar separator = '_'; 02987 02988 QString CEF = m_url.path(); 02989 02990 int p = CEF.find('/'); 02991 02992 while(p != -1) 02993 { 02994 CEF[p] = separator; 02995 p = CEF.find('/', p); 02996 } 02997 02998 QString host = m_url.host().toLower(); 02999 CEF = host + CEF + '_'; 03000 03001 QString dir = KProtocolManager::cacheDir(); 03002 if (dir[dir.length()-1] != '/') 03003 dir += '/'; 03004 03005 int l = m_url.host().length(); 03006 for(int i = 0; i < l; i++) 03007 { 03008 if (host[i].isLetter() && (host[i] != 'w')) 03009 { 03010 dir += host[i]; 03011 break; 03012 } 03013 } 03014 if (dir[dir.length()-1] == '/') 03015 dir += '0'; 03016 03017 unsigned long hash = 0x00000000; 03018 QString u = m_url.url().toLatin1(); 03019 for(int i = u.length(); i--;) 03020 { 03021 hash = (hash * 12211 + u[i]) % 2147483563; 03022 } 03023 03024 QString hashString; 03025 hashString.sprintf("%08lx", hash); 03026 03027 CEF = CEF + hashString; 03028 03029 CEF = dir + '/' + CEF; 03030 03031 return CEF; 03032 } 03033 03034 QFile *CacheInfo::cachedFile() 03035 { 03036 #ifdef Q_WS_WIN 03037 const char *mode = (readWrite ? "rb+" : "rb"); 03038 #else 03039 const char *mode = (readWrite ? "r+" : "r"); 03040 #endif 03041 03042 FILE *fs = KDE::fopen(CEF, mode); // Open for reading and writing 03043 if (!fs) 03044 return 0; 03045 03046 char buffer[401]; 03047 bool ok = true; 03048 03049 // CacheRevision 03050 if (ok && (!fgets(buffer, 400, fs))) 03051 ok = false; 03052 if (ok && (strcmp(buffer, CACHE_REVISION) != 0)) 03053 ok = false; 03054 03055 time_t date; 03056 time_t currentDate = time(0); 03057 03058 // URL 03059 if (ok && (!fgets(buffer, 400, fs))) 03060 ok = false; 03061 if (ok) 03062 { 03063 int l = strlen(buffer); 03064 if (l>0) 03065 buffer[l-1] = 0; // Strip newline 03066 if (m_.url.url() != buffer) 03067 { 03068 ok = false; // Hash collision 03069 } 03070 } 03071 03072 // Creation Date 03073 if (ok && (!fgets(buffer, 400, fs))) 03074 ok = false; 03075 if (ok) 03076 { 03077 date = (time_t) strtoul(buffer, 0, 10); 03078 if (m_maxCacheAge && (difftime(currentDate, date) > m_maxCacheAge)) 03079 { 03080 m_bMustRevalidate = true; 03081 m_expireDate = currentDate; 03082 } 03083 } 03084 03085 // Expiration Date 03086 m_cacheExpireDateOffset = KDE_ftell(fs); 03087 if (ok && (!fgets(buffer, 400, fs))) 03088 ok = false; 03089 if (ok) 03090 { 03091 if (m_request.cache == CC_Verify) 03092 { 03093 date = (time_t) strtoul(buffer, 0, 10); 03094 // After the expire date we need to revalidate. 03095 if (!date || difftime(currentDate, date) >= 0) 03096 m_bMustRevalidate = true; 03097 m_expireDate = date; 03098 } 03099 } 03100 03101 // ETag 03102 if (ok && (!fgets(buffer, 400, fs))) 03103 ok = false; 03104 if (ok) 03105 { 03106 m_etag = QString(buffer).trimmed(); 03107 } 03108 03109 // Last-Modified 03110 if (ok && (!fgets(buffer, 400, fs))) 03111 ok = false; 03112 if (ok) 03113 { 03114 m_lastModified = QString(buffer).trimmed(); 03115 } 03116 03117 fclose(fs); 03118 03119 if (ok) 03120 return fs; 03121 03122 unlink( QFile::encodeName(CEF) ); 03123 return 0; 03124 03125 } 03126 03127 void CacheInfo::flush() 03128 { 03129 cachedFile().remove(); 03130 } 03131 03132 void CacheInfo::touch() 03133 { 03134 03135 } 03136 void CacheInfo::setExpireDate(int); 03137 void CacheInfo::setExpireTimeout(int); 03138 03139 03140 int CacheInfo::creationDate(); 03141 int CacheInfo::expireDate(); 03142 int CacheInfo::expireTimeout(); 03143 #endif 03144 03145 #include "jobclasses.moc" 03146 #include "job_p.moc"
KDE 4.7 API Reference