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, url ); 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 KMessageBox::information( 0, buildErrorString(ERR_CANNOT_DELETE, m_currentSrcURL.prettyURL())); 02295 statNextSrc(); // we could use a loop instead of a recursive call :) 02296 return; 02297 } 02298 // Stat the next src url 02299 Job * job = KIO::stat( m_currentSrcURL, true, 2, false ); 02300 //kdDebug(7007) << "KIO::stat on " << m_currentSrcURL.prettyURL() << endl; 02301 state = STATE_STATING; 02302 addSubjob(job); 02303 m_currentDestURL=m_dest; 02304 m_bOnlyRenames = false; 02305 } 02306 } else 02307 { 02308 // Finished the stat'ing phase 02309 // First make sure that the totals were correctly emitted 02310 state = STATE_STATING; 02311 slotReport(); 02312 // Check if we are copying a single file 02313 m_bSingleFileCopy = ( files.count() == 1 && dirs.isEmpty() ); 02314 // Then start copying things 02315 state = STATE_CREATING_DIRS; 02316 createNextDir(); 02317 } 02318 } 02319 02320 02321 void CopyJob::startListing( const KURL & src ) 02322 { 02323 state = STATE_LISTING; 02324 ListJob * newjob = listRecursive( src, false ); 02325 newjob->setUnrestricted(true); 02326 connect(newjob, SIGNAL(entries( KIO::Job *, 02327 const KIO::UDSEntryList& )), 02328 SLOT( slotEntries( KIO::Job*, 02329 const KIO::UDSEntryList& ))); 02330 addSubjob( newjob ); 02331 } 02332 02333 void CopyJob::skip( const KURL & sourceUrl ) 02334 { 02335 // Check if this is one if toplevel sources 02336 // IF yes, remove it from m_srcList, for a correct FilesRemoved() signal 02337 //kdDebug(7007) << "CopyJob::skip: looking for " << sourceUrl.prettyURL() << endl; 02338 KURL::List::Iterator sit = m_srcList.find( sourceUrl ); 02339 if ( sit != m_srcList.end() ) 02340 { 02341 //kdDebug(7007) << "CopyJob::skip: removing " << sourceUrl.prettyURL() << " from list" << endl; 02342 m_srcList.remove( sit ); 02343 } 02344 dirsToRemove.remove( sourceUrl ); 02345 } 02346 02347 void CopyJob::slotResultCreatingDirs( Job * job ) 02348 { 02349 // The dir we are trying to create: 02350 QValueList<CopyInfo>::Iterator it = dirs.begin(); 02351 // Was there an error creating a dir ? 02352 if ( job->error() ) 02353 { 02354 m_conflictError = job->error(); 02355 if ( (m_conflictError == ERR_DIR_ALREADY_EXIST) 02356 || (m_conflictError == ERR_FILE_ALREADY_EXIST) ) 02357 { 02358 KURL oldURL = ((SimpleJob*)job)->url(); 02359 // Should we skip automatically ? 02360 if ( m_bAutoSkip ) { 02361 // We dont want to copy files in this directory, so we put it on the skip list 02362 m_skipList.append( oldURL.path( 1 ) ); 02363 skip( oldURL ); 02364 dirs.remove( it ); // Move on to next dir 02365 } else { 02366 // Did the user choose to overwrite already? 02367 bool bOverwrite = m_bOverwriteAll; 02368 QString destFile = (*it).uDest.path(); 02369 QStringList::Iterator sit = m_overwriteList.begin(); 02370 for( ; sit != m_overwriteList.end() && !bOverwrite; sit++ ) 02371 if ( *sit == destFile.left( (*sit).length() ) ) 02372 bOverwrite = true; 02373 if ( bOverwrite ) { // overwrite => just skip 02374 emit copyingDone( this, ( *it ).uSource, ( *it ).uDest, true /* directory */, false /* renamed */ ); 02375 dirs.remove( it ); // Move on to next dir 02376 } else { 02377 assert( ((SimpleJob*)job)->url().url() == (*it).uDest.url() ); 02378 subjobs.remove( job ); 02379 assert ( subjobs.isEmpty() ); // We should have only one job at a time ... 02380 02381 // We need to stat the existing dir, to get its last-modification time 02382 KURL existingDest( (*it).uDest ); 02383 SimpleJob * newJob = KIO::stat( existingDest, false, 2, false ); 02384 Scheduler::scheduleJob(newJob); 02385 kdDebug(7007) << "KIO::stat for resolving conflict on " << existingDest.prettyURL() << endl; 02386 state = STATE_CONFLICT_CREATING_DIRS; 02387 addSubjob(newJob); 02388 return; // Don't move to next dir yet ! 02389 } 02390 } 02391 } 02392 else 02393 { 02394 // Severe error, abort 02395 Job::slotResult( job ); // will set the error and emit result(this) 02396 return; 02397 } 02398 } 02399 else // no error : remove from list, to move on to next dir 02400 { 02401 //this is required for the undo feature 02402 emit copyingDone( this, (*it).uSource, (*it).uDest, true, false ); 02403 dirs.remove( it ); 02404 } 02405 02406 m_processedDirs++; 02407 //emit processedDirs( this, m_processedDirs ); 02408 subjobs.remove( job ); 02409 assert ( subjobs.isEmpty() ); // We should have only one job at a time ... 02410 createNextDir(); 02411 } 02412 02413 void CopyJob::slotResultConflictCreatingDirs( KIO::Job * job ) 02414 { 02415 // We come here after a conflict has been detected and we've stated the existing dir 02416 02417 // The dir we were trying to create: 02418 QValueList<CopyInfo>::Iterator it = dirs.begin(); 02419 // Its modification time: 02420 time_t destmtime = (time_t)-1; 02421 time_t destctime = (time_t)-1; 02422 KIO::filesize_t destsize = 0; 02423 UDSEntry entry = ((KIO::StatJob*)job)->statResult(); 02424 KIO::UDSEntry::ConstIterator it2 = entry.begin(); 02425 for( ; it2 != entry.end(); it2++ ) { 02426 switch ((*it2).m_uds) { 02427 case UDS_MODIFICATION_TIME: 02428 destmtime = (time_t)((*it2).m_long); 02429 break; 02430 case UDS_CREATION_TIME: 02431 destctime = (time_t)((*it2).m_long); 02432 break; 02433 case UDS_SIZE: 02434 destsize = (*it2).m_long; 02435 break; 02436 } 02437 } 02438 subjobs.remove( job ); 02439 assert ( subjobs.isEmpty() ); // We should have only one job at a time ... 02440 02441 // Always multi and skip (since there are files after that) 02442 RenameDlg_Mode mode = (RenameDlg_Mode)( M_MULTI | M_SKIP ); 02443 // Overwrite only if the existing thing is a dir (no chance with a file) 02444 if ( m_conflictError == ERR_DIR_ALREADY_EXIST ) 02445 mode = (RenameDlg_Mode)( mode | (((*it).uSource == (*it).uDest) ? M_OVERWRITE_ITSELF : M_OVERWRITE )); 02446 02447 QString existingDest = (*it).uDest.path(); 02448 QString newPath; 02449 if (m_reportTimer) 02450 m_reportTimer->stop(); 02451 RenameDlg_Result r = Observer::self()->open_RenameDlg( this, i18n("Folder Already Exists"), 02452 (*it).uSource.prettyURL(0, KURL::StripFileProtocol), 02453 (*it).uDest.prettyURL(0, KURL::StripFileProtocol), 02454 mode, newPath, 02455 (*it).size, destsize, 02456 (*it).ctime, destctime, 02457 (*it).mtime, destmtime ); 02458 if (m_reportTimer) 02459 m_reportTimer->start(REPORT_TIMEOUT,false); 02460 switch ( r ) { 02461 case R_CANCEL: 02462 m_error = ERR_USER_CANCELED; 02463 emitResult(); 02464 return; 02465 case R_RENAME: 02466 { 02467 QString oldPath = (*it).uDest.path( 1 ); 02468 KURL newUrl( (*it).uDest ); 02469 newUrl.setPath( newPath ); 02470 emit renamed( this, (*it).uDest, newUrl ); // for e.g. kpropsdlg 02471 02472 // Change the current one and strip the trailing '/' 02473 (*it).uDest.setPath( newUrl.path( -1 ) ); 02474 newPath = newUrl.path( 1 ); // With trailing slash 02475 QValueList<CopyInfo>::Iterator renamedirit = it; 02476 ++renamedirit; 02477 // Change the name of subdirectories inside the directory 02478 for( ; renamedirit != dirs.end() ; ++renamedirit ) 02479 { 02480 QString path = (*renamedirit).uDest.path(); 02481 if ( path.left(oldPath.length()) == oldPath ) { 02482 QString n = path; 02483 n.replace( 0, oldPath.length(), newPath ); 02484 kdDebug(7007) << "dirs list: " << (*renamedirit).uSource.path() 02485 << " was going to be " << path 02486 << ", changed into " << n << endl; 02487 (*renamedirit).uDest.setPath( n ); 02488 } 02489 } 02490 // Change filenames inside the directory 02491 QValueList<CopyInfo>::Iterator renamefileit = files.begin(); 02492 for( ; renamefileit != files.end() ; ++renamefileit ) 02493 { 02494 QString path = (*renamefileit).uDest.path(); 02495 if ( path.left(oldPath.length()) == oldPath ) { 02496 QString n = path; 02497 n.replace( 0, oldPath.length(), newPath ); 02498 kdDebug(7007) << "files list: " << (*renamefileit).uSource.path() 02499 << " was going to be " << path 02500 << ", changed into " << n << endl; 02501 (*renamefileit).uDest.setPath( n ); 02502 } 02503 } 02504 if (!dirs.isEmpty()) 02505 emit aboutToCreate( this, dirs ); 02506 if (!files.isEmpty()) 02507 emit aboutToCreate( this, files ); 02508 } 02509 break; 02510 case R_AUTO_SKIP: 02511 m_bAutoSkip = true; 02512 // fall through 02513 case R_SKIP: 02514 m_skipList.append( existingDest ); 02515 skip( (*it).uSource ); 02516 // Move on to next dir 02517 dirs.remove( it ); 02518 m_processedDirs++; 02519 break; 02520 case R_OVERWRITE: 02521 m_overwriteList.append( existingDest ); 02522 emit copyingDone( this, ( *it ).uSource, ( *it ).uDest, true /* directory */, false /* renamed */ ); 02523 // Move on to next dir 02524 dirs.remove( it ); 02525 m_processedDirs++; 02526 break; 02527 case R_OVERWRITE_ALL: 02528 m_bOverwriteAll = true; 02529 emit copyingDone( this, ( *it ).uSource, ( *it ).uDest, true /* directory */, false /* renamed */ ); 02530 // Move on to next dir 02531 dirs.remove( it ); 02532 m_processedDirs++; 02533 break; 02534 default: 02535 assert( 0 ); 02536 } 02537 state = STATE_CREATING_DIRS; 02538 //emit processedDirs( this, m_processedDirs ); 02539 createNextDir(); 02540 } 02541 02542 void CopyJob::createNextDir() 02543 { 02544 KURL udir; 02545 if ( !dirs.isEmpty() ) 02546 { 02547 // Take first dir to create out of list 02548 QValueList<CopyInfo>::Iterator it = dirs.begin(); 02549 // Is this URL on the skip list or the overwrite list ? 02550 while( it != dirs.end() && udir.isEmpty() ) 02551 { 02552 QString dir = (*it).uDest.path(); 02553 bool bCreateDir = true; // we'll create it if it's not in any list 02554 02555 QStringList::Iterator sit = m_skipList.begin(); 02556 for( ; sit != m_skipList.end() && bCreateDir; sit++ ) 02557 // Is dir a subdirectory of *sit ? 02558 if ( *sit == dir.left( (*sit).length() ) ) 02559 bCreateDir = false; // skip this dir 02560 02561 if ( !bCreateDir ) { 02562 dirs.remove( it ); 02563 it = dirs.begin(); 02564 } else 02565 udir = (*it).uDest; 02566 } 02567 } 02568 if ( !udir.isEmpty() ) // any dir to create, finally ? 02569 { 02570 // Create the directory - with default permissions so that we can put files into it 02571 // TODO : change permissions once all is finished; but for stuff coming from CDROM it sucks... 02572 KIO::SimpleJob *newjob = KIO::mkdir( udir, -1 ); 02573 Scheduler::scheduleJob(newjob); 02574 02575 m_currentDestURL = udir; 02576 02577 addSubjob(newjob); 02578 return; 02579 } 02580 else // we have finished creating dirs 02581 { 02582 state = STATE_COPYING_FILES; 02583 m_processedFiles++; // Ralf wants it to start a 1, not 0 02584 copyNextFile(); 02585 } 02586 } 02587 02588 void CopyJob::slotResultCopyingFiles( Job * job ) 02589 { 02590 // The file we were trying to copy: 02591 QValueList<CopyInfo>::Iterator it = files.begin(); 02592 if ( job->error() ) 02593 { 02594 // Should we skip automatically ? 02595 if ( m_bAutoSkip ) 02596 { 02597 skip( (*it).uSource ); 02598 m_fileProcessedSize = (*it).size; 02599 files.remove( it ); // Move on to next file 02600 } 02601 else 02602 { 02603 m_conflictError = job->error(); // save for later 02604 // Existing dest ? 02605 if ( ( m_conflictError == ERR_FILE_ALREADY_EXIST ) 02606 || ( m_conflictError == ERR_DIR_ALREADY_EXIST ) ) 02607 { 02608 subjobs.remove( job ); 02609 assert ( subjobs.isEmpty() ); 02610 // We need to stat the existing file, to get its last-modification time 02611 KURL existingFile( (*it).uDest ); 02612 SimpleJob * newJob = KIO::stat( existingFile, false, 2, false ); 02613 Scheduler::scheduleJob(newJob); 02614 kdDebug(7007) << "KIO::stat for resolving conflict on " << existingFile.prettyURL() << endl; 02615 state = STATE_CONFLICT_COPYING_FILES; 02616 addSubjob(newJob); 02617 return; // Don't move to next file yet ! 02618 } 02619 else 02620 { 02621 if ( m_bCurrentOperationIsLink && job->inherits( "KIO::DeleteJob" ) ) 02622 { 02623 // Very special case, see a few lines below 02624 // We are deleting the source of a symlink we successfully moved... ignore error 02625 m_fileProcessedSize = (*it).size; 02626 files.remove( it ); 02627 } else { 02628 // Go directly to the conflict resolution, there is nothing to stat 02629 slotResultConflictCopyingFiles( job ); 02630 return; 02631 } 02632 } 02633 } 02634 } else // no error 02635 { 02636 // Special case for moving links. That operation needs two jobs, unlike others. 02637 if ( m_bCurrentOperationIsLink && m_mode == Move 02638 && !job->inherits( "KIO::DeleteJob" ) // Deleting source not already done 02639 ) 02640 { 02641 subjobs.remove( job ); 02642 assert ( subjobs.isEmpty() ); 02643 // The only problem with this trick is that the error handling for this del operation 02644 // is not going to be right... see 'Very special case' above. 02645 KIO::Job * newjob = KIO::del( (*it).uSource, false /*don't shred*/, false /*no GUI*/ ); 02646 addSubjob( newjob ); 02647 return; // Don't move to next file yet ! 02648 } 02649 02650 if ( m_bCurrentOperationIsLink ) 02651 { 02652 QString target = ( m_mode == Link ? (*it).uSource.path() : (*it).linkDest ); 02653 //required for the undo feature 02654 emit copyingLinkDone( this, (*it).uSource, target, (*it).uDest ); 02655 } 02656 else 02657 //required for the undo feature 02658 emit copyingDone( this, (*it).uSource, (*it).uDest, false, false ); 02659 // remove from list, to move on to next file 02660 files.remove( it ); 02661 } 02662 m_processedFiles++; 02663 02664 // clear processed size for last file and add it to overall processed size 02665 m_processedSize += m_fileProcessedSize; 02666 m_fileProcessedSize = 0; 02667 02668 //kdDebug(7007) << files.count() << " files remaining" << endl; 02669 subjobs.remove( job ); 02670 assert ( subjobs.isEmpty() ); // We should have only one job at a time ... 02671 copyNextFile(); 02672 } 02673 02674 void CopyJob::slotResultConflictCopyingFiles( KIO::Job * job ) 02675 { 02676 // We come here after a conflict has been detected and we've stated the existing file 02677 // The file we were trying to create: 02678 QValueList<CopyInfo>::Iterator it = files.begin(); 02679 02680 RenameDlg_Result res; 02681 QString newPath; 02682 02683 if (m_reportTimer) 02684 m_reportTimer->stop(); 02685 02686 if ( ( m_conflictError == ERR_FILE_ALREADY_EXIST ) 02687 || ( m_conflictError == ERR_DIR_ALREADY_EXIST ) ) 02688 { 02689 // Its modification time: 02690 time_t destmtime = (time_t)-1; 02691 time_t destctime = (time_t)-1; 02692 KIO::filesize_t destsize = 0; 02693 UDSEntry entry = ((KIO::StatJob*)job)->statResult(); 02694 KIO::UDSEntry::ConstIterator it2 = entry.begin(); 02695 for( ; it2 != entry.end(); it2++ ) { 02696 switch ((*it2).m_uds) { 02697 case UDS_MODIFICATION_TIME: 02698 destmtime = (time_t)((*it2).m_long); 02699 break; 02700 case UDS_CREATION_TIME: 02701 destctime = (time_t)((*it2).m_long); 02702 break; 02703 case UDS_SIZE: 02704 destsize = (*it2).m_long; 02705 break; 02706 } 02707 } 02708 02709 // Offer overwrite only if the existing thing is a file 02710 // If src==dest, use "overwrite-itself" 02711 RenameDlg_Mode mode = (RenameDlg_Mode) 02712 ( ( m_conflictError == ERR_DIR_ALREADY_EXIST ? 0 : 02713 ( (*it).uSource == (*it).uDest ) ? M_OVERWRITE_ITSELF : M_OVERWRITE ) ); 02714 if ( files.count() > 1 ) // Not last one 02715 mode = (RenameDlg_Mode) ( mode | M_MULTI | M_SKIP ); 02716 else 02717 mode = (RenameDlg_Mode) ( mode | M_SINGLE ); 02718 res = Observer::self()->open_RenameDlg( this, m_conflictError == ERR_FILE_ALREADY_EXIST ? 02719 i18n("File Already Exists") : i18n("Already Exists as Folder"), 02720 (*it).uSource.prettyURL(0, KURL::StripFileProtocol), 02721 (*it).uDest.prettyURL(0, KURL::StripFileProtocol), 02722 mode, newPath, 02723 (*it).size, destsize, 02724 (*it).ctime, destctime, 02725 (*it).mtime, destmtime ); 02726 02727 } 02728 else 02729 { 02730 if ( job->error() == ERR_USER_CANCELED ) 02731 res = R_CANCEL; 02732 else 02733 { 02734 SkipDlg_Result skipResult = Observer::self()->open_SkipDlg( this, files.count() > 1, 02735 job->errorString() ); 02736 02737 // Convert the return code from SkipDlg into a RenameDlg code 02738 res = ( skipResult == S_SKIP ) ? R_SKIP : 02739 ( skipResult == S_AUTO_SKIP ) ? R_AUTO_SKIP : 02740 R_CANCEL; 02741 } 02742 } 02743 02744 if (m_reportTimer) 02745 m_reportTimer->start(REPORT_TIMEOUT,false); 02746 02747 subjobs.remove( job ); 02748 assert ( subjobs.isEmpty() ); 02749 switch ( res ) { 02750 case R_CANCEL: 02751 m_error = ERR_USER_CANCELED; 02752 emitResult(); 02753 return; 02754 case R_RENAME: 02755 { 02756 KURL newUrl( (*it).uDest ); 02757 newUrl.setPath( newPath ); 02758 emit renamed( this, (*it).uDest, newUrl ); // for e.g. kpropsdlg 02759 (*it).uDest = newUrl; 02760 02761 QValueList<CopyInfo> files; 02762 files.append(*it); 02763 emit aboutToCreate( this, files ); 02764 } 02765 break; 02766 case R_AUTO_SKIP: 02767 m_bAutoSkip = true; 02768 // fall through 02769 case R_SKIP: 02770 // Move on to next file 02771 skip( (*it).uSource ); 02772 m_processedSize += (*it).size; 02773 files.remove( it ); 02774 m_processedFiles++; 02775 break; 02776 case R_OVERWRITE_ALL: 02777 m_bOverwriteAll = true; 02778 break; 02779 case R_OVERWRITE: 02780 // Add to overwrite list, so that copyNextFile knows to overwrite 02781 m_overwriteList.append( (*it).uDest.path() ); 02782 break; 02783 default: 02784 assert( 0 ); 02785 } 02786 state = STATE_COPYING_FILES; 02787 //emit processedFiles( this, m_processedFiles ); 02788 copyNextFile(); 02789 } 02790 02791 void CopyJob::copyNextFile() 02792 { 02793 bool bCopyFile = false; 02794 //kdDebug(7007) << "CopyJob::copyNextFile()" << endl; 02795 // Take the first file in the list 02796 QValueList<CopyInfo>::Iterator it = files.begin(); 02797 // Is this URL on the skip list ? 02798 while (it != files.end() && !bCopyFile) 02799 { 02800 bCopyFile = true; 02801 QString destFile = (*it).uDest.path(); 02802 02803 QStringList::Iterator sit = m_skipList.begin(); 02804 for( ; sit != m_skipList.end() && bCopyFile; sit++ ) 02805 // Is destFile in *sit (or a subdirectory of *sit) ? 02806 if ( *sit == destFile.left( (*sit).length() ) ) 02807 bCopyFile = false; // skip this file 02808 02809 if (!bCopyFile) { 02810 files.remove( it ); 02811 it = files.begin(); 02812 } 02813 } 02814 02815 if (bCopyFile) // any file to create, finally ? 02816 { 02817 // Do we set overwrite ? 02818 bool bOverwrite = m_bOverwriteAll; // yes if overwrite all 02819 QString destFile = (*it).uDest.path(); 02820 kdDebug(7007) << "copying " << destFile << endl; 02821 if ( (*it).uDest == (*it).uSource ) 02822 bOverwrite = false; 02823 else 02824 { 02825 // or if on the overwrite list 02826 QStringList::Iterator sit = m_overwriteList.begin(); 02827 for( ; sit != m_overwriteList.end() && !bOverwrite; sit++ ) 02828 if ( *sit == destFile.left( (*sit).length() ) ) 02829 bOverwrite = true; 02830 } 02831 02832 m_bCurrentOperationIsLink = false; 02833 KIO::Job * newjob = 0L; 02834 if ( m_mode == Link ) 02835 { 02836 //kdDebug(7007) << "Linking" << endl; 02837 if ( 02838 ((*it).uSource.protocol() == (*it).uDest.protocol()) && 02839 ((*it).uSource.host() == (*it).uDest.host()) && 02840 ((*it).uSource.port() == (*it).uDest.port()) && 02841 ((*it).uSource.user() == (*it).uDest.user()) && 02842 ((*it).uSource.pass() == (*it).uDest.pass()) ) 02843 { 02844 // This is the case of creating a real symlink 02845 KIO::SimpleJob *newJob = KIO::symlink( (*it).uSource.path(), (*it).uDest, bOverwrite, false /*no GUI*/ ); 02846 newjob = newJob; 02847 Scheduler::scheduleJob(newJob); 02848 //kdDebug(7007) << "CopyJob::copyNextFile : Linking target=" << (*it).uSource.path() << " link=" << (*it).uDest.prettyURL() << endl; 02849 //emit linking( this, (*it).uSource.path(), (*it).uDest ); 02850 m_bCurrentOperationIsLink = true; 02851 m_currentSrcURL=(*it).uSource; 02852 m_currentDestURL=(*it).uDest; 02853 //Observer::self()->slotCopying( this, (*it).uSource, (*it).uDest ); // should be slotLinking perhaps 02854 } else { 02855 //kdDebug(7007) << "CopyJob::copyNextFile : Linking URL=" << (*it).uSource.prettyURL() << " link=" << (*it).uDest.prettyURL() << endl; 02856 if ( (*it).uDest.isLocalFile() ) 02857 { 02858 bool devicesOk=false; 02859 02860 // if the source is a devices url, handle it a littlebit special 02861 if ((*it).uSource.protocol()==QString::fromLatin1("devices")) 02862 { 02863 QByteArray data; 02864 QByteArray param; 02865 QCString retType; 02866 QDataStream streamout(param,IO_WriteOnly); 02867 streamout<<(*it).uSource; 02868 streamout<<(*it).uDest; 02869 if ( kapp->dcopClient()->call( "kded", 02870 "mountwatcher", "createLink(KURL, KURL)", param,retType,data,false ) ) 02871 { 02872 QDataStream streamin(data,IO_ReadOnly); 02873 streamin>>devicesOk; 02874 } 02875 if (devicesOk) 02876 { 02877 files.remove( it ); 02878 m_processedFiles++; 02879 //emit processedFiles( this, m_processedFiles ); 02880 copyNextFile(); 02881 return; 02882 } 02883 } 02884 02885 if (!devicesOk) 02886 { 02887 QString path = (*it).uDest.path(); 02888 //kdDebug(7007) << "CopyJob::copyNextFile path=" << path << endl; 02889 QFile f( path ); 02890 if ( f.open( IO_ReadWrite ) ) 02891 { 02892 f.close(); 02893 KSimpleConfig config( path ); 02894 config.setDesktopGroup(); 02895 config.writePathEntry( QString::fromLatin1("URL"), (*it).uSource.url() ); 02896 config.writeEntry( QString::fromLatin1("Name"), (*it).uSource.url() ); 02897 config.writeEntry( QString::fromLatin1("Type"), QString::fromLatin1("Link") ); 02898 QString protocol = (*it).uSource.protocol(); 02899 if ( protocol == QString::fromLatin1("ftp") ) 02900 config.writeEntry( QString::fromLatin1("Icon"), QString::fromLatin1("ftp") ); 02901 else if ( protocol == QString::fromLatin1("http") ) 02902 config.writeEntry( QString::fromLatin1("Icon"), QString::fromLatin1("www") ); 02903 else if ( protocol == QString::fromLatin1("info") ) 02904 config.writeEntry( QString::fromLatin1("Icon"), QString::fromLatin1("info") ); 02905 else if ( protocol == QString::fromLatin1("mailto") ) // sven: 02906 config.writeEntry( QString::fromLatin1("Icon"), QString::fromLatin1("kmail") ); // added mailto: support 02907 else 02908 config.writeEntry( QString::fromLatin1("Icon"), QString::fromLatin1("unknown") ); 02909 config.sync(); 02910 files.remove( it ); 02911 m_processedFiles++; 02912 //emit processedFiles( this, m_processedFiles ); 02913 copyNextFile(); 02914 return; 02915 } 02916 else 02917 { 02918 kdDebug(7007) << "CopyJob::copyNextFile ERR_CANNOT_OPEN_FOR_WRITING" << endl; 02919 m_error = ERR_CANNOT_OPEN_FOR_WRITING; 02920 m_errorText = (*it).uDest.path(); 02921 emitResult(); 02922 return; 02923 } 02924 } 02925 } else { 02926 // Todo: not show "link" on remote dirs if the src urls are not from the same protocol+host+... 02927 m_error = ERR_CANNOT_SYMLINK; 02928 m_errorText = (*it).uDest.prettyURL(); 02929 emitResult(); 02930 return; 02931 } 02932 } 02933 } 02934 else if ( !(*it).linkDest.isEmpty() && 02935 ((*it).uSource.protocol() == (*it).uDest.protocol()) && 02936 ((*it).uSource.host() == (*it).uDest.host()) && 02937 ((*it).uSource.port() == (*it).uDest.port()) && 02938 ((*it).uSource.user() == (*it).uDest.user()) && 02939 ((*it).uSource.pass() == (*it).uDest.pass())) 02940 // Copying a symlink - only on the same protocol/host/etc. (#5601, downloading an FTP file through its link), 02941 { 02942 KIO::SimpleJob *newJob = KIO::symlink( (*it).linkDest, (*it).uDest, bOverwrite, false /*no GUI*/ ); 02943 Scheduler::scheduleJob(newJob); 02944 newjob = newJob; 02945 //kdDebug(7007) << "CopyJob::copyNextFile : Linking target=" << (*it).linkDest << " link=" << (*it).uDest.prettyURL() << endl; 02946 //emit linking( this, (*it).linkDest, (*it).uDest ); 02947 m_currentSrcURL=(*it).linkDest; 02948 m_currentDestURL=(*it).uDest; 02949 //Observer::self()->slotCopying( this, (*it).linkDest, (*it).uDest ); // should be slotLinking perhaps 02950 m_bCurrentOperationIsLink = true; 02951 // NOTE: if we are moving stuff, the deletion of the source will be done in slotResultCopyingFiles 02952 } else if (m_mode == Move) // Moving a file 02953 { 02954 KIO::FileCopyJob * moveJob = KIO::file_move( (*it).uSource, (*it).uDest, (*it).permissions, bOverwrite, false, false/*no GUI*/ ); 02955 moveJob->setSourceSize64( (*it).size ); 02956 newjob = moveJob; 02957 //kdDebug(7007) << "CopyJob::copyNextFile : Moving " << (*it).uSource << " to " << (*it).uDest << endl; 02958 //emit moving( this, (*it).uSource, (*it).uDest ); 02959 m_currentSrcURL=(*it).uSource; 02960 m_currentDestURL=(*it).uDest; 02961 //Observer::self()->slotMoving( this, (*it).uSource, (*it).uDest ); 02962 } 02963 else // Copying a file 02964 { 02965 // If source isn't local and target is local, we ignore the original permissions 02966 // Otherwise, files downloaded from HTTP end up with -r--r--r-- 02967 bool remoteSource = !KProtocolInfo::supportsListing((*it).uSource); 02968 int permissions = (*it).permissions; 02969 if ( d->m_defaultPermissions || ( remoteSource && (*it).uDest.isLocalFile() ) ) 02970 permissions = -1; 02971 KIO::FileCopyJob * copyJob = KIO::file_copy( (*it).uSource, (*it).uDest, permissions, bOverwrite, false, false/*no GUI*/ ); 02972 copyJob->setParentJob( this ); // in case of rename dialog 02973 copyJob->setSourceSize64( (*it).size ); 02974 newjob = copyJob; 02975 //kdDebug(7007) << "CopyJob::copyNextFile : Copying " << (*it).uSource << " to " << (*it).uDest << endl; 02976 m_currentSrcURL=(*it).uSource; 02977 m_currentDestURL=(*it).uDest; 02978 } 02979 addSubjob(newjob); 02980 connect( newjob, SIGNAL( processedSize( KIO::Job*, KIO::filesize_t ) ), 02981 this, SLOT( slotProcessedSize( KIO::Job*, KIO::filesize_t ) ) ); 02982 connect( newjob, SIGNAL( totalSize( KIO::Job*, KIO::filesize_t ) ), 02983 this, SLOT( slotTotalSize( KIO::Job*, KIO::filesize_t ) ) ); 02984 } 02985 else 02986 { 02987 // We're done 02988 //kdDebug(7007) << "copyNextFile finished" << endl; 02989 deleteNextDir(); 02990 } 02991 } 02992 02993 void CopyJob::deleteNextDir() 02994 { 02995 if ( m_mode == Move && !dirsToRemove.isEmpty() ) // some dirs to delete ? 02996 { 02997 state = STATE_DELETING_DIRS; 02998 // Take first dir to delete out of list - last ones first ! 02999 KURL::List::Iterator it = dirsToRemove.fromLast(); 03000 SimpleJob *job = KIO::rmdir( *it ); 03001 Scheduler::scheduleJob(job); 03002 dirsToRemove.remove(it); 03003 addSubjob( job ); 03004 } 03005 else 03006 { 03007 // Finished - tell the world 03008 if ( !m_bOnlyRenames ) 03009 { 03010 KDirNotify_stub allDirNotify("*", "KDirNotify*"); 03011 KURL url( d->m_globalDest ); 03012 if ( d->m_globalDestinationState != DEST_IS_DIR || m_asMethod ) 03013 url.setPath( url.directory() ); 03014 //kdDebug(7007) << "KDirNotify'ing FilesAdded " << url.prettyURL() << endl; 03015 allDirNotify.FilesAdded( url ); 03016 03017 if ( m_mode == Move && !m_srcList.isEmpty() ) 03018 allDirNotify.FilesRemoved( m_srcList ); 03019 } 03020 if (m_reportTimer!=0) 03021 m_reportTimer->stop(); 03022 emitResult(); 03023 } 03024 } 03025 03026 void CopyJob::slotProcessedSize( KIO::Job*, KIO::filesize_t data_size ) 03027 { 03028 //kdDebug(7007) << "CopyJob::slotProcessedSize " << (unsigned long)data_size << endl; 03029 m_fileProcessedSize = data_size; 03030 setProcessedSize(m_processedSize + m_fileProcessedSize); 03031 03032 if ( m_processedSize + m_fileProcessedSize > m_totalSize ) 03033 { 03034 m_totalSize = m_processedSize + m_fileProcessedSize; 03035 //kdDebug(7007) << "Adjusting m_totalSize to " << (unsigned long) m_totalSize << endl; 03036 emit totalSize( this, m_totalSize ); // safety 03037 } 03038 //kdDebug(7007) << "emit processedSize " << (unsigned long) (m_processedSize + m_fileProcessedSize) << endl; 03039 emit processedSize( this, m_processedSize + m_fileProcessedSize ); 03040 emitPercent( m_processedSize + m_fileProcessedSize, m_totalSize ); 03041 } 03042 03043 void CopyJob::slotTotalSize( KIO::Job*, KIO::filesize_t size ) 03044 { 03045 // Special case for copying a single file 03046 // This is because some protocols don't implement stat properly 03047 // (e.g. HTTP), and don't give us a size in some cases (redirection) 03048 // so we'd rather rely on the size given for the transfer 03049 if ( m_bSingleFileCopy ) 03050 { 03051 //kdDebug(7007) << "Single file -> updating totalsize to " << (long)size << endl; 03052 m_totalSize = size; 03053 emit totalSize( this, size ); 03054 } 03055 } 03056 03057 void CopyJob::slotResultDeletingDirs( Job * job ) 03058 { 03059 if (job->error()) 03060 { 03061 // Couldn't remove directory. Well, perhaps it's not empty 03062 // because the user pressed Skip for a given file in it. 03063 // Let's not display "Could not remove dir ..." for each of those dir ! 03064 } 03065 subjobs.remove( job ); 03066 assert ( subjobs.isEmpty() ); 03067 deleteNextDir(); 03068 } 03069 03070 void CopyJob::slotResult( Job *job ) 03071 { 03072 //kdDebug(7007) << "CopyJob::slotResult() state=" << (int) state << endl; 03073 // In each case, what we have to do is : 03074 // 1 - check for errors and treat them 03075 // 2 - subjobs.remove(job); 03076 // 3 - decide what to do next 03077 03078 switch ( state ) { 03079 case STATE_STATING: // We were trying to stat a src url or the dest 03080 slotResultStating( job ); 03081 break; 03082 case STATE_RENAMING: // We were trying to do a direct renaming, before even stat'ing 03083 { 03084 int err = job->error(); 03085 subjobs.remove( job ); 03086 assert ( subjobs.isEmpty() ); 03087 // Determine dest again 03088 KURL dest = m_dest; 03089 if ( destinationState == DEST_IS_DIR && !m_asMethod ) 03090 dest.addPath( m_currentSrcURL.fileName() ); 03091 if ( err ) 03092 { 03093 // Direct renaming didn't work. Try renaming to a temp name, 03094 // this can help e.g. when renaming 'a' to 'A' on a VFAT partition. 03095 // In that case it's the _same_ dir, we don't want to copy+del (data loss!) 03096 if ( m_currentSrcURL.isLocalFile() && m_currentSrcURL.url(-1) != dest.url(-1) && 03097 m_currentSrcURL.url(-1).lower() == dest.url(-1).lower() && 03098 ( err == ERR_FILE_ALREADY_EXIST || err == ERR_DIR_ALREADY_EXIST ) ) 03099 { 03100 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; 03101 QCString _src( QFile::encodeName(m_currentSrcURL.path()) ); 03102 QCString _dest( QFile::encodeName(dest.path()) ); 03103 KTempFile tmpFile( m_currentSrcURL.directory(false) ); 03104 QCString _tmp( QFile::encodeName(tmpFile.name()) ); 03105 kdDebug(7007) << "CopyJob::slotResult KTempFile status:" << tmpFile.status() << " using " << _tmp << " as intermediary" << endl; 03106 tmpFile.unlink(); 03107 if ( ::rename( _src, _tmp ) == 0 ) 03108 { 03109 if ( !QFile::exists( _dest ) && ::rename( _tmp, _dest ) == 0 ) 03110 { 03111 kdDebug(7007) << "Success." << endl; 03112 err = 0; 03113 } 03114 else 03115 { 03116 // Revert back to original name! 03117 if ( ::rename( _tmp, _src ) != 0 ) { 03118 kdError(7007) << "Couldn't rename " << tmpFile.name() << " back to " << _src << " !" << endl; 03119 // Severe error, abort 03120 Job::slotResult( job ); // will set the error and emit result(this) 03121 return; 03122 } 03123 } 03124 } 03125 } 03126 } 03127 if ( err ) 03128 { 03129 // This code is similar to CopyJob::slotResultConflictCopyingFiles 03130 // but here it's about the base src url being moved/renamed 03131 // (*m_currentStatSrc) and its dest (m_dest), not about a single file. 03132 // It also means we already stated the dest, here. 03133 // On the other hand we haven't stated the src yet (we skipped doing it 03134 // to save time, since it's not necessary to rename directly!)... 03135 03136 Q_ASSERT( m_currentSrcURL == *m_currentStatSrc ); 03137 03138 // Existing dest? 03139 if ( err == ERR_DIR_ALREADY_EXIST || err == ERR_FILE_ALREADY_EXIST ) 03140 { 03141 if (m_reportTimer) 03142 m_reportTimer->stop(); 03143 03144 QString newPath; 03145 // Offer overwrite only if the existing thing is a file 03146 // If src==dest, use "overwrite-itself" 03147 RenameDlg_Mode mode = (RenameDlg_Mode) 03148 ( ( m_conflictError == ERR_DIR_ALREADY_EXIST ? 0 : 03149 ( m_currentSrcURL == dest ) ? M_OVERWRITE_ITSELF : M_OVERWRITE ) ); 03150 // I won't use M_MULTI or M_SKIP there. It's too ambiguous: 03151 // we are in the middle of the stat phase, so it's hard to know 03152 // if it will apply to the already-stated-dirs that will be copied later, 03153 // and/oor to the to-be-stated src urls that might be in the same case... 03154 mode = (RenameDlg_Mode) ( mode | M_SINGLE ); 03155 // we lack mtime info for both the src (not stated) 03156 // and the dest (stated but this info wasn't stored) 03157 RenameDlg_Result r = Observer::self()->open_RenameDlg( this, 03158 err == ERR_FILE_ALREADY_EXIST ? i18n("File Already Exists") : i18n("Already Exists as Folder"), 03159 m_currentSrcURL.prettyURL(0, KURL::StripFileProtocol), 03160 dest.prettyURL(0, KURL::StripFileProtocol), 03161 mode, newPath ); 03162 if (m_reportTimer) 03163 m_reportTimer->start(REPORT_TIMEOUT,false); 03164 03165 switch ( r ) 03166 { 03167 case R_CANCEL: 03168 { 03169 m_error = ERR_USER_CANCELED; 03170 emitResult(); 03171 return; 03172 } 03173 case R_RENAME: 03174 { 03175 // Set m_dest to the chosen destination 03176 // This is only for this src url; the next one will revert to d->m_globalDest 03177 m_dest.setPath( newPath ); 03178 KIO::Job* job = KIO::stat( m_dest, false, 2, false ); 03179 state = STATE_STATING; 03180 destinationState = DEST_NOT_STATED; 03181 addSubjob(job); 03182 return; 03183 } 03184 case R_OVERWRITE: 03185 // Add to overwrite list 03186 // Note that we add dest, not m_dest. 03187 // This ensures that when moving several urls into a dir (m_dest), 03188 // we only overwrite for the current one, not for all. 03189 // When renaming a single file (m_asMethod), it makes no difference. 03190 kdDebug(7007) << "adding to overwrite list: " << dest.path() << endl; 03191 m_overwriteList.append( dest.path() ); 03192 break; 03193 default: 03194 //assert( 0 ); 03195 break; 03196 } 03197 } 03198 03199 kdDebug(7007) << "Couldn't rename, reverting to normal way, starting with stat" << endl; 03200 //kdDebug(7007) << "KIO::stat on " << m_currentSrcURL.prettyURL() << endl; 03201 KIO::Job* job = KIO::stat( m_currentSrcURL, true, 2, false ); 03202 state = STATE_STATING; 03203 addSubjob(job); 03204 m_bOnlyRenames = false; 03205 } 03206 else 03207 { 03208 //kdDebug(7007) << "Renaming succeeded, move on" << endl; 03209 emit copyingDone( this, *m_currentStatSrc, dest, true, true ); 03210 statNextSrc(); 03211 } 03212 } 03213 break; 03214 case STATE_LISTING: // recursive listing finished 03215 //kdDebug(7007) << "totalSize: " << (unsigned int) m_totalSize << " files: " << files.count() << " dirs: " << dirs.count() << endl; 03216 // Was there an error ? 03217 if (job->error()) 03218 { 03219 Job::slotResult( job ); // will set the error and emit result(this) 03220 return; 03221 } 03222 03223 subjobs.remove( job ); 03224 assert ( subjobs.isEmpty() ); 03225 03226 statNextSrc(); 03227 break; 03228 case STATE_CREATING_DIRS: 03229 slotResultCreatingDirs( job ); 03230 break; 03231 case STATE_CONFLICT_CREATING_DIRS: 03232 slotResultConflictCreatingDirs( job ); 03233 break; 03234 case STATE_COPYING_FILES: 03235 slotResultCopyingFiles( job ); 03236 break; 03237 case STATE_CONFLICT_COPYING_FILES: 03238 slotResultConflictCopyingFiles( job ); 03239 break; 03240 case STATE_DELETING_DIRS: 03241 slotResultDeletingDirs( job ); 03242 break; 03243 default: 03244 assert( 0 ); 03245 } 03246 } 03247 03248 void KIO::CopyJob::setDefaultPermissions( bool b ) 03249 { 03250 d->m_defaultPermissions = b; 03251 } 03252 03253 CopyJob *KIO::copy(const KURL& src, const KURL& dest, bool showProgressInfo ) 03254 { 03255 //kdDebug(7007) << "KIO::copy src=" << src.url() << " dest=" << dest.url() << endl; 03256 KURL::List srcList; 03257 srcList.append( src ); 03258 return new CopyJob( srcList, dest, CopyJob::Copy, false, showProgressInfo ); 03259 } 03260 03261 CopyJob *KIO::copyAs(const KURL& src, const KURL& dest, bool showProgressInfo ) 03262 { 03263 //kdDebug(7007) << "KIO::copyAs src=" << src.url() << " dest=" << dest.url() << endl; 03264 KURL::List srcList; 03265 srcList.append( src ); 03266 return new CopyJob( srcList, dest, CopyJob::Copy, true, showProgressInfo ); 03267 } 03268 03269 CopyJob *KIO::copy( const KURL::List& src, const KURL& dest, bool showProgressInfo ) 03270 { 03271 return new CopyJob( src, dest, CopyJob::Copy, false, showProgressInfo ); 03272 } 03273 03274 CopyJob *KIO::move(const KURL& src, const KURL& dest, bool showProgressInfo ) 03275 { 03276 KURL::List srcList; 03277 srcList.append( src ); 03278 return new CopyJob( srcList, dest, CopyJob::Move, false, showProgressInfo ); 03279 } 03280 03281 CopyJob *KIO::moveAs(const KURL& src, const KURL& dest, bool showProgressInfo ) 03282 { 03283 KURL::List srcList; 03284 srcList.append( src ); 03285 return new CopyJob( srcList, dest, CopyJob::Move, true, showProgressInfo ); 03286 } 03287 03288 CopyJob *KIO::move( const KURL::List& src, const KURL& dest, bool showProgressInfo ) 03289 { 03290 return new CopyJob( src, dest, CopyJob::Move, false, showProgressInfo ); 03291 } 03292 03293 CopyJob *KIO::link(const KURL& src, const KURL& destDir, bool showProgressInfo ) 03294 { 03295 KURL::List srcList; 03296 srcList.append( src ); 03297 return new CopyJob( srcList, destDir, CopyJob::Link, false, showProgressInfo ); 03298 } 03299 03300 CopyJob *KIO::link(const KURL::List& srcList, const KURL& destDir, bool showProgressInfo ) 03301 { 03302 return new CopyJob( srcList, destDir, CopyJob::Link, false, showProgressInfo ); 03303 } 03304 03305 CopyJob *KIO::linkAs(const KURL& src, const KURL& destDir, bool showProgressInfo ) 03306 { 03307 KURL::List srcList; 03308 srcList.append( src ); 03309 return new CopyJob( srcList, destDir, CopyJob::Link, false, showProgressInfo ); 03310 } 03311 03313 03314 DeleteJob::DeleteJob( const KURL::List& src, bool shred, bool showProgressInfo ) 03315 : Job(showProgressInfo), m_totalSize( 0 ), m_processedSize( 0 ), m_fileProcessedSize( 0 ), 03316 m_processedFiles( 0 ), m_processedDirs( 0 ), m_totalFilesDirs( 0 ), 03317 m_srcList(src), m_currentStat(m_srcList.begin()), m_shred(shred), m_reportTimer(0) 03318 { 03319 if ( showProgressInfo ) { 03320 03321 connect( this, SIGNAL( totalFiles( KIO::Job*, unsigned long ) ), 03322 Observer::self(), SLOT( slotTotalFiles( KIO::Job*, unsigned long ) ) ); 03323 03324 connect( this, SIGNAL( totalDirs( KIO::Job*, unsigned long ) ), 03325 Observer::self(), SLOT( slotTotalDirs( KIO::Job*, unsigned long ) ) ); 03326 03327 // See slotReport 03328 /*connect( this, SIGNAL( processedFiles( KIO::Job*, unsigned long ) ), 03329 m_observer, SLOT( slotProcessedFiles( KIO::Job*, unsigned long ) ) ); 03330 03331 connect( this, SIGNAL( processedDirs( KIO::Job*, unsigned long ) ), 03332 m_observer, SLOT( slotProcessedDirs( KIO::Job*, unsigned long ) ) ); 03333 03334 connect( this, SIGNAL( deleting( KIO::Job*, const KURL& ) ), 03335 m_observer, SLOT( slotDeleting( KIO::Job*, const KURL& ) ) );*/ 03336 03337 m_reportTimer=new QTimer(this); 03338 connect(m_reportTimer,SIGNAL(timeout()),this,SLOT(slotReport())); 03339 //this will update the report dialog with 5 Hz, I think this is fast enough, aleXXX 03340 m_reportTimer->start(REPORT_TIMEOUT,false); 03341 } 03342 03343 QTimer::singleShot(0, this, SLOT(slotStart())); 03344 } 03345 03346 void DeleteJob::slotStart() 03347 { 03348 statNextSrc(); 03349 } 03350 03351 //this is called often, so calling the functions 03352 //from Observer here directly might improve the performance a little bit 03353 //aleXXX 03354 void DeleteJob::slotReport() 03355 { 03356 if (m_progressId==0) 03357 return; 03358 03359 Observer * observer = Observer::self(); 03360 03361 emit deleting( this, m_currentURL ); 03362 observer->slotDeleting(this,m_currentURL); 03363 03364 switch( state ) { 03365 case STATE_STATING: 03366 case STATE_LISTING: 03367 emit totalSize( this, m_totalSize ); 03368 emit totalFiles( this, files.count() ); 03369 emit totalDirs( this, dirs.count() ); 03370 break; 03371 case STATE_DELETING_DIRS: 03372 emit processedDirs( this, m_processedDirs ); 03373 observer->slotProcessedDirs(this,m_processedDirs); 03374 emitPercent( m_processedFiles + m_processedDirs, m_totalFilesDirs ); 03375 break; 03376 case STATE_DELETING_FILES: 03377 observer->slotProcessedFiles(this,m_processedFiles); 03378 emit processedFiles( this, m_processedFiles ); 03379 if (!m_shred) 03380 emitPercent( m_processedFiles, m_totalFilesDirs ); 03381 break; 03382 } 03383 } 03384 03385 03386 void DeleteJob::slotEntries(KIO::Job* job, const UDSEntryList& list) 03387 { 03388 UDSEntryListConstIterator it = list.begin(); 03389 UDSEntryListConstIterator end = list.end(); 03390 for (; it != end; ++it) 03391 { 03392 UDSEntry::ConstIterator it2 = (*it).begin(); 03393 bool bDir = false; 03394 bool bLink = false; 03395 QString relName; 03396 int atomsFound(0); 03397 for( ; it2 != (*it).end(); it2++ ) 03398 { 03399 switch ((*it2).m_uds) 03400 { 03401 case UDS_FILE_TYPE: 03402 bDir = S_ISDIR((*it2).m_long); 03403 atomsFound++; 03404 break; 03405 case UDS_NAME: 03406 relName = ((*it2).m_str); 03407 atomsFound++; 03408 break; 03409 case UDS_LINK_DEST: 03410 bLink = !(*it2).m_str.isEmpty(); 03411 atomsFound++; 03412 break; 03413 case UDS_SIZE: 03414 m_totalSize += (KIO::filesize_t)((*it2).m_long); 03415 atomsFound++; 03416 break; 03417 default: 03418 break; 03419 } 03420 if (atomsFound==4) break; 03421 } 03422 assert(!relName.isEmpty()); 03423 if (relName != ".." && relName != ".") 03424 { 03425 KURL url = ((SimpleJob *)job)->url(); // assumed to be a dir 03426 url.addPath( relName ); 03427 //kdDebug(7007) << "DeleteJob::slotEntries " << relName << " (" << url.prettyURL() << ")" << endl; 03428 if ( bLink ) 03429 symlinks.append( url ); 03430 else if ( bDir ) 03431 dirs.append( url ); 03432 else 03433 files.append( url ); 03434 } 03435 } 03436 } 03437 03438 03439 void DeleteJob::statNextSrc() 03440 { 03441 //kdDebug(7007) << "statNextSrc" << endl; 03442 if ( m_currentStat != m_srcList.end() ) 03443 { 03444 m_currentURL = (*m_currentStat); 03445 03446 // if the file system doesn't support deleting, we do not even stat 03447 if (!KProtocolInfo::supportsDeleting(m_currentURL)) { 03448 KMessageBox::information( 0, buildErrorString(ERR_CANNOT_DELETE, m_currentURL.prettyURL())); 03449 ++m_currentStat; 03450 statNextSrc(); // we could use a loop instead of a recursive call :) 03451 return; 03452 } 03453 // Stat it 03454 state = STATE_STATING; 03455 KIO::SimpleJob * job = KIO::stat( m_currentURL, true, 1, false ); 03456 Scheduler::scheduleJob(job); 03457 //kdDebug(7007) << "KIO::stat (DeleteJob) " << m_currentURL.prettyURL() << endl; 03458 addSubjob(job); 03459 //if ( m_progressId ) // Did we get an ID from the observer ? 03460 // Observer::self()->slotDeleting( this, *it ); // show asap 03461 } else 03462 { 03463 m_totalFilesDirs = files.count()+symlinks.count() + dirs.count(); 03464 slotReport(); 03465 // Now we know which dirs hold the files we're going to delete. 03466 // To speed things up and prevent double-notification, we disable KDirWatch 03467 // on those dirs temporarily (using KDirWatch::self, that's the instanced 03468 // used by e.g. kdirlister). 03469 for ( QStringList::Iterator it = m_parentDirs.begin() ; it != m_parentDirs.end() ; ++it ) 03470 KDirWatch::self()->stopDirScan( *it ); 03471 state = STATE_DELETING_FILES; 03472 deleteNextFile(); 03473 } 03474 } 03475 03476 void DeleteJob::deleteNextFile() 03477 { 03478 //kdDebug(7007) << "deleteNextFile" << endl; 03479 if ( !files.isEmpty() || !symlinks.isEmpty() ) 03480 { 03481 SimpleJob *job; 03482 do { 03483 // Take first file to delete out of list 03484 KURL::List::Iterator it = files.begin(); 03485 bool isLink = false; 03486 if ( it == files.end() ) // No more files 03487 { 03488 it = symlinks.begin(); // Pick up a symlink to delete 03489 isLink = true; 03490 } 03491 // Use shredding ? 03492 if ( m_shred && (*it).isLocalFile() && !isLink ) 03493 { 03494 // KShred your KTie 03495 KIO_ARGS << int(3) << (*it).path(); 03496 job = KIO::special(KURL("file:/"), packedArgs, false /*no GUI*/); 03497 Scheduler::scheduleJob(job); 03498 m_currentURL=(*it); 03499 connect( job, SIGNAL( processedSize( KIO::Job*, KIO::filesize_t ) ), 03500 this, SLOT( slotProcessedSize( KIO::Job*, KIO::filesize_t ) ) ); 03501 } else 03502 { 03503 // Normal deletion 03504 // If local file, try do it directly 03505 if ( (*it).isLocalFile() && unlink( QFile::encodeName((*it).path()) ) == 0 ) { 03506 job = 0; 03507 m_processedFiles++; 03508 if ( m_processedFiles % 300 == 0 ) { // update progress info every 300 files 03509 m_currentURL = *it; 03510 slotReport(); 03511 } 03512 } else 03513 { // if remote - or if unlink() failed (we'll use the job's error handling in that case) 03514 job = KIO::file_delete( *it, false /*no GUI*/); 03515 Scheduler::scheduleJob(job); 03516 m_currentURL=(*it); 03517 } 03518 } 03519 if ( isLink ) 03520 symlinks.remove(it); 03521 else 03522 files.remove(it); 03523 if ( job ) { 03524 addSubjob(job); 03525 return; 03526 } 03527 // loop only if direct deletion worked (job=0) and there is something else to delete 03528 } while (!job && (!files.isEmpty() || !symlinks.isEmpty())); 03529 } 03530 state = STATE_DELETING_DIRS; 03531 deleteNextDir(); 03532 } 03533 03534 void DeleteJob::deleteNextDir() 03535 { 03536 if ( !dirs.isEmpty() ) // some dirs to delete ? 03537 { 03538 do { 03539 // Take first dir to delete out of list - last ones first ! 03540 KURL::List::Iterator it = dirs.fromLast(); 03541 // If local dir, try to rmdir it directly 03542 if ( (*it).isLocalFile() && ::rmdir( QFile::encodeName((*it).path()) ) == 0 ) { 03543 03544 m_processedDirs++; 03545 if ( m_processedDirs % 100 == 0 ) { // update progress info every 100 dirs 03546 m_currentURL = *it; 03547 slotReport(); 03548 } 03549 } else 03550 { 03551 SimpleJob *job = KIO::rmdir( *it ); 03552 Scheduler::scheduleJob(job); 03553 dirs.remove(it); 03554 addSubjob( job ); 03555 return; 03556 } 03557 dirs.remove(it); 03558 } while ( !dirs.isEmpty() ); 03559 } 03560 03561 // Re-enable watching on the dirs that held the deleted files 03562 for ( QStringList::Iterator it = m_parentDirs.begin() ; it != m_parentDirs.end() ; ++it ) 03563 KDirWatch::self()->restartDirScan( *it ); 03564 03565 // Finished - tell the world 03566 if ( !m_srcList.isEmpty() ) 03567 { 03568 KDirNotify_stub allDirNotify("*", "KDirNotify*"); 03569 allDirNotify.FilesRemoved( m_srcList ); 03570 } 03571 if (m_reportTimer!=0) 03572 m_reportTimer->stop(); 03573 emitResult(); 03574 } 03575 03576 void DeleteJob::slotProcessedSize( KIO::Job*, KIO::filesize_t data_size ) 03577 { 03578 // Note: this is the same implementation as CopyJob::slotProcessedSize but 03579 // it's different from FileCopyJob::slotProcessedSize - which is why this 03580 // is not in Job. 03581 03582 m_fileProcessedSize = data_size; 03583 setProcessedSize(m_processedSize + m_fileProcessedSize); 03584 03585 //kdDebug(7007) << "DeleteJob::slotProcessedSize " << (unsigned int) (m_processedSize + m_fileProcessedSize) << endl; 03586 03587 emit processedSize( this, m_processedSize + m_fileProcessedSize ); 03588 03589 // calculate percents 03590 unsigned long ipercent = m_percent; 03591 03592 if ( m_totalSize == 0 ) 03593 m_percent = 100; 03594 else 03595 m_percent = (unsigned long)(( (float)(m_processedSize + m_fileProcessedSize) / (float)m_totalSize ) * 100.0); 03596 03597 if ( m_percent > ipercent ) 03598 { 03599 emit percent( this, m_percent ); 03600 //kdDebug(7007) << "DeleteJob::slotProcessedSize - percent = " << (unsigned int) m_percent << endl; 03601 } 03602 03603 } 03604 03605 void DeleteJob::slotResult( Job *job ) 03606 { 03607 switch ( state ) 03608 { 03609 case STATE_STATING: 03610 { 03611 // Was there an error while stating ? 03612 if (job->error() ) 03613 { 03614 // Probably : doesn't exist 03615 Job::slotResult( job ); // will set the error and emit result(this) 03616 return; 03617 } 03618 03619 // Is it a file or a dir ? 03620 UDSEntry entry = ((StatJob*)job)->statResult(); 03621 bool bDir = false; 03622 bool bLink = false; 03623 KIO::filesize_t size = (KIO::filesize_t)-1; 03624 UDSEntry::ConstIterator it2 = entry.begin(); 03625 int atomsFound(0); 03626 for( ; it2 != entry.end(); it2++ ) 03627 { 03628 if ( ((*it2).m_uds) == UDS_FILE_TYPE ) 03629 { 03630 bDir = S_ISDIR( (mode_t)(*it2).m_long ); 03631 atomsFound++; 03632 } 03633 else if ( ((*it2).m_uds) == UDS_LINK_DEST ) 03634 { 03635 bLink = !((*it2).m_str.isEmpty()); 03636 atomsFound++; 03637 } 03638 else if ( ((*it2).m_uds) == UDS_SIZE ) 03639 { 03640 size = (*it2).m_long; 03641 atomsFound++; 03642 }; 03643 if (atomsFound==3) break; 03644 } 03645 03646 KURL url = ((SimpleJob*)job)->url(); 03647 03648 subjobs.remove( job ); 03649 assert( subjobs.isEmpty() ); 03650 03651 if (bDir && !bLink) 03652 { 03653 // Add toplevel dir in list of dirs 03654 dirs.append( url ); 03655 if ( url.isLocalFile() && !m_parentDirs.contains( url.path(-1) ) ) 03656 m_parentDirs.append( url.path(-1) ); 03657 03658 //kdDebug(7007) << " Target is a directory " << endl; 03659 // List it 03660 state = STATE_LISTING; 03661 ListJob *newjob = listRecursive( url, false ); 03662 newjob->setUnrestricted(true); // No KIOSK restrictions 03663 Scheduler::scheduleJob(newjob); 03664 connect(newjob, SIGNAL(entries( KIO::Job *, 03665 const KIO::UDSEntryList& )), 03666 SLOT( slotEntries( KIO::Job*, 03667 const KIO::UDSEntryList& ))); 03668 addSubjob(newjob); 03669 } 03670 else 03671 { 03672 if ( bLink ) { 03673 //kdDebug(7007) << " Target is a symlink" << endl; 03674 symlinks.append( url ); 03675 } else { 03676 //kdDebug(7007) << " Target is a file" << endl; 03677 files.append( url ); 03678 } 03679 if ( url.isLocalFile() && !m_parentDirs.contains( url.directory(-1) ) ) 03680 m_parentDirs.append( url.directory(-1) ); 03681 ++m_currentStat; 03682 statNextSrc(); 03683 } 03684 } 03685 break; 03686 case STATE_LISTING: 03687 if ( job->error() ) 03688 { 03689 // Try deleting nonetheless, it may be empty (and non-listable) 03690 } 03691 subjobs.remove( job ); 03692 assert( subjobs.isEmpty() ); 03693 ++m_currentStat; 03694 statNextSrc(); 03695 break; 03696 case STATE_DELETING_FILES: 03697 if ( job->error() ) 03698 { 03699 Job::slotResult( job ); // will set the error and emit result(this) 03700 return; 03701 } 03702 subjobs.remove( job ); 03703 assert( subjobs.isEmpty() ); 03704 m_processedFiles++; 03705 03706 deleteNextFile(); 03707 break; 03708 case STATE_DELETING_DIRS: 03709 if ( job->error() ) 03710 { 03711 Job::slotResult( job ); // will set the error and emit result(this) 03712 return; 03713 } 03714 subjobs.remove( job ); 03715 assert( subjobs.isEmpty() ); 03716 m_processedDirs++; 03717 //emit processedDirs( this, m_processedDirs ); 03718 //if (!m_shred) 03719 //emitPercent( m_processedFiles + m_processedDirs, m_totalFilesDirs ); 03720 03721 deleteNextDir(); 03722 break; 03723 default: 03724 assert(0); 03725 } 03726 } 03727 03728 DeleteJob *KIO::del( const KURL& src, bool shred, bool showProgressInfo ) 03729 { 03730 KURL::List srcList; 03731 srcList.append( src ); 03732 DeleteJob *job = new DeleteJob( srcList, shred, showProgressInfo ); 03733 return job; 03734 } 03735 03736 DeleteJob *KIO::del( const KURL::List& src, bool shred, bool showProgressInfo ) 03737 { 03738 DeleteJob *job = new DeleteJob( src, shred, showProgressInfo ); 03739 return job; 03740 } 03741 03742 MultiGetJob::MultiGetJob(const KURL& url, 03743 bool showProgressInfo) 03744 : TransferJob(url, 0, QByteArray(), QByteArray(), showProgressInfo) 03745 { 03746 m_waitQueue.setAutoDelete(true); 03747 m_activeQueue.setAutoDelete(true); 03748 m_currentEntry = 0; 03749 } 03750 03751 void MultiGetJob::get(long id, const KURL &url, const MetaData &metaData) 03752 { 03753 GetRequest *entry = new GetRequest(id, url, metaData); 03754 entry->metaData["request-id"] = QString("%1").arg(id); 03755 m_waitQueue.append(entry); 03756 } 03757 03758 void MultiGetJob::flushQueue(QPtrList<GetRequest> &queue) 03759 { 03760 GetRequest *entry; 03761 // Use multi-get 03762 // Scan all jobs in m_waitQueue 03763 for(entry = m_waitQueue.first(); entry; ) 03764 { 03765 if ((m_url.protocol() == entry->url.protocol()) && 03766 (m_url.host() == entry->url.host()) && 03767 (m_url.port() == entry->url.port()) && 03768 (m_url.user() == entry->url.user())) 03769 { 03770 m_waitQueue.take(); 03771 queue.append(entry); 03772 entry = m_waitQueue.current(); 03773 } 03774 else 03775 { 03776 entry = m_waitQueue.next(); 03777 } 03778 } 03779 // Send number of URLs, (URL, metadata)* 03780 KIO_ARGS << (Q_INT32) queue.count(); 03781 for(entry = queue.first(); entry; entry = queue.next()) 03782 { 03783 stream << entry->url << entry->metaData; 03784 } 03785 m_packedArgs = packedArgs; 03786 m_command = CMD_MULTI_GET; 03787 m_outgoingMetaData.clear(); 03788 } 03789 03790 void MultiGetJob::start(Slave *slave) 03791 { 03792 // Add first job from m_waitQueue and add it to m_activeQueue 03793 GetRequest *entry = m_waitQueue.take(0); 03794 m_activeQueue.append(entry); 03795 03796 m_url = entry->url; 03797 03798 if (!entry->url.protocol().startsWith("http")) 03799 { 03800 // Use normal get 03801 KIO_ARGS << entry->url; 03802 m_packedArgs = packedArgs; 03803 m_outgoingMetaData = entry->metaData; 03804 m_command = CMD_GET; 03805 b_multiGetActive = false; 03806 } 03807 else 03808 { 03809 flushQueue(m_activeQueue); 03810 b_multiGetActive = true; 03811 } 03812 03813 TransferJob::start(slave); // Anything else to do?? 03814 } 03815 03816 bool MultiGetJob::findCurrentEntry() 03817 { 03818 if (b_multiGetActive) 03819 { 03820 long id = m_incomingMetaData["request-id"].toLong(); 03821 for(GetRequest *entry = m_activeQueue.first(); entry; entry = m_activeQueue.next()) 03822 { 03823 if (entry->id == id) 03824 { 03825 m_currentEntry = entry; 03826 return true; 03827 } 03828 } 03829 m_currentEntry = 0; 03830 return false; 03831 } 03832 else 03833 { 03834 m_currentEntry = m_activeQueue.first(); 03835 return (m_currentEntry != 0); 03836 } 03837 } 03838 03839 void MultiGetJob::slotRedirection( const KURL &url) 03840 { 03841 if (!findCurrentEntry()) return; // Error 03842 if (!kapp->authorizeURLAction("redirect", m_url, url)) 03843 { 03844 kdWarning(7007) << "MultiGetJob: Redirection from " << m_currentEntry->url.prettyURL() << " to " << url.prettyURL() << " REJECTED!" << endl; 03845 return; 03846 } 03847 m_redirectionURL = url; 03848 if (m_currentEntry->url.hasUser() && !url.hasUser() && (m_currentEntry->url.host().lower() == url.host().lower())) 03849 m_redirectionURL.setUser(m_currentEntry->url.user()); // Preserve user 03850 get(m_currentEntry->id, m_redirectionURL, m_currentEntry->metaData); // Try again 03851 } 03852 03853 03854 void MultiGetJob::slotFinished() 03855 { 03856 if (!findCurrentEntry()) return; 03857 if (m_redirectionURL.isEmpty()) 03858 { 03859 // No redirection, tell the world that we are finished. 03860 emit result(m_currentEntry->id); 03861 } 03862 m_redirectionURL = KURL(); 03863 m_error = 0; 03864 m_incomingMetaData.clear(); 03865 m_activeQueue.removeRef(m_currentEntry); 03866 if (m_activeQueue.count() == 0) 03867 { 03868 if (m_waitQueue.count() == 0) 03869 { 03870 // All done 03871 TransferJob::slotFinished(); 03872 } 03873 else 03874 { 03875 // return slave to pool 03876 // fetch new slave for first entry in m_waitQueue and call start 03877 // again. 03878 GetRequest *entry = m_waitQueue.at(0); 03879 m_url = entry->url; 03880 slaveDone(); 03881 Scheduler::doJob(this); 03882 } 03883 } 03884 } 03885 03886 void MultiGetJob::slotData( const QByteArray &_data) 03887 { 03888 if(!m_currentEntry) return;// Error, unknown request! 03889 if(m_redirectionURL.isEmpty() || !m_redirectionURL.isValid() || m_error) 03890 emit data(m_currentEntry->id, _data); 03891 } 03892 03893 void MultiGetJob::slotMimetype( const QString &_mimetype ) 03894 { 03895 if (b_multiGetActive) 03896 { 03897 QPtrList<GetRequest> newQueue; 03898 flushQueue(newQueue); 03899 if (!newQueue.isEmpty()) 03900 { 03901 while(!newQueue.isEmpty()) 03902 m_activeQueue.append(newQueue.take(0)); 03903 m_slave->send( m_command, m_packedArgs ); 03904 } 03905 } 03906 if (!findCurrentEntry()) return; // Error, unknown request! 03907 emit mimetype(m_currentEntry->id, _mimetype); 03908 } 03909 03910 MultiGetJob *KIO::multi_get(long id, const KURL &url, const MetaData &metaData) 03911 { 03912 MultiGetJob * job = new MultiGetJob( url, false ); 03913 job->get(id, url, metaData); 03914 return job; 03915 } 03916 03917 03918 #ifdef CACHE_INFO 03919 CacheInfo::CacheInfo(const KURL &url) 03920 { 03921 m_url = url; 03922 } 03923 03924 QString CacheInfo::cachedFileName() 03925 { 03926 const QChar separator = '_'; 03927 03928 QString CEF = m_url.path(); 03929 03930 int p = CEF.find('/'); 03931 03932 while(p != -1) 03933 { 03934 CEF[p] = separator; 03935 p = CEF.find('/', p); 03936 } 03937 03938 QString host = m_url.host().lower(); 03939 CEF = host + CEF + '_'; 03940 03941 QString dir = KProtocolManager::cacheDir(); 03942 if (dir[dir.length()-1] != '/') 03943 dir += "/"; 03944 03945 int l = m_url.host().length(); 03946 for(int i = 0; i < l; i++) 03947 { 03948 if (host[i].isLetter() && (host[i] != 'w')) 03949 { 03950 dir += host[i]; 03951 break; 03952 } 03953 } 03954 if (dir[dir.length()-1] == '/') 03955 dir += "0"; 03956 03957 unsigned long hash = 0x00000000; 03958 QCString u = m_url.url().latin1(); 03959 for(int i = u.length(); i--;) 03960 { 03961 hash = (hash * 12211 + u[i]) % 2147483563; 03962 } 03963 03964 QString hashString; 03965 hashString.sprintf("%08lx", hash); 03966 03967 CEF = CEF + hashString; 03968 03969 CEF = dir + "/" + CEF; 03970 03971 return CEF; 03972 } 03973 03974 QFile *CacheInfo::cachedFile() 03975 { 03976 const char *mode = (readWrite ? "r+" : "r"); 03977 03978 FILE *fs = fopen( CEF.latin1(), mode); // Open for reading and writing 03979 if (!fs) 03980 return 0; 03981 03982 char buffer[401]; 03983 bool ok = true; 03984 03985 // CacheRevision 03986 if (ok && (!fgets(buffer, 400, fs))) 03987 ok = false; 03988 if (ok && (strcmp(buffer, CACHE_REVISION) != 0)) 03989 ok = false; 03990 03991 time_t date; 03992 time_t currentDate = time(0); 03993 03994 // URL 03995 if (ok && (!fgets(buffer, 400, fs))) 03996 ok = false; 03997 if (ok) 03998 { 03999 int l = strlen(buffer); 04000 if (l>0) 04001 buffer[l-1] = 0; // Strip newline 04002 if (m_.url.url() != buffer) 04003 { 04004 ok = false; // Hash collision 04005 } 04006 } 04007 04008 // Creation Date 04009 if (ok && (!fgets(buffer, 400, fs))) 04010 ok = false; 04011 if (ok) 04012 { 04013 date = (time_t) strtoul(buffer, 0, 10); 04014 if (m_maxCacheAge && (difftime(currentDate, date) > m_maxCacheAge)) 04015 { 04016 m_bMustRevalidate = true; 04017 m_expireDate = currentDate; 04018 } 04019 } 04020 04021 // Expiration Date 04022 m_cacheExpireDateOffset = ftell(fs); 04023 if (ok && (!fgets(buffer, 400, fs))) 04024 ok = false; 04025 if (ok) 04026 { 04027 if (m_request.cache == CC_Verify) 04028 { 04029 date = (time_t) strtoul(buffer, 0, 10); 04030 // After the expire date we need to revalidate. 04031 if (!date || difftime(currentDate, date) >= 0) 04032 m_bMustRevalidate = true; 04033 m_expireDate = date; 04034 } 04035 } 04036 04037 // ETag 04038 if (ok && (!fgets(buffer, 400, fs))) 04039 ok = false; 04040 if (ok) 04041 { 04042 m_etag = QString(buffer).stripWhiteSpace(); 04043 } 04044 04045 // Last-Modified 04046 if (ok && (!fgets(buffer, 400, fs))) 04047 ok = false; 04048 if (ok) 04049 { 04050 m_lastModified = QString(buffer).stripWhiteSpace(); 04051 } 04052 04053 fclose(fs); 04054 04055 if (ok) 04056 return fs; 04057 04058 unlink( CEF.latin1()); 04059 return 0; 04060 04061 } 04062 04063 void CacheInfo::flush() 04064 { 04065 cachedFile().remove(); 04066 } 04067 04068 void CacheInfo::touch() 04069 { 04070 04071 } 04072 void CacheInfo::setExpireDate(int); 04073 void CacheInfo::setExpireTimeout(int); 04074 04075 04076 int CacheInfo::creationDate(); 04077 int CacheInfo::expireDate(); 04078 int CacheInfo::expireTimeout(); 04079 #endif 04080 04081 void Job::virtual_hook( int, void* ) 04082 { /*BASE::virtual_hook( id, data );*/ } 04083 04084 void SimpleJob::virtual_hook( int id, void* data ) 04085 { KIO::Job::virtual_hook( id, data ); } 04086 04087 void StatJob::virtual_hook( int id, void* data ) 04088 { SimpleJob::virtual_hook( id, data ); } 04089 04090 void TransferJob::virtual_hook( int id, void* data ) 04091 { SimpleJob::virtual_hook( id, data ); } 04092 04093 void MultiGetJob::virtual_hook( int id, void* data ) 04094 { TransferJob::virtual_hook( id, data ); } 04095 04096 void MimetypeJob::virtual_hook( int id, void* data ) 04097 { TransferJob::virtual_hook( id, data ); } 04098 04099 void FileCopyJob::virtual_hook( int id, void* data ) 04100 { Job::virtual_hook( id, data ); } 04101 04102 void ListJob::virtual_hook( int id, void* data ) 04103 { SimpleJob::virtual_hook( id, data ); } 04104 04105 void CopyJob::virtual_hook( int id, void* data ) 04106 { Job::virtual_hook( id, data ); } 04107 04108 void DeleteJob::virtual_hook( int id, void* data ) 04109 { Job::virtual_hook( id, data ); } 04110 04111 04112 #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 Sat Jun 12 15:08:43 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003