kio Library API Documentation

job.cpp

00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2000 Stephan Kulow <coolo@kde.org> 00003 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., 59 Temple Place - Suite 330, 00019 Boston, MA 02111-1307, USA. 00020 */ 00021 00022 #include "kio/job.h" 00023 00024 #include <config.h> 00025 00026 #include <sys/types.h> 00027 #include <sys/wait.h> 00028 #include <sys/stat.h> 00029 00030 #include <assert.h> 00031 00032 #include <signal.h> 00033 #include <stdlib.h> 00034 #include <stdio.h> 00035 #include <time.h> 00036 #include <unistd.h> 00037 extern "C" { 00038 #include <pwd.h> 00039 #include <grp.h> 00040 } 00041 #include <qtimer.h> 00042 #include <qfile.h> 00043 00044 #include <kapplication.h> 00045 #include <kglobal.h> 00046 #include <klocale.h> 00047 #include <ksimpleconfig.h> 00048 #include <kdebug.h> 00049 #include <kdialog.h> 00050 #include <kmessagebox.h> 00051 #include <kdatastream.h> 00052 #include <kmainwindow.h> 00053 00054 #include <errno.h> 00055 00056 #include "slave.h" 00057 #include "scheduler.h" 00058 #include "kdirwatch.h" 00059 #include "kmimemagic.h" 00060 #include "kprotocolinfo.h" 00061 #include "kprotocolmanager.h" 00062 00063 #include "kio/observer.h" 00064 00065 #include "kssl/ksslcsessioncache.h" 00066 00067 #include <kdirnotify_stub.h> 00068 #include <ktempfile.h> 00069 #include <dcopclient.h> 00070 00071 using namespace KIO; 00072 template class QPtrList<KIO::Job>; 00073 00074 //this will update the report dialog with 5 Hz, I think this is fast enough, aleXXX 00075 #define REPORT_TIMEOUT 200 00076 00077 #define KIO_ARGS QByteArray packedArgs; QDataStream stream( packedArgs, IO_WriteOnly ); stream 00078 00079 class Job::JobPrivate 00080 { 00081 public: 00082 JobPrivate() : m_autoErrorHandling( false ), m_parentJob( 0L ), m_extraFlags(0), 00083 m_processedSize(0) 00084 {} 00085 00086 bool m_autoErrorHandling; 00087 QGuardedPtr<QWidget> m_errorParentWidget; 00088 // Maybe we could use the QObject parent/child mechanism instead 00089 // (requires a new ctor, and moving the ctor code to some init()). 00090 Job* m_parentJob; 00091 int m_extraFlags; 00092 KIO::filesize_t m_processedSize; 00093 }; 00094 00095 Job::Job(bool showProgressInfo) : QObject(0, "job"), m_error(0), m_percent(0) 00096 , m_progressId(0), m_speedTimer(0), d( new JobPrivate ) 00097 { 00098 // All jobs delete themselves after emiting 'result'. 00099 00100 // Notify the UI Server and get a progress id 00101 if ( showProgressInfo ) 00102 { 00103 m_progressId = Observer::self()->newJob( this, true ); 00104 //kdDebug(7007) << "Created job " << this << " with progress info -- m_progressId=" << m_progressId << endl; 00105 // Connect global progress info signals 00106 connect( this, SIGNAL( percent( KIO::Job*, unsigned long ) ), 00107 Observer::self(), SLOT( slotPercent( KIO::Job*, unsigned long ) ) ); 00108 connect( this, SIGNAL( infoMessage( KIO::Job*, const QString & ) ), 00109 Observer::self(), SLOT( slotInfoMessage( KIO::Job*, const QString & ) ) ); 00110 connect( this, SIGNAL( totalSize( KIO::Job*, KIO::filesize_t ) ), 00111 Observer::self(), SLOT( slotTotalSize( KIO::Job*, KIO::filesize_t ) ) ); 00112 connect( this, SIGNAL( processedSize( KIO::Job*, KIO::filesize_t ) ), 00113 Observer::self(), SLOT( slotProcessedSize( KIO::Job*, KIO::filesize_t ) ) ); 00114 connect( this, SIGNAL( speed( KIO::Job*, unsigned long ) ), 00115 Observer::self(), SLOT( slotSpeed( KIO::Job*, unsigned long ) ) ); 00116 } 00117 // Don't exit while this job is running 00118 kapp->ref(); 00119 } 00120 00121 Job::~Job() 00122 { 00123 delete m_speedTimer; 00124 delete d; 00125 kapp->deref(); 00126 } 00127 00128 int& Job::extraFlags() 00129 { 00130 return d->m_extraFlags; 00131 } 00132 00133 void Job::setProcessedSize(KIO::filesize_t size) 00134 { 00135 d->m_processedSize = size; 00136 } 00137 00138 KIO::filesize_t Job::getProcessedSize() 00139 { 00140 return d->m_processedSize; 00141 } 00142 00143 void Job::addSubjob(Job *job, bool inheritMetaData) 00144 { 00145 //kdDebug(7007) << "addSubjob(" << job << ") this = " << this << endl; 00146 subjobs.append(job); 00147 00148 connect( job, SIGNAL(result(KIO::Job*)), 00149 SLOT(slotResult(KIO::Job*)) ); 00150 00151 // Forward information from that subjob. 00152 connect( job, SIGNAL(speed( KIO::Job*, unsigned long )), 00153 SLOT(slotSpeed(KIO::Job*, unsigned long)) ); 00154 00155 connect( job, SIGNAL(infoMessage( KIO::Job*, const QString & )), 00156 SLOT(slotInfoMessage(KIO::Job*, const QString &)) ); 00157 00158 if (inheritMetaData) 00159 job->mergeMetaData(m_outgoingMetaData); 00160 00161 job->setWindow( m_window ); 00162 } 00163 00164 void Job::removeSubjob( Job *job ) 00165 { 00166 //kdDebug(7007) << "removeSubjob(" << job << ") this = " << this << " subjobs = " << subjobs.count() << endl; 00167 subjobs.remove(job); 00168 if (subjobs.isEmpty()) 00169 emitResult(); 00170 } 00171 00172 void Job::emitPercent( KIO::filesize_t processedSize, KIO::filesize_t totalSize ) 00173 { 00174 // calculate percents 00175 unsigned long ipercent = m_percent; 00176 00177 if ( totalSize == 0 ) 00178 m_percent = 100; 00179 else 00180 m_percent = (unsigned long)(( (float)(processedSize) / (float)(totalSize) ) * 100.0); 00181 00182 if ( m_percent != ipercent || m_percent == 100 /* for those buggy total sizes that grow */ ) { 00183 emit percent( this, m_percent ); 00184 //kdDebug(7007) << "Job::emitPercent - percent = " << (unsigned int) m_percent << endl; 00185 } 00186 } 00187 00188 void Job::emitSpeed( unsigned long bytes_per_second ) 00189 { 00190 //kdDebug(7007) << "Job " << this << " emitSpeed " << bytes_per_second << endl; 00191 if ( !m_speedTimer ) 00192 { 00193 m_speedTimer = new QTimer(); 00194 connect( m_speedTimer, SIGNAL( timeout() ), SLOT( slotSpeedTimeout() ) ); 00195 } 00196 emit speed( this, bytes_per_second ); 00197 m_speedTimer->start( 5000 ); // 5 seconds interval should be enough 00198 } 00199 00200 void Job::emitResult() 00201 { 00202 // If we are displaying a progress dialog, remove it first. 00203 if ( m_progressId ) // Did we get an ID from the observer ? 00204 Observer::self()->jobFinished( m_progressId ); 00205 if ( m_error && d->m_autoErrorHandling ) 00206 showErrorDialog( d->m_errorParentWidget ); 00207 emit result(this); 00208 delete this; 00209 } 00210 00211 void Job::kill( bool quietly ) 00212 { 00213 kdDebug(7007) << "Job::kill this=" << this << " m_progressId=" << m_progressId << " quietly=" << quietly << endl; 00214 // kill all subjobs, without triggering their result slot 00215 QPtrListIterator<Job> it( subjobs ); 00216 for ( ; it.current() ; ++it ) 00217 (*it)->kill( true ); 00218 subjobs.clear(); 00219 00220 if ( ! quietly ) { 00221 m_error = ERR_USER_CANCELED; 00222 emit canceled( this ); // Not very useful (deprecated) 00223 emitResult(); 00224 } else 00225 { 00226 if ( m_progressId ) // in both cases we want to hide the progress window 00227 Observer::self()->jobFinished( m_progressId ); 00228 delete this; 00229 } 00230 } 00231 00232 void Job::slotResult( Job *job ) 00233 { 00234 // Did job have an error ? 00235 if ( job->error() && !m_error ) 00236 { 00237 // Store it in the parent only if first error 00238 m_error = job->error(); 00239 m_errorText = job->errorText(); 00240 } 00241 removeSubjob(job); 00242 } 00243 00244 void Job::slotSpeed( KIO::Job*, unsigned long bytes_per_second ) 00245 { 00246 //kdDebug(7007) << "Job::slotSpeed " << bytes_per_second << endl; 00247 emitSpeed( bytes_per_second ); 00248 } 00249 00250 void Job::slotInfoMessage( KIO::Job*, const QString & msg ) 00251 { 00252 emit infoMessage( this, msg ); 00253 } 00254 00255 void Job::slotSpeedTimeout() 00256 { 00257 //kdDebug(7007) << "slotSpeedTimeout()" << endl; 00258 // send 0 and stop the timer 00259 // timer will be restarted only when we receive another speed event 00260 emit speed( this, 0 ); 00261 m_speedTimer->stop(); 00262 } 00263 00264 //Job::errorString is implemented in global.cpp 00265 00266 void Job::showErrorDialog( QWidget * parent ) 00267 { 00268 //kdDebug(7007) << "Job::showErrorDialog parent=" << parent << endl; 00269 kapp->enableStyles(); 00270 // Show a message box, except for "user canceled" or "no content" 00271 if ( (m_error != ERR_USER_CANCELED) && (m_error != ERR_NO_CONTENT) ) { 00272 //old plain error message 00273 //kdDebug(7007) << "Default language: " << KGlobal::locale()->defaultLanguage() << endl; 00274 if ( 1 ) 00275 KMessageBox::queuedMessageBox( parent, KMessageBox::Error, errorString() ); 00276 #if 0 00277 } else { 00278 QStringList errors = detailedErrorStrings(); 00279 QString caption, err, detail; 00280 QStringList::iterator it = errors.begin(); 00281 if ( it != errors.end() ) 00282 caption = *(it++); 00283 if ( it != errors.end() ) 00284 err = *(it++); 00285 if ( it != errors.end() ) 00286 detail = *it; 00287 KMessageBox::queuedDetailedError( parent, err, detail, caption ); 00288 } 00289 #endif 00290 } 00291 } 00292 00293 void Job::setAutoErrorHandlingEnabled( bool enable, QWidget *parentWidget ) 00294 { 00295 d->m_autoErrorHandling = enable; 00296 d->m_errorParentWidget = parentWidget; 00297 } 00298 00299 bool Job::isAutoErrorHandlingEnabled() const 00300 { 00301 return d->m_autoErrorHandling; 00302 } 00303 00304 void Job::setWindow(QWidget *window) 00305 { 00306 m_window = window; 00307 KIO::Scheduler::registerWindow(window); 00308 } 00309 00310 QWidget *Job::window() const 00311 { 00312 return m_window; 00313 } 00314 00315 void Job::setParentJob(Job* job) 00316 { 00317 Q_ASSERT(d->m_parentJob == 0L); 00318 Q_ASSERT(job); 00319 d->m_parentJob = job; 00320 } 00321 00322 Job* Job::parentJob() const 00323 { 00324 return d->m_parentJob; 00325 } 00326 00327 MetaData Job::metaData() const 00328 { 00329 return m_incomingMetaData; 00330 } 00331 00332 QString Job::queryMetaData(const QString &key) 00333 { 00334 if (!m_incomingMetaData.contains(key)) 00335 return QString::null; 00336 return m_incomingMetaData[key]; 00337 } 00338 00339 void Job::setMetaData( const KIO::MetaData &_metaData) 00340 { 00341 m_outgoingMetaData = _metaData; 00342 } 00343 00344 void Job::addMetaData( const QString &key, const QString &value) 00345 { 00346 m_outgoingMetaData.insert(key, value); 00347 } 00348 00349 void Job::addMetaData( const QMap<QString,QString> &values) 00350 { 00351 QMapConstIterator<QString,QString> it = values.begin(); 00352 for(;it != values.end(); ++it) 00353 m_outgoingMetaData.insert(it.key(), it.data()); 00354 } 00355 00356 void Job::mergeMetaData( const QMap<QString,QString> &values) 00357 { 00358 QMapConstIterator<QString,QString> it = values.begin(); 00359 for(;it != values.end(); ++it) 00360 m_outgoingMetaData.insert(it.key(), it.data(), false); 00361 } 00362 00363 MetaData Job::outgoingMetaData() const 00364 { 00365 return m_outgoingMetaData; 00366 } 00367 00368 00369 SimpleJob::SimpleJob(const KURL& url, int command, const QByteArray &packedArgs, 00370 bool showProgressInfo ) 00371 : Job(showProgressInfo), m_slave(0), m_packedArgs(packedArgs), 00372 m_url(url), m_command(command), m_totalSize(0) 00373 { 00374 if (!m_url.isValid()) 00375 { 00376 m_error = ERR_MALFORMED_URL; 00377 m_errorText = m_url.url(); 00378 QTimer::singleShot(0, this, SLOT(slotFinished()) ); 00379 return; 00380 } 00381 00382 00383 if (m_url.hasSubURL()) 00384 { 00385 KURL::List list = KURL::split(m_url); 00386 KURL::List::Iterator it = list.fromLast(); 00387 list.remove(it); 00388 m_subUrl = KURL::join(list); 00389 //kdDebug(7007) << "New URL = " << m_url.url() << endl; 00390 //kdDebug(7007) << "Sub URL = " << m_subUrl.url() << endl; 00391 } 00392 00393 Scheduler::doJob(this); 00394 } 00395 00396 void SimpleJob::kill( bool quietly ) 00397 { 00398 Scheduler::cancelJob( this ); // deletes the slave if not 0 00399 m_slave = 0; // -> set to 0 00400 Job::kill( quietly ); 00401 } 00402 00403 void SimpleJob::putOnHold() 00404 { 00405 Scheduler::putSlaveOnHold(this, m_url); 00406 m_slave = 0; 00407 kill(true); 00408 } 00409 00410 void SimpleJob::removeOnHold() 00411 { 00412 Scheduler::removeSlaveOnHold(); 00413 } 00414 00415 SimpleJob::~SimpleJob() 00416 { 00417 if (m_slave) // was running 00418 { 00419 kdDebug(7007) << "SimpleJob::~SimpleJob: Killing running job in destructor!" << endl; 00420 #if 0 00421 m_slave->kill(); 00422 Scheduler::jobFinished( this, m_slave ); // deletes the slave 00423 #endif 00424 Scheduler::cancelJob( this ); 00425 m_slave = 0; // -> set to 0 00426 } 00427 } 00428 00429 void SimpleJob::start(Slave *slave) 00430 { 00431 m_slave = slave; 00432 00433 connect( m_slave, SIGNAL( error( int , const QString & ) ), 00434 SLOT( slotError( int , const QString & ) ) ); 00435 00436 connect( m_slave, SIGNAL( warning( const QString & ) ), 00437 SLOT( slotWarning( const QString & ) ) ); 00438 00439 connect( m_slave, SIGNAL( infoMessage( const QString & ) ), 00440 SLOT( slotInfoMessage( const QString & ) ) ); 00441 00442 connect( m_slave, SIGNAL( connected() ), 00443 SLOT( slotConnected() ) ); 00444 00445 connect( m_slave, SIGNAL( finished() ), 00446 SLOT( slotFinished() ) ); 00447 00448 if ((extraFlags() & EF_TransferJobDataSent) == 0) 00449 { 00450 connect( m_slave, SIGNAL( totalSize( KIO::filesize_t ) ), 00451 SLOT( slotTotalSize( KIO::filesize_t ) ) ); 00452 00453 connect( m_slave, SIGNAL( processedSize( KIO::filesize_t ) ), 00454 SLOT( slotProcessedSize( KIO::filesize_t ) ) ); 00455 00456 connect( m_slave, SIGNAL( speed( unsigned long ) ), 00457 SLOT( slotSpeed( unsigned long ) ) ); 00458 } 00459 00460 connect( slave, SIGNAL( needProgressId() ), 00461 SLOT( slotNeedProgressId() ) ); 00462 00463 connect( slave, SIGNAL(metaData( const KIO::MetaData& ) ), 00464 SLOT( slotMetaData( const KIO::MetaData& ) ) ); 00465 00466 if (m_window) 00467 { 00468 QString id; 00469 addMetaData("window-id", id.setNum(m_window->winId())); 00470 } 00471 00472 QString sslSession = KSSLCSessionCache::getSessionForURL(m_url); 00473 if (sslSession != QString::null) 00474 addMetaData("ssl_session_id", sslSession); 00475 00476 if (!m_outgoingMetaData.isEmpty()) 00477 { 00478 KIO_ARGS << m_outgoingMetaData; 00479 slave->send( CMD_META_DATA, packedArgs ); 00480 } 00481 00482 if (!m_subUrl.isEmpty()) 00483 { 00484 KIO_ARGS << m_subUrl; 00485 m_slave->send( CMD_SUBURL, packedArgs ); 00486 } 00487 00488 m_slave->send( m_command, m_packedArgs ); 00489 } 00490 00491 void SimpleJob::slaveDone() 00492 { 00493 if (!m_slave) return; 00494 disconnect(m_slave); // Remove all signals between slave and job 00495 Scheduler::jobFinished( this, m_slave ); 00496 m_slave = 0; 00497 } 00498 00499 void SimpleJob::slotFinished( ) 00500 { 00501 // Return slave to the scheduler 00502 slaveDone(); 00503 00504 if (subjobs.isEmpty()) 00505 { 00506 if ( !m_error ) 00507 { 00508 KDirNotify_stub allDirNotify( "*", "KDirNotify*" ); 00509 if ( m_command == CMD_MKDIR ) 00510 { 00511 KURL urlDir( url() ); 00512 urlDir.setPath( urlDir.directory() ); 00513 allDirNotify.FilesAdded( urlDir ); 00514 } 00515 else if ( m_command == CMD_RENAME ) 00516 { 00517 KURL src, dst; 00518 QDataStream str( m_packedArgs, IO_ReadOnly ); 00519 str >> src >> dst; 00520 if ( src.directory() == dst.directory() ) // For the user, moving isn't renaming. Only renaming is. 00521 allDirNotify.FileRenamed( src, dst ); 00522 } 00523 } 00524 emitResult(); 00525 } 00526 } 00527 00528 void SimpleJob::slotError( int error, const QString & errorText ) 00529 { 00530 m_error = error; 00531 m_errorText = errorText; 00532 if ((m_error == ERR_UNKNOWN_HOST) && m_url.host().isEmpty()) 00533 m_errorText = QString::null; 00534 // error terminates the job 00535 slotFinished(); 00536 } 00537 00538 void SimpleJob::slotWarning( const QString & errorText ) 00539 { 00540 static uint msgBoxDisplayed = 0; 00541 if ( msgBoxDisplayed == 0 ) // don't bomb the user with message boxes, only one at a time 00542 { 00543 msgBoxDisplayed++; 00544 KMessageBox::information( 0L, errorText ); 00545 msgBoxDisplayed--; 00546 } 00547 // otherwise just discard it. 00548 } 00549 00550 void SimpleJob::slotInfoMessage( const QString & msg ) 00551 { 00552 emit infoMessage( this, msg ); 00553 } 00554 00555 void SimpleJob::slotConnected() 00556 { 00557 emit connected( this ); 00558 } 00559 00560 void SimpleJob::slotNeedProgressId() 00561 { 00562 if ( !m_progressId ) 00563 m_progressId = Observer::self()->newJob( this, false ); 00564 m_slave->setProgressId( m_progressId ); 00565 } 00566 00567 void SimpleJob::slotTotalSize( KIO::filesize_t size ) 00568 { 00569 m_totalSize = size; 00570 emit totalSize( this, size ); 00571 } 00572 00573 void SimpleJob::slotProcessedSize( KIO::filesize_t size ) 00574 { 00575 //kdDebug(7007) << "SimpleJob::slotProcessedSize " << KIO::number(size) << endl; 00576 setProcessedSize(size); 00577 emit processedSize( this, size ); 00578 if ( size > m_totalSize ) { 00579 slotTotalSize(size); // safety 00580 } 00581 emitPercent( size, m_totalSize ); 00582 } 00583 00584 void SimpleJob::slotSpeed( unsigned long bytes_per_second ) 00585 { 00586 //kdDebug(7007) << "SimpleJob::slotSpeed( " << bytes_per_second << " )" << endl; 00587 emitSpeed( bytes_per_second ); 00588 } 00589 00590 void SimpleJob::slotMetaData( const KIO::MetaData &_metaData) 00591 { 00592 m_incomingMetaData += _metaData; 00593 } 00594 00595 void SimpleJob::storeSSLSessionFromJob(const KURL &m_redirectionURL) { 00596 QString sslSession = queryMetaData("ssl_session_id"); 00597 00598 if (sslSession != QString::null) { 00599 const KURL &queryURL = m_redirectionURL.isEmpty()?m_url:m_redirectionURL; 00600 KSSLCSessionCache::putSessionForURL(queryURL, sslSession); 00601 } 00602 } 00603 00604 SimpleJob *KIO::mkdir( const KURL& url, int permissions ) 00605 { 00606 //kdDebug(7007) << "mkdir " << url.prettyURL() << endl; 00607 KIO_ARGS << url << permissions; 00608 return new SimpleJob(url, CMD_MKDIR, packedArgs, false); 00609 } 00610 00611 SimpleJob *KIO::rmdir( const KURL& url ) 00612 { 00613 //kdDebug(7007) << "rmdir " << url.prettyURL() << endl; 00614 KIO_ARGS << url << Q_INT8(false); // isFile is false 00615 return new SimpleJob(url, CMD_DEL, packedArgs, false); 00616 } 00617 00618 SimpleJob *KIO::chmod( const KURL& url, int permissions ) 00619 { 00620 //kdDebug(7007) << "chmod " << url.prettyURL() << endl; 00621 KIO_ARGS << url << permissions; 00622 return new SimpleJob(url, CMD_CHMOD, packedArgs, false); 00623 } 00624 00625 SimpleJob *KIO::rename( const KURL& src, const KURL & dest, bool overwrite ) 00626 { 00627 //kdDebug(7007) << "rename " << src.prettyURL() << " " << dest.prettyURL() << endl; 00628 KIO_ARGS << src << dest << (Q_INT8) overwrite; 00629 return new SimpleJob(src, CMD_RENAME, packedArgs, false); 00630 } 00631 00632 SimpleJob *KIO::symlink( const QString& target, const KURL & dest, bool overwrite, bool showProgressInfo ) 00633 { 00634 //kdDebug(7007) << "symlink target=" << target << " " << dest.prettyURL() << endl; 00635 KIO_ARGS << target << dest << (Q_INT8) overwrite; 00636 return new SimpleJob(dest, CMD_SYMLINK, packedArgs, showProgressInfo); 00637 } 00638 00639 SimpleJob *KIO::special(const KURL& url, const QByteArray & data, bool showProgressInfo) 00640 { 00641 //kdDebug(7007) << "special " << url.prettyURL() << endl; 00642 return new SimpleJob(url, CMD_SPECIAL, data, showProgressInfo); 00643 } 00644 00645 SimpleJob *KIO::mount( bool ro, const char *fstype, const QString& dev, const QString& point, bool showProgressInfo ) 00646 { 00647 KIO_ARGS << int(1) << Q_INT8( ro ? 1 : 0 ) 00648 << QString::fromLatin1(fstype) << dev << point; 00649 SimpleJob *job = special( KURL("file:/"), packedArgs, showProgressInfo ); 00650 if ( showProgressInfo ) 00651 Observer::self()->mounting( job, dev, point ); 00652 return job; 00653 } 00654 00655 SimpleJob *KIO::unmount( const QString& point, bool showProgressInfo ) 00656 { 00657 KIO_ARGS << int(2) << point; 00658 SimpleJob *job = special( KURL("file:/"), packedArgs, showProgressInfo ); 00659 if ( showProgressInfo ) 00660 Observer::self()->unmounting( job, point ); 00661 return job; 00662 } 00663 00665 00666 StatJob::StatJob( const KURL& url, int command, 00667 const QByteArray &packedArgs, bool showProgressInfo ) 00668 : SimpleJob(url, command, packedArgs, showProgressInfo), 00669 m_bSource(true), m_details(2) 00670 { 00671 } 00672 00673 void StatJob::start(Slave *slave) 00674 { 00675 m_outgoingMetaData.replace( "statSide", m_bSource ? "source" : "dest" ); 00676 m_outgoingMetaData.replace( "details", QString::number(m_details) ); 00677 00678 SimpleJob::start(slave); 00679 00680 connect( m_slave, SIGNAL( statEntry( const KIO::UDSEntry& ) ), 00681 SLOT( slotStatEntry( const KIO::UDSEntry & ) ) ); 00682 connect( slave, SIGNAL( redirection(const KURL &) ), 00683 SLOT( slotRedirection(const KURL &) ) ); 00684 } 00685 00686 void StatJob::slotStatEntry( const KIO::UDSEntry & entry ) 00687 { 00688 //kdDebug(7007) << "StatJob::slotStatEntry" << endl; 00689 m_statResult = entry; 00690 } 00691 00692 // Slave got a redirection request 00693 void StatJob::slotRedirection( const KURL &url) 00694 { 00695 kdDebug(7007) << "StatJob::slotRedirection(" << url.prettyURL() << ")" << endl; 00696 if (!kapp->authorizeURLAction("redirect", m_url, url)) 00697 { 00698 kdWarning(7007) << "StatJob: Redirection from " << m_url.prettyURL() << " to " << url.prettyURL() << " REJECTED!" << endl; 00699 m_error = ERR_ACCESS_DENIED; 00700 m_errorText = url.prettyURL(); 00701 return; 00702 } 00703 m_redirectionURL = url; // We'll remember that when the job finishes 00704 if (m_url.hasUser() && !url.hasUser() && (m_url.host().lower() == url.host().lower())) 00705 m_redirectionURL.setUser(m_url.user()); // Preserve user 00706 // Tell the user that we haven't finished yet 00707 emit redirection(this, m_redirectionURL); 00708 } 00709 00710 void StatJob::slotFinished() 00711 { 00712 if ( m_redirectionURL.isEmpty() || !m_redirectionURL.isValid()) 00713 { 00714 // Return slave to the scheduler 00715 SimpleJob::slotFinished(); 00716 } else { 00717 //kdDebug(7007) << "StatJob: Redirection to " << m_redirectionURL.prettyURL() << endl; 00718 if (queryMetaData("permanent-redirect")=="true") 00719 emit permanentRedirection(this, m_url, m_redirectionURL); 00720 m_url = m_redirectionURL; 00721 m_redirectionURL = KURL(); 00722 m_packedArgs.truncate(0); 00723 QDataStream stream( m_packedArgs, IO_WriteOnly ); 00724 stream << m_url; 00725 00726 // Return slave to the scheduler 00727 slaveDone(); 00728 Scheduler::doJob(this); 00729 } 00730 } 00731 00732 void StatJob::slotMetaData( const KIO::MetaData &_metaData) { 00733 SimpleJob::slotMetaData(_metaData); 00734 storeSSLSessionFromJob(m_redirectionURL); 00735 } 00736 00737 StatJob *KIO::stat(const KURL& url, bool showProgressInfo) 00738 { 00739 // Assume sideIsSource. Gets are more common than puts. 00740 return stat( url, true, 2, showProgressInfo ); 00741 } 00742 00743 StatJob *KIO::stat(const KURL& url, bool sideIsSource, short int details, bool showProgressInfo) 00744 { 00745 kdDebug(7007) << "stat " << url.prettyURL() << endl; 00746 KIO_ARGS << url; 00747 StatJob * job = new StatJob(url, CMD_STAT, packedArgs, showProgressInfo ); 00748 job->setSide( sideIsSource ); 00749 job->setDetails( details ); 00750 if ( showProgressInfo ) 00751 Observer::self()->stating( job, url ); 00752 return job; 00753 } 00754 00755 SimpleJob *KIO::http_update_cache( const KURL& url, bool no_cache, time_t expireDate) 00756 { 00757 assert( (url.protocol() == "http") || (url.protocol() == "https") ); 00758 // Send http update_cache command (2) 00759 KIO_ARGS << (int)2 << url << no_cache << expireDate; 00760 SimpleJob * job = new SimpleJob( url, CMD_SPECIAL, packedArgs, false ); 00761 Scheduler::scheduleJob(job); 00762 return job; 00763 } 00764 00766 00767 TransferJob::TransferJob( const KURL& url, int command, 00768 const QByteArray &packedArgs, 00769 const QByteArray &_staticData, 00770 bool showProgressInfo) 00771 : SimpleJob(url, command, packedArgs, showProgressInfo), staticData( _staticData) 00772 { 00773 m_suspended = false; 00774 m_errorPage = false; 00775 m_subJob = 0L; 00776 if ( showProgressInfo ) 00777 Observer::self()->slotTransferring( this, url ); 00778 } 00779 00780 // Slave sends data 00781 void TransferJob::slotData( const QByteArray &_data) 00782 { 00783 if(m_redirectionURL.isEmpty() || !m_redirectionURL.isValid() || m_error) 00784 emit data( this, _data); 00785 } 00786 00787 // Slave got a redirection request 00788 void TransferJob::slotRedirection( const KURL &url) 00789 { 00790 kdDebug(7007) << "TransferJob::slotRedirection(" << url.prettyURL() << ")" << endl; 00791 if (!kapp->authorizeURLAction("redirect", m_url, url)) 00792 { 00793 kdWarning(7007) << "TransferJob: Redirection from " << m_url.prettyURL() << " to " << url.prettyURL() << " REJECTED!" << endl; 00794 return; 00795 } 00796 00797 // Some websites keep redirecting to themselves where each redirection 00798 // acts as the stage in a state-machine. We define "endless redirections" 00799 // as 5 redirections to the same URL. 00800 if (m_redirectionList.contains(url) > 5) 00801 { 00802 kdDebug(7007) << "TransferJob::slotRedirection: CYCLIC REDIRECTION!" << endl; 00803 m_error = ERR_CYCLIC_LINK; 00804 m_errorText = m_url.prettyURL(); 00805 } 00806 else 00807 { 00808 m_redirectionURL = url; // We'll remember that when the job finishes 00809 if (m_url.hasUser() && !url.hasUser() && (m_url.host().lower() == url.host().lower())) 00810 m_redirectionURL.setUser(m_url.user()); // Preserve user 00811 m_redirectionList.append(url); 00812 m_outgoingMetaData["ssl_was_in_use"] = m_incomingMetaData["ssl_in_use"]; 00813 // Tell the user that we haven't finished yet 00814 emit redirection(this, m_redirectionURL); 00815 } 00816 } 00817 00818 void TransferJob::slotFinished() 00819 { 00820 //kdDebug(7007) << "TransferJob::slotFinished(" << this << ", " << m_url.prettyURL() << ")" << endl; 00821 if (m_redirectionURL.isEmpty() || !m_redirectionURL.isValid()) 00822 SimpleJob::slotFinished(); 00823 else { 00824 //kdDebug(7007) << "TransferJob: Redirection to " << m_redirectionURL.prettyURL() << endl; 00825 if (queryMetaData("permanent-redirect")=="true") 00826 emit permanentRedirection(this, m_url, m_redirectionURL); 00827 // Honour the redirection 00828 // We take the approach of "redirecting this same job" 00829 // Another solution would be to create a subjob, but the same problem 00830 // happens (unpacking+repacking) 00831 staticData.truncate(0); 00832 m_incomingMetaData.clear(); 00833 if (queryMetaData("cache") != "reload") 00834 addMetaData("cache","refresh"); 00835 m_suspended = false; 00836 m_url = m_redirectionURL; 00837 m_redirectionURL = KURL(); 00838 // The very tricky part is the packed arguments business 00839 QString dummyStr; 00840 KURL dummyUrl; 00841 QDataStream istream( m_packedArgs, IO_ReadOnly ); 00842 switch( m_command ) { 00843 case CMD_GET: { 00844 m_packedArgs.truncate(0); 00845 QDataStream stream( m_packedArgs, IO_WriteOnly ); 00846 stream << m_url; 00847 break; 00848 } 00849 case CMD_PUT: { 00850 int permissions; 00851 Q_INT8 iOverwrite, iResume; 00852 istream >> dummyUrl >> iOverwrite >> iResume >> permissions; 00853 m_packedArgs.truncate(0); 00854 QDataStream stream( m_packedArgs, IO_WriteOnly ); 00855 stream << m_url << iOverwrite << iResume << permissions; 00856 break; 00857 } 00858 case CMD_SPECIAL: { 00859 int specialcmd; 00860 istream >> specialcmd; 00861 if (specialcmd == 1) // HTTP POST 00862 { 00863 addMetaData("cache","reload"); 00864 m_packedArgs.truncate(0); 00865 QDataStream stream( m_packedArgs, IO_WriteOnly ); 00866 stream << m_url; 00867 m_command = CMD_GET; 00868 } 00869 break; 00870 } 00871 } 00872 00873 // Return slave to the scheduler 00874 slaveDone(); 00875 Scheduler::doJob(this); 00876 } 00877 } 00878 00879 void TransferJob::setAsyncDataEnabled(bool enabled) 00880 { 00881 if (enabled) 00882 extraFlags() |= EF_TransferJobAsync; 00883 else 00884 extraFlags() &= ~EF_TransferJobAsync; 00885 } 00886 00887 void TransferJob::sendAsyncData(const QByteArray &dataForSlave) 00888 { 00889 if (extraFlags() & EF_TransferJobNeedData) 00890 { 00891 m_slave->send( MSG_DATA, dataForSlave ); 00892 if (extraFlags() & EF_TransferJobDataSent) 00893 { 00894 KIO::filesize_t size = getProcessedSize()+dataForSlave.size(); 00895 setProcessedSize(size); 00896 emit processedSize( this, size ); 00897 if ( size > m_totalSize ) { 00898 slotTotalSize(size); // safety 00899 } 00900 emitPercent( size, m_totalSize ); 00901 } 00902 } 00903 00904 extraFlags() &= ~EF_TransferJobNeedData; 00905 } 00906 00907 void TransferJob::setReportDataSent(bool enabled) 00908 { 00909 if (enabled) 00910 extraFlags() |= EF_TransferJobDataSent; 00911 else 00912 extraFlags() &= ~EF_TransferJobDataSent; 00913 } 00914 00915 bool TransferJob::reportDataSent() 00916 { 00917 return (extraFlags() & EF_TransferJobDataSent); 00918 } 00919 00920 00921 // Slave requests data 00922 void TransferJob::slotDataReq() 00923 { 00924 QByteArray dataForSlave; 00925 00926 extraFlags() |= EF_TransferJobNeedData; 00927 00928 if (!staticData.isEmpty()) 00929 { 00930 dataForSlave = staticData; 00931 staticData = QByteArray(); 00932 } 00933 else 00934 { 00935 emit dataReq( this, dataForSlave); 00936 00937 if (extraFlags() & EF_TransferJobAsync) 00938 return; 00939 } 00940 00941 static const size_t max_size = 14 * 1024 * 1024; 00942 if (dataForSlave.size() > max_size) 00943 { 00944 kdDebug(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"; 00945 staticData.duplicate(dataForSlave.data() + max_size , dataForSlave.size() - max_size); 00946 dataForSlave.truncate(max_size); 00947 } 00948 00949 sendAsyncData(dataForSlave); 00950 00951 if (m_subJob) 00952 { 00953 // Bitburger protocol in action 00954 suspend(); // Wait for more data from subJob. 00955 m_subJob->resume(); // Ask for more! 00956 } 00957 } 00958 00959 void TransferJob::slotMimetype( const QString& type ) 00960 { 00961 m_mimetype = type; 00962 emit mimetype( this, m_mimetype); 00963 } 00964 00965 00966 void TransferJob::suspend() 00967 { 00968 m_suspended = true; 00969 if (m_slave) 00970 m_slave->suspend(); 00971 } 00972 00973 void TransferJob::resume() 00974 { 00975 m_suspended = false; 00976 if (m_slave) 00977 m_slave->resume(); 00978 } 00979 00980 void TransferJob::start(Slave *slave) 00981 { 00982 assert(slave); 00983 connect( slave, SIGNAL( data( const QByteArray & ) ), 00984 SLOT( slotData( const QByteArray & ) ) ); 00985 00986 connect( slave, SIGNAL( dataReq() ), 00987 SLOT( slotDataReq() ) ); 00988 00989 connect( slave, SIGNAL( redirection(const KURL &) ), 00990 SLOT( slotRedirection(const KURL &) ) ); 00991 00992 connect( slave, SIGNAL(mimeType( const QString& ) ), 00993 SLOT( slotMimetype( const QString& ) ) ); 00994 00995 connect( slave, SIGNAL(errorPage() ), 00996 SLOT( slotErrorPage() ) ); 00997 00998 connect( slave, SIGNAL( needSubURLData() ), 00999 SLOT( slotNeedSubURLData() ) ); 01000 01001 connect( slave, SIGNAL(canResume( KIO::filesize_t ) ), 01002 SLOT( slotCanResume( KIO::filesize_t ) ) ); 01003 01004 if (slave->suspended()) 01005 { 01006 m_mimetype = "unknown"; 01007 // WABA: The slave was put on hold. Resume operation. 01008 slave->resume(); 01009 } 01010 01011 SimpleJob::start(slave); 01012 if (m_suspended) 01013 slave->suspend(); 01014 } 01015 01016 void TransferJob::slotNeedSubURLData() 01017 { 01018 // Job needs data from subURL. 01019 m_subJob = KIO::get( m_subUrl, false, false); 01020 suspend(); // Put job on hold until we have some data. 01021 connect(m_subJob, SIGNAL( data(KIO::Job*,const QByteArray &)), 01022 SLOT( slotSubURLData(KIO::Job*,const QByteArray &))); 01023 addSubjob(m_subJob); 01024 } 01025 01026 void TransferJob::slotSubURLData(KIO::Job*, const QByteArray &data) 01027 { 01028 // The Alternating Bitburg protocol in action again. 01029 staticData = data; 01030 m_subJob->suspend(); // Put job on hold until we have delivered the data. 01031 resume(); // Activate ourselves again. 01032 } 01033 01034 void TransferJob::slotMetaData( const KIO::MetaData &_metaData) { 01035 SimpleJob::slotMetaData(_metaData); 01036 storeSSLSessionFromJob(m_redirectionURL); 01037 } 01038 01039 void TransferJob::slotErrorPage() 01040 { 01041 m_errorPage = true; 01042 } 01043 01044 void TransferJob::slotCanResume( KIO::filesize_t offset ) 01045 { 01046 emit canResume(this, offset); 01047 } 01048 01049 void TransferJob::slotResult( KIO::Job *job) 01050 { 01051 // This can only be our suburl. 01052 assert(job == m_subJob); 01053 // Did job have an error ? 01054 if ( job->error() ) 01055 { 01056 m_error = job->error(); 01057 m_errorText = job->errorText(); 01058 01059 emitResult(); 01060 return; 01061 } 01062 01063 if (job == m_subJob) 01064 { 01065 m_subJob = 0; // No action required 01066 resume(); // Make sure we get the remaining data. 01067 } 01068 subjobs.remove(job); // Remove job, but don't kill this job. 01069 } 01070 01071 TransferJob *KIO::get( const KURL& url, bool reload, bool showProgressInfo ) 01072 { 01073 // Send decoded path and encoded query 01074 KIO_ARGS << url; 01075 TransferJob * job = new TransferJob( url, CMD_GET, packedArgs, QByteArray(), showProgressInfo ); 01076 if (reload) 01077 job->addMetaData("cache", "reload"); 01078 return job; 01079 } 01080 01081 class PostErrorJob : public TransferJob 01082 { 01083 public: 01084 01085 PostErrorJob(int _error, const QString& url, const QByteArray &packedArgs, const QByteArray &postData, bool showProgressInfo) 01086 : TransferJob(KURL(), CMD_SPECIAL, packedArgs, postData, showProgressInfo) 01087 { 01088 m_error = _error; 01089 m_errorText = url; 01090 } 01091 01092 }; 01093 01094 TransferJob *KIO::http_post( const KURL& url, const QByteArray &postData, bool showProgressInfo ) 01095 { 01096 int _error = 0; 01097 01098 // filter out some malicious ports 01099 static const int bad_ports[] = { 01100 1, // tcpmux 01101 7, // echo 01102 9, // discard 01103 11, // systat 01104 13, // daytime 01105 15, // netstat 01106 17, // qotd 01107 19, // chargen 01108 20, // ftp-data 01109 21, // ftp-cntl 01110 22, // ssh 01111 23, // telnet 01112 25, // smtp 01113 37, // time 01114 42, // name 01115 43, // nicname 01116 53, // domain 01117 77, // priv-rjs 01118 79, // finger 01119 87, // ttylink 01120 95, // supdup 01121 101, // hostriame 01122 102, // iso-tsap 01123 103, // gppitnp 01124 104, // acr-nema 01125 109, // pop2 01126 110, // pop3 01127 111, // sunrpc 01128 113, // auth 01129 115, // sftp 01130 117, // uucp-path 01131 119, // nntp 01132 123, // NTP 01133 135, // loc-srv / epmap 01134 139, // netbios 01135 143, // imap2 01136 179, // BGP 01137 389, // ldap 01138 512, // print / exec 01139 513, // login 01140 514, // shell 01141 515, // printer 01142 526, // tempo 01143 530, // courier 01144 531, // Chat 01145 532, // netnews 01146 540, // uucp 01147 556, // remotefs 01148 587, // sendmail 01149 601, // 01150 989, // ftps data 01151 990, // ftps 01152 992, // telnets 01153 993, // imap/SSL 01154 995, // pop3/SSL 01155 1080, // SOCKS 01156 2049, // nfs 01157 4045, // lockd 01158 6000, // x11 01159 6667, // irc 01160 0}; 01161 for (int cnt=0; bad_ports[cnt]; ++cnt) 01162 if (url.port() == bad_ports[cnt]) 01163 { 01164 _error = KIO::ERR_POST_DENIED; 01165 break; 01166 } 01167 01168 if( _error ) 01169 { 01170 static bool override_loaded = false; 01171 static QValueList< int >* overriden_ports = NULL; 01172 if( !override_loaded ) 01173 { 01174 KConfig cfg( "kio_httprc", true ); 01175 overriden_ports = new QValueList< int >; 01176 *overriden_ports = cfg.readIntListEntry( "OverriddenPorts" ); 01177 override_loaded = true; 01178 } 01179 for( QValueList< int >::ConstIterator it = overriden_ports->begin(); 01180 it != overriden_ports->end(); 01181 ++it ) 01182 if( overriden_ports->contains( url.port())) 01183 _error = 0; 01184 } 01185 01186 // filter out non https? protocols 01187 if ((url.protocol() != "http") && (url.protocol() != "https" )) 01188 _error = KIO::ERR_POST_DENIED; 01189 01190 bool redirection = false; 01191 KURL _url(url); 01192 if (_url.path().isEmpty()) 01193 { 01194 redirection = true; 01195 _url.setPath("/"); 01196 } 01197 01198 if (!_error && !kapp->authorizeURLAction("open", KURL(), _url)) 01199 _error = KIO::ERR_ACCESS_DENIED; 01200 01201 // if request is not valid, return an invalid transfer job 01202 if (_error) 01203 { 01204 KIO_ARGS << (int)1 << url; 01205 TransferJob * job = new PostErrorJob(_error, url.prettyURL(), packedArgs, postData, showProgressInfo); 01206 return job; 01207 } 01208 01209 // Send http post command (1), decoded path and encoded query 01210 KIO_ARGS << (int)1 << _url; 01211 TransferJob * job = new TransferJob( _url, CMD_SPECIAL, 01212 packedArgs, postData, showProgressInfo ); 01213 01214 if (redirection) 01215 QTimer::singleShot(0, job, SLOT(slotPostRedirection()) ); 01216 01217 return job; 01218 } 01219 01220 // http post got redirected from http://host to http://host/ by TransferJob 01221 // We must do this redirection ourselves because redirections by the 01222 // slave change post jobs into get jobs. 01223 void TransferJob::slotPostRedirection() 01224 { 01225 kdDebug(7007) << "TransferJob::slotPostRedirection(" << m_url.prettyURL() << ")" << endl; 01226 // Tell the user about the new url. 01227 emit redirection(this, m_url); 01228 } 01229 01230 01231 TransferJob *KIO::put( const KURL& url, int permissions, 01232 bool overwrite, bool resume, bool showProgressInfo ) 01233 { 01234 KIO_ARGS << url << Q_INT8( overwrite ? 1 : 0 ) << Q_INT8( resume ? 1 : 0 ) << permissions; 01235 TransferJob * job = new TransferJob( url, CMD_PUT, packedArgs, QByteArray(), showProgressInfo ); 01236 return job; 01237 } 01238 01240 01241 MimetypeJob::MimetypeJob( const KURL& url, int command, 01242 const QByteArray &packedArgs, bool showProgressInfo ) 01243 : TransferJob(url, command, packedArgs, QByteArray(), showProgressInfo) 01244 { 01245 } 01246 01247 void MimetypeJob::start(Slave *slave) 01248 { 01249 TransferJob::start(slave); 01250 } 01251 01252 01253 void MimetypeJob::slotFinished( ) 01254 { 01255 //kdDebug(7007) << "MimetypeJob::slotFinished()" << endl; 01256 if ( m_error == KIO::ERR_IS_DIRECTORY ) 01257 { 01258 // It is in fact a directory. This happens when HTTP redirects to FTP. 01259 // Due to the "protocol doesn't support listing" code in KRun, we 01260 // assumed it was a file. 01261 kdDebug(7007) << "It is in fact a directory!" << endl; 01262 m_mimetype = QString::fromLatin1("inode/directory"); 01263 emit TransferJob::mimetype( this, m_mimetype ); 01264 m_error = 0; 01265 } 01266 if ( m_redirectionURL.isEmpty() || !m_redirectionURL.isValid() || m_error ) 01267 { 01268 // Return slave to the scheduler 01269 TransferJob::slotFinished(); 01270 } else { 01271 //kdDebug(7007) << "MimetypeJob: Redirection to " << m_redirectionURL.prettyURL() << endl; 01272 if (queryMetaData("permanent-redirect")=="true") 01273 emit permanentRedirection(this, m_url, m_redirectionURL); 01274 staticData.truncate(0); 01275 m_suspended = false; 01276 m_url = m_redirectionURL; 01277 m_redirectionURL = KURL(); 01278 m_packedArgs.truncate(0); 01279 QDataStream stream( m_packedArgs, IO_WriteOnly ); 01280 stream << m_url; 01281 01282 // Return slave to the scheduler 01283 slaveDone(); 01284 Scheduler::doJob(this); 01285 } 01286 } 01287 01288 MimetypeJob *KIO::mimetype(const KURL& url, bool showProgressInfo ) 01289 { 01290 KIO_ARGS << url; 01291 MimetypeJob * job = new MimetypeJob(url, CMD_MIMETYPE, packedArgs, showProgressInfo); 01292 if ( showProgressInfo ) 01293 Observer::self()->stating( job, url ); 01294 return job; 01295 } 01296 01298 01299 01300 class FileCopyJob::FileCopyJobPrivate 01301 { 01302 public: 01303 KIO::filesize_t m_sourceSize; 01304 SimpleJob *m_delJob; 01305 }; 01306 01307 /* 01308 * The FileCopyJob works according to the famous Bayern 01309 * 'Alternating Bitburger Protocol': we either drink a beer or we 01310 * we order a beer, but never both at the same time. 01311 * Tranlated to io-slaves: We alternate between receiving a block of data 01312 * and sending it away. 01313 */ 01314 FileCopyJob::FileCopyJob( const KURL& src, const KURL& dest, int permissions, 01315 bool move, bool overwrite, bool resume, bool showProgressInfo) 01316 : Job(showProgressInfo), m_src(src), m_dest(dest), 01317 m_permissions(permissions), m_move(move), m_overwrite(overwrite), m_resume(resume), 01318 m_totalSize(0) 01319 { 01320 if (showProgressInfo && !move) 01321 Observer::self()->slotCopying( this, src, dest ); 01322 else if (showProgressInfo && move) 01323 Observer::self()->slotMoving( this, src, dest ); 01324 01325 //kdDebug(7007) << "FileCopyJob::FileCopyJob()" << endl; 01326 m_moveJob = 0; 01327 m_copyJob = 0; 01328 m_getJob = 0; 01329 m_putJob = 0; 01330 d = new FileCopyJobPrivate; 01331 d->m_delJob = 0; 01332 d->m_sourceSize = (KIO::filesize_t) -1; 01333 QTimer::singleShot(0, this, SLOT(slotStart())); 01334 } 01335 01336 void FileCopyJob::slotStart() 01337 { 01338 if ((m_src.protocol() == m_dest.protocol()) && 01339 (m_src.host() == m_dest.host()) && 01340 (m_src.port() == m_dest.port()) && 01341 (m_src.user() == m_dest.user()) && 01342 (m_src.pass() == m_dest.pass()) && 01343 !m_src.hasSubURL() && !m_dest.hasSubURL()) 01344 { 01345 if (m_move) 01346 { 01347 m_moveJob = KIO::rename( m_src, m_dest, m_overwrite ); 01348 addSubjob( m_moveJob ); 01349 connectSubjob( m_moveJob ); 01350 } 01351 else 01352 { 01353 startCopyJob(); 01354 } 01355 } 01356 else 01357 { 01358 if (!m_move && 01359 (m_src.isLocalFile() && KProtocolInfo::canCopyFromFile(m_dest)) 01360 ) 01361 { 01362 startCopyJob(m_dest); 01363 } 01364 else if (!m_move && 01365 (m_dest.isLocalFile() && KProtocolInfo::canCopyToFile(m_src)) 01366 ) 01367 { 01368 startCopyJob(m_src); 01369 } 01370 else 01371 { 01372 startDataPump(); 01373 } 01374 } 01375 } 01376 01377 FileCopyJob::~FileCopyJob() 01378 { 01379 delete d; 01380 } 01381 01382 void FileCopyJob::setSourceSize( off_t size ) 01383 { 01384 d->m_sourceSize = size; 01385 m_totalSize = size; 01386 } 01387 01388 void FileCopyJob::setSourceSize64( KIO::filesize_t size ) 01389 { 01390 d->m_sourceSize = size; 01391 m_totalSize = size; 01392 } 01393 01394 void FileCopyJob::startCopyJob() 01395 { 01396 startCopyJob(m_src); 01397 } 01398 01399 void FileCopyJob::startCopyJob(const KURL &slave_url) 01400 { 01401 //kdDebug(7007) << "FileCopyJob::startCopyJob()" << endl; 01402 KIO_ARGS << m_src << m_dest << m_permissions << (Q_INT8) m_overwrite; 01403 m_copyJob = new SimpleJob(slave_url, CMD_COPY, packedArgs, false); 01404 addSubjob( m_copyJob ); 01405 connectSubjob( m_copyJob ); 01406 } 01407 01408 void FileCopyJob::connectSubjob( SimpleJob * job ) 01409 { 01410 connect( job, SIGNAL(totalSize( KIO::Job*, KIO::filesize_t )), 01411 this, SLOT( slotTotalSize(KIO::Job*, KIO::filesize_t)) ); 01412 01413 connect( job, SIGNAL(processedSize( KIO::Job*, KIO::filesize_t )), 01414 this, SLOT( slotProcessedSize(KIO::Job*, KIO::filesize_t)) ); 01415 01416 connect( job, SIGNAL(percent( KIO::Job*, unsigned long )), 01417 this, SLOT( slotPercent(KIO::Job*, unsigned long)) ); 01418 01419 } 01420 01421 void FileCopyJob::slotProcessedSize( KIO::Job *, KIO::filesize_t size ) 01422 { 01423 setProcessedSize(size); 01424 emit processedSize( this, size ); 01425 if ( size > m_totalSize ) { 01426 slotTotalSize( this, size ); // safety 01427 } 01428 emitPercent( size, m_totalSize ); 01429 } 01430 01431 void FileCopyJob::slotTotalSize( KIO::Job*, KIO::filesize_t size ) 01432 { 01433 m_totalSize = size; 01434 emit totalSize( this, m_totalSize ); 01435 } 01436 01437 void FileCopyJob::slotPercent( KIO::Job*, unsigned long pct ) 01438 { 01439 if ( pct > m_percent ) 01440 { 01441 m_percent = pct; 01442 emit percent( this, m_percent ); 01443 } 01444 } 01445 01446 void FileCopyJob::startDataPump() 01447 { 01448 //kdDebug(7007) << "FileCopyJob::startDataPump()" << endl; 01449 01450 m_canResume = false; 01451 m_resumeAnswerSent = false; 01452 m_getJob = 0L; // for now 01453 m_putJob = put( m_dest, m_permissions, m_overwrite, m_resume, false /* no GUI */); 01454 //kdDebug(7007) << "FileCopyJob: m_putJob = " << m_putJob << " m_dest=" << m_dest.prettyURL() << endl; 01455 01456 // The first thing the put job will tell us is whether we can 01457 // resume or not (this is always emitted) 01458 connect( m_putJob, SIGNAL(canResume(KIO::Job *, KIO::filesize_t)), 01459 SLOT( slotCanResume(KIO::Job *, KIO::filesize_t))); 01460 connect( m_putJob, SIGNAL(dataReq(KIO::Job *, QByteArray&)), 01461 SLOT( slotDataReq(KIO::Job *, QByteArray&))); 01462 addSubjob( m_putJob ); 01463 } 01464 01465 void FileCopyJob::slotCanResume( KIO::Job* job, KIO::filesize_t offset ) 01466 { 01467 if ( job == m_putJob ) 01468 { 01469 //kdDebug(7007) << "FileCopyJob::slotCanResume from PUT job. offset=" << KIO::number(offset) << endl; 01470 if (offset) 01471 { 01472 RenameDlg_Result res = R_RESUME; 01473 01474 if (!KProtocolManager::autoResume()) 01475 { 01476 QString newPath; 01477 KIO::Job* job = ( !m_progressId && parentJob() ) ? parentJob() : this; 01478 // Ask confirmation about resuming previous transfer 01479 res = Observer::self()->open_RenameDlg( 01480 job, i18n("File Already Exists"), 01481 m_src.prettyURL(0, KURL::StripFileProtocol), 01482 m_dest.prettyURL(0, KURL::StripFileProtocol), 01483 (RenameDlg_Mode) (M_OVERWRITE | M_RESUME | M_NORENAME), newPath, 01484 d->m_sourceSize, offset ); 01485 } 01486 01487 if ( res == R_OVERWRITE ) 01488 offset = 0; 01489 else if ( res == R_CANCEL ) 01490 { 01491 m_putJob->kill(true); 01492 m_error = ERR_USER_CANCELED; 01493 emitResult(); 01494 return; 01495 } 01496 } 01497 else 01498 m_resumeAnswerSent = true; // No need for an answer 01499 01500 m_getJob = get( m_src, false, false /* no GUI */ ); 01501 //kdDebug(7007) << "FileCopyJob: m_getJob = " << m_getJob << endl; 01502 m_getJob->addMetaData( "errorPage", "false" ); 01503 m_getJob->addMetaData( "AllowCompressedPage", "false" ); 01504 // Set size in subjob. This helps if the slave doesn't emit totalSize. 01505 if ( d->m_sourceSize != (KIO::filesize_t)-1 ) 01506 m_getJob->slotTotalSize( d->m_sourceSize ); 01507 if (offset) 01508 { 01509 //kdDebug(7007) << "Setting metadata for resume to " << (unsigned long) offset << endl; 01510 m_getJob->addMetaData( "resume", KIO::number(offset) ); 01511 01512 // Might or might not get emitted 01513 connect( m_getJob, SIGNAL(canResume(KIO::Job *, KIO::filesize_t)), 01514 SLOT( slotCanResume(KIO::Job *, KIO::filesize_t))); 01515 } 01516 m_putJob->slave()->setOffset( offset ); 01517 01518 m_putJob->suspend(); 01519 addSubjob( m_getJob ); 01520 connectSubjob( m_getJob ); // Progress info depends on get 01521 m_getJob->resume(); // Order a beer 01522 01523 connect( m_getJob, SIGNAL(data(KIO::Job *, const QByteArray&)), 01524 SLOT( slotData(KIO::Job *, const QByteArray&))); 01525 } 01526 else if ( job == m_getJob ) 01527 { 01528 // Cool, the get job said ok, we can resume 01529 m_canResume = true; 01530 //kdDebug(7007) << "FileCopyJob::slotCanResume from the GET job -> we can resume" << endl; 01531 01532 m_getJob->slave()->setOffset( m_putJob->slave()->offset() ); 01533 } 01534 else 01535 kdWarning(7007) << "FileCopyJob::slotCanResume from unknown job=" << job 01536 << " m_getJob=" << m_getJob << " m_putJob=" << m_putJob << endl; 01537 } 01538 01539 void FileCopyJob::slotData( KIO::Job * , const QByteArray &data) 01540 { 01541 //kdDebug(7007) << "FileCopyJob::slotData" << endl; 01542 //kdDebug(7007) << " data size : " << data.size() << endl; 01543 assert(m_putJob); 01544 m_getJob->suspend(); 01545 m_putJob->resume(); // Drink the beer 01546 m_buffer = data; 01547 01548 // On the first set of data incoming, we tell the "put" slave about our 01549 // decision about resuming 01550 if (!m_resumeAnswerSent) 01551 { 01552 m_resumeAnswerSent = true; 01553 //kdDebug(7007) << "FileCopyJob::slotData (first time) -> send resume answer " << m_canResume << endl; 01554 m_putJob->slave()->sendResumeAnswer( m_canResume ); 01555 } 01556 } 01557 01558 void FileCopyJob::slotDataReq( KIO::Job * , QByteArray &data) 01559 { 01560 //kdDebug(7007) << "FileCopyJob::slotDataReq" << endl; 01561 if (!m_resumeAnswerSent && !m_getJob) 01562 { 01563 // This can't happen (except as a migration bug on 12/10/2000) 01564 m_error = ERR_INTERNAL; 01565 m_errorText = "'Put' job didn't send canResume or 'Get' job didn't send data!"; 01566 m_putJob->kill(true); 01567 emitResult(); 01568 return; 01569 } 01570 if (m_getJob) 01571 { 01572 m_getJob->resume(); // Order more beer 01573 m_putJob->suspend(); 01574 } 01575 data = m_buffer; 01576 m_buffer = QByteArray(); 01577 } 01578 01579 void FileCopyJob::slotResult( KIO::Job *job) 01580 { 01581 //kdDebug(7007) << "FileCopyJob this=" << this << " ::slotResult(" << job << ")" << endl; 01582 // Did job have an error ? 01583 if ( job->error() ) 01584 { 01585 if ((job == m_moveJob) && (job->error() == ERR_UNSUPPORTED_ACTION)) 01586 { 01587 m_moveJob = 0; 01588 startCopyJob(); 01589 removeSubjob(job); 01590 return; 01591 } 01592 else if ((job == m_copyJob) && (job->error() == ERR_UNSUPPORTED_ACTION)) 01593 { 01594 m_copyJob = 0; 01595 startDataPump(); 01596 removeSubjob(job); 01597 return; 01598 } 01599 else if (job == m_getJob) 01600 { 01601 m_getJob = 0L; 01602 if (m_putJob) 01603 m_putJob->kill(true); 01604 } 01605 else if (job == m_putJob) 01606 { 01607 m_putJob = 0L; 01608 if (m_getJob) 01609 m_getJob->kill(true); 01610 } 01611 m_error = job->error(); 01612 m_errorText = job->errorText(); 01613 emitResult(); 01614 return; 01615 } 01616 01617 if (job == m_moveJob) 01618 { 01619 m_moveJob = 0; // Finished 01620 } 01621 01622 if (job == m_copyJob) 01623 { 01624 m_copyJob = 0; 01625 if (m_move) 01626 { 01627 d->m_delJob = file_delete( m_src, false/*no GUI*/ ); // Delete source 01628 addSubjob(d->m_delJob); 01629 } 01630 } 01631 01632 if (job == m_getJob) 01633 { 01634 m_getJob = 0; // No action required 01635 if (m_putJob) 01636 m_putJob->resume(); 01637 } 01638 01639 if (job == m_putJob) 01640 { 01641 //kdDebug(7007) << "FileCopyJob: m_putJob finished " << endl; 01642 m_putJob = 0; 01643 if (m_getJob) 01644 { 01645 kdWarning(7007) << "WARNING ! Get still going on..." << endl; 01646 m_getJob->resume(); 01647 } 01648 if (m_move) 01649 { 01650 d->m_delJob = file_delete( m_src, false/*no GUI*/ ); // Delete source 01651 addSubjob(d->m_delJob); 01652 } 01653 } 01654 01655 if (job == d->m_delJob) 01656 { 01657 d->m_delJob = 0; // Finished 01658 } 01659 removeSubjob(job); 01660 } 01661 01662 FileCopyJob *KIO::file_copy( const KURL& src, const KURL& dest, int permissions, 01663 bool overwrite, bool resume, bool showProgressInfo) 01664 { 01665 return new FileCopyJob( src, dest, permissions, false, overwrite, resume, showProgressInfo ); 01666 } 01667 01668 FileCopyJob *KIO::file_move( const KURL& src, const KURL& dest, int permissions, 01669 bool overwrite, bool resume, bool showProgressInfo) 01670 { 01671 return new FileCopyJob( src, dest, permissions, true, overwrite, resume, showProgressInfo ); 01672 } 01673 01674 SimpleJob *KIO::file_delete( const KURL& src, bool showProgressInfo) 01675 { 01676 KIO_ARGS << src << Q_INT8(true); // isFile 01677 return new SimpleJob(src, CMD_DEL, packedArgs, showProgressInfo ); 01678 } 01679 01681 01682 // KDE 4: Make it const QString & _prefix 01683 ListJob::ListJob(const KURL& u, bool showProgressInfo, bool _recursive, QString _prefix, bool _includeHidden) : 01684 SimpleJob(u, CMD_LISTDIR, QByteArray(), showProgressInfo), 01685 recursive(_recursive), includeHidden(_includeHidden), prefix(_prefix), m_processedEntries(0) 01686 { 01687 // We couldn't set the args when calling the parent constructor, 01688 // so do it now. 01689 QDataStream stream( m_packedArgs, IO_WriteOnly ); 01690 stream << u; 01691 } 01692 01693 void ListJob::slotListEntries( const KIO::UDSEntryList& list ) 01694 { 01695 // Emit progress info (takes care of emit processedSize and percent) 01696 m_processedEntries += list.count(); 01697 slotProcessedSize( m_processedEntries ); 01698 01699 if (recursive) { 01700 UDSEntryListConstIterator it = list.begin(); 01701 UDSEntryListConstIterator end = list.end(); 01702 01703 for (; it != end; ++it) { 01704 bool isDir = false; 01705 bool isLink = false; 01706 QString filename; 01707 01708 UDSEntry::ConstIterator it2 = (*it).begin(); 01709 UDSEntry::ConstIterator end2 = (*it).end(); 01710 for( ; it2 != end2; it2++ ) { 01711 switch( (*it2).m_uds ) { 01712 case UDS_FILE_TYPE: 01713 isDir = S_ISDIR((*it2).m_long); 01714 break; 01715 case UDS_NAME: 01716 filename = (*it2).m_str; 01717 break; 01718 case UDS_LINK_DEST: 01719 // This is a link !!! Don't follow ! 01720 isLink = !(*it2).m_str.isEmpty(); 01721 break; 01722 default: 01723 break; 01724 } 01725 } 01726 if (isDir && !isLink) { 01727 // skip hidden dirs when listing if requested 01728 if (filename != ".." && filename != "." && (includeHidden || filename[0] != '.')) { 01729 KURL newone = url(); 01730 newone.addPath(filename); 01731 ListJob *job = new ListJob(newone, 01732 false /*no progress info!*/, 01733 true /*recursive*/, 01734 prefix + filename + "/", 01735 includeHidden); 01736 Scheduler::scheduleJob(job); 01737 connect(job, SIGNAL(entries( KIO::Job *, 01738 const KIO::UDSEntryList& )), 01739 SLOT( gotEntries( KIO::Job*, 01740 const KIO::UDSEntryList& ))); 01741 addSubjob(job); 01742 } 01743 } 01744 } 01745 } 01746 01747 // Not recursive, or top-level of recursive listing : return now (send . and .. as well) 01748 // exclusion of hidden files also requires the full sweep, but the case for full-listing 01749 // a single dir is probably common enough to justify the shortcut 01750 if (prefix.isNull() && includeHidden) { 01751 emit entries(this, list); 01752 } else { 01753 // cull the unwanted hidden dirs and/or parent dir references from the listing, then emit that 01754 UDSEntryList newlist; 01755 01756 UDSEntryListConstIterator it = list.begin(); 01757 UDSEntryListConstIterator end = list.end(); 01758 for (; it != end; ++it) { 01759 01760 UDSEntry newone = *it; 01761 UDSEntry::Iterator it2 = newone.begin(); 01762 QString filename; 01763 for( ; it2 != newone.end(); it2++ ) { 01764 if ((*it2).m_uds == UDS_NAME) { 01765 filename = (*it2).m_str; 01766 (*it2).m_str = prefix + filename; 01767 } 01768 } 01769 // Avoid returning entries like subdir/. and subdir/.., but include . and .. for 01770 // the the toplevel dir, and skip hidden files/dirs if that was requested 01771 if ( (prefix.isNull() || (filename != ".." && filename != ".") ) 01772 && (includeHidden || (filename[0] != '.') ) ) 01773 newlist.append(newone); 01774 } 01775 01776 emit entries(this, newlist); 01777 } 01778 } 01779 01780 void ListJob::gotEntries(KIO::Job *, const KIO::UDSEntryList& list ) 01781 { 01782 // Forward entries received by subjob - faking we received them ourselves 01783 emit entries(this, list); 01784 } 01785 01786 void ListJob::slotResult( KIO::Job * job ) 01787 { 01788 // If we can't list a subdir, the result is still ok 01789 // This is why we override Job::slotResult() - to skip error checking 01790 removeSubjob( job ); 01791 } 01792 01793 void ListJob::slotRedirection( const KURL & url ) 01794 { 01795 if (!kapp->authorizeURLAction("redirect", m_url, url)) 01796 { 01797 kdWarning(7007) << "ListJob: Redirection from " << m_url.prettyURL() << " to " << url.prettyURL() << " REJECTED!" << endl; 01798 return; 01799 } 01800 m_redirectionURL = url; // We'll remember that when the job finishes 01801 if (m_url.hasUser() && !url.hasUser() && (m_url.host().lower() == url.host().lower())) 01802 m_redirectionURL.setUser(m_url.user()); // Preserve user 01803 emit redirection( this, m_redirectionURL ); 01804 } 01805 01806 void ListJob::slotFinished() 01807 { 01808 if ( m_redirectionURL.isEmpty() || !m_redirectionURL.isValid() || m_error ) 01809 { 01810 // Return slave to the scheduler 01811 SimpleJob::slotFinished(); 01812 } else { 01813 //kdDebug(7007) << "ListJob: Redirection to " << m_redirectionURL.prettyURL() << endl; 01814 if (queryMetaData("permanent-redirect")=="true") 01815 emit permanentRedirection(this, m_url, m_redirectionURL); 01816 m_url = m_redirectionURL; 01817 m_redirectionURL = KURL(); 01818 m_packedArgs.truncate(0); 01819 QDataStream stream( m_packedArgs, IO_WriteOnly ); 01820 stream << m_url; 01821 01822 // Return slave to the scheduler 01823 slaveDone(); 01824 Scheduler::doJob(this); 01825 } 01826 } 01827 01828 void ListJob::slotMetaData( const KIO::MetaData &_metaData) { 01829 SimpleJob::slotMetaData(_metaData); 01830 storeSSLSessionFromJob(m_redirectionURL); 01831 } 01832 01833 ListJob *KIO::listDir( const KURL& url, bool showProgressInfo, bool includeHidden ) 01834 { 01835 ListJob * job = new ListJob(url, showProgressInfo,false,QString::null,includeHidden); 01836 return job; 01837 } 01838 01839 ListJob *KIO::listRecursive( const KURL& url, bool showProgressInfo, bool includeHidden ) 01840 { 01841 ListJob * job = new ListJob(url, showProgressInfo, true,QString::null,includeHidden); 01842 return job; 01843 } 01844 01845 void ListJob::setUnrestricted(bool unrestricted) 01846 { 01847 if (unrestricted) 01848 extraFlags() |= EF_ListJobUnrestricted; 01849 else 01850 extraFlags() &= ~EF_ListJobUnrestricted; 01851 } 01852 01853 void ListJob::start(Slave *slave) 01854 { 01855 if (!kapp->authorizeURLAction("list", m_url, m_url) && !(extraFlags() & EF_ListJobUnrestricted)) 01856 { 01857 m_error = ERR_ACCESS_DENIED; 01858 m_errorText = m_url.url(); 01859 QTimer::singleShot(0, this, SLOT(slotFinished()) ); 01860 return; 01861 } 01862 connect( slave, SIGNAL( listEntries( const KIO::UDSEntryList& )), 01863 SLOT( slotListEntries( const KIO::UDSEntryList& ))); 01864 connect( slave, SIGNAL( totalSize( KIO::filesize_t ) ), 01865 SLOT( slotTotalSize( KIO::filesize_t ) ) ); 01866 connect( slave, SIGNAL( redirection(const KURL &) ), 01867 SLOT( slotRedirection(const KURL &) ) ); 01868 01869 SimpleJob::start(slave); 01870 } 01871 01872 class CopyJob::CopyJobPrivate 01873 { 01874 public: 01875 CopyJobPrivate() { 01876 m_defaultPermissions = false; 01877 } 01878 // This is the dest URL that was initially given to CopyJob 01879 // It is copied into m_dest, which can be changed for a given src URL 01880 // (when using the RENAME dialog in slotResult), 01881 // and which will be reset for the next src URL. 01882 KURL m_globalDest; 01883 // The state info about that global dest 01884 CopyJob::DestinationState m_globalDestinationState; 01885 // See setDefaultPermissions 01886 bool m_defaultPermissions; 01887 }; 01888 01889 CopyJob::CopyJob( const KURL::List& src, const KURL& dest, CopyMode mode, bool asMethod, bool showProgressInfo ) 01890 : Job(showProgressInfo), m_mode(mode), m_asMethod(asMethod), 01891 destinationState(DEST_NOT_STATED), state(STATE_STATING), 01892 m_totalSize(0), m_processedSize(0), m_fileProcessedSize(0), 01893 m_processedFiles(0), m_processedDirs(0), 01894 m_srcList(src), m_currentStatSrc(m_srcList.begin()), 01895 m_bCurrentOperationIsLink(false), m_bSingleFileCopy(false), m_bOnlyRenames(mode==Move), 01896 m_dest(dest), m_bAutoSkip( false ), m_bOverwriteAll( false ), 01897 m_conflictError(0), m_reportTimer(0) 01898 { 01899 d = new CopyJobPrivate; 01900 d->m_globalDest = dest; 01901 d->m_globalDestinationState = destinationState; 01902 01903 if ( showProgressInfo ) { 01904 connect( this, SIGNAL( totalFiles( KIO::Job*, unsigned long ) ), 01905 Observer::self(), SLOT( slotTotalFiles( KIO::Job*, unsigned long ) ) ); 01906 01907 connect( this, SIGNAL( totalDirs( KIO::Job*, unsigned long ) ), 01908 Observer::self(), SLOT( slotTotalDirs( KIO::Job*, unsigned long ) ) ); 01909 } 01910 QTimer::singleShot(0, this, SLOT(slotStart())); 01924 } 01925 01926 CopyJob::~CopyJob() 01927 { 01928 delete d; 01929 } 01930 01931 void CopyJob::slotStart() 01932 { 01938 m_reportTimer = new QTimer(this); 01939 01940 connect(m_reportTimer,SIGNAL(timeout()),this,SLOT(slotReport())); 01941 m_reportTimer->start(REPORT_TIMEOUT,false); 01942 01943 // Stat the dest 01944 KIO::Job * job = KIO::stat( m_dest, false, 2, false ); 01945 //kdDebug(7007) << "CopyJob:stating the dest " << m_dest.prettyURL() << endl; 01946 addSubjob(job); 01947 } 01948 01949 void CopyJob::slotResultStating( Job *job ) 01950 { 01951 //kdDebug(7007) << "CopyJob::slotResultStating" << endl; 01952 // Was there an error while stating the src ? 01953 if (job->error() && destinationState != DEST_NOT_STATED ) 01954 { 01955 KURL srcurl = ((SimpleJob*)job)->url(); 01956 if ( !srcurl.isLocalFile() ) 01957 { 01958 // Probably : src doesn't exist. Well, over some protocols (e.g. FTP) 01959 // this info isn't really reliable (thanks to MS FTP servers). 01960 // We'll assume a file, and try to download anyway. 01961 kdDebug(7007) << "Error while stating source. Activating hack" << endl; 01962 subjobs.remove( job ); 01963 assert ( subjobs.isEmpty() ); // We should have only one job at a time ... 01964 struct CopyInfo info; 01965 info.permissions = (mode_t) -1; 01966 info.mtime = (time_t) -1; 01967 info.ctime = (time_t) -1; 01968 info.size = (KIO::filesize_t)-1; 01969 info.uSource = srcurl; 01970 info.uDest = m_dest; 01971 // Append filename or dirname to destination URL, if allowed 01972 if ( destinationState == DEST_IS_DIR && !m_asMethod ) 01973 info.uDest.addPath( srcurl.fileName() ); 01974 01975 files.append( info ); 01976 statNextSrc(); 01977 return; 01978 } 01979 // Local file. If stat fails, the file definitely doesn't exist. 01980 Job::slotResult( job ); // will set the error and emit result(this) 01981 return; 01982 } 01983 01984 // Is it a file or a dir ? 01985 UDSEntry entry = ((StatJob*)job)->statResult(); 01986 bool bDir = false; 01987 bool bLink = false; 01988 UDSEntry::ConstIterator it2 = entry.begin(); 01989 for( ; it2 != entry.end(); it2++ ) { 01990 if ( ((*it2).m_uds) == UDS_FILE_TYPE ) 01991 bDir = S_ISDIR( (mode_t)(*it2).m_long ); 01992 else if ( ((*it2).m_uds) == UDS_LINK_DEST ) 01993 bLink = !((*it2).m_str.isEmpty()); 01994 } 01995 01996 if ( destinationState == DEST_NOT_STATED ) 01997 // we were stating the dest 01998 { 01999 if (job->error()) 02000 destinationState = DEST_DOESNT_EXIST; 02001 else { 02002 // Treat symlinks to dirs as dirs here, so no test on bLink 02003 destinationState = bDir ? DEST_IS_DIR : DEST_IS_FILE; 02004 //kdDebug(7007) << "CopyJob::slotResultStating dest is dir:" << bDir << endl; 02005 } 02006 if ( m_dest == d->m_globalDest ) 02007 d->m_globalDestinationState = destinationState; 02008 subjobs.remove( job ); 02009 assert ( subjobs.isEmpty() ); 02010 02011 // After knowing what the dest is, we can start stat'ing the first src. 02012 statCurrentSrc(); 02013 return; 02014 } 02015 // We were stating the current source URL 02016 m_currentDest = m_dest; // used by slotEntries 02017 // Create a dummy list with it, for slotEntries 02018 UDSEntryList lst; 02019 lst.append(entry); 02020 02021 // There 6 cases, and all end up calling slotEntries(job, lst) first : 02022 // 1 - src is a dir, destination is a directory, 02023 // slotEntries will append the source-dir-name to the destination 02024 // 2 - src is a dir, destination is a file, ERROR (done later on) 02025 // 3 - src is a dir, destination doesn't exist, then it's the destination dirname, 02026 // so slotEntries will use it as destination. 02027 02028 // 4 - src is a file, destination is a directory, 02029 // slotEntries will append the filename to the destination. 02030 // 5 - src is a file, destination is a file, m_dest is the exact destination name 02031 // 6 - src is a file, destination doesn't exist, m_dest is the exact destination name 02032 // Tell slotEntries not to alter the src url 02033 m_bCurrentSrcIsDir = false; 02034 slotEntries(job, lst); 02035 02036 KURL srcurl = ((SimpleJob*)job)->url(); 02037 02038 subjobs.remove( job ); 02039 assert ( subjobs.isEmpty() ); // We should have only one job at a time ... 02040 02041 if ( bDir 02042 && !bLink // treat symlinks as files (no recursion) 02043 && m_mode != Link ) // No recursion in Link mode either. 02044 { 02045 //kdDebug(7007) << " Source is a directory " << endl; 02046 02047 m_bCurrentSrcIsDir = true; // used by slotEntries 02048 if ( destinationState == DEST_IS_DIR ) // (case 1) 02049 { 02050 if ( !m_asMethod ) 02051 // Use <desturl>/<directory_copied> as destination, from now on 02052 m_currentDest.addPath( srcurl.fileName() ); 02053 } 02054 else if ( destinationState == DEST_IS_FILE ) // (case 2) 02055 { 02056 m_error = ERR_IS_FILE; 02057 m_errorText = m_dest.prettyURL(); 02058 emitResult(); 02059 return; 02060 } 02061 else // (case 3) 02062 { 02063 // otherwise dest is new name for toplevel dir 02064 // so the destination exists, in fact, from now on. 02065 // (This even works with other src urls in the list, since the 02066 // dir has effectively been created) 02067 destinationState = DEST_IS_DIR; 02068 if ( m_dest == d->m_globalDest ) 02069 d->m_globalDestinationState = destinationState; 02070 } 02071 02072 startListing( srcurl ); 02073 } 02074 else 02075 { 02076 //kdDebug(7007) << " Source is a file (or a symlink), or we are linking -> no recursive listing " << endl; 02077 statNextSrc(); 02078 } 02079 } 02080 02081 void CopyJob::slotReport() 02082 { 02083 // If showProgressInfo was set, m_progressId is > 0. 02084 Observer * observer = m_progressId ? Observer::self() : 0L; 02085 switch (state) { 02086 case STATE_COPYING_FILES: 02087 emit processedFiles( this, m_processedFiles ); 02088 if (observer) observer->slotProcessedFiles(this,m_processedFiles); 02089 if (m_mode==Move) 02090 { 02091 if (observer) observer->slotMoving( this, m_currentSrcURL,m_currentDestURL); 02092 emit moving( this, m_currentSrcURL, m_currentDestURL); 02093 } 02094 else if (m_mode==Link) 02095 { 02096 if (observer) observer->slotCopying( this, m_currentSrcURL, m_currentDestURL ); // we don't have a slotLinking 02097 emit linking( this, m_currentSrcURL.path(), m_currentDestURL ); 02098 } 02099 else 02100 { 02101 if (observer) observer->slotCopying( this, m_currentSrcURL, m_currentDestURL ); 02102 emit copying( this, m_currentSrcURL, m_currentDestURL ); 02103 }; 02104 break; 02105 02106 case STATE_CREATING_DIRS: 02107 if (observer) { 02108 observer->slotProcessedDirs( this, m_processedDirs ); 02109 observer->slotCreatingDir( this,m_currentDestURL); 02110 } 02111 emit processedDirs( this, m_processedDirs ); 02112 emit creatingDir( this, m_currentDestURL ); 02113 break; 02114 02115 case STATE_STATING: 02116 case STATE_LISTING: 02117 if (observer) observer->slotCopying( this, m_currentSrcURL, m_currentDestURL ); 02118 emit totalSize( this, m_totalSize ); 02119 emit totalFiles( this, files.count() ); 02120 emit totalDirs( this, dirs.count() ); 02121 if (!dirs.isEmpty()) 02122 emit aboutToCreate( this, dirs ); 02123 if (!files.isEmpty()) 02124 emit aboutToCreate( this, files ); 02125 break; 02126 02127 default: 02128 break; 02129 } 02130 } 02131 02132 void CopyJob::slotEntries(KIO::Job* job, const UDSEntryList& list) 02133 { 02134 UDSEntryListConstIterator it = list.begin(); 02135 UDSEntryListConstIterator end = list.end(); 02136 for (; it != end; ++it) { 02137 UDSEntry::ConstIterator it2 = (*it).begin(); 02138 struct CopyInfo info; 02139 info.permissions = -1; 02140 info.mtime = (time_t) -1; 02141 info.ctime = (time_t) -1; 02142 info.size = (KIO::filesize_t)-1; 02143 QString relName; 02144 bool isDir = false; 02145 for( ; it2 != (*it).end(); it2++ ) { 02146 switch ((*it2).m_uds) { 02147 case UDS_FILE_TYPE: 02148 //info.type = (mode_t)((*it2).m_long); 02149 isDir = S_ISDIR( (mode_t)((*it2).m_long) ); 02150 break; 02151 case UDS_NAME: 02152 relName = (*it2).m_str; 02153 break; 02154 case UDS_LINK_DEST: 02155 info.linkDest = (*it2).m_str; 02156 break; 02157 case UDS_ACCESS: 02158 info.permissions = ((*it2).m_long); 02159 break; 02160 case UDS_SIZE: 02161 info.size = (KIO::filesize_t)((*it2).m_long); 02162 m_totalSize += info.size; 02163 break; 02164 case UDS_MODIFICATION_TIME: 02165 info.mtime = (time_t)((*it2).m_long); 02166 break; 02167 case UDS_CREATION_TIME: 02168 info.ctime = (time_t)((*it2).m_long); 02169 default: 02170 break; 02171 } 02172 } 02173 if (relName != ".." && relName != ".") 02174 { 02175 //kdDebug(7007) << "CopyJob::slotEntries '" << relName << "'" << endl; 02176 info.uSource = ((SimpleJob *)job)->url(); 02177 if ( m_bCurrentSrcIsDir ) // Only if src is a directory. Otherwise uSource is fine as is 02178 info.uSource.addPath( relName ); 02179 info.uDest = m_currentDest; 02180 //kdDebug(7007) << " uSource=" << info.uSource << " uDest(1)=" << info.uDest.prettyURL() << endl; 02181 // Append filename or dirname to destination URL, if allowed 02182 if ( destinationState == DEST_IS_DIR && 02183 // "copy/move as <foo>" means 'foo' is the dest for the base srcurl 02184 // (passed here during stating) but not its children (during listing) 02185 ( ! ( m_asMethod && state == STATE_STATING ) ) ) 02186 { 02187 // Here we _really_ have to add some filename to the dest. 02188 // Otherwise, we end up with e.g. dest=..../Desktop/ itself. 02189 // (This can happen when dropping a link to a webpage with no path) 02190 if ( relName.isEmpty() ) 02191 info.uDest.addPath( KIO::encodeFileName( info.uSource.prettyURL() ) ); 02192 else 02193 info.uDest.addPath( relName ); 02194 } 02195 //kdDebug(7007) << " uDest(2)=" << info.uDest.prettyURL() << endl; 02196 //kdDebug(7007) << " " << info.uSource << " -> " << info.uDest << endl; 02197 if ( info.linkDest.isEmpty() && (isDir /*S_ISDIR(info.type)*/) && m_mode != Link ) // Dir 02198 { 02199 dirs.append( info ); // Directories 02200 if (m_mode == Move) 02201 dirsToRemove.append( info.uSource ); 02202 } 02203 else { 02204 files.append( info ); // Files and any symlinks 02205 } 02206 } 02207 } 02208 } 02209 02210 void CopyJob::statNextSrc() 02211 { 02212 m_dest = d->m_globalDest; 02213 destinationState = d->m_globalDestinationState; 02214 ++m_currentStatSrc; 02215 statCurrentSrc(); 02216 } 02217 02218 void CopyJob::statCurrentSrc() 02219 { 02220 if ( m_currentStatSrc != m_srcList.end() ) 02221 { 02222 m_currentSrcURL = (*m_currentStatSrc); 02223 if ( m_mode == Link ) 02224 { 02225 // Skip the "stating the source" stage, we don't need it for linking 02226 m_currentDest = m_dest; 02227 struct CopyInfo info; 02228 info.permissions = -1; 02229 info.mtime = (time_t) -1; 02230 info.ctime = (time_t) -1; 02231 info.size = (KIO::filesize_t)-1; 02232 info.uSource = m_currentSrcURL; 02233 info.uDest = m_currentDest; 02234 // Append filename or dirname to destination URL, if allowed 02235 if ( destinationState == DEST_IS_DIR && !m_asMethod ) 02236 { 02237 if ( 02238 (m_currentSrcURL.protocol() == info.uDest.protocol()) && 02239 (m_currentSrcURL.host() == info.uDest.host()) && 02240 (m_currentSrcURL.port() == info.uDest.port()) && 02241 (m_currentSrcURL.user() == info.uDest.user()) && 02242 (m_currentSrcURL.pass() == info.uDest.pass()) ) 02243 { 02244 // This is the case of creating a real symlink 02245 info.uDest.addPath( m_currentSrcURL.fileName() ); 02246 } 02247 else 02248 { 02249 // Different protocols, we'll create a .desktop file 02250 // We have to change the extension anyway, so while we're at it, 02251 // name the file like the URL 02252 info.uDest.addPath( KIO::encodeFileName( m_currentSrcURL.prettyURL() )+".desktop" ); 02253 } 02254 } 02255 files.append( info ); // Files and any symlinks 02256 statNextSrc(); // we could use a loop instead of a recursive call :) 02257 } 02258 // If moving, before going for the full stat+[list+]copy+del thing, try to rename 02259 else if ( m_mode == Move && 02260 (m_currentSrcURL.protocol() == m_dest.protocol()) && 02261 (m_currentSrcURL.host() == m_dest.host()) && 02262 (m_currentSrcURL.port() == m_dest.port()) && 02263 (m_currentSrcURL.user() == m_dest.user()) && 02264 (m_currentSrcURL.pass() == m_dest.pass()) ) 02265 { 02266 KURL dest = m_dest; 02267 // Append filename or dirname to destination URL, if allowed 02268 if ( destinationState == DEST_IS_DIR && !m_asMethod ) 02269 dest.addPath( m_currentSrcURL.fileName() ); 02270 kdDebug(7007) << "This seems to be a suitable case for trying to rename before stat+[list+]copy+del" << endl; 02271 state = STATE_RENAMING; 02272 02273 struct CopyInfo info; 02274 info.permissions = -1; 02275 info.mtime = (time_t) -1; 02276 info.ctime = (time_t) -1; 02277 info.size = (KIO::filesize_t)-1; 02278 info.uSource = m_currentSrcURL; 02279 info.uDest = dest; 02280 QValueList<CopyInfo> files; 02281 files.append(info); 02282 emit aboutToCreate( this, files ); 02283 02284 SimpleJob * newJob = KIO::rename( m_currentSrcURL, dest, false /*no overwrite */); 02285 Scheduler::scheduleJob(newJob); 02286 addSubjob( newJob ); 02287 if ( m_currentSrcURL.directory() != dest.directory() ) // For the user, moving isn't renaming. Only renaming is. 02288 m_bOnlyRenames = false; 02289 } 02290 else 02291 { 02292 // if the file system doesn't support deleting, we do not even stat 02293 if (m_mode == Move && !KProtocolInfo::supportsDeleting(m_currentSrcURL)) { 02294 QGuardedPtr<CopyJob> that = this; 02295 KMessageBox::information( 0, buildErrorString(ERR_CANNOT_DELETE, m_currentSrcURL.prettyURL())); 02296 if(that) 02297 statNextSrc(); // we could use a loop instead of a recursive call :) 02298 return; 02299 } 02300 // Stat the next src url 02301 Job * job = KIO::stat( m_currentSrcURL, true, 2, false ); 02302 //kdDebug(7007) << "KIO::stat on " << m_currentSrcURL.prettyURL() << endl; 02303 state = STATE_STATING; 02304 addSubjob(job); 02305 m_currentDestURL=m_dest; 02306 m_bOnlyRenames = false; 02307 } 02308 } else 02309 { 02310 // Finished the stat'ing phase 02311 // First make sure that the totals were correctly emitted 02312 state = STATE_STATING; 02313 slotReport(); 02314 // Check if we are copying a single file 02315 m_bSingleFileCopy = ( files.count() == 1 && dirs.isEmpty() ); 02316 // Then start copying things 02317 state = STATE_CREATING_DIRS; 02318 createNextDir(); 02319 } 02320 } 02321 02322 02323 void CopyJob::startListing( const KURL & src ) 02324 { 02325 state = STATE_LISTING; 02326 ListJob * newjob = listRecursive( src, false ); 02327 newjob->setUnrestricted(true); 02328 connect(newjob, SIGNAL(entries( KIO::Job *, 02329 const KIO::UDSEntryList& )), 02330 SLOT( slotEntries( KIO::Job*, 02331 const KIO::UDSEntryList& ))); 02332 addSubjob( newjob ); 02333 } 02334 02335 void CopyJob::skip( const KURL & sourceUrl ) 02336 { 02337 // Check if this is one if toplevel sources 02338 // IF yes, remove it from m_srcList, for a correct FilesRemoved() signal 02339 //kdDebug(7007) << "CopyJob::skip: looking for " << sourceUrl.prettyURL() << endl; 02340 KURL::List::Iterator sit = m_srcList.find( sourceUrl ); 02341 if ( sit != m_srcList.end() ) 02342 { 02343 //kdDebug(7007) << "CopyJob::skip: removing " << sourceUrl.prettyURL() << " from list" << endl; 02344 m_srcList.remove( sit ); 02345 } 02346 dirsToRemove.remove( sourceUrl ); 02347 } 02348 02349 void CopyJob::slotResultCreatingDirs( Job * job ) 02350 { 02351 // The dir we are trying to create: 02352 QValueList<CopyInfo>::Iterator it = dirs.begin(); 02353 // Was there an error creating a dir ? 02354 if ( job->error() ) 02355 { 02356 m_conflictError = job->error(); 02357 if ( (m_conflictError == ERR_DIR_ALREADY_EXIST) 02358 || (m_conflictError == ERR_FILE_ALREADY_EXIST) ) 02359 { 02360 KURL oldURL = ((SimpleJob*)job)->url(); 02361 // Should we skip automatically ? 02362 if ( m_bAutoSkip ) { 02363 // We dont want to copy files in this directory, so we put it on the skip list 02364 m_skipList.append( oldURL.path( 1 ) ); 02365 skip( oldURL ); 02366 dirs.remove( it ); // Move on to next dir 02367 } else { 02368 // Did the user choose to overwrite already? 02369 bool bOverwrite = m_bOverwriteAll; 02370 QString destFile = (*it).uDest.path(); 02371 QStringList::Iterator sit = m_overwriteList.begin(); 02372 for( ; sit != m_overwriteList.end() && !bOverwrite; sit++ ) 02373 if ( *sit == destFile.left( (*sit).length() ) ) 02374 bOverwrite = true; 02375 if ( bOverwrite ) { // overwrite => just skip 02376 emit copyingDone( this, ( *it ).uSource, ( *it ).uDest, true /* directory */, false /* renamed */ ); 02377 dirs.remove( it ); // Move on to next dir 02378 } else { 02379 assert( ((SimpleJob*)job)->url().url() == (*it).uDest.url() ); 02380 subjobs.remove( job ); 02381 assert ( subjobs.isEmpty() ); // We should have only one job at a time ... 02382 02383 // We need to stat the existing dir, to get its last-modification time 02384 KURL existingDest( (*it).uDest ); 02385 SimpleJob * newJob = KIO::stat( existingDest, false, 2, false ); 02386 Scheduler::scheduleJob(newJob); 02387 kdDebug(7007) << "KIO::stat for resolving conflict on " << existingDest.prettyURL() << endl; 02388 state = STATE_CONFLICT_CREATING_DIRS; 02389 addSubjob(newJob); 02390 return; // Don't move to next dir yet ! 02391 } 02392 } 02393 } 02394 else 02395 { 02396 // Severe error, abort 02397 Job::slotResult( job ); // will set the error and emit result(this) 02398 return; 02399 } 02400 } 02401 else // no error : remove from list, to move on to next dir 02402 { 02403 //this is required for the undo feature 02404 emit copyingDone( this, (*it).uSource, (*it).uDest, true, false ); 02405 dirs.remove( it ); 02406 } 02407 02408 m_processedDirs++; 02409 //emit processedDirs( this, m_processedDirs ); 02410 subjobs.remove( job ); 02411 assert ( subjobs.isEmpty() ); // We should have only one job at a time ... 02412 createNextDir(); 02413 } 02414 02415 void CopyJob::slotResultConflictCreatingDirs( KIO::Job * job ) 02416 { 02417 // We come here after a conflict has been detected and we've stated the existing dir 02418 02419 // The dir we were trying to create: 02420 QValueList<CopyInfo>::Iterator it = dirs.begin(); 02421 // Its modification time: 02422 time_t destmtime = (time_t)-1; 02423 time_t destctime = (time_t)-1; 02424 KIO::filesize_t destsize = 0; 02425 UDSEntry entry = ((KIO::StatJob*)job)->statResult(); 02426 KIO::UDSEntry::ConstIterator it2 = entry.begin(); 02427 for( ; it2 != entry.end(); it2++ ) { 02428 switch ((*it2).m_uds) { 02429 case UDS_MODIFICATION_TIME: 02430 destmtime = (time_t)((*it2).m_long); 02431 break; 02432 case UDS_CREATION_TIME: 02433 destctime = (time_t)((*it2).m_long); 02434 break; 02435 case UDS_SIZE: 02436 destsize = (*it2).m_long; 02437 break; 02438 } 02439 } 02440 subjobs.remove( job ); 02441 assert ( subjobs.isEmpty() ); // We should have only one job at a time ... 02442 02443 // Always multi and skip (since there are files after that) 02444 RenameDlg_Mode mode = (RenameDlg_Mode)( M_MULTI | M_SKIP ); 02445 // Overwrite only if the existing thing is a dir (no chance with a file) 02446 if ( m_conflictError == ERR_DIR_ALREADY_EXIST ) 02447 mode = (RenameDlg_Mode)( mode | (((*it).uSource == (*it).uDest) ? M_OVERWRITE_ITSELF : M_OVERWRITE )); 02448 02449 QString existingDest = (*it).uDest.path(); 02450 QString newPath; 02451 if (m_reportTimer) 02452 m_reportTimer->stop(); 02453 RenameDlg_Result r = Observer::self()->open_RenameDlg( this, i18n("Folder Already Exists"), 02454 (*it).uSource.prettyURL(0, KURL::StripFileProtocol), 02455 (*it).uDest.prettyURL(0, KURL::StripFileProtocol), 02456 mode, newPath, 02457 (*it).size, destsize, 02458 (*it).ctime, destctime, 02459 (*it).mtime, destmtime ); 02460 if (m_reportTimer) 02461 m_reportTimer->start(REPORT_TIMEOUT,false); 02462 switch ( r ) { 02463 case R_CANCEL: 02464 m_error = ERR_USER_CANCELED; 02465 emitResult(); 02466 return; 02467 case R_RENAME: 02468 { 02469 QString oldPath = (*it).uDest.path( 1 ); 02470 KURL newUrl( (*it).uDest ); 02471 newUrl.setPath( newPath ); 02472 emit renamed( this, (*it).uDest, newUrl ); // for e.g. kpropsdlg 02473 02474 // Change the current one and strip the trailing '/' 02475 (*it).uDest.setPath( newUrl.path( -1 ) ); 02476 newPath = newUrl.path( 1 ); // With trailing slash 02477 QValueList<CopyInfo>::Iterator renamedirit = it; 02478 ++renamedirit; 02479 // Change the name of subdirectories inside the directory 02480 for( ; renamedirit != dirs.end() ; ++renamedirit ) 02481 { 02482 QString path = (*renamedirit).uDest.path(); 02483 if ( path.left(oldPath.length()) == oldPath ) { 02484 QString n = path; 02485 n.replace( 0, oldPath.length(), newPath ); 02486 kdDebug(7007) << "dirs list: " << (*renamedirit).uSource.path() 02487 << " was going to be " << path 02488 << ", changed into " << n << endl; 02489 (*renamedirit).uDest.setPath( n ); 02490 } 02491 } 02492 // Change filenames inside the directory 02493 QValueList<CopyInfo>::Iterator renamefileit = files.begin(); 02494 for( ; renamefileit != files.end() ; ++renamefileit ) 02495 { 02496 QString path = (*renamefileit).uDest.path(); 02497 if ( path.left(oldPath.length()) == oldPath ) { 02498 QString n = path; 02499 n.replace( 0, oldPath.length(), newPath ); 02500 kdDebug(7007) << "files list: " << (*renamefileit).uSource.path() 02501 << " was going to be " << path 02502 << ", changed into " << n << endl; 02503 (*renamefileit).uDest.setPath( n ); 02504 } 02505 } 02506 if (!dirs.isEmpty()) 02507 emit aboutToCreate( this, dirs ); 02508 if (!files.isEmpty()) 02509 emit aboutToCreate( this, files ); 02510 } 02511 break; 02512 case R_AUTO_SKIP: 02513 m_bAutoSkip = true; 02514 // fall through 02515 case R_SKIP: 02516 m_skipList.append( existingDest ); 02517 skip( (*it).uSource ); 02518 // Move on to next dir 02519 dirs.remove( it ); 02520 m_processedDirs++; 02521 break; 02522 case R_OVERWRITE: 02523 m_overwriteList.append( existingDest ); 02524 emit copyingDone( this, ( *it ).uSource, ( *it ).uDest, true /* directory */, false /* renamed */ ); 02525 // Move on to next dir 02526 dirs.remove( it ); 02527 m_processedDirs++; 02528 break; 02529 case R_OVERWRITE_ALL: 02530 m_bOverwriteAll = true; 02531 emit copyingDone( this, ( *it ).uSource, ( *it ).uDest, true /* directory */, false /* renamed */ ); 02532 // Move on to next dir 02533 dirs.remove( it ); 02534 m_processedDirs++; 02535 break; 02536 default: 02537 assert( 0 ); 02538 } 02539 state = STATE_CREATING_DIRS; 02540 //emit processedDirs( this, m_processedDirs ); 02541 createNextDir(); 02542 } 02543 02544 void CopyJob::createNextDir() 02545 { 02546 KURL udir; 02547 if ( !dirs.isEmpty() ) 02548 { 02549 // Take first dir to create out of list 02550 QValueList<CopyInfo>::Iterator it = dirs.begin(); 02551 // Is this URL on the skip list or the overwrite list ? 02552 while( it != dirs.end() && udir.isEmpty() ) 02553 { 02554 QString dir = (*it).uDest.path(); 02555 bool bCreateDir = true; // we'll create it if it's not in any list 02556 02557 QStringList::Iterator sit = m_skipList.begin(); 02558 for( ; sit != m_skipList.end() && bCreateDir; sit++ ) 02559 // Is dir a subdirectory of *sit ? 02560 if ( *sit == dir.left( (*sit).length() ) ) 02561 bCreateDir = false; // skip this dir 02562 02563 if ( !bCreateDir ) { 02564 dirs.remove( it ); 02565 it = dirs.begin(); 02566 } else 02567 udir = (*it).uDest; 02568 } 02569 } 02570 if ( !udir.isEmpty() ) // any dir to create, finally ? 02571 { 02572 // Create the directory - with default permissions so that we can put files into it 02573 // TODO : change permissions once all is finished; but for stuff coming from CDROM it sucks... 02574 KIO::SimpleJob *newjob = KIO::mkdir( udir, -1 ); 02575 Scheduler::scheduleJob(newjob); 02576 02577 m_currentDestURL = udir; 02578 02579 addSubjob(newjob); 02580 return; 02581 } 02582 else // we have finished creating dirs 02583 { 02584 state = STATE_COPYING_FILES; 02585 m_processedFiles++; // Ralf wants it to start a 1, not 0 02586 copyNextFile(); 02587 } 02588 } 02589 02590 void CopyJob::slotResultCopyingFiles( Job * job ) 02591 { 02592 // The file we were trying to copy: 02593 QValueList<CopyInfo>::Iterator it = files.begin(); 02594 if ( job->error() ) 02595 { 02596 // Should we skip automatically ? 02597 if ( m_bAutoSkip ) 02598 { 02599 skip( (*it).uSource ); 02600 m_fileProcessedSize = (*it).size; 02601 files.remove( it ); // Move on to next file 02602 } 02603 else 02604 { 02605 m_conflictError = job->error(); // save for later 02606 // Existing dest ? 02607 if ( ( m_conflictError == ERR_FILE_ALREADY_EXIST ) 02608 || ( m_conflictError == ERR_DIR_ALREADY_EXIST ) ) 02609 { 02610 subjobs.remove( job ); 02611 assert ( subjobs.isEmpty() ); 02612 // We need to stat the existing file, to get its last-modification time 02613 KURL existingFile( (*it).uDest ); 02614 SimpleJob * newJob = KIO::stat( existingFile, false, 2, false ); 02615 Scheduler::scheduleJob(newJob); 02616 kdDebug(7007) << "KIO::stat for resolving conflict on " << existingFile.prettyURL() << endl; 02617 state = STATE_CONFLICT_COPYING_FILES; 02618 addSubjob(newJob); 02619 return; // Don't move to next file yet ! 02620 } 02621 else 02622 { 02623 if ( m_bCurrentOperationIsLink && job->inherits( "KIO::DeleteJob" ) ) 02624 { 02625 // Very special case, see a few lines below 02626 // We are deleting the source of a symlink we successfully moved... ignore error 02627 m_fileProcessedSize = (*it).size; 02628 files.remove( it ); 02629 } else { 02630 // Go directly to the conflict resolution, there is nothing to stat 02631 slotResultConflictCopyingFiles( job ); 02632 return; 02633 } 02634 } 02635 } 02636 } else // no error 02637 { 02638 // Special case for moving links. That operation needs two jobs, unlike others. 02639 if ( m_bCurrentOperationIsLink && m_mode == Move 02640 && !job->inherits( "KIO::DeleteJob" ) // Deleting source not already done 02641 ) 02642 { 02643 subjobs.remove( job ); 02644 assert ( subjobs.isEmpty() ); 02645 // The only problem with this trick is that the error handling for this del operation 02646 // is not going to be right... see 'Very special case' above. 02647 KIO::Job * newjob = KIO::del( (*it).uSource, false /*don't shred*/, false /*no GUI*/ ); 02648 addSubjob( newjob ); 02649 return; // Don't move to next file yet ! 02650 } 02651 02652 if ( m_bCurrentOperationIsLink ) 02653 { 02654 QString target = ( m_mode == Link ? (*it).uSource.path() : (*it).linkDest ); 02655 //required for the undo feature 02656 emit copyingLinkDone( this, (*it).uSource, target, (*it).uDest ); 02657 } 02658 else 02659 //required for the undo feature 02660 emit copyingDone( this, (*it).uSource, (*it).uDest, false, false ); 02661 // remove from list, to move on to next file 02662 files.remove( it ); 02663 } 02664 m_processedFiles++; 02665 02666 // clear processed size for last file and add it to overall processed size 02667 m_processedSize += m_fileProcessedSize; 02668 m_fileProcessedSize = 0; 02669 02670 //kdDebug(7007) << files.count() << " files remaining" << endl; 02671 subjobs.remove( job ); 02672 assert ( subjobs.isEmpty() ); // We should have only one job at a time ... 02673 copyNextFile(); 02674 } 02675 02676 void CopyJob::slotResultConflictCopyingFiles( KIO::Job * job ) 02677 { 02678 // We come here after a conflict has been detected and we've stated the existing file 02679 // The file we were trying to create: 02680 QValueList<CopyInfo>::Iterator it = files.begin(); 02681 02682 RenameDlg_Result res; 02683 QString newPath; 02684 02685 if (m_reportTimer) 02686 m_reportTimer->stop(); 02687 02688 if ( ( m_conflictError == ERR_FILE_ALREADY_EXIST ) 02689 || ( m_conflictError == ERR_DIR_ALREADY_EXIST ) ) 02690 { 02691 // Its modification time: 02692 time_t destmtime = (time_t)-1; 02693 time_t destctime = (time_t)-1; 02694 KIO::filesize_t destsize = 0; 02695 UDSEntry entry = ((KIO::StatJob*)job)->statResult(); 02696 KIO::UDSEntry::ConstIterator it2 = entry.begin(); 02697 for( ; it2 != entry.end(); it2++ ) { 02698 switch ((*it2).m_uds) { 02699 case UDS_MODIFICATION_TIME: 02700 destmtime = (time_t)((*it2).m_long); 02701 break; 02702 case UDS_CREATION_TIME: 02703 destctime = (time_t)((*it2).m_long); 02704 break; 02705 case UDS_SIZE: 02706 destsize = (*it2).m_long; 02707 break; 02708 } 02709 } 02710 02711 // Offer overwrite only if the existing thing is a file 02712 // If src==dest, use "overwrite-itself" 02713 RenameDlg_Mode mode = (RenameDlg_Mode) 02714 ( ( m_conflictError == ERR_DIR_ALREADY_EXIST ? 0 : 02715 ( (*it).uSource == (*it).uDest ) ? M_OVERWRITE_ITSELF : M_OVERWRITE ) ); 02716 if ( files.count() > 1 ) // Not last one 02717 mode = (RenameDlg_Mode) ( mode | M_MULTI | M_SKIP ); 02718 else 02719 mode = (RenameDlg_Mode) ( mode | M_SINGLE ); 02720 res = Observer::self()->open_RenameDlg( this, m_conflictError == ERR_FILE_ALREADY_EXIST ? 02721 i18n("File Already Exists") : i18n("Already Exists as Folder"), 02722 (*it).uSource.prettyURL(0, KURL::StripFileProtocol), 02723 (*it).uDest.prettyURL(0, KURL::StripFileProtocol), 02724 mode, newPath, 02725 (*it).size, destsize, 02726 (*it).ctime, destctime, 02727 (*it).mtime, destmtime ); 02728 02729 } 02730 else 02731 { 02732 if ( job->error() == ERR_USER_CANCELED ) 02733 res = R_CANCEL; 02734 else 02735 { 02736 SkipDlg_Result skipResult = Observer::self()->open_SkipDlg( this, files.count() > 1, 02737 job->errorString() ); 02738 02739 // Convert the return code from SkipDlg into a RenameDlg code 02740 res = ( skipResult == S_SKIP ) ? R_SKIP : 02741 ( skipResult == S_AUTO_SKIP ) ? R_AUTO_SKIP : 02742 R_CANCEL; 02743 } 02744 } 02745 02746 if (m_reportTimer) 02747 m_reportTimer->start(REPORT_TIMEOUT,false); 02748 02749 subjobs.remove( job ); 02750 assert ( subjobs.isEmpty() ); 02751 switch ( res ) { 02752 case R_CANCEL: 02753 m_error = ERR_USER_CANCELED; 02754 emitResult(); 02755 return; 02756 case R_RENAME: 02757 { 02758 KURL newUrl( (*it).uDest ); 02759 newUrl.setPath( newPath ); 02760 emit renamed( this, (*it).uDest, newUrl ); // for e.g. kpropsdlg 02761 (*it).uDest = newUrl; 02762 02763 QValueList<CopyInfo> files; 02764 files.append(*it); 02765 emit aboutToCreate( this, files ); 02766 } 02767 break; 02768 case R_AUTO_SKIP: 02769 m_bAutoSkip = true; 02770 // fall through 02771 case R_SKIP: 02772 // Move on to next file 02773 skip( (*it).uSource ); 02774 m_processedSize += (*it).size; 02775 files.remove( it ); 02776 m_processedFiles++; 02777 break; 02778 case R_OVERWRITE_ALL: 02779 m_bOverwriteAll = true; 02780 break; 02781 case R_OVERWRITE: 02782 // Add to overwrite list, so that copyNextFile knows to overwrite 02783 m_overwriteList.append( (*it).uDest.path() ); 02784 break; 02785 default: 02786 assert( 0 ); 02787 } 02788 state = STATE_COPYING_FILES; 02789 //emit processedFiles( this, m_processedFiles ); 02790 copyNextFile(); 02791 } 02792 02793 void CopyJob::copyNextFile() 02794 { 02795 bool bCopyFile = false; 02796 //kdDebug(7007) << "CopyJob::copyNextFile()" << endl; 02797 // Take the first file in the list 02798 QValueList<CopyInfo>::Iterator it = files.begin(); 02799 // Is this URL on the skip list ? 02800 while (it != files.end() && !bCopyFile) 02801 { 02802 bCopyFile = true; 02803 QString destFile = (*it).uDest.path(); 02804 02805 QStringList::Iterator sit = m_skipList.begin(); 02806 for( ; sit != m_skipList.end() && bCopyFile; sit++ ) 02807 // Is destFile in *sit (or a subdirectory of *sit) ? 02808 if ( *sit == destFile.left( (*sit).length() ) ) 02809 bCopyFile = false; // skip this file 02810 02811 if (!bCopyFile) { 02812 files.remove( it ); 02813 it = files.begin(); 02814 } 02815 } 02816 02817 if (bCopyFile) // any file to create, finally ? 02818 { 02819 // Do we set overwrite ? 02820 bool bOverwrite = m_bOverwriteAll; // yes if overwrite all 02821 QString destFile = (*it).uDest.path(); 02822 kdDebug(7007) << "copying " << destFile << endl; 02823 if ( (*it).uDest == (*it).uSource ) 02824 bOverwrite = false; 02825 else 02826 { 02827 // or if on the overwrite list 02828 QStringList::Iterator sit = m_overwriteList.begin(); 02829 for( ; sit != m_overwriteList.end() && !bOverwrite; sit++ ) 02830 if ( *sit == destFile.left( (*sit).length() ) ) 02831 bOverwrite = true; 02832 } 02833 02834 m_bCurrentOperationIsLink = false; 02835 KIO::Job * newjob = 0L; 02836 if ( m_mode == Link ) 02837 { 02838 //kdDebug(7007) << "Linking" << endl; 02839 if ( 02840 ((*it).uSource.protocol() == (*it).uDest.protocol()) && 02841 ((*it).uSource.host() == (*it).uDest.host()) && 02842 ((*it).uSource.port() == (*it).uDest.port()) && 02843 ((*it).uSource.user() == (*it).uDest.user()) && 02844 ((*it).uSource.pass() == (*it).uDest.pass()) ) 02845 { 02846 // This is the case of creating a real symlink 02847 KIO::SimpleJob *newJob = KIO::symlink( (*it).uSource.path(), (*it).uDest, bOverwrite, false /*no GUI*/ ); 02848 newjob = newJob; 02849 Scheduler::scheduleJob(newJob); 02850 //kdDebug(7007) << "CopyJob::copyNextFile : Linking target=" << (*it).uSource.path() << " link=" << (*it).uDest.prettyURL() << endl; 02851 //emit linking( this, (*it).uSource.path(), (*it).uDest ); 02852 m_bCurrentOperationIsLink = true; 02853 m_currentSrcURL=(*it).uSource; 02854 m_currentDestURL=(*it).uDest; 02855 //Observer::self()->slotCopying( this, (*it).uSource, (*it).uDest ); // should be slotLinking perhaps 02856 } else { 02857 //kdDebug(7007) << "CopyJob::copyNextFile : Linking URL=" << (*it).uSource.prettyURL() << " link=" << (*it).uDest.prettyURL() << endl; 02858 if ( (*it).uDest.isLocalFile() ) 02859 { 02860 bool devicesOk=false; 02861 02862 // if the source is a devices url, handle it a littlebit special 02863 if ((*it).uSource.protocol()==QString::fromLatin1("devices")) 02864 { 02865 QByteArray data; 02866 QByteArray param; 02867 QCString retType; 02868 QDataStream streamout(param,IO_WriteOnly); 02869 streamout<<(*it).uSource; 02870 streamout<<(*it).uDest; 02871 if ( kapp->dcopClient()->call( "kded", 02872 "mountwatcher", "createLink(KURL, KURL)", param,retType,data,false ) ) 02873 { 02874 QDataStream streamin(data,IO_ReadOnly); 02875 streamin>>devicesOk; 02876 } 02877 if (devicesOk) 02878 { 02879 files.remove( it ); 02880 m_processedFiles++; 02881 //emit processedFiles( this, m_processedFiles ); 02882 copyNextFile(); 02883 return; 02884 } 02885 } 02886 02887 if (!devicesOk) 02888 { 02889 QString path = (*it).uDest.path(); 02890 //kdDebug(7007) << "CopyJob::copyNextFile path=" << path << endl; 02891 QFile f( path ); 02892 if ( f.open( IO_ReadWrite ) ) 02893 { 02894 f.close(); 02895 KSimpleConfig config( path ); 02896 config.setDesktopGroup(); 02897 config.writePathEntry( QString::fromLatin1("URL"), (*it).uSource.url() ); 02898 config.writeEntry( QString::fromLatin1("Name"), (*it).uSource.url() ); 02899 config.writeEntry( QString::fromLatin1("Type"), QString::fromLatin1("Link") ); 02900 QString protocol = (*it).uSource.protocol(); 02901 if ( protocol == QString::fromLatin1("ftp") ) 02902 config.writeEntry( QString::fromLatin1("Icon"), QString::fromLatin1("ftp") ); 02903 else if ( protocol == QString::fromLatin1("http") ) 02904 config.writeEntry( QString::fromLatin1("Icon"), QString::fromLatin1("www") ); 02905 else if ( protocol == QString::fromLatin1("info") ) 02906 config.writeEntry( QString::fromLatin1("Icon"), QString::fromLatin1("info") ); 02907 else if ( protocol == QString::fromLatin1("mailto") ) // sven: 02908 config.writeEntry( QString::fromLatin1("Icon"), QString::fromLatin1("kmail") ); // added mailto: support 02909 else 02910 config.writeEntry( QString::fromLatin1("Icon"), QString::fromLatin1("unknown") ); 02911 config.sync(); 02912 files.remove( it ); 02913 m_processedFiles++; 02914 //emit processedFiles( this, m_processedFiles ); 02915 copyNextFile(); 02916 return; 02917 } 02918 else 02919 { 02920 kdDebug(7007) << "CopyJob::copyNextFile ERR_CANNOT_OPEN_FOR_WRITING" << endl; 02921 m_error = ERR_CANNOT_OPEN_FOR_WRITING; 02922 m_errorText = (*it).uDest.path(); 02923 emitResult(); 02924 return; 02925 } 02926 } 02927 } else { 02928 // Todo: not show "link" on remote dirs if the src urls are not from the same protocol+host+... 02929 m_error = ERR_CANNOT_SYMLINK; 02930 m_errorText = (*it).uDest.prettyURL(); 02931 emitResult(); 02932 return; 02933 } 02934 } 02935 } 02936 else if ( !(*it).linkDest.isEmpty() && 02937 ((*it).uSource.protocol() == (*it).uDest.protocol()) && 02938 ((*it).uSource.host() == (*it).uDest.host()) && 02939 ((*it).uSource.port() == (*it).uDest.port()) && 02940 ((*it).uSource.user() == (*it).uDest.user()) && 02941 ((*it).uSource.pass() == (*it).uDest.pass())) 02942 // Copying a symlink - only on the same protocol/host/etc. (#5601, downloading an FTP file through its link), 02943 { 02944 KIO::SimpleJob *newJob = KIO::symlink( (*it).linkDest, (*it).uDest, bOverwrite, false /*no GUI*/ ); 02945 Scheduler::scheduleJob(newJob); 02946 newjob = newJob; 02947 //kdDebug(7007) << "CopyJob::copyNextFile : Linking target=" << (*it).linkDest << " link=" << (*it).uDest.prettyURL() << endl; 02948 //emit linking( this, (*it).linkDest, (*it).uDest ); 02949 m_currentSrcURL=(*it).linkDest; 02950 m_currentDestURL=(*it).uDest; 02951 //Observer::self()->slotCopying( this, (*it).linkDest, (*it).uDest ); // should be slotLinking perhaps 02952 m_bCurrentOperationIsLink = true; 02953 // NOTE: if we are moving stuff, the deletion of the source will be done in slotResultCopyingFiles 02954 } else if (m_mode == Move) // Moving a file 02955 { 02956 KIO::FileCopyJob * moveJob = KIO::file_move( (*it).uSource, (*it).uDest, (*it).permissions, bOverwrite, false, false/*no GUI*/ ); 02957 moveJob->setSourceSize64( (*it).size ); 02958 newjob = moveJob; 02959 //kdDebug(7007) << "CopyJob::copyNextFile : Moving " << (*it).uSource << " to " << (*it).uDest << endl; 02960 //emit moving( this, (*it).uSource, (*it).uDest ); 02961 m_currentSrcURL=(*it).uSource; 02962 m_currentDestURL=(*it).uDest; 02963 //Observer::self()->slotMoving( this, (*it).uSource, (*it).uDest ); 02964 } 02965 else // Copying a file 02966 { 02967 // If source isn't local and target is local, we ignore the original permissions 02968 // Otherwise, files downloaded from HTTP end up with -r--r--r-- 02969 bool remoteSource = !KProtocolInfo::supportsListing((*it).uSource); 02970 int permissions = (*it).permissions; 02971 if ( d->m_defaultPermissions || ( remoteSource && (*it).uDest.isLocalFile() ) ) 02972 permissions = -1; 02973 KIO::FileCopyJob * copyJob = KIO::file_copy( (*it).uSource, (*it).uDest, permissions, bOverwrite, false, false/*no GUI*/ ); 02974 copyJob->setParentJob( this ); // in case of rename dialog 02975 copyJob->setSourceSize64( (*it).size ); 02976 newjob = copyJob; 02977 //kdDebug(7007) << "CopyJob::copyNextFile : Copying " << (*it).uSource << " to " << (*it).uDest << endl; 02978 m_currentSrcURL=(*it).uSource; 02979 m_currentDestURL=(*it).uDest; 02980 } 02981 addSubjob(newjob); 02982 connect( newjob, SIGNAL( processedSize( KIO::Job*, KIO::filesize_t ) ), 02983 this, SLOT( slotProcessedSize( KIO::Job*, KIO::filesize_t ) ) ); 02984 connect( newjob, SIGNAL( totalSize( KIO::Job*, KIO::filesize_t ) ), 02985 this, SLOT( slotTotalSize( KIO::Job*, KIO::filesize_t ) ) ); 02986 } 02987 else 02988 { 02989 // We're done 02990 //kdDebug(7007) << "copyNextFile finished" << endl; 02991 deleteNextDir(); 02992 } 02993 } 02994 02995 void CopyJob::deleteNextDir() 02996 { 02997 if ( m_mode == Move && !dirsToRemove.isEmpty() ) // some dirs to delete ? 02998 { 02999 state = STATE_DELETING_DIRS; 03000 // Take first dir to delete out of list - last ones first ! 03001 KURL::List::Iterator it = dirsToRemove.fromLast(); 03002 SimpleJob *job = KIO::rmdir( *it ); 03003 Scheduler::scheduleJob(job); 03004 dirsToRemove.remove(it); 03005 addSubjob( job ); 03006 } 03007 else 03008 { 03009 // Finished - tell the world 03010 if ( !m_bOnlyRenames ) 03011 { 03012 KDirNotify_stub allDirNotify("*", "KDirNotify*"); 03013 KURL url( d->m_globalDest ); 03014 if ( d->m_globalDestinationState != DEST_IS_DIR || m_asMethod ) 03015 url.setPath( url.directory() ); 03016 //kdDebug(7007) << "KDirNotify'ing FilesAdded " << url.prettyURL() << endl; 03017 allDirNotify.FilesAdded( url ); 03018 03019 if ( m_mode == Move && !m_srcList.isEmpty() ) 03020 allDirNotify.FilesRemoved( m_srcList ); 03021 } 03022 if (m_reportTimer!=0) 03023 m_reportTimer->stop(); 03024 emitResult(); 03025 } 03026 } 03027 03028 void CopyJob::slotProcessedSize( KIO::Job*, KIO::filesize_t data_size ) 03029 { 03030 //kdDebug(7007) << "CopyJob::slotProcessedSize " << (unsigned long)data_size << endl; 03031 m_fileProcessedSize = data_size; 03032 setProcessedSize(m_processedSize + m_fileProcessedSize); 03033 03034 if ( m_processedSize + m_fileProcessedSize > m_totalSize ) 03035 { 03036 m_totalSize = m_processedSize + m_fileProcessedSize; 03037 //kdDebug(7007) << "Adjusting m_totalSize to " << (unsigned long) m_totalSize << endl; 03038 emit totalSize( this, m_totalSize ); // safety 03039 } 03040 //kdDebug(7007) << "emit processedSize " << (unsigned long) (m_processedSize + m_fileProcessedSize) << endl; 03041 emit processedSize( this, m_processedSize + m_fileProcessedSize ); 03042 emitPercent( m_processedSize + m_fileProcessedSize, m_totalSize ); 03043 } 03044 03045 void CopyJob::slotTotalSize( KIO::Job*, KIO::filesize_t size ) 03046 { 03047 // Special case for copying a single file 03048 // This is because some protocols don't implement stat properly 03049 // (e.g. HTTP), and don't give us a size in some cases (redirection) 03050 // so we'd rather rely on the size given for the transfer 03051 if ( m_bSingleFileCopy ) 03052 { 03053 //kdDebug(7007) << "Single file -> updating totalsize to " << (long)size << endl; 03054 m_totalSize = size; 03055 emit totalSize( this, size ); 03056 } 03057 } 03058 03059 void CopyJob::slotResultDeletingDirs( Job * job ) 03060 { 03061 if (job->error()) 03062 { 03063 // Couldn't remove directory. Well, perhaps it's not empty 03064 // because the user pressed Skip for a given file in it. 03065 // Let's not display "Could not remove dir ..." for each of those dir ! 03066 } 03067 subjobs.remove( job ); 03068 assert ( subjobs.isEmpty() ); 03069 deleteNextDir(); 03070 } 03071 03072 void CopyJob::slotResult( Job *job ) 03073 { 03074 //kdDebug(7007) << "CopyJob::slotResult() state=" << (int) state << endl; 03075 // In each case, what we have to do is : 03076 // 1 - check for errors and treat them 03077 // 2 - subjobs.remove(job); 03078 // 3 - decide what to do next 03079 03080 switch ( state ) { 03081 case STATE_STATING: // We were trying to stat a src url or the dest 03082 slotResultStating( job ); 03083 break; 03084 case STATE_RENAMING: // We were trying to do a direct renaming, before even stat'ing 03085 { 03086 int err = job->error(); 03087 subjobs.remove( job ); 03088 assert ( subjobs.isEmpty() ); 03089 // Determine dest again 03090 KURL dest = m_dest; 03091 if ( destinationState == DEST_IS_DIR && !m_asMethod ) 03092 dest.addPath( m_currentSrcURL.fileName() ); 03093 if ( err ) 03094 { 03095 // Direct renaming didn't work. Try renaming to a temp name, 03096 // this can help e.g. when renaming 'a' to 'A' on a VFAT partition. 03097 // In that case it's the _same_ dir, we don't want to copy+del (data loss!) 03098 if ( m_currentSrcURL.isLocalFile() && m_currentSrcURL.url(-1) != dest.url(-1) && 03099 m_currentSrcURL.url(-1).lower() == dest.url(-1).lower() && 03100 ( err == ERR_FILE_ALREADY_EXIST || err == ERR_DIR_ALREADY_EXIST ) ) 03101 { 03102 kdDebug(7007) << "Couldn't rename directly, dest already exists. Detected special case of lower/uppercase renaming in same dir, try with 2 rename calls" << endl; 03103 QCString _src( QFile::encodeName(m_currentSrcURL.path()) ); 03104 QCString _dest( QFile::encodeName(dest.path()) ); 03105 KTempFile tmpFile( m_currentSrcURL.directory(false) ); 03106 QCString _tmp( QFile::encodeName(tmpFile.name()) ); 03107 kdDebug(7007) << "CopyJob::slotResult KTempFile status:" << tmpFile.status() << " using " << _tmp << " as intermediary" << endl; 03108 tmpFile.unlink(); 03109 if ( ::rename( _src, _tmp ) == 0 ) 03110 { 03111 if ( !QFile::exists( _dest ) && ::rename( _tmp, _dest ) == 0 ) 03112 { 03113 kdDebug(7007) << "Success." << endl; 03114 err = 0; 03115 } 03116 else 03117 { 03118 // Revert back to original name! 03119 if ( ::rename( _tmp, _src ) != 0 ) { 03120 kdError(7007) << "Couldn't rename " << tmpFile.name() << " back to " << _src << " !" << endl; 03121 // Severe error, abort 03122 Job::slotResult( job ); // will set the error and emit result(this) 03123 return; 03124 } 03125 } 03126 } 03127 } 03128 } 03129 if ( err ) 03130 { 03131 // This code is similar to CopyJob::slotResultConflictCopyingFiles 03132 // but here it's about the base src url being moved/renamed 03133 // (*m_currentStatSrc) and its dest (m_dest), not about a single file. 03134 // It also means we already stated the dest, here. 03135 // On the other hand we haven't stated the src yet (we skipped doing it 03136 // to save time, since it's not necessary to rename directly!)... 03137 03138 Q_ASSERT( m_currentSrcURL == *m_currentStatSrc ); 03139 03140 // Existing dest? 03141 if ( err == ERR_DIR_ALREADY_EXIST || err == ERR_FILE_ALREADY_EXIST ) 03142 { 03143 if (m_reportTimer) 03144 m_reportTimer->stop(); 03145 03146 QString newPath; 03147 // Offer overwrite only if the existing thing is a file 03148 // If src==dest, use "overwrite-itself" 03149 RenameDlg_Mode mode = (RenameDlg_Mode) 03150 ( ( m_conflictError == ERR_DIR_ALREADY_EXIST ? 0 : 03151 ( m_currentSrcURL == dest ) ? M_OVERWRITE_ITSELF : M_OVERWRITE ) ); 03152 // I won't use M_MULTI or M_SKIP there. It's too ambiguous: 03153 // we are in the middle of the stat phase, so it's hard to know 03154 // if it will apply to the already-stated-dirs that will be copied later, 03155 // and/oor to the to-be-stated src urls that might be in the same case... 03156 mode = (RenameDlg_Mode) ( mode | M_SINGLE ); 03157 // we lack mtime info for both the src (not stated) 03158 // and the dest (stated but this info wasn't stored) 03159 RenameDlg_Result r = Observer::self()->open_RenameDlg( this, 03160 err == ERR_FILE_ALREADY_EXIST ? i18n("File Already Exists") : i18n("Already Exists as Folder"), 03161 m_currentSrcURL.prettyURL(0, KURL::StripFileProtocol), 03162 dest.prettyURL(0, KURL::StripFileProtocol), 03163 mode, newPath ); 03164 if (m_reportTimer) 03165 m_reportTimer->start(REPORT_TIMEOUT,false); 03166 03167 switch ( r ) 03168 { 03169 case R_CANCEL: 03170 { 03171 m_error = ERR_USER_CANCELED; 03172 emitResult(); 03173 return; 03174 } 03175 case R_RENAME: 03176 { 03177 // Set m_dest to the chosen destination 03178 // This is only for this src url; the next one will revert to d->m_globalDest 03179 m_dest.setPath( newPath ); 03180 KIO::Job* job = KIO::stat( m_dest, false, 2, false ); 03181 state = STATE_STATING; 03182 destinationState = DEST_NOT_STATED; 03183 addSubjob(job); 03184 return; 03185 } 03186 case R_OVERWRITE: 03187 // Add to overwrite list 03188 // Note that we add dest, not m_dest. 03189 // This ensures that when moving several urls into a dir (m_dest), 03190 // we only overwrite for the current one, not for all. 03191 // When renaming a single file (m_asMethod), it makes no difference. 03192 kdDebug(7007) << "adding to overwrite list: " << dest.path() << endl; 03193 m_overwriteList.append( dest.path() ); 03194 break; 03195 default: 03196 //assert( 0 ); 03197 break; 03198 } 03199 } 03200 03201 kdDebug(7007) << "Couldn't rename, reverting to normal way, starting with stat" << endl; 03202 //kdDebug(7007) << "KIO::stat on " << m_currentSrcURL.prettyURL() << endl; 03203 KIO::Job* job = KIO::stat( m_currentSrcURL, true, 2, false ); 03204 state = STATE_STATING; 03205 addSubjob(job); 03206 m_bOnlyRenames = false; 03207 } 03208 else 03209 { 03210 //kdDebug(7007) << "Renaming succeeded, move on" << endl; 03211 emit copyingDone( this, *m_currentStatSrc, dest, true, true ); 03212 statNextSrc(); 03213 } 03214 } 03215 break; 03216 case STATE_LISTING: // recursive listing finished 03217 //kdDebug(7007) << "totalSize: " << (unsigned int) m_totalSize << " files: " << files.count() << " dirs: " << dirs.count() << endl; 03218 // Was there an error ? 03219 if (job->error()) 03220 { 03221 Job::slotResult( job ); // will set the error and emit result(this) 03222 return; 03223 } 03224 03225 subjobs.remove( job ); 03226 assert ( subjobs.isEmpty() ); 03227 03228 statNextSrc(); 03229 break; 03230 case STATE_CREATING_DIRS: 03231 slotResultCreatingDirs( job ); 03232 break; 03233 case STATE_CONFLICT_CREATING_DIRS: 03234 slotResultConflictCreatingDirs( job ); 03235 break; 03236 case STATE_COPYING_FILES: 03237 slotResultCopyingFiles( job ); 03238 break; 03239 case STATE_CONFLICT_COPYING_FILES: 03240 slotResultConflictCopyingFiles( job ); 03241 break; 03242 case STATE_DELETING_DIRS: 03243 slotResultDeletingDirs( job ); 03244 break; 03245 default: 03246 assert( 0 ); 03247 } 03248 } 03249 03250 void KIO::CopyJob::setDefaultPermissions( bool b ) 03251 { 03252 d->m_defaultPermissions = b; 03253 } 03254 03255 CopyJob *KIO::copy(const KURL& src, const KURL& dest, bool showProgressInfo ) 03256 { 03257 //kdDebug(7007) << "KIO::copy src=" << src.url() << " dest=" << dest.url() << endl; 03258 KURL::List srcList; 03259 srcList.append( src ); 03260 return new CopyJob( srcList, dest, CopyJob::Copy, false, showProgressInfo ); 03261 } 03262 03263 CopyJob *KIO::copyAs(const KURL& src, const KURL& dest, bool showProgressInfo ) 03264 { 03265 //kdDebug(7007) << "KIO::copyAs src=" << src.url() << " dest=" << dest.url() << endl; 03266 KURL::List srcList; 03267 srcList.append( src ); 03268 return new CopyJob( srcList, dest, CopyJob::Copy, true, showProgressInfo ); 03269 } 03270 03271 CopyJob *KIO::copy( const KURL::List& src, const KURL& dest, bool showProgressInfo ) 03272 { 03273 return new CopyJob( src, dest, CopyJob::Copy, false, showProgressInfo ); 03274 } 03275 03276 CopyJob *KIO::move(const KURL& src, const KURL& dest, bool showProgressInfo ) 03277 { 03278 KURL::List srcList; 03279 srcList.append( src ); 03280 return new CopyJob( srcList, dest, CopyJob::Move, false, showProgressInfo ); 03281 } 03282 03283 CopyJob *KIO::moveAs(const KURL& src, const KURL& dest, bool showProgressInfo ) 03284 { 03285 KURL::List srcList; 03286 srcList.append( src ); 03287 return new CopyJob( srcList, dest, CopyJob::Move, true, showProgressInfo ); 03288 } 03289 03290 CopyJob *KIO::move( const KURL::List& src, const KURL& dest, bool showProgressInfo ) 03291 { 03292 return new CopyJob( src, dest, CopyJob::Move, false, showProgressInfo ); 03293 } 03294 03295 CopyJob *KIO::link(const KURL& src, const KURL& destDir, bool showProgressInfo ) 03296 { 03297 KURL::List srcList; 03298 srcList.append( src ); 03299 return new CopyJob( srcList, destDir, CopyJob::Link, false, showProgressInfo ); 03300 } 03301 03302 CopyJob *KIO::link(const KURL::List& srcList, const KURL& destDir, bool showProgressInfo ) 03303 { 03304 return new CopyJob( srcList, destDir, CopyJob::Link, false, showProgressInfo ); 03305 } 03306 03307 CopyJob *KIO::linkAs(const KURL& src, const KURL& destDir, bool showProgressInfo ) 03308 { 03309 KURL::List srcList; 03310 srcList.append( src ); 03311 return new CopyJob( srcList, destDir, CopyJob::Link, false, showProgressInfo ); 03312 } 03313 03315 03316 DeleteJob::DeleteJob( const KURL::List& src, bool shred, bool showProgressInfo ) 03317 : Job(showProgressInfo), m_totalSize( 0 ), m_processedSize( 0 ), m_fileProcessedSize( 0 ), 03318 m_processedFiles( 0 ), m_processedDirs( 0 ), m_totalFilesDirs( 0 ), 03319 m_srcList(src), m_currentStat(m_srcList.begin()), m_shred(shred), m_reportTimer(0) 03320 { 03321 if ( showProgressInfo ) { 03322 03323 connect( this, SIGNAL( totalFiles( KIO::Job*, unsigned long ) ), 03324 Observer::self(), SLOT( slotTotalFiles( KIO::Job*, unsigned long ) ) ); 03325 03326 connect( this, SIGNAL( totalDirs( KIO::Job*, unsigned long ) ), 03327 Observer::self(), SLOT( slotTotalDirs( KIO::Job*, unsigned long ) ) ); 03328 03329 // See slotReport 03330 /*connect( this, SIGNAL( processedFiles( KIO::Job*, unsigned long ) ), 03331 m_observer, SLOT( slotProcessedFiles( KIO::Job*, unsigned long ) ) ); 03332 03333 connect( this, SIGNAL( processedDirs( KIO::Job*, unsigned long ) ), 03334 m_observer, SLOT( slotProcessedDirs( KIO::Job*, unsigned long ) ) ); 03335 03336 connect( this, SIGNAL( deleting( KIO::Job*, const KURL& ) ), 03337 m_observer, SLOT( slotDeleting( KIO::Job*, const KURL& ) ) );*/ 03338 03339 m_reportTimer=new QTimer(this); 03340 connect(m_reportTimer,SIGNAL(timeout()),this,SLOT(slotReport())); 03341 //this will update the report dialog with 5 Hz, I think this is fast enough, aleXXX 03342 m_reportTimer->start(REPORT_TIMEOUT,false); 03343 } 03344 03345 QTimer::singleShot(0, this, SLOT(slotStart())); 03346 } 03347 03348 void DeleteJob::slotStart() 03349 { 03350 statNextSrc(); 03351 } 03352 03353 //this is called often, so calling the functions 03354 //from Observer here directly might improve the performance a little bit 03355 //aleXXX 03356 void DeleteJob::slotReport() 03357 { 03358 if (m_progressId==0) 03359 return; 03360 03361 Observer * observer = Observer::self(); 03362 03363 emit deleting( this, m_currentURL ); 03364 observer->slotDeleting(this,m_currentURL); 03365 03366 switch( state ) { 03367 case STATE_STATING: 03368 case STATE_LISTING: 03369 emit totalSize( this, m_totalSize ); 03370 emit totalFiles( this, files.count() ); 03371 emit totalDirs( this, dirs.count() ); 03372 break; 03373 case STATE_DELETING_DIRS: 03374 emit processedDirs( this, m_processedDirs ); 03375 observer->slotProcessedDirs(this,m_processedDirs); 03376 emitPercent( m_processedFiles + m_processedDirs, m_totalFilesDirs ); 03377 break; 03378 case STATE_DELETING_FILES: 03379 observer->slotProcessedFiles(this,m_processedFiles); 03380 emit processedFiles( this, m_processedFiles ); 03381 if (!m_shred) 03382 emitPercent( m_processedFiles, m_totalFilesDirs ); 03383 break; 03384 } 03385 } 03386 03387 03388 void DeleteJob::slotEntries(KIO::Job* job, const UDSEntryList& list) 03389 { 03390 UDSEntryListConstIterator it = list.begin(); 03391 UDSEntryListConstIterator end = list.end(); 03392 for (; it != end; ++it) 03393 { 03394 UDSEntry::ConstIterator it2 = (*it).begin(); 03395 bool bDir = false; 03396 bool bLink = false; 03397 QString relName; 03398 int atomsFound(0); 03399 for( ; it2 != (*it).end(); it2++ ) 03400 { 03401 switch ((*it2).m_uds) 03402 { 03403 case UDS_FILE_TYPE: 03404 bDir = S_ISDIR((*it2).m_long); 03405 atomsFound++; 03406 break; 03407 case UDS_NAME: 03408 relName = ((*it2).m_str); 03409 atomsFound++; 03410 break; 03411 case UDS_LINK_DEST: 03412 bLink = !(*it2).m_str.isEmpty(); 03413 atomsFound++; 03414 break; 03415 case UDS_SIZE: 03416 m_totalSize += (KIO::filesize_t)((*it2).m_long); 03417 atomsFound++; 03418 break; 03419 default: 03420 break; 03421 } 03422 if (atomsFound==4) break; 03423 } 03424 assert(!relName.isEmpty()); 03425 if (relName != ".." && relName != ".") 03426 { 03427 KURL url = ((SimpleJob *)job)->url(); // assumed to be a dir 03428 url.addPath( relName ); 03429 //kdDebug(7007) << "DeleteJob::slotEntries " << relName << " (" << url.prettyURL() << ")" << endl; 03430 if ( bLink ) 03431 symlinks.append( url ); 03432 else if ( bDir ) 03433 dirs.append( url ); 03434 else 03435 files.append( url ); 03436 } 03437 } 03438 } 03439 03440 03441 void DeleteJob::statNextSrc() 03442 { 03443 //kdDebug(7007) << "statNextSrc" << endl; 03444 if ( m_currentStat != m_srcList.end() ) 03445 { 03446 m_currentURL = (*m_currentStat); 03447 03448 // if the file system doesn't support deleting, we do not even stat 03449 if (!KProtocolInfo::supportsDeleting(m_currentURL)) { 03450 QGuardedPtr<DeleteJob> that = this; 03451 KMessageBox::information( 0, buildErrorString(ERR_CANNOT_DELETE, m_currentURL.prettyURL())); 03452 ++m_currentStat; 03453 if(that) 03454 statNextSrc(); // we could use a loop instead of a recursive call :) 03455 return; 03456 } 03457 // Stat it 03458 state = STATE_STATING; 03459 KIO::SimpleJob * job = KIO::stat( m_currentURL, true, 1, false ); 03460 Scheduler::scheduleJob(job); 03461 //kdDebug(7007) << "KIO::stat (DeleteJob) " << m_currentURL.prettyURL() << endl; 03462 addSubjob(job); 03463 //if ( m_progressId ) // Did we get an ID from the observer ? 03464 // Observer::self()->slotDeleting( this, *it ); // show asap 03465 } else 03466 { 03467 m_totalFilesDirs = files.count()+symlinks.count() + dirs.count(); 03468 slotReport(); 03469 // Now we know which dirs hold the files we're going to delete. 03470 // To speed things up and prevent double-notification, we disable KDirWatch 03471 // on those dirs temporarily (using KDirWatch::self, that's the instanced 03472 // used by e.g. kdirlister). 03473 for ( QStringList::Iterator it = m_parentDirs.begin() ; it != m_parentDirs.end() ; ++it ) 03474 KDirWatch::self()->stopDirScan( *it ); 03475 state = STATE_DELETING_FILES; 03476 deleteNextFile(); 03477 } 03478 } 03479 03480 void DeleteJob::deleteNextFile() 03481 { 03482 //kdDebug(7007) << "deleteNextFile" << endl; 03483 if ( !files.isEmpty() || !symlinks.isEmpty() ) 03484 { 03485 SimpleJob *job; 03486 do { 03487 // Take first file to delete out of list 03488 KURL::List::Iterator it = files.begin(); 03489 bool isLink = false; 03490 if ( it == files.end() ) // No more files 03491 { 03492 it = symlinks.begin(); // Pick up a symlink to delete 03493 isLink = true; 03494 } 03495 // Use shredding ? 03496 if ( m_shred && (*it).isLocalFile() && !isLink ) 03497 { 03498 // KShred your KTie 03499 KIO_ARGS << int(3) << (*it).path(); 03500 job = KIO::special(KURL("file:/"), packedArgs, false /*no GUI*/); 03501 Scheduler::scheduleJob(job); 03502 m_currentURL=(*it); 03503 connect( job, SIGNAL( processedSize( KIO::Job*, KIO::filesize_t ) ), 03504 this, SLOT( slotProcessedSize( KIO::Job*, KIO::filesize_t ) ) ); 03505 } else 03506 { 03507 // Normal deletion 03508 // If local file, try do it directly 03509 if ( (*it).isLocalFile() && unlink( QFile::encodeName((*it).path()) ) == 0 ) { 03510 job = 0; 03511 m_processedFiles++; 03512 if ( m_processedFiles % 300 == 0 ) { // update progress info every 300 files 03513 m_currentURL = *it; 03514 slotReport(); 03515 } 03516 } else 03517 { // if remote - or if unlink() failed (we'll use the job's error handling in that case) 03518 job = KIO::file_delete( *it, false /*no GUI*/); 03519 Scheduler::scheduleJob(job); 03520 m_currentURL=(*it); 03521 } 03522 } 03523 if ( isLink ) 03524 symlinks.remove(it); 03525 else 03526 files.remove(it); 03527 if ( job ) { 03528 addSubjob(job); 03529 return; 03530 } 03531 // loop only if direct deletion worked (job=0) and there is something else to delete 03532 } while (!job && (!files.isEmpty() || !symlinks.isEmpty())); 03533 } 03534 state = STATE_DELETING_DIRS; 03535 deleteNextDir(); 03536 } 03537 03538 void DeleteJob::deleteNextDir() 03539 { 03540 if ( !dirs.isEmpty() ) // some dirs to delete ? 03541 { 03542 do { 03543 // Take first dir to delete out of list - last ones first ! 03544 KURL::List::Iterator it = dirs.fromLast(); 03545 // If local dir, try to rmdir it directly 03546 if ( (*it).isLocalFile() && ::rmdir( QFile::encodeName((*it).path()) ) == 0 ) { 03547 03548 m_processedDirs++; 03549 if ( m_processedDirs % 100 == 0 ) { // update progress info every 100 dirs 03550 m_currentURL = *it; 03551 slotReport(); 03552 } 03553 } else 03554 { 03555 SimpleJob *job = KIO::rmdir( *it ); 03556 Scheduler::scheduleJob(job); 03557 dirs.remove(it); 03558 addSubjob( job ); 03559 return; 03560 } 03561 dirs.remove(it); 03562 } while ( !dirs.isEmpty() ); 03563 } 03564 03565 // Re-enable watching on the dirs that held the deleted files 03566 for ( QStringList::Iterator it = m_parentDirs.begin() ; it != m_parentDirs.end() ; ++it ) 03567 KDirWatch::self()->restartDirScan( *it ); 03568 03569 // Finished - tell the world 03570 if ( !m_srcList.isEmpty() ) 03571 { 03572 KDirNotify_stub allDirNotify("*", "KDirNotify*"); 03573 allDirNotify.FilesRemoved( m_srcList ); 03574 } 03575 if (m_reportTimer!=0) 03576 m_reportTimer->stop(); 03577 emitResult(); 03578 } 03579 03580 void DeleteJob::slotProcessedSize( KIO::Job*, KIO::filesize_t data_size ) 03581 { 03582 // Note: this is the same implementation as CopyJob::slotProcessedSize but 03583 // it's different from FileCopyJob::slotProcessedSize - which is why this 03584 // is not in Job. 03585 03586 m_fileProcessedSize = data_size; 03587 setProcessedSize(m_processedSize + m_fileProcessedSize); 03588 03589 //kdDebug(7007) << "DeleteJob::slotProcessedSize " << (unsigned int) (m_processedSize + m_fileProcessedSize) << endl; 03590 03591 emit processedSize( this, m_processedSize + m_fileProcessedSize ); 03592 03593 // calculate percents 03594 unsigned long ipercent = m_percent; 03595 03596 if ( m_totalSize == 0 ) 03597 m_percent = 100; 03598 else 03599 m_percent = (unsigned long)(( (float)(m_processedSize + m_fileProcessedSize) / (float)m_totalSize ) * 100.0); 03600 03601 if ( m_percent > ipercent ) 03602 { 03603 emit percent( this, m_percent ); 03604 //kdDebug(7007) << "DeleteJob::slotProcessedSize - percent = " << (unsigned int) m_percent << endl; 03605 } 03606 03607 } 03608 03609 void DeleteJob::slotResult( Job *job ) 03610 { 03611 switch ( state ) 03612 { 03613 case STATE_STATING: 03614 { 03615 // Was there an error while stating ? 03616 if (job->error() ) 03617 { 03618 // Probably : doesn't exist 03619 Job::slotResult( job ); // will set the error and emit result(this) 03620 return; 03621 } 03622 03623 // Is it a file or a dir ? 03624 UDSEntry entry = ((StatJob*)job)->statResult(); 03625 bool bDir = false; 03626 bool bLink = false; 03627 KIO::filesize_t size = (KIO::filesize_t)-1; 03628 UDSEntry::ConstIterator it2 = entry.begin(); 03629 int atomsFound(0); 03630 for( ; it2 != entry.end(); it2++ ) 03631 { 03632 if ( ((*it2).m_uds) == UDS_FILE_TYPE ) 03633 { 03634 bDir = S_ISDIR( (mode_t)(*it2).m_long ); 03635 atomsFound++; 03636 } 03637 else if ( ((*it2).m_uds) == UDS_LINK_DEST ) 03638 { 03639 bLink = !((*it2).m_str.isEmpty()); 03640 atomsFound++; 03641 } 03642 else if ( ((*it2).m_uds) == UDS_SIZE ) 03643 { 03644 size = (*it2).m_long; 03645 atomsFound++; 03646 }; 03647 if (atomsFound==3) break; 03648 } 03649 03650 KURL url = ((SimpleJob*)job)->url(); 03651 03652 subjobs.remove( job ); 03653 assert( subjobs.isEmpty() ); 03654 03655 if (bDir && !bLink) 03656 { 03657 // Add toplevel dir in list of dirs 03658 dirs.append( url ); 03659 if ( url.isLocalFile() && !m_parentDirs.contains( url.path(-1) ) ) 03660 m_parentDirs.append( url.path(-1) ); 03661 03662 //kdDebug(7007) << " Target is a directory " << endl; 03663 // List it 03664 state = STATE_LISTING; 03665 ListJob *newjob = listRecursive( url, false ); 03666 newjob->setUnrestricted(true); // No KIOSK restrictions 03667 Scheduler::scheduleJob(newjob); 03668 connect(newjob, SIGNAL(entries( KIO::Job *, 03669 const KIO::UDSEntryList& )), 03670 SLOT( slotEntries( KIO::Job*, 03671 const KIO::UDSEntryList& ))); 03672 addSubjob(newjob); 03673 } 03674 else 03675 { 03676 if ( bLink ) { 03677 //kdDebug(7007) << " Target is a symlink" << endl; 03678 symlinks.append( url ); 03679 } else { 03680 //kdDebug(7007) << " Target is a file" << endl; 03681 files.append( url ); 03682 } 03683 if ( url.isLocalFile() && !m_parentDirs.contains( url.directory(-1) ) ) 03684 m_parentDirs.append( url.directory(-1) ); 03685 ++m_currentStat; 03686 statNextSrc(); 03687 } 03688 } 03689 break; 03690 case STATE_LISTING: 03691 if ( job->error() ) 03692 { 03693 // Try deleting nonetheless, it may be empty (and non-listable) 03694 } 03695 subjobs.remove( job ); 03696 assert( subjobs.isEmpty() ); 03697 ++m_currentStat; 03698 statNextSrc(); 03699 break; 03700 case STATE_DELETING_FILES: 03701 if ( job->error() ) 03702 { 03703 Job::slotResult( job ); // will set the error and emit result(this) 03704 return; 03705 } 03706 subjobs.remove( job ); 03707 assert( subjobs.isEmpty() ); 03708 m_processedFiles++; 03709 03710 deleteNextFile(); 03711 break; 03712 case STATE_DELETING_DIRS: 03713 if ( job->error() ) 03714 { 03715 Job::slotResult( job ); // will set the error and emit result(this) 03716 return; 03717 } 03718 subjobs.remove( job ); 03719 assert( subjobs.isEmpty() ); 03720 m_processedDirs++; 03721 //emit processedDirs( this, m_processedDirs ); 03722 //if (!m_shred) 03723 //emitPercent( m_processedFiles + m_processedDirs, m_totalFilesDirs ); 03724 03725 deleteNextDir(); 03726 break; 03727 default: 03728 assert(0); 03729 } 03730 } 03731 03732 DeleteJob *KIO::del( const KURL& src, bool shred, bool showProgressInfo ) 03733 { 03734 KURL::List srcList; 03735 srcList.append( src ); 03736 DeleteJob *job = new DeleteJob( srcList, shred, showProgressInfo ); 03737 return job; 03738 } 03739 03740 DeleteJob *KIO::del( const KURL::List& src, bool shred, bool showProgressInfo ) 03741 { 03742 DeleteJob *job = new DeleteJob( src, shred, showProgressInfo ); 03743 return job; 03744 } 03745 03746 MultiGetJob::MultiGetJob(const KURL& url, 03747 bool showProgressInfo) 03748 : TransferJob(url, 0, QByteArray(), QByteArray(), showProgressInfo) 03749 { 03750 m_waitQueue.setAutoDelete(true); 03751 m_activeQueue.setAutoDelete(true); 03752 m_currentEntry = 0; 03753 } 03754 03755 void MultiGetJob::get(long id, const KURL &url, const MetaData &metaData) 03756 { 03757 GetRequest *entry = new GetRequest(id, url, metaData); 03758 entry->metaData["request-id"] = QString("%1").arg(id); 03759 m_waitQueue.append(entry); 03760 } 03761 03762 void MultiGetJob::flushQueue(QPtrList<GetRequest> &queue) 03763 { 03764 GetRequest *entry; 03765 // Use multi-get 03766 // Scan all jobs in m_waitQueue 03767 for(entry = m_waitQueue.first(); entry; ) 03768 { 03769 if ((m_url.protocol() == entry->url.protocol()) && 03770 (m_url.host() == entry->url.host()) && 03771 (m_url.port() == entry->url.port()) && 03772 (m_url.user() == entry->url.user())) 03773 { 03774 m_waitQueue.take(); 03775 queue.append(entry); 03776 entry = m_waitQueue.current(); 03777 } 03778 else 03779 { 03780 entry = m_waitQueue.next(); 03781 } 03782 } 03783 // Send number of URLs, (URL, metadata)* 03784 KIO_ARGS << (Q_INT32) queue.count(); 03785 for(entry = queue.first(); entry; entry = queue.next()) 03786 { 03787 stream << entry->url << entry->metaData; 03788 } 03789 m_packedArgs = packedArgs; 03790 m_command = CMD_MULTI_GET; 03791 m_outgoingMetaData.clear(); 03792 } 03793 03794 void MultiGetJob::start(Slave *slave) 03795 { 03796 // Add first job from m_waitQueue and add it to m_activeQueue 03797 GetRequest *entry = m_waitQueue.take(0); 03798 m_activeQueue.append(entry); 03799 03800 m_url = entry->url; 03801 03802 if (!entry->url.protocol().startsWith("http")) 03803 { 03804 // Use normal get 03805 KIO_ARGS << entry->url; 03806 m_packedArgs = packedArgs; 03807 m_outgoingMetaData = entry->metaData; 03808 m_command = CMD_GET; 03809 b_multiGetActive = false; 03810 } 03811 else 03812 { 03813 flushQueue(m_activeQueue); 03814 b_multiGetActive = true; 03815 } 03816 03817 TransferJob::start(slave); // Anything else to do?? 03818 } 03819 03820 bool MultiGetJob::findCurrentEntry() 03821 { 03822 if (b_multiGetActive) 03823 { 03824 long id = m_incomingMetaData["request-id"].toLong(); 03825 for(GetRequest *entry = m_activeQueue.first(); entry; entry = m_activeQueue.next()) 03826 { 03827 if (entry->id == id) 03828 { 03829 m_currentEntry = entry; 03830 return true; 03831 } 03832 } 03833 m_currentEntry = 0; 03834 return false; 03835 } 03836 else 03837 { 03838 m_currentEntry = m_activeQueue.first(); 03839 return (m_currentEntry != 0); 03840 } 03841 } 03842 03843 void MultiGetJob::slotRedirection( const KURL &url) 03844 { 03845 if (!findCurrentEntry()) return; // Error 03846 if (!kapp->authorizeURLAction("redirect", m_url, url)) 03847 { 03848 kdWarning(7007) << "MultiGetJob: Redirection from " << m_currentEntry->url.prettyURL() << " to " << url.prettyURL() << " REJECTED!" << endl; 03849 return; 03850 } 03851 m_redirectionURL = url; 03852 if (m_currentEntry->url.hasUser() && !url.hasUser() && (m_currentEntry->url.host().lower() == url.host().lower())) 03853 m_redirectionURL.setUser(m_currentEntry->url.user()); // Preserve user 03854 get(m_currentEntry->id, m_redirectionURL, m_currentEntry->metaData); // Try again 03855 } 03856 03857 03858 void MultiGetJob::slotFinished() 03859 { 03860 if (!findCurrentEntry()) return; 03861 if (m_redirectionURL.isEmpty()) 03862 { 03863 // No redirection, tell the world that we are finished. 03864 emit result(m_currentEntry->id); 03865 } 03866 m_redirectionURL = KURL(); 03867 m_error = 0; 03868 m_incomingMetaData.clear(); 03869 m_activeQueue.removeRef(m_currentEntry); 03870 if (m_activeQueue.count() == 0) 03871 { 03872 if (m_waitQueue.count() == 0) 03873 { 03874 // All done 03875 TransferJob::slotFinished(); 03876 } 03877 else 03878 { 03879 // return slave to pool 03880 // fetch new slave for first entry in m_waitQueue and call start 03881 // again. 03882 GetRequest *entry = m_waitQueue.at(0); 03883 m_url = entry->url; 03884 slaveDone(); 03885 Scheduler::doJob(this); 03886 } 03887 } 03888 } 03889 03890 void MultiGetJob::slotData( const QByteArray &_data) 03891 { 03892 if(!m_currentEntry) return;// Error, unknown request! 03893 if(m_redirectionURL.isEmpty() || !m_redirectionURL.isValid() || m_error) 03894 emit data(m_currentEntry->id, _data); 03895 } 03896 03897 void MultiGetJob::slotMimetype( const QString &_mimetype ) 03898 { 03899 if (b_multiGetActive) 03900 { 03901 QPtrList<GetRequest> newQueue; 03902 flushQueue(newQueue); 03903 if (!newQueue.isEmpty()) 03904 { 03905 while(!newQueue.isEmpty()) 03906 m_activeQueue.append(newQueue.take(0)); 03907 m_slave->send( m_command, m_packedArgs ); 03908 } 03909 } 03910 if (!findCurrentEntry()) return; // Error, unknown request! 03911 emit mimetype(m_currentEntry->id, _mimetype); 03912 } 03913 03914 MultiGetJob *KIO::multi_get(long id, const KURL &url, const MetaData &metaData) 03915 { 03916 MultiGetJob * job = new MultiGetJob( url, false ); 03917 job->get(id, url, metaData); 03918 return job; 03919 } 03920 03921 03922 #ifdef CACHE_INFO 03923 CacheInfo::CacheInfo(const KURL &url) 03924 { 03925 m_url = url; 03926 } 03927 03928 QString CacheInfo::cachedFileName() 03929 { 03930 const QChar separator = '_'; 03931 03932 QString CEF = m_url.path(); 03933 03934 int p = CEF.find('/'); 03935 03936 while(p != -1) 03937 { 03938 CEF[p] = separator; 03939 p = CEF.find('/', p); 03940 } 03941 03942 QString host = m_url.host().lower(); 03943 CEF = host + CEF + '_'; 03944 03945 QString dir = KProtocolManager::cacheDir(); 03946 if (dir[dir.length()-1] != '/') 03947 dir += "/"; 03948 03949 int l = m_url.host().length(); 03950 for(int i = 0; i < l; i++) 03951 { 03952 if (host[i].isLetter() && (host[i] != 'w')) 03953 { 03954 dir += host[i]; 03955 break; 03956 } 03957 } 03958 if (dir[dir.length()-1] == '/') 03959 dir += "0"; 03960 03961 unsigned long hash = 0x00000000; 03962 QCString u = m_url.url().latin1(); 03963 for(int i = u.length(); i--;) 03964 { 03965 hash = (hash * 12211 + u[i]) % 2147483563; 03966 } 03967 03968 QString hashString; 03969 hashString.sprintf("%08lx", hash); 03970 03971 CEF = CEF + hashString; 03972 03973 CEF = dir + "/" + CEF; 03974 03975 return CEF; 03976 } 03977 03978 QFile *CacheInfo::cachedFile() 03979 { 03980 const char *mode = (readWrite ? "r+" : "r"); 03981 03982 FILE *fs = fopen( CEF.latin1(), mode); // Open for reading and writing 03983 if (!fs) 03984 return 0; 03985 03986 char buffer[401]; 03987 bool ok = true; 03988 03989 // CacheRevision 03990 if (ok && (!fgets(buffer, 400, fs))) 03991 ok = false; 03992 if (ok && (strcmp(buffer, CACHE_REVISION) != 0)) 03993 ok = false; 03994 03995 time_t date; 03996 time_t currentDate = time(0); 03997 03998 // URL 03999 if (ok && (!fgets(buffer, 400, fs))) 04000 ok = false; 04001 if (ok) 04002 { 04003 int l = strlen(buffer); 04004 if (l>0) 04005 buffer[l-1] = 0; // Strip newline 04006 if (m_.url.url() != buffer) 04007 { 04008 ok = false; // Hash collision 04009 } 04010 } 04011 04012 // Creation Date 04013 if (ok && (!fgets(buffer, 400, fs))) 04014 ok = false; 04015 if (ok) 04016 { 04017 date = (time_t) strtoul(buffer, 0, 10); 04018 if (m_maxCacheAge && (difftime(currentDate, date) > m_maxCacheAge)) 04019 { 04020 m_bMustRevalidate = true; 04021 m_expireDate = currentDate; 04022 } 04023 } 04024 04025 // Expiration Date 04026 m_cacheExpireDateOffset = ftell(fs); 04027 if (ok && (!fgets(buffer, 400, fs))) 04028 ok = false; 04029 if (ok) 04030 { 04031 if (m_request.cache == CC_Verify) 04032 { 04033 date = (time_t) strtoul(buffer, 0, 10); 04034 // After the expire date we need to revalidate. 04035 if (!date || difftime(currentDate, date) >= 0) 04036 m_bMustRevalidate = true; 04037 m_expireDate = date; 04038 } 04039 } 04040 04041 // ETag 04042 if (ok && (!fgets(buffer, 400, fs))) 04043 ok = false; 04044 if (ok) 04045 { 04046 m_etag = QString(buffer).stripWhiteSpace(); 04047 } 04048 04049 // Last-Modified 04050 if (ok && (!fgets(buffer, 400, fs))) 04051 ok = false; 04052 if (ok) 04053 { 04054 m_lastModified = QString(buffer).stripWhiteSpace(); 04055 } 04056 04057 fclose(fs); 04058 04059 if (ok) 04060 return fs; 04061 04062 unlink( CEF.latin1()); 04063 return 0; 04064 04065 } 04066 04067 void CacheInfo::flush() 04068 { 04069 cachedFile().remove(); 04070 } 04071 04072 void CacheInfo::touch() 04073 { 04074 04075 } 04076 void CacheInfo::setExpireDate(int); 04077 void CacheInfo::setExpireTimeout(int); 04078 04079 04080 int CacheInfo::creationDate(); 04081 int CacheInfo::expireDate(); 04082 int CacheInfo::expireTimeout(); 04083 #endif 04084 04085 void Job::virtual_hook( int, void* ) 04086 { /*BASE::virtual_hook( id, data );*/ } 04087 04088 void SimpleJob::virtual_hook( int id, void* data ) 04089 { KIO::Job::virtual_hook( id, data ); } 04090 04091 void StatJob::virtual_hook( int id, void* data ) 04092 { SimpleJob::virtual_hook( id, data ); } 04093 04094 void TransferJob::virtual_hook( int id, void* data ) 04095 { SimpleJob::virtual_hook( id, data ); } 04096 04097 void MultiGetJob::virtual_hook( int id, void* data ) 04098 { TransferJob::virtual_hook( id, data ); } 04099 04100 void MimetypeJob::virtual_hook( int id, void* data ) 04101 { TransferJob::virtual_hook( id, data ); } 04102 04103 void FileCopyJob::virtual_hook( int id, void* data ) 04104 { Job::virtual_hook( id, data ); } 04105 04106 void ListJob::virtual_hook( int id, void* data ) 04107 { SimpleJob::virtual_hook( id, data ); } 04108 04109 void CopyJob::virtual_hook( int id, void* data ) 04110 { Job::virtual_hook( id, data ); } 04111 04112 void DeleteJob::virtual_hook( int id, void* data ) 04113 { Job::virtual_hook( id, data ); } 04114 04115 04116 #include "jobclasses.moc"
KDE Logo
This file is part of the documentation for kio Library Version 3.2.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sun Oct 10 18:55:25 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003