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(KUrl)), 00627 SLOT(slotRedirection(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(KIO::UDSEntry)), 00844 SLOT(slotStatEntry(KIO::UDSEntry)) ); 00845 q->connect( slave, SIGNAL(redirection(KUrl)), 00846 SLOT(slotRedirection(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(QByteArray)), 01219 SLOT(slotData(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(KUrl)), 01229 SLOT(slotRedirection(KUrl)) ); 01230 01231 q->connect( slave, SIGNAL(mimeType(QString)), 01232 SLOT(slotMimetype(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*,QByteArray)), 01262 SLOT(slotSubUrlData(KIO::Job*,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 TransferJob* KIO::http_delete(const KUrl& url, JobFlags flags) 01623 { 01624 // Send decoded path and encoded query 01625 KIO_ARGS << url; 01626 TransferJob * job = TransferJobPrivate::newJob(url, CMD_DEL, packedArgs, 01627 QByteArray(), flags); 01628 return job; 01629 } 01630 01631 StoredTransferJob *KIO::storedHttpPost( const QByteArray& postData, const KUrl& url, JobFlags flags ) 01632 { 01633 KUrl _url(url); 01634 if (_url.path().isEmpty()) 01635 { 01636 _url.setPath("/"); 01637 } 01638 01639 StoredTransferJob* job = precheckHttpPost(_url, postData, flags); 01640 if (job) 01641 return job; 01642 01643 // Send http post command (1), decoded path and encoded query 01644 KIO_ARGS << (int)1 << _url << static_cast<qint64>(postData.size()); 01645 job = StoredTransferJobPrivate::newJob(_url, CMD_SPECIAL, packedArgs, postData, flags ); 01646 return job; 01647 } 01648 01649 StoredTransferJob *KIO::storedHttpPost( QIODevice* ioDevice, const KUrl& url, qint64 size, JobFlags flags ) 01650 { 01651 KUrl _url(url); 01652 if (_url.path().isEmpty()) 01653 { 01654 _url.setPath("/"); 01655 } 01656 01657 StoredTransferJob* job = precheckHttpPost(_url, ioDevice, flags); 01658 if (job) 01659 return job; 01660 01661 // If no size is specified and the QIODevice is not a sequential one, 01662 // attempt to obtain the size information from it. 01663 Q_ASSERT(ioDevice); 01664 if (size < 0) 01665 size = ((ioDevice && !ioDevice->isSequential()) ? ioDevice->size() : -1); 01666 01667 // Send http post command (1), decoded path and encoded query 01668 KIO_ARGS << (int)1 << _url << size; 01669 job = StoredTransferJobPrivate::newJob(_url, CMD_SPECIAL, packedArgs, ioDevice, flags ); 01670 return job; 01671 } 01672 01673 // http post got redirected from http://host to http://host/ by TransferJob 01674 // We must do this redirection ourselves because redirections by the 01675 // slave change post jobs into get jobs. 01676 void TransferJobPrivate::slotPostRedirection() 01677 { 01678 Q_Q(TransferJob); 01679 kDebug(7007) << "TransferJob::slotPostRedirection(" << m_url << ")"; 01680 // Tell the user about the new url. 01681 emit q->redirection(q, m_url); 01682 } 01683 01684 01685 TransferJob *KIO::put( const KUrl& url, int permissions, JobFlags flags ) 01686 { 01687 KIO_ARGS << url << qint8( (flags & Overwrite) ? 1 : 0 ) << qint8( (flags & Resume) ? 1 : 0 ) << permissions; 01688 return TransferJobPrivate::newJob(url, CMD_PUT, packedArgs, QByteArray(), flags); 01689 } 01690 01692 01693 StoredTransferJob::StoredTransferJob(StoredTransferJobPrivate &dd) 01694 : TransferJob(dd) 01695 { 01696 connect( this, SIGNAL(data(KIO::Job*,QByteArray)), 01697 SLOT(slotStoredData(KIO::Job*,QByteArray)) ); 01698 connect( this, SIGNAL(dataReq(KIO::Job*,QByteArray&)), 01699 SLOT(slotStoredDataReq(KIO::Job*,QByteArray&)) ); 01700 } 01701 01702 StoredTransferJob::~StoredTransferJob() 01703 { 01704 } 01705 01706 void StoredTransferJob::setData( const QByteArray& arr ) 01707 { 01708 Q_D(StoredTransferJob); 01709 Q_ASSERT( d->m_data.isNull() ); // check that we're only called once 01710 Q_ASSERT( d->m_uploadOffset == 0 ); // no upload started yet 01711 d->m_data = arr; 01712 setTotalSize( d->m_data.size() ); 01713 } 01714 01715 QByteArray StoredTransferJob::data() const 01716 { 01717 return d_func()->m_data; 01718 } 01719 01720 void StoredTransferJobPrivate::slotStoredData( KIO::Job *, const QByteArray &data ) 01721 { 01722 // check for end-of-data marker: 01723 if ( data.size() == 0 ) 01724 return; 01725 unsigned int oldSize = m_data.size(); 01726 m_data.resize( oldSize + data.size() ); 01727 memcpy( m_data.data() + oldSize, data.data(), data.size() ); 01728 } 01729 01730 void StoredTransferJobPrivate::slotStoredDataReq( KIO::Job *, QByteArray &data ) 01731 { 01732 // Inspired from kmail's KMKernel::byteArrayToRemoteFile 01733 // send the data in 64 KB chunks 01734 const int MAX_CHUNK_SIZE = 64*1024; 01735 int remainingBytes = m_data.size() - m_uploadOffset; 01736 if( remainingBytes > MAX_CHUNK_SIZE ) { 01737 // send MAX_CHUNK_SIZE bytes to the receiver (deep copy) 01738 data = QByteArray( m_data.data() + m_uploadOffset, MAX_CHUNK_SIZE ); 01739 m_uploadOffset += MAX_CHUNK_SIZE; 01740 //kDebug() << "Sending " << MAX_CHUNK_SIZE << " bytes (" 01741 // << remainingBytes - MAX_CHUNK_SIZE << " bytes remain)\n"; 01742 } else { 01743 // send the remaining bytes to the receiver (deep copy) 01744 data = QByteArray( m_data.data() + m_uploadOffset, remainingBytes ); 01745 m_data = QByteArray(); 01746 m_uploadOffset = 0; 01747 //kDebug() << "Sending " << remainingBytes << " bytes\n"; 01748 } 01749 } 01750 01751 StoredTransferJob *KIO::storedGet( const KUrl& url, LoadType reload, JobFlags flags ) 01752 { 01753 // Send decoded path and encoded query 01754 KIO_ARGS << url; 01755 StoredTransferJob * job = StoredTransferJobPrivate::newJob(url, CMD_GET, packedArgs, QByteArray(), flags); 01756 if (reload == Reload) 01757 job->addMetaData("cache", "reload"); 01758 return job; 01759 } 01760 01761 StoredTransferJob *KIO::storedPut( const QByteArray& arr, const KUrl& url, int permissions, 01762 JobFlags flags ) 01763 { 01764 KIO_ARGS << url << qint8( (flags & Overwrite) ? 1 : 0 ) << qint8( (flags & Resume) ? 1 : 0 ) << permissions; 01765 StoredTransferJob * job = StoredTransferJobPrivate::newJob(url, CMD_PUT, packedArgs, QByteArray(), flags ); 01766 job->setData( arr ); 01767 return job; 01768 } 01769 01771 01772 class KIO::MimetypeJobPrivate: public KIO::TransferJobPrivate 01773 { 01774 public: 01775 MimetypeJobPrivate(const KUrl& url, int command, const QByteArray &packedArgs) 01776 : TransferJobPrivate(url, command, packedArgs, QByteArray()) 01777 {} 01778 01779 Q_DECLARE_PUBLIC(MimetypeJob) 01780 01781 static inline MimetypeJob *newJob(const KUrl& url, int command, const QByteArray &packedArgs, 01782 JobFlags flags) 01783 { 01784 MimetypeJob *job = new MimetypeJob(*new MimetypeJobPrivate(url, command, packedArgs)); 01785 job->setUiDelegate(new JobUiDelegate); 01786 if (!(flags & HideProgressInfo)) { 01787 KIO::getJobTracker()->registerJob(job); 01788 emitStating(job, url); 01789 } 01790 return job; 01791 } 01792 }; 01793 01794 MimetypeJob::MimetypeJob(MimetypeJobPrivate &dd) 01795 : TransferJob(dd) 01796 { 01797 } 01798 01799 MimetypeJob::~MimetypeJob() 01800 { 01801 } 01802 01803 void MimetypeJob::slotFinished( ) 01804 { 01805 Q_D(MimetypeJob); 01806 //kDebug(7007); 01807 if ( error() == KIO::ERR_IS_DIRECTORY ) 01808 { 01809 // It is in fact a directory. This happens when HTTP redirects to FTP. 01810 // Due to the "protocol doesn't support listing" code in KRun, we 01811 // assumed it was a file. 01812 kDebug(7007) << "It is in fact a directory!"; 01813 d->m_mimetype = QString::fromLatin1("inode/directory"); 01814 emit TransferJob::mimetype( this, d->m_mimetype ); 01815 setError( 0 ); 01816 } 01817 01818 if ( !d->m_redirectionURL.isEmpty() && d->m_redirectionURL.isValid() && !error() ) 01819 { 01820 //kDebug(7007) << "Redirection to " << m_redirectionURL; 01821 if (queryMetaData("permanent-redirect")=="true") 01822 emit permanentRedirection(this, d->m_url, d->m_redirectionURL); 01823 01824 if (d->m_redirectionHandlingEnabled) 01825 { 01826 d->staticData.truncate(0); 01827 d->m_internalSuspended = false; 01828 d->m_packedArgs.truncate(0); 01829 QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly ); 01830 stream << d->m_redirectionURL; 01831 01832 d->restartAfterRedirection(&d->m_redirectionURL); 01833 return; 01834 } 01835 } 01836 01837 // Return slave to the scheduler 01838 TransferJob::slotFinished(); 01839 } 01840 01841 MimetypeJob *KIO::mimetype(const KUrl& url, JobFlags flags) 01842 { 01843 KIO_ARGS << url; 01844 return MimetypeJobPrivate::newJob(url, CMD_MIMETYPE, packedArgs, flags); 01845 } 01846 01848 01849 class KIO::DirectCopyJobPrivate: public KIO::SimpleJobPrivate 01850 { 01851 public: 01852 DirectCopyJobPrivate(const KUrl& url, int command, const QByteArray &packedArgs) 01853 : SimpleJobPrivate(url, command, packedArgs) 01854 {} 01855 01862 virtual void start(Slave *slave); 01863 01864 Q_DECLARE_PUBLIC(DirectCopyJob) 01865 }; 01866 01867 DirectCopyJob::DirectCopyJob(const KUrl &url, const QByteArray &packedArgs) 01868 : SimpleJob(*new DirectCopyJobPrivate(url, CMD_COPY, packedArgs)) 01869 { 01870 setUiDelegate(new JobUiDelegate); 01871 } 01872 01873 DirectCopyJob::~DirectCopyJob() 01874 { 01875 } 01876 01877 void DirectCopyJobPrivate::start( Slave* slave ) 01878 { 01879 Q_Q(DirectCopyJob); 01880 q->connect( slave, SIGNAL(canResume(KIO::filesize_t)), 01881 SLOT(slotCanResume(KIO::filesize_t)) ); 01882 SimpleJobPrivate::start(slave); 01883 } 01884 01885 void DirectCopyJob::slotCanResume( KIO::filesize_t offset ) 01886 { 01887 emit canResume(this, offset); 01888 } 01889 01891 01893 class KIO::FileCopyJobPrivate: public KIO::JobPrivate 01894 { 01895 public: 01896 FileCopyJobPrivate(const KUrl& src, const KUrl& dest, int permissions, 01897 bool move, JobFlags flags) 01898 : m_sourceSize(filesize_t(-1)), m_src(src), m_dest(dest), m_moveJob(0), m_copyJob(0), m_delJob(0), 01899 m_chmodJob(0), m_getJob(0), m_putJob(0), m_permissions(permissions), 01900 m_move(move), m_mustChmod(0), m_flags(flags) 01901 { 01902 } 01903 KIO::filesize_t m_sourceSize; 01904 QDateTime m_modificationTime; 01905 KUrl m_src; 01906 KUrl m_dest; 01907 QByteArray m_buffer; 01908 SimpleJob *m_moveJob; 01909 SimpleJob *m_copyJob; 01910 SimpleJob *m_delJob; 01911 SimpleJob *m_chmodJob; 01912 TransferJob *m_getJob; 01913 TransferJob *m_putJob; 01914 int m_permissions; 01915 bool m_move:1; 01916 bool m_canResume:1; 01917 bool m_resumeAnswerSent:1; 01918 bool m_mustChmod:1; 01919 JobFlags m_flags; 01920 01921 void startBestCopyMethod(); 01922 void startCopyJob(); 01923 void startCopyJob(const KUrl &slave_url); 01924 void startRenameJob(const KUrl &slave_url); 01925 void startDataPump(); 01926 void connectSubjob( SimpleJob * job ); 01927 01928 void slotStart(); 01929 void slotData( KIO::Job *, const QByteArray &data); 01930 void slotDataReq( KIO::Job *, QByteArray &data); 01931 void slotMimetype( KIO::Job*, const QString& type ); 01937 void slotProcessedSize( KJob *job, qulonglong size ); 01943 void slotTotalSize( KJob *job, qulonglong size ); 01949 void slotPercent( KJob *job, unsigned long pct ); 01955 void slotCanResume( KIO::Job *job, KIO::filesize_t offset ); 01956 01957 Q_DECLARE_PUBLIC(FileCopyJob) 01958 01959 static inline FileCopyJob* newJob(const KUrl& src, const KUrl& dest, int permissions, bool move, 01960 JobFlags flags) 01961 { 01962 //kDebug(7007) << src << "->" << dest; 01963 FileCopyJob *job = new FileCopyJob( 01964 *new FileCopyJobPrivate(src, dest, permissions, move, flags)); 01965 job->setProperty("destUrl", dest.url()); 01966 job->setUiDelegate(new JobUiDelegate); 01967 if (!(flags & HideProgressInfo)) 01968 KIO::getJobTracker()->registerJob(job); 01969 return job; 01970 } 01971 }; 01972 01973 /* 01974 * The FileCopyJob works according to the famous Bavarian 01975 * 'Alternating Bitburger Protocol': we either drink a beer or we 01976 * we order a beer, but never both at the same time. 01977 * Translated to io-slaves: We alternate between receiving a block of data 01978 * and sending it away. 01979 */ 01980 FileCopyJob::FileCopyJob(FileCopyJobPrivate &dd) 01981 : Job(dd) 01982 { 01983 //kDebug(7007); 01984 QTimer::singleShot(0, this, SLOT(slotStart())); 01985 } 01986 01987 void FileCopyJobPrivate::slotStart() 01988 { 01989 Q_Q(FileCopyJob); 01990 if (!m_move) 01991 JobPrivate::emitCopying( q, m_src, m_dest ); 01992 else 01993 JobPrivate::emitMoving( q, m_src, m_dest ); 01994 01995 if ( m_move ) 01996 { 01997 // The if() below must be the same as the one in startBestCopyMethod 01998 if ((m_src.protocol() == m_dest.protocol()) && 01999 (m_src.host() == m_dest.host()) && 02000 (m_src.port() == m_dest.port()) && 02001 (m_src.user() == m_dest.user()) && 02002 (m_src.pass() == m_dest.pass()) && 02003 !m_src.hasSubUrl() && !m_dest.hasSubUrl()) 02004 { 02005 startRenameJob(m_src); 02006 return; 02007 } 02008 else if (m_src.isLocalFile() && KProtocolManager::canRenameFromFile(m_dest)) 02009 { 02010 startRenameJob(m_dest); 02011 return; 02012 } 02013 else if (m_dest.isLocalFile() && KProtocolManager::canRenameToFile(m_src)) 02014 { 02015 startRenameJob(m_src); 02016 return; 02017 } 02018 // No fast-move available, use copy + del. 02019 } 02020 startBestCopyMethod(); 02021 } 02022 02023 void FileCopyJobPrivate::startBestCopyMethod() 02024 { 02025 if ((m_src.protocol() == m_dest.protocol()) && 02026 (m_src.host() == m_dest.host()) && 02027 (m_src.port() == m_dest.port()) && 02028 (m_src.user() == m_dest.user()) && 02029 (m_src.pass() == m_dest.pass()) && 02030 !m_src.hasSubUrl() && !m_dest.hasSubUrl()) 02031 { 02032 startCopyJob(); 02033 } 02034 else if (m_src.isLocalFile() && KProtocolManager::canCopyFromFile(m_dest)) 02035 { 02036 startCopyJob(m_dest); 02037 } 02038 else if (m_dest.isLocalFile() && KProtocolManager::canCopyToFile(m_src) && 02039 !KIO::Scheduler::isSlaveOnHoldFor(m_src)) 02040 { 02041 startCopyJob(m_src); 02042 } 02043 else 02044 { 02045 startDataPump(); 02046 } 02047 } 02048 02049 FileCopyJob::~FileCopyJob() 02050 { 02051 } 02052 02053 void FileCopyJob::setSourceSize( KIO::filesize_t size ) 02054 { 02055 Q_D(FileCopyJob); 02056 d->m_sourceSize = size; 02057 if (size != (KIO::filesize_t) -1) 02058 setTotalAmount(KJob::Bytes, size); 02059 } 02060 02061 void FileCopyJob::setModificationTime( const QDateTime& mtime ) 02062 { 02063 Q_D(FileCopyJob); 02064 d->m_modificationTime = mtime; 02065 } 02066 02067 KUrl FileCopyJob::srcUrl() const 02068 { 02069 return d_func()->m_src; 02070 } 02071 02072 KUrl FileCopyJob::destUrl() const 02073 { 02074 return d_func()->m_dest; 02075 } 02076 02077 void FileCopyJobPrivate::startCopyJob() 02078 { 02079 startCopyJob(m_src); 02080 } 02081 02082 void FileCopyJobPrivate::startCopyJob(const KUrl &slave_url) 02083 { 02084 Q_Q(FileCopyJob); 02085 //kDebug(7007); 02086 KIO_ARGS << m_src << m_dest << m_permissions << (qint8) (m_flags & Overwrite); 02087 m_copyJob = new DirectCopyJob(slave_url, packedArgs); 02088 q->addSubjob( m_copyJob ); 02089 connectSubjob( m_copyJob ); 02090 q->connect( m_copyJob, SIGNAL(canResume(KIO::Job*,KIO::filesize_t)), 02091 SLOT(slotCanResume(KIO::Job*,KIO::filesize_t))); 02092 } 02093 02094 void FileCopyJobPrivate::startRenameJob(const KUrl &slave_url) 02095 { 02096 Q_Q(FileCopyJob); 02097 m_mustChmod = true; // CMD_RENAME by itself doesn't change permissions 02098 KIO_ARGS << m_src << m_dest << (qint8) (m_flags & Overwrite); 02099 m_moveJob = SimpleJobPrivate::newJobNoUi(slave_url, CMD_RENAME, packedArgs); 02100 q->addSubjob( m_moveJob ); 02101 connectSubjob( m_moveJob ); 02102 } 02103 02104 void FileCopyJobPrivate::connectSubjob( SimpleJob * job ) 02105 { 02106 Q_Q(FileCopyJob); 02107 q->connect( job, SIGNAL(totalSize(KJob*,qulonglong)), 02108 SLOT(slotTotalSize(KJob*,qulonglong)) ); 02109 02110 q->connect( job, SIGNAL(processedSize(KJob*,qulonglong)), 02111 SLOT(slotProcessedSize(KJob*,qulonglong)) ); 02112 02113 q->connect( job, SIGNAL(percent(KJob*,ulong)), 02114 SLOT(slotPercent(KJob*,ulong)) ); 02115 02116 } 02117 02118 bool FileCopyJob::doSuspend() 02119 { 02120 Q_D(FileCopyJob); 02121 if (d->m_moveJob) 02122 d->m_moveJob->suspend(); 02123 02124 if (d->m_copyJob) 02125 d->m_copyJob->suspend(); 02126 02127 if (d->m_getJob) 02128 d->m_getJob->suspend(); 02129 02130 if (d->m_putJob) 02131 d->m_putJob->suspend(); 02132 02133 Job::doSuspend(); 02134 return true; 02135 } 02136 02137 bool FileCopyJob::doResume() 02138 { 02139 Q_D(FileCopyJob); 02140 if (d->m_moveJob) 02141 d->m_moveJob->resume(); 02142 02143 if (d->m_copyJob) 02144 d->m_copyJob->resume(); 02145 02146 if (d->m_getJob) 02147 d->m_getJob->resume(); 02148 02149 if (d->m_putJob) 02150 d->m_putJob->resume(); 02151 02152 Job::doResume(); 02153 return true; 02154 } 02155 02156 void FileCopyJobPrivate::slotProcessedSize( KJob *, qulonglong size ) 02157 { 02158 Q_Q(FileCopyJob); 02159 q->setProcessedAmount(KJob::Bytes, size); 02160 } 02161 02162 void FileCopyJobPrivate::slotTotalSize( KJob*, qulonglong size ) 02163 { 02164 Q_Q(FileCopyJob); 02165 if (size != q->totalAmount(KJob::Bytes)) 02166 { 02167 q->setTotalAmount(KJob::Bytes, size); 02168 } 02169 } 02170 02171 void FileCopyJobPrivate::slotPercent( KJob*, unsigned long pct ) 02172 { 02173 Q_Q(FileCopyJob); 02174 if ( pct > q->percent() ) { 02175 q->setPercent( pct ); 02176 } 02177 } 02178 02179 void FileCopyJobPrivate::startDataPump() 02180 { 02181 Q_Q(FileCopyJob); 02182 //kDebug(7007); 02183 02184 m_canResume = false; 02185 m_resumeAnswerSent = false; 02186 m_getJob = 0L; // for now 02187 m_putJob = put( m_dest, m_permissions, (m_flags | HideProgressInfo) /* no GUI */); 02188 //kDebug(7007) << "m_putJob=" << m_putJob << "m_dest=" << m_dest; 02189 if ( m_modificationTime.isValid() ) { 02190 m_putJob->setModificationTime( m_modificationTime ); 02191 } 02192 02193 // The first thing the put job will tell us is whether we can 02194 // resume or not (this is always emitted) 02195 q->connect( m_putJob, SIGNAL(canResume(KIO::Job*,KIO::filesize_t)), 02196 SLOT(slotCanResume(KIO::Job*,KIO::filesize_t))); 02197 q->connect( m_putJob, SIGNAL(dataReq(KIO::Job*,QByteArray&)), 02198 SLOT(slotDataReq(KIO::Job*,QByteArray&))); 02199 q->addSubjob( m_putJob ); 02200 } 02201 02202 void FileCopyJobPrivate::slotCanResume( KIO::Job* job, KIO::filesize_t offset ) 02203 { 02204 Q_Q(FileCopyJob); 02205 if ( job == m_putJob || job == m_copyJob ) 02206 { 02207 //kDebug(7007) << "'can resume' from PUT job. offset=" << KIO::number(offset); 02208 if (offset) 02209 { 02210 RenameDialog_Result res = R_RESUME; 02211 02212 if (!KProtocolManager::autoResume() && !(m_flags & Overwrite)) 02213 { 02214 QString newPath; 02215 KIO::Job* job = ( q->parentJob() ) ? q->parentJob() : q; 02216 // Ask confirmation about resuming previous transfer 02217 res = ui()->askFileRename( 02218 job, i18n("File Already Exists"), 02219 m_src.url(), 02220 m_dest.url(), 02221 (RenameDialog_Mode) (M_OVERWRITE | M_RESUME | M_NORENAME), newPath, 02222 m_sourceSize, offset ); 02223 } 02224 02225 if ( res == R_OVERWRITE || (m_flags & Overwrite) ) 02226 offset = 0; 02227 else if ( res == R_CANCEL ) 02228 { 02229 if ( job == m_putJob ) { 02230 m_putJob->kill( FileCopyJob::Quietly ); 02231 q->removeSubjob(m_putJob); 02232 m_putJob = 0; 02233 } else { 02234 m_copyJob->kill( FileCopyJob::Quietly ); 02235 q->removeSubjob(m_copyJob); 02236 m_copyJob = 0; 02237 } 02238 q->setError( ERR_USER_CANCELED ); 02239 q->emitResult(); 02240 return; 02241 } 02242 } 02243 else 02244 m_resumeAnswerSent = true; // No need for an answer 02245 02246 if ( job == m_putJob ) 02247 { 02248 m_getJob = KIO::get( m_src, NoReload, HideProgressInfo /* no GUI */ ); 02249 //kDebug(7007) << "m_getJob=" << m_getJob << m_src; 02250 m_getJob->addMetaData( "errorPage", "false" ); 02251 m_getJob->addMetaData( "AllowCompressedPage", "false" ); 02252 // Set size in subjob. This helps if the slave doesn't emit totalSize. 02253 if ( m_sourceSize != (KIO::filesize_t)-1 ) 02254 m_getJob->setTotalAmount(KJob::Bytes, m_sourceSize); 02255 if (offset) 02256 { 02257 //kDebug(7007) << "Setting metadata for resume to" << (unsigned long) offset; 02258 // TODO KDE4: rename to seek or offset and document it 02259 // This isn't used only for resuming, but potentially also for extracting (#72302). 02260 m_getJob->addMetaData( "resume", KIO::number(offset) ); 02261 02262 // Might or might not get emitted 02263 q->connect( m_getJob, SIGNAL(canResume(KIO::Job*,KIO::filesize_t)), 02264 SLOT(slotCanResume(KIO::Job*,KIO::filesize_t))); 02265 } 02266 jobSlave(m_putJob)->setOffset( offset ); 02267 02268 m_putJob->d_func()->internalSuspend(); 02269 q->addSubjob( m_getJob ); 02270 connectSubjob( m_getJob ); // Progress info depends on get 02271 m_getJob->d_func()->internalResume(); // Order a beer 02272 02273 q->connect( m_getJob, SIGNAL(data(KIO::Job*,QByteArray)), 02274 SLOT(slotData(KIO::Job*,QByteArray)) ); 02275 q->connect( m_getJob, SIGNAL(mimetype(KIO::Job*,QString)), 02276 SLOT(slotMimetype(KIO::Job*,QString)) ); 02277 } 02278 else // copyjob 02279 { 02280 jobSlave(m_copyJob)->sendResumeAnswer( offset != 0 ); 02281 } 02282 } 02283 else if ( job == m_getJob ) 02284 { 02285 // Cool, the get job said ok, we can resume 02286 m_canResume = true; 02287 //kDebug(7007) << "'can resume' from the GET job -> we can resume"; 02288 02289 jobSlave(m_getJob)->setOffset( jobSlave(m_putJob)->offset() ); 02290 } 02291 else 02292 kWarning(7007) << "unknown job=" << job 02293 << "m_getJob=" << m_getJob << "m_putJob=" << m_putJob; 02294 } 02295 02296 void FileCopyJobPrivate::slotData( KIO::Job * , const QByteArray &data) 02297 { 02298 //kDebug(7007) << "data size:" << data.size(); 02299 Q_ASSERT(m_putJob); 02300 if (!m_putJob) return; // Don't crash 02301 m_getJob->d_func()->internalSuspend(); 02302 m_putJob->d_func()->internalResume(); // Drink the beer 02303 m_buffer += data; 02304 02305 // On the first set of data incoming, we tell the "put" slave about our 02306 // decision about resuming 02307 if (!m_resumeAnswerSent) 02308 { 02309 m_resumeAnswerSent = true; 02310 //kDebug(7007) << "(first time) -> send resume answer " << m_canResume; 02311 jobSlave(m_putJob)->sendResumeAnswer( m_canResume ); 02312 } 02313 } 02314 02315 void FileCopyJobPrivate::slotDataReq( KIO::Job * , QByteArray &data) 02316 { 02317 Q_Q(FileCopyJob); 02318 //kDebug(7007); 02319 if (!m_resumeAnswerSent && !m_getJob) { 02320 // This can't happen 02321 q->setError( ERR_INTERNAL ); 02322 q->setErrorText( i18n( "'Put' job did not send canResume or 'Get' job did not send data!" ) ); 02323 m_putJob->kill( FileCopyJob::Quietly ); 02324 q->removeSubjob(m_putJob); 02325 m_putJob = 0; 02326 q->emitResult(); 02327 return; 02328 } 02329 if (m_getJob) 02330 { 02331 m_getJob->d_func()->internalResume(); // Order more beer 02332 m_putJob->d_func()->internalSuspend(); 02333 } 02334 data = m_buffer; 02335 m_buffer = QByteArray(); 02336 } 02337 02338 void FileCopyJobPrivate::slotMimetype( KIO::Job*, const QString& type ) 02339 { 02340 Q_Q(FileCopyJob); 02341 emit q->mimetype( q, type ); 02342 } 02343 02344 void FileCopyJob::slotResult( KJob *job) 02345 { 02346 Q_D(FileCopyJob); 02347 //kDebug(7007) << "this=" << this << "job=" << job; 02348 removeSubjob(job); 02349 // Did job have an error ? 02350 if ( job->error() ) 02351 { 02352 if ((job == d->m_moveJob) && (job->error() == ERR_UNSUPPORTED_ACTION)) 02353 { 02354 d->m_moveJob = 0; 02355 d->startBestCopyMethod(); 02356 return; 02357 } 02358 else if ((job == d->m_copyJob) && (job->error() == ERR_UNSUPPORTED_ACTION)) 02359 { 02360 d->m_copyJob = 0; 02361 d->startDataPump(); 02362 return; 02363 } 02364 else if (job == d->m_getJob) 02365 { 02366 d->m_getJob = 0L; 02367 if (d->m_putJob) 02368 { 02369 d->m_putJob->kill( Quietly ); 02370 removeSubjob( d->m_putJob ); 02371 } 02372 } 02373 else if (job == d->m_putJob) 02374 { 02375 d->m_putJob = 0L; 02376 if (d->m_getJob) 02377 { 02378 d->m_getJob->kill( Quietly ); 02379 removeSubjob( d->m_getJob ); 02380 } 02381 } 02382 setError( job->error() ); 02383 setErrorText( job->errorText() ); 02384 emitResult(); 02385 return; 02386 } 02387 02388 if (d->m_mustChmod) 02389 { 02390 // If d->m_permissions == -1, keep the default permissions 02391 if (d->m_permissions != -1) 02392 { 02393 d->m_chmodJob = chmod(d->m_dest, d->m_permissions); 02394 } 02395 d->m_mustChmod = false; 02396 } 02397 02398 if (job == d->m_moveJob) 02399 { 02400 d->m_moveJob = 0; // Finished 02401 } 02402 02403 if (job == d->m_copyJob) 02404 { 02405 d->m_copyJob = 0; 02406 if (d->m_move) 02407 { 02408 d->m_delJob = file_delete( d->m_src, HideProgressInfo/*no GUI*/ ); // Delete source 02409 addSubjob(d->m_delJob); 02410 } 02411 } 02412 02413 if (job == d->m_getJob) 02414 { 02415 //kDebug(7007) << "m_getJob finished"; 02416 d->m_getJob = 0; // No action required 02417 if (d->m_putJob) 02418 d->m_putJob->d_func()->internalResume(); 02419 } 02420 02421 if (job == d->m_putJob) 02422 { 02423 //kDebug(7007) << "m_putJob finished"; 02424 d->m_putJob = 0; 02425 if (d->m_getJob) 02426 { 02427 // The get job is still running, probably after emitting data(QByteArray()) 02428 // and before we receive its finished(). 02429 d->m_getJob->d_func()->internalResume(); 02430 } 02431 if (d->m_move) 02432 { 02433 d->m_delJob = file_delete( d->m_src, HideProgressInfo/*no GUI*/ ); // Delete source 02434 addSubjob(d->m_delJob); 02435 } 02436 } 02437 02438 if (job == d->m_delJob) 02439 { 02440 d->m_delJob = 0; // Finished 02441 } 02442 02443 if (job == d->m_chmodJob) 02444 { 02445 d->m_chmodJob = 0; // Finished 02446 } 02447 02448 if ( !hasSubjobs() ) 02449 emitResult(); 02450 } 02451 02452 FileCopyJob *KIO::file_copy( const KUrl& src, const KUrl& dest, int permissions, 02453 JobFlags flags ) 02454 { 02455 return FileCopyJobPrivate::newJob(src, dest, permissions, false, flags); 02456 } 02457 02458 FileCopyJob *KIO::file_move( const KUrl& src, const KUrl& dest, int permissions, 02459 JobFlags flags ) 02460 { 02461 return FileCopyJobPrivate::newJob(src, dest, permissions, true, flags); 02462 } 02463 02464 SimpleJob *KIO::file_delete( const KUrl& src, JobFlags flags ) 02465 { 02466 KIO_ARGS << src << qint8(true); // isFile 02467 return SimpleJobPrivate::newJob(src, CMD_DEL, packedArgs, flags); 02468 } 02469 02471 02472 class KIO::ListJobPrivate: public KIO::SimpleJobPrivate 02473 { 02474 public: 02475 ListJobPrivate(const KUrl& url, bool _recursive, const QString &_prefix, bool _includeHidden) 02476 : SimpleJobPrivate(url, CMD_LISTDIR, QByteArray()), 02477 recursive(_recursive), includeHidden(_includeHidden), 02478 prefix(_prefix), m_processedEntries(0) 02479 {} 02480 bool recursive; 02481 bool includeHidden; 02482 QString prefix; 02483 unsigned long m_processedEntries; 02484 KUrl m_redirectionURL; 02485 02492 virtual void start( Slave *slave ); 02493 02494 void slotListEntries( const KIO::UDSEntryList& list ); 02495 void slotRedirection( const KUrl &url ); 02496 void gotEntries( KIO::Job * subjob, const KIO::UDSEntryList& list ); 02497 02498 Q_DECLARE_PUBLIC(ListJob) 02499 02500 static inline ListJob *newJob(const KUrl& u, bool _recursive, const QString &_prefix, 02501 bool _includeHidden, JobFlags flags = HideProgressInfo) 02502 { 02503 ListJob *job = new ListJob(*new ListJobPrivate(u, _recursive, _prefix, _includeHidden)); 02504 job->setUiDelegate(new JobUiDelegate); 02505 if (!(flags & HideProgressInfo)) 02506 KIO::getJobTracker()->registerJob(job); 02507 return job; 02508 } 02509 static inline ListJob *newJobNoUi(const KUrl& u, bool _recursive, const QString &_prefix, 02510 bool _includeHidden) 02511 { 02512 return new ListJob(*new ListJobPrivate(u, _recursive, _prefix, _includeHidden)); 02513 } 02514 }; 02515 02516 ListJob::ListJob(ListJobPrivate &dd) 02517 : SimpleJob(dd) 02518 { 02519 Q_D(ListJob); 02520 // We couldn't set the args when calling the parent constructor, 02521 // so do it now. 02522 QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly ); 02523 stream << d->m_url; 02524 } 02525 02526 ListJob::~ListJob() 02527 { 02528 } 02529 02530 void ListJobPrivate::slotListEntries( const KIO::UDSEntryList& list ) 02531 { 02532 Q_Q(ListJob); 02533 // Emit progress info (takes care of emit processedSize and percent) 02534 m_processedEntries += list.count(); 02535 slotProcessedSize( m_processedEntries ); 02536 02537 if (recursive) { 02538 UDSEntryList::ConstIterator it = list.begin(); 02539 const UDSEntryList::ConstIterator end = list.end(); 02540 02541 for (; it != end; ++it) { 02542 02543 const UDSEntry& entry = *it; 02544 02545 KUrl itemURL; 02546 // const UDSEntry::ConstIterator end2 = entry.end(); 02547 // UDSEntry::ConstIterator it2 = entry.find( KIO::UDSEntry::UDS_URL ); 02548 // if ( it2 != end2 ) 02549 if (entry.contains(KIO::UDSEntry::UDS_URL)) 02550 // itemURL = it2.value().toString(); 02551 itemURL = entry.stringValue(KIO::UDSEntry::UDS_URL); 02552 else { // no URL, use the name 02553 itemURL = q->url(); 02554 const QString fileName = entry.stringValue(KIO::UDSEntry::UDS_NAME); 02555 Q_ASSERT(!fileName.isEmpty()); // we'll recurse forever otherwise :) 02556 itemURL.addPath(fileName); 02557 } 02558 02559 if (entry.isDir() && !entry.isLink()) { 02560 const QString filename = itemURL.fileName(); 02561 // skip hidden dirs when listing if requested 02562 if (filename != ".." && filename != "." && (includeHidden || filename[0] != '.')) { 02563 ListJob *job = ListJobPrivate::newJobNoUi(itemURL, 02564 true /*recursive*/, 02565 prefix + filename + '/', 02566 includeHidden); 02567 Scheduler::setJobPriority(job, 1); 02568 q->connect(job, SIGNAL(entries(KIO::Job*,KIO::UDSEntryList)), 02569 SLOT(gotEntries(KIO::Job*,KIO::UDSEntryList))); 02570 q->addSubjob(job); 02571 } 02572 } 02573 } 02574 } 02575 02576 // Not recursive, or top-level of recursive listing : return now (send . and .. as well) 02577 // exclusion of hidden files also requires the full sweep, but the case for full-listing 02578 // a single dir is probably common enough to justify the shortcut 02579 if (prefix.isNull() && includeHidden) { 02580 emit q->entries(q, list); 02581 } else { 02582 // cull the unwanted hidden dirs and/or parent dir references from the listing, then emit that 02583 UDSEntryList newlist; 02584 02585 UDSEntryList::const_iterator it = list.begin(); 02586 const UDSEntryList::const_iterator end = list.end(); 02587 for (; it != end; ++it) { 02588 02589 // Modify the name in the UDSEntry 02590 UDSEntry newone = *it; 02591 const QString filename = newone.stringValue( KIO::UDSEntry::UDS_NAME ); 02592 // Avoid returning entries like subdir/. and subdir/.., but include . and .. for 02593 // the toplevel dir, and skip hidden files/dirs if that was requested 02594 if ( (prefix.isNull() || (filename != ".." && filename != ".") ) 02595 && (includeHidden || (filename[0] != '.') ) ) 02596 { 02597 // ## Didn't find a way to use the iterator instead of re-doing a key lookup 02598 newone.insert( KIO::UDSEntry::UDS_NAME, prefix + filename ); 02599 newlist.append(newone); 02600 } 02601 } 02602 02603 emit q->entries(q, newlist); 02604 } 02605 } 02606 02607 void ListJobPrivate::gotEntries(KIO::Job *, const KIO::UDSEntryList& list ) 02608 { 02609 // Forward entries received by subjob - faking we received them ourselves 02610 Q_Q(ListJob); 02611 emit q->entries(q, list); 02612 } 02613 02614 void ListJob::slotResult( KJob * job ) 02615 { 02616 // If we can't list a subdir, the result is still ok 02617 // This is why we override Job::slotResult() - to skip error checking 02618 removeSubjob( job ); 02619 if ( !hasSubjobs() ) 02620 emitResult(); 02621 } 02622 02623 void ListJobPrivate::slotRedirection( const KUrl & url ) 02624 { 02625 Q_Q(ListJob); 02626 if (!KAuthorized::authorizeUrlAction("redirect", m_url, url)) 02627 { 02628 kWarning(7007) << "ListJob: Redirection from " << m_url << " to " << url << " REJECTED!"; 02629 return; 02630 } 02631 m_redirectionURL = url; // We'll remember that when the job finishes 02632 emit q->redirection( q, m_redirectionURL ); 02633 } 02634 02635 void ListJob::slotFinished() 02636 { 02637 Q_D(ListJob); 02638 02639 // Support for listing archives as directories 02640 if ( error() == KIO::ERR_IS_FILE && d->m_url.isLocalFile() ) { 02641 KMimeType::Ptr ptr = KMimeType::findByUrl( d->m_url, 0, true, true ); 02642 if ( ptr ) { 02643 QString proto = ptr->property("X-KDE-LocalProtocol").toString(); 02644 if ( !proto.isEmpty() && KProtocolInfo::isKnownProtocol( proto) ) { 02645 d->m_redirectionURL = d->m_url; 02646 d->m_redirectionURL.setProtocol( proto ); 02647 setError( 0 ); 02648 emit redirection(this,d->m_redirectionURL); 02649 } 02650 } 02651 } 02652 02653 if ( !d->m_redirectionURL.isEmpty() && d->m_redirectionURL.isValid() && !error() ) { 02654 02655 //kDebug(7007) << "Redirection to " << d->m_redirectionURL; 02656 if (queryMetaData("permanent-redirect")=="true") 02657 emit permanentRedirection(this, d->m_url, d->m_redirectionURL); 02658 02659 if ( d->m_redirectionHandlingEnabled ) { 02660 d->m_packedArgs.truncate(0); 02661 QDataStream stream( &d->m_packedArgs, QIODevice::WriteOnly ); 02662 stream << d->m_redirectionURL; 02663 02664 d->restartAfterRedirection(&d->m_redirectionURL); 02665 return; 02666 } 02667 } 02668 02669 // Return slave to the scheduler 02670 SimpleJob::slotFinished(); 02671 } 02672 02673 void ListJob::slotMetaData( const KIO::MetaData &_metaData) 02674 { 02675 Q_D(ListJob); 02676 SimpleJob::slotMetaData(_metaData); 02677 storeSSLSessionFromJob(d->m_redirectionURL); 02678 } 02679 02680 ListJob *KIO::listDir( const KUrl& url, JobFlags flags, bool includeHidden ) 02681 { 02682 return ListJobPrivate::newJob(url, false, QString(), includeHidden, flags); 02683 } 02684 02685 ListJob *KIO::listRecursive( const KUrl& url, JobFlags flags, bool includeHidden ) 02686 { 02687 return ListJobPrivate::newJob(url, true, QString(), includeHidden, flags); 02688 } 02689 02690 void ListJob::setUnrestricted(bool unrestricted) 02691 { 02692 Q_D(ListJob); 02693 if (unrestricted) 02694 d->m_extraFlags |= JobPrivate::EF_ListJobUnrestricted; 02695 else 02696 d->m_extraFlags &= ~JobPrivate::EF_ListJobUnrestricted; 02697 } 02698 02699 void ListJobPrivate::start(Slave *slave) 02700 { 02701 Q_Q(ListJob); 02702 if (!KAuthorized::authorizeUrlAction("list", m_url, m_url) && 02703 !(m_extraFlags & EF_ListJobUnrestricted)) 02704 { 02705 q->setError( ERR_ACCESS_DENIED ); 02706 q->setErrorText( m_url.url() ); 02707 QTimer::singleShot(0, q, SLOT(slotFinished()) ); 02708 return; 02709 } 02710 q->connect( slave, SIGNAL(listEntries(KIO::UDSEntryList)), 02711 SLOT(slotListEntries(KIO::UDSEntryList))); 02712 q->connect( slave, SIGNAL(totalSize(KIO::filesize_t)), 02713 SLOT(slotTotalSize(KIO::filesize_t)) ); 02714 q->connect( slave, SIGNAL(redirection(KUrl)), 02715 SLOT(slotRedirection(KUrl)) ); 02716 02717 SimpleJobPrivate::start(slave); 02718 } 02719 02720 const KUrl& ListJob::redirectionUrl() const 02721 { 02722 return d_func()->m_redirectionURL; 02723 } 02724 02726 02727 class KIO::MultiGetJobPrivate: public KIO::TransferJobPrivate 02728 { 02729 public: 02730 MultiGetJobPrivate(const KUrl& url) 02731 : TransferJobPrivate(url, 0, QByteArray(), QByteArray()), 02732 m_currentEntry( 0, KUrl(), MetaData() ) 02733 {} 02734 struct GetRequest { 02735 GetRequest(long _id, const KUrl &_url, const MetaData &_metaData) 02736 : id(_id), url(_url), metaData(_metaData) { } 02737 long id; 02738 KUrl url; 02739 MetaData metaData; 02740 02741 inline bool operator==( const GetRequest& req ) const 02742 { return req.id == id; } 02743 }; 02744 typedef QLinkedList<GetRequest> RequestQueue; 02745 02746 RequestQueue m_waitQueue; 02747 RequestQueue m_activeQueue; 02748 GetRequest m_currentEntry; 02749 bool b_multiGetActive; 02750 02757 virtual void start(Slave *slave); 02758 02759 bool findCurrentEntry(); 02760 void flushQueue(QLinkedList<GetRequest> &queue); 02761 02762 Q_DECLARE_PUBLIC(MultiGetJob) 02763 02764 static inline MultiGetJob *newJob(const KUrl &url) 02765 { 02766 MultiGetJob *job = new MultiGetJob(*new MultiGetJobPrivate(url)); 02767 job->setUiDelegate(new JobUiDelegate); 02768 return job; 02769 } 02770 }; 02771 02772 MultiGetJob::MultiGetJob(MultiGetJobPrivate &dd) 02773 : TransferJob(dd) 02774 { 02775 } 02776 02777 MultiGetJob::~MultiGetJob() 02778 { 02779 } 02780 02781 void MultiGetJob::get(long id, const KUrl &url, const MetaData &metaData) 02782 { 02783 Q_D(MultiGetJob); 02784 MultiGetJobPrivate::GetRequest entry(id, url, metaData); 02785 entry.metaData["request-id"] = QString::number(id); 02786 d->m_waitQueue.append(entry); 02787 } 02788 02789 void MultiGetJobPrivate::flushQueue(RequestQueue &queue) 02790 { 02791 // Use multi-get 02792 // Scan all jobs in m_waitQueue 02793 RequestQueue::iterator wqit = m_waitQueue.begin(); 02794 const RequestQueue::iterator wqend = m_waitQueue.end(); 02795 while ( wqit != wqend ) 02796 { 02797 const GetRequest& entry = *wqit; 02798 if ((m_url.protocol() == entry.url.protocol()) && 02799 (m_url.host() == entry.url.host()) && 02800 (m_url.port() == entry.url.port()) && 02801 (m_url.user() == entry.url.user())) 02802 { 02803 queue.append( entry ); 02804 wqit = m_waitQueue.erase( wqit ); 02805 } 02806 else 02807 { 02808 ++wqit; 02809 } 02810 } 02811 // Send number of URLs, (URL, metadata)* 02812 KIO_ARGS << (qint32) queue.count(); 02813 RequestQueue::const_iterator qit = queue.begin(); 02814 const RequestQueue::const_iterator qend = queue.end(); 02815 for( ; qit != qend; ++qit ) 02816 { 02817 stream << (*qit).url << (*qit).metaData; 02818 } 02819 m_packedArgs = packedArgs; 02820 m_command = CMD_MULTI_GET; 02821 m_outgoingMetaData.clear(); 02822 } 02823 02824 void MultiGetJobPrivate::start(Slave *slave) 02825 { 02826 // Add first job from m_waitQueue and add it to m_activeQueue 02827 GetRequest entry = m_waitQueue.takeFirst(); 02828 m_activeQueue.append(entry); 02829 02830 m_url = entry.url; 02831 02832 if (!entry.url.protocol().startsWith(QLatin1String("http"))) 02833 { 02834 // Use normal get 02835 KIO_ARGS << entry.url; 02836 m_packedArgs = packedArgs; 02837 m_outgoingMetaData = entry.metaData; 02838 m_command = CMD_GET; 02839 b_multiGetActive = false; 02840 } 02841 else 02842 { 02843 flushQueue(m_activeQueue); 02844 b_multiGetActive = true; 02845 } 02846 02847 TransferJobPrivate::start(slave); // Anything else to do?? 02848 } 02849 02850 bool MultiGetJobPrivate::findCurrentEntry() 02851 { 02852 if (b_multiGetActive) 02853 { 02854 long id = m_incomingMetaData["request-id"].toLong(); 02855 RequestQueue::const_iterator qit = m_activeQueue.begin(); 02856 const RequestQueue::const_iterator qend = m_activeQueue.end(); 02857 for( ; qit != qend; ++qit ) 02858 { 02859 if ((*qit).id == id) 02860 { 02861 m_currentEntry = *qit; 02862 return true; 02863 } 02864 } 02865 m_currentEntry.id = 0; 02866 return false; 02867 } 02868 else 02869 { 02870 if ( m_activeQueue.isEmpty() ) 02871 return false; 02872 m_currentEntry = m_activeQueue.first(); 02873 return true; 02874 } 02875 } 02876 02877 void MultiGetJob::slotRedirection( const KUrl &url) 02878 { 02879 Q_D(MultiGetJob); 02880 if (!d->findCurrentEntry()) return; // Error 02881 if (!KAuthorized::authorizeUrlAction("redirect", d->m_url, url)) 02882 { 02883 kWarning(7007) << "MultiGetJob: Redirection from " << d->m_currentEntry.url << " to " << url << " REJECTED!"; 02884 return; 02885 } 02886 d->m_redirectionURL = url; 02887 get(d->m_currentEntry.id, d->m_redirectionURL, d->m_currentEntry.metaData); // Try again 02888 } 02889 02890 02891 void MultiGetJob::slotFinished() 02892 { 02893 Q_D(MultiGetJob); 02894 if (!d->findCurrentEntry()) return; 02895 if (d->m_redirectionURL.isEmpty()) 02896 { 02897 // No redirection, tell the world that we are finished. 02898 emit result(d->m_currentEntry.id); 02899 } 02900 d->m_redirectionURL = KUrl(); 02901 setError( 0 ); 02902 d->m_incomingMetaData.clear(); 02903 d->m_activeQueue.removeAll(d->m_currentEntry); 02904 if (d->m_activeQueue.count() == 0) 02905 { 02906 if (d->m_waitQueue.count() == 0) 02907 { 02908 // All done 02909 TransferJob::slotFinished(); 02910 } 02911 else 02912 { 02913 // return slave to pool 02914 // fetch new slave for first entry in d->m_waitQueue and call start 02915 // again. 02916 d->slaveDone(); 02917 02918 d->m_url = d->m_waitQueue.first().url; 02919 if ((d->m_extraFlags & JobPrivate::EF_KillCalled) == 0) { 02920 Scheduler::doJob(this); 02921 } 02922 } 02923 } 02924 } 02925 02926 void MultiGetJob::slotData( const QByteArray &_data) 02927 { 02928 Q_D(MultiGetJob); 02929 if(d->m_redirectionURL.isEmpty() || !d->m_redirectionURL.isValid() || error()) 02930 emit data(d->m_currentEntry.id, _data); 02931 } 02932 02933 void MultiGetJob::slotMimetype( const QString &_mimetype ) 02934 { 02935 Q_D(MultiGetJob); 02936 if (d->b_multiGetActive) 02937 { 02938 MultiGetJobPrivate::RequestQueue newQueue; 02939 d->flushQueue(newQueue); 02940 if (!newQueue.isEmpty()) 02941 { 02942 d->m_activeQueue += newQueue; 02943 d->m_slave->send( d->m_command, d->m_packedArgs ); 02944 } 02945 } 02946 if (!d->findCurrentEntry()) return; // Error, unknown request! 02947 emit mimetype(d->m_currentEntry.id, _mimetype); 02948 } 02949 02950 MultiGetJob *KIO::multi_get(long id, const KUrl &url, const MetaData &metaData) 02951 { 02952 MultiGetJob * job = MultiGetJobPrivate::newJob(url); 02953 job->get(id, url, metaData); 02954 return job; 02955 } 02956 02957 class KIO::SpecialJobPrivate: public TransferJobPrivate 02958 { 02959 SpecialJobPrivate(const KUrl& url, int command, 02960 const QByteArray &packedArgs, 02961 const QByteArray &_staticData) 02962 : TransferJobPrivate(url, command, packedArgs, _staticData) 02963 {} 02964 }; 02965 02966 SpecialJob::SpecialJob(const KUrl &url, const QByteArray &packedArgs) 02967 : TransferJob(*new TransferJobPrivate(url, CMD_SPECIAL, packedArgs, QByteArray())) 02968 { 02969 } 02970 02971 SpecialJob::~SpecialJob() 02972 { 02973 } 02974 02975 void SpecialJob::setArguments(const QByteArray &data) 02976 { 02977 Q_D(SpecialJob); 02978 d->m_packedArgs = data; 02979 } 02980 02981 QByteArray SpecialJob::arguments() const 02982 { 02983 return d_func()->m_packedArgs; 02984 } 02985 02986 // Never defined, never used - what's this code about? 02987 #ifdef CACHE_INFO 02988 CacheInfo::CacheInfo(const KUrl &url) 02989 { 02990 m_url = url; 02991 } 02992 02993 QString CacheInfo::cachedFileName() 02994 { 02995 const QChar separator = '_'; 02996 02997 QString CEF = m_url.path(); 02998 02999 int p = CEF.find('/'); 03000 03001 while(p != -1) 03002 { 03003 CEF[p] = separator; 03004 p = CEF.find('/', p); 03005 } 03006 03007 QString host = m_url.host().toLower(); 03008 CEF = host + CEF + '_'; 03009 03010 QString dir = KProtocolManager::cacheDir(); 03011 if (dir[dir.length()-1] != '/') 03012 dir += '/'; 03013 03014 int l = m_url.host().length(); 03015 for(int i = 0; i < l; i++) 03016 { 03017 if (host[i].isLetter() && (host[i] != 'w')) 03018 { 03019 dir += host[i]; 03020 break; 03021 } 03022 } 03023 if (dir[dir.length()-1] == '/') 03024 dir += '0'; 03025 03026 unsigned long hash = 0x00000000; 03027 QString u = m_url.url().toLatin1(); 03028 for(int i = u.length(); i--;) 03029 { 03030 hash = (hash * 12211 + u[i]) % 2147483563; 03031 } 03032 03033 QString hashString; 03034 hashString.sprintf("%08lx", hash); 03035 03036 CEF = CEF + hashString; 03037 03038 CEF = dir + '/' + CEF; 03039 03040 return CEF; 03041 } 03042 03043 QFile *CacheInfo::cachedFile() 03044 { 03045 #ifdef Q_WS_WIN 03046 const char *mode = (readWrite ? "rb+" : "rb"); 03047 #else 03048 const char *mode = (readWrite ? "r+" : "r"); 03049 #endif 03050 03051 FILE *fs = KDE::fopen(CEF, mode); // Open for reading and writing 03052 if (!fs) 03053 return 0; 03054 03055 char buffer[401]; 03056 bool ok = true; 03057 03058 // CacheRevision 03059 if (ok && (!fgets(buffer, 400, fs))) 03060 ok = false; 03061 if (ok && (strcmp(buffer, CACHE_REVISION) != 0)) 03062 ok = false; 03063 03064 time_t date; 03065 time_t currentDate = time(0); 03066 03067 // URL 03068 if (ok && (!fgets(buffer, 400, fs))) 03069 ok = false; 03070 if (ok) 03071 { 03072 int l = strlen(buffer); 03073 if (l>0) 03074 buffer[l-1] = 0; // Strip newline 03075 if (m_.url.url() != buffer) 03076 { 03077 ok = false; // Hash collision 03078 } 03079 } 03080 03081 // Creation Date 03082 if (ok && (!fgets(buffer, 400, fs))) 03083 ok = false; 03084 if (ok) 03085 { 03086 date = (time_t) strtoul(buffer, 0, 10); 03087 if (m_maxCacheAge && (difftime(currentDate, date) > m_maxCacheAge)) 03088 { 03089 m_bMustRevalidate = true; 03090 m_expireDate = currentDate; 03091 } 03092 } 03093 03094 // Expiration Date 03095 m_cacheExpireDateOffset = KDE_ftell(fs); 03096 if (ok && (!fgets(buffer, 400, fs))) 03097 ok = false; 03098 if (ok) 03099 { 03100 if (m_request.cache == CC_Verify) 03101 { 03102 date = (time_t) strtoul(buffer, 0, 10); 03103 // After the expire date we need to revalidate. 03104 if (!date || difftime(currentDate, date) >= 0) 03105 m_bMustRevalidate = true; 03106 m_expireDate = date; 03107 } 03108 } 03109 03110 // ETag 03111 if (ok && (!fgets(buffer, 400, fs))) 03112 ok = false; 03113 if (ok) 03114 { 03115 m_etag = QString(buffer).trimmed(); 03116 } 03117 03118 // Last-Modified 03119 if (ok && (!fgets(buffer, 400, fs))) 03120 ok = false; 03121 if (ok) 03122 { 03123 m_lastModified = QString(buffer).trimmed(); 03124 } 03125 03126 fclose(fs); 03127 03128 if (ok) 03129 return fs; 03130 03131 unlink( QFile::encodeName(CEF) ); 03132 return 0; 03133 03134 } 03135 03136 void CacheInfo::flush() 03137 { 03138 cachedFile().remove(); 03139 } 03140 03141 void CacheInfo::touch() 03142 { 03143 03144 } 03145 void CacheInfo::setExpireDate(int); 03146 void CacheInfo::setExpireTimeout(int); 03147 03148 03149 int CacheInfo::creationDate(); 03150 int CacheInfo::expireDate(); 03151 int CacheInfo::expireTimeout(); 03152 #endif 03153 03154 #include "jobclasses.moc" 03155 #include "job_p.moc"
This file is part of the KDE documentation.
Documentation copyright © 1996-2012 The KDE developers.
Generated on Wed May 2 2012 18:20:59 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
Documentation copyright © 1996-2012 The KDE developers.
Generated on Wed May 2 2012 18:20:59 by doxygen 1.8.0 written by Dimitri van Heesch, © 1997-2006
KDE's Doxygen guidelines are available online.