kio Library API Documentation

job.cpp

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