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