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