00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <config.h>
00023
00024 #include <sys/types.h>
00025 #include <sys/wait.h>
00026 #include <sys/stat.h>
00027
00028 #include <assert.h>
00029
00030 #include <signal.h>
00031 #include <stdlib.h>
00032 #include <stdio.h>
00033 #include <time.h>
00034 #include <unistd.h>
00035 extern "C" {
00036 #include <pwd.h>
00037 #include <grp.h>
00038 }
00039 #include <qtimer.h>
00040 #include <qfile.h>
00041
00042 #include <kapplication.h>
00043 #include <kglobal.h>
00044 #include <klocale.h>
00045 #include <ksimpleconfig.h>
00046 #include <kdebug.h>
00047 #include <kdialog.h>
00048 #include <kmessagebox.h>
00049 #include <kdatastream.h>
00050 #include <kmainwindow.h>
00051
00052 #include <errno.h>
00053
00054 #include "slave.h"
00055 #include "kio/job.h"
00056 #include "scheduler.h"
00057 #include "kdirwatch.h"
00058 #include "kmimemagic.h"
00059 #include "kprotocolinfo.h"
00060 #include "kprotocolmanager.h"
00061
00062 #include "kio/observer.h"
00063
00064 #include <kdirnotify_stub.h>
00065 #include <ktempfile.h>
00066 #include <dcopclient.h>
00067
00068 using namespace KIO;
00069 template class QPtrList<KIO::Job>;
00070
00071
00072 #define REPORT_TIMEOUT 200
00073
00074 #define KIO_ARGS QByteArray packedArgs; QDataStream stream( packedArgs, IO_WriteOnly ); stream
00075
00076 class Job::JobPrivate
00077 {
00078 public:
00079 JobPrivate() : m_autoErrorHandling( false ), m_parentJob( 0L ) {}
00080
00081 bool m_autoErrorHandling;
00082 QGuardedPtr<QWidget> m_errorParentWidget;
00083
00084
00085 Job* m_parentJob;
00086 };
00087
00088 Job::Job(bool showProgressInfo) : QObject(0, "job"), m_error(0), m_percent(0)
00089 , m_progressId(0), m_speedTimer(0), d( new JobPrivate )
00090 {
00091
00092
00093
00094 if ( showProgressInfo )
00095 {
00096 m_progressId = Observer::self()->newJob( this, true );
00097
00098
00099 connect( this, SIGNAL( percent( KIO::Job*, unsigned long ) ),
00100 Observer::self(), SLOT( slotPercent( KIO::Job*, unsigned long ) ) );
00101 connect( this, SIGNAL( infoMessage( KIO::Job*, const QString & ) ),
00102 Observer::self(), SLOT( slotInfoMessage( KIO::Job*, const QString & ) ) );
00103 connect( this, SIGNAL( totalSize( KIO::Job*, KIO::filesize_t ) ),
00104 Observer::self(), SLOT( slotTotalSize( KIO::Job*, KIO::filesize_t ) ) );
00105 connect( this, SIGNAL( processedSize( KIO::Job*, KIO::filesize_t ) ),
00106 Observer::self(), SLOT( slotProcessedSize( KIO::Job*, KIO::filesize_t ) ) );
00107 connect( this, SIGNAL( speed( KIO::Job*, unsigned long ) ),
00108 Observer::self(), SLOT( slotSpeed( KIO::Job*, unsigned long ) ) );
00109 }
00110
00111 kapp->ref();
00112 }
00113
00114 Job::~Job()
00115 {
00116 delete m_speedTimer;
00117 delete d;
00118 kapp->deref();
00119 }
00120
00121 void Job::addSubjob(Job *job, bool inheritMetaData)
00122 {
00123
00124 subjobs.append(job);
00125
00126 connect( job, SIGNAL(result(KIO::Job*)),
00127 SLOT(slotResult(KIO::Job*)) );
00128
00129
00130 connect( job, SIGNAL(speed( KIO::Job*, unsigned long )),
00131 SLOT(slotSpeed(KIO::Job*, unsigned long)) );
00132
00133 connect( job, SIGNAL(infoMessage( KIO::Job*, const QString & )),
00134 SLOT(slotInfoMessage(KIO::Job*, const QString &)) );
00135
00136 if (inheritMetaData)
00137 job->mergeMetaData(m_outgoingMetaData);
00138 }
00139
00140 void Job::removeSubjob( Job *job )
00141 {
00142
00143 subjobs.remove(job);
00144 if (subjobs.isEmpty())
00145 emitResult();
00146 }
00147
00148 void Job::emitPercent( KIO::filesize_t processedSize, KIO::filesize_t totalSize )
00149 {
00150
00151 unsigned long ipercent = m_percent;
00152
00153 if ( totalSize == 0 )
00154 m_percent = 100;
00155 else
00156 m_percent = (unsigned long)(( (float)(processedSize) / (float)(totalSize) ) * 100.0);
00157
00158 if ( m_percent != ipercent || m_percent == 100 ) {
00159 emit percent( this, m_percent );
00160
00161 }
00162 }
00163
00164 void Job::emitSpeed( unsigned long bytes_per_second )
00165 {
00166
00167 if ( !m_speedTimer )
00168 {
00169 m_speedTimer = new QTimer();
00170 connect( m_speedTimer, SIGNAL( timeout() ), SLOT( slotSpeedTimeout() ) );
00171 }
00172 emit speed( this, bytes_per_second );
00173 m_speedTimer->start( 5000 );
00174 }
00175
00176 void Job::emitResult()
00177 {
00178
00179 if ( m_progressId )
00180 Observer::self()->jobFinished( m_progressId );
00181 if ( m_error && d->m_autoErrorHandling )
00182 showErrorDialog( d->m_errorParentWidget );
00183 emit result(this);
00184 delete this;
00185 }
00186
00187 void Job::kill( bool quietly )
00188 {
00189 kdDebug(7007) << "Job::kill this=" << this << " m_progressId=" << m_progressId << " quietly=" << quietly << endl;
00190
00191 QPtrListIterator<Job> it( subjobs );
00192 for ( ; it.current() ; ++it )
00193 (*it)->kill( true );
00194 subjobs.clear();
00195
00196 if ( ! quietly ) {
00197 m_error = ERR_USER_CANCELED;
00198 emit canceled( this );
00199 emitResult();
00200 } else
00201 {
00202 if ( m_progressId )
00203 Observer::self()->jobFinished( m_progressId );
00204 delete this;
00205 }
00206 }
00207
00208 void Job::slotResult( Job *job )
00209 {
00210
00211 if ( job->error() && !m_error )
00212 {
00213
00214 m_error = job->error();
00215 m_errorText = job->errorText();
00216 }
00217 removeSubjob(job);
00218 }
00219
00220 void Job::slotSpeed( KIO::Job*, unsigned long bytes_per_second )
00221 {
00222
00223 emitSpeed( bytes_per_second );
00224 }
00225
00226 void Job::slotInfoMessage( KIO::Job*, const QString & msg )
00227 {
00228 emit infoMessage( this, msg );
00229 }
00230
00231 void Job::slotSpeedTimeout()
00232 {
00233
00234
00235
00236 emit speed( this, 0 );
00237 m_speedTimer->stop();
00238 }
00239
00240
00241
00242 void Job::showErrorDialog( QWidget * parent )
00243 {
00244
00245 kapp->enableStyles();
00246
00247 if ( (m_error != ERR_USER_CANCELED) && (m_error != ERR_NO_CONTENT) ) {
00248
00249 kdDebug() << "Default language: " << KGlobal::locale()->defaultLanguage() << endl;
00250 if ( 1 )
00251 KMessageBox::queuedMessageBox( parent, KMessageBox::Error, errorString() );
00252 #if 0
00253 } else {
00254 QStringList errors = detailedErrorStrings();
00255 QString caption, err, detail;
00256 QStringList::iterator it = errors.begin();
00257 if ( it != errors.end() )
00258 caption = *(it++);
00259 if ( it != errors.end() )
00260 err = *(it++);
00261 if ( it != errors.end() )
00262 detail = *it;
00263 KMessageBox::queuedDetailedError( parent, err, detail, caption );
00264 }
00265 #endif
00266 }
00267 }
00268
00269 void Job::setAutoErrorHandlingEnabled( bool enable, QWidget *parentWidget )
00270 {
00271 d->m_autoErrorHandling = enable;
00272 d->m_errorParentWidget = parentWidget;
00273 }
00274
00275 bool Job::isAutoErrorHandlingEnabled() const
00276 {
00277 return d->m_autoErrorHandling;
00278 }
00279
00280 void Job::setWindow(QWidget *window)
00281 {
00282 m_window = window;
00283 KIO::Scheduler::registerWindow(window);
00284 }
00285
00286 QWidget *Job::window() const
00287 {
00288 return m_window;
00289 }
00290
00291 void Job::setParentJob(Job* job)
00292 {
00293 Q_ASSERT(d->m_parentJob == 0L);
00294 Q_ASSERT(job);
00295 d->m_parentJob = job;
00296 }
00297
00298 Job* Job::parentJob() const
00299 {
00300 return d->m_parentJob;
00301 }
00302
00303 MetaData Job::metaData() const
00304 {
00305 return m_incomingMetaData;
00306 }
00307
00308 QString Job::queryMetaData(const QString &key)
00309 {
00310 if (!m_incomingMetaData.contains(key))
00311 return QString::null;
00312 return m_incomingMetaData[key];
00313 }
00314
00315 void Job::setMetaData( const KIO::MetaData &_metaData)
00316 {
00317 m_outgoingMetaData = _metaData;
00318 }
00319
00320 void Job::addMetaData( const QString &key, const QString &value)
00321 {
00322 m_outgoingMetaData.insert(key, value);
00323 }
00324
00325 void Job::addMetaData( const QMap<QString,QString> &values)
00326 {
00327 QMapConstIterator<QString,QString> it = values.begin();
00328 for(;it != values.end(); ++it)
00329 m_outgoingMetaData.insert(it.key(), it.data());
00330 }
00331
00332 void Job::mergeMetaData( const QMap<QString,QString> &values)
00333 {
00334 QMapConstIterator<QString,QString> it = values.begin();
00335 for(;it != values.end(); ++it)
00336 m_outgoingMetaData.insert(it.key(), it.data(), false);
00337 }
00338
00339 MetaData Job::outgoingMetaData() const
00340 {
00341 return m_outgoingMetaData;
00342 }
00343
00344
00345 SimpleJob::SimpleJob(const KURL& url, int command, const QByteArray &packedArgs,
00346 bool showProgressInfo )
00347 : Job(showProgressInfo), m_slave(0), m_packedArgs(packedArgs),
00348 m_url(url), m_command(command), m_totalSize(0)
00349 {
00350 if (m_url.isMalformed())
00351 {
00352 m_error = ERR_MALFORMED_URL;
00353 m_errorText = m_url.url();
00354 QTimer::singleShot(0, this, SLOT(slotFinished()) );
00355 return;
00356 }
00357
00358 if ((m_command == CMD_LISTDIR) && !kapp->authorizeURLAction("list", m_url, m_url))
00359 {
00360 m_error = ERR_ACCESS_DENIED;
00361 m_errorText = m_url.url();
00362 QTimer::singleShot(0, this, SLOT(slotFinished()) );
00363 return;
00364 }
00365
00366 if (m_url.hasSubURL())
00367 {
00368 KURL::List list = KURL::split(m_url);
00369 KURL::List::Iterator it = list.fromLast();
00370 m_url = *it;
00371 list.remove(it);
00372 m_subUrl = KURL::join(list);
00373
00374
00375 }
00376
00377 Scheduler::doJob(this);
00378 }
00379
00380 void SimpleJob::kill( bool quietly )
00381 {
00382 Scheduler::cancelJob( this );
00383 m_slave = 0;
00384 Job::kill( quietly );
00385 }
00386
00387 void SimpleJob::putOnHold()
00388 {
00389 Scheduler::putSlaveOnHold(this, m_url);
00390 m_slave = 0;
00391 kill(true);
00392 }
00393
00394 void SimpleJob::removeOnHold()
00395 {
00396 Scheduler::removeSlaveOnHold();
00397 }
00398
00399 SimpleJob::~SimpleJob()
00400 {
00401 if (m_slave)
00402 {
00403 kdDebug(7007) << "SimpleJob::~SimpleJob: Killing running job in destructor!" << endl;
00404 #if 0
00405 m_slave->kill();
00406 Scheduler::jobFinished( this, m_slave );
00407 #endif
00408 Scheduler::cancelJob( this );
00409 m_slave = 0;
00410 }
00411 }
00412
00413 void SimpleJob::start(Slave *slave)
00414 {
00415 m_slave = slave;
00416
00417 connect( m_slave, SIGNAL( error( int , const QString & ) ),
00418 SLOT( slotError( int , const QString & ) ) );
00419
00420 connect( m_slave, SIGNAL( warning( const QString & ) ),
00421 SLOT( slotWarning( const QString & ) ) );
00422
00423 connect( m_slave, SIGNAL( infoMessage( const QString & ) ),
00424 SLOT( slotInfoMessage( const QString & ) ) );
00425
00426 connect( m_slave, SIGNAL( connected() ),
00427 SLOT( slotConnected() ) );
00428
00429 connect( m_slave, SIGNAL( finished() ),
00430 SLOT( slotFinished() ) );
00431
00432 connect( m_slave, SIGNAL( totalSize( KIO::filesize_t ) ),
00433 SLOT( slotTotalSize( KIO::filesize_t ) ) );
00434
00435 connect( m_slave, SIGNAL( processedSize( KIO::filesize_t ) ),
00436 SLOT( slotProcessedSize( KIO::filesize_t ) ) );
00437
00438 connect( m_slave, SIGNAL( speed( unsigned long ) ),
00439 SLOT( slotSpeed( unsigned long ) ) );
00440
00441 connect( slave, SIGNAL( needProgressId() ),
00442 SLOT( slotNeedProgressId() ) );
00443
00444 connect( slave, SIGNAL(metaData( const KIO::MetaData& ) ),
00445 SLOT( slotMetaData( const KIO::MetaData& ) ) );
00446
00447 if (m_window)
00448 {
00449 QString id;
00450 addMetaData("window-id", id.setNum(m_window->winId()));
00451 }
00452
00453 if (!m_outgoingMetaData.isEmpty())
00454 {
00455 KIO_ARGS << m_outgoingMetaData;
00456 slave->connection()->send( CMD_META_DATA, packedArgs );
00457 }
00458
00459 if (!m_subUrl.isEmpty())
00460 {
00461 KIO_ARGS << m_subUrl;
00462 m_slave->connection()->send( CMD_SUBURL, packedArgs );
00463 }
00464
00465 m_slave->connection()->send( m_command, m_packedArgs );
00466 }
00467
00468 void SimpleJob::slaveDone()
00469 {
00470 if (!m_slave) return;
00471 disconnect(m_slave);
00472 Scheduler::jobFinished( this, m_slave );
00473 m_slave = 0;
00474 }
00475
00476 void SimpleJob::slotFinished( )
00477 {
00478
00479 slaveDone();
00480
00481 if (subjobs.isEmpty())
00482 {
00483 if ( !m_error )
00484 {
00485 KDirNotify_stub allDirNotify( "*", "KDirNotify*" );
00486 if ( m_command == CMD_MKDIR )
00487 {
00488 KURL urlDir( url() );
00489 urlDir.setPath( urlDir.directory() );
00490 allDirNotify.FilesAdded( urlDir );
00491 }
00492 else if ( m_command == CMD_RENAME )
00493 {
00494 KURL src, dst;
00495 QDataStream str( m_packedArgs, IO_ReadOnly );
00496 str >> src >> dst;
00497 if ( src.directory() == dst.directory() )
00498 allDirNotify.FileRenamed( src, dst );
00499 }
00500 }
00501 emitResult();
00502 }
00503 }
00504
00505 void SimpleJob::slotError( int error, const QString & errorText )
00506 {
00507 m_error = error;
00508 m_errorText = errorText;
00509
00510 slotFinished();
00511 }
00512
00513 void SimpleJob::slotWarning( const QString & errorText )
00514 {
00515 static uint msgBoxDisplayed = 0;
00516 if ( msgBoxDisplayed == 0 )
00517 {
00518 msgBoxDisplayed++;
00519 KMessageBox::information( 0L, errorText );
00520 msgBoxDisplayed--;
00521 }
00522
00523 }
00524
00525 void SimpleJob::slotInfoMessage( const QString & msg )
00526 {
00527 emit infoMessage( this, msg );
00528 }
00529
00530 void SimpleJob::slotConnected()
00531 {
00532 emit connected( this );
00533 }
00534
00535 void SimpleJob::slotNeedProgressId()
00536 {
00537 if ( !m_progressId )
00538 m_progressId = Observer::self()->newJob( this, false );
00539 m_slave->setProgressId( m_progressId );
00540 }
00541
00542 void SimpleJob::slotTotalSize( KIO::filesize_t size )
00543 {
00544 m_totalSize = size;
00545 emit totalSize( this, size );
00546 }
00547
00548 void SimpleJob::slotProcessedSize( KIO::filesize_t size )
00549 {
00550
00551 emit processedSize( this, size );
00552 if ( size > m_totalSize ) {
00553 slotTotalSize(size);
00554 }
00555 emitPercent( size, m_totalSize );
00556 }
00557
00558 void SimpleJob::slotSpeed( unsigned long bytes_per_second )
00559 {
00560
00561 emitSpeed( bytes_per_second );
00562 }
00563
00564 void SimpleJob::slotMetaData( const KIO::MetaData &_metaData)
00565 {
00566 m_incomingMetaData += _metaData;
00567 }
00568
00569 SimpleJob *KIO::mkdir( const KURL& url, int permissions )
00570 {
00571
00572 KIO_ARGS << url << permissions;
00573 return new SimpleJob(url, CMD_MKDIR, packedArgs, false);
00574 }
00575
00576 SimpleJob *KIO::rmdir( const KURL& url )
00577 {
00578
00579 KIO_ARGS << url << Q_INT8(false);
00580 return new SimpleJob(url, CMD_DEL, packedArgs, false);
00581 }
00582
00583 SimpleJob *KIO::chmod( const KURL& url, int permissions )
00584 {
00585
00586 KIO_ARGS << url << permissions;
00587 return new SimpleJob(url, CMD_CHMOD, packedArgs, false);
00588 }
00589
00590 SimpleJob *KIO::rename( const KURL& src, const KURL & dest, bool overwrite )
00591 {
00592
00593 KIO_ARGS << src << dest << (Q_INT8) overwrite;
00594 return new SimpleJob(src, CMD_RENAME, packedArgs, false);
00595 }
00596
00597 SimpleJob *KIO::symlink( const QString& target, const KURL & dest, bool overwrite, bool showProgressInfo )
00598 {
00599
00600 KIO_ARGS << target << dest << (Q_INT8) overwrite;
00601 return new SimpleJob(dest, CMD_SYMLINK, packedArgs, showProgressInfo);
00602 }
00603
00604 SimpleJob *KIO::special(const KURL& url, const QByteArray & data, bool showProgressInfo)
00605 {
00606
00607 return new SimpleJob(url, CMD_SPECIAL, data, showProgressInfo);
00608 }
00609
00610 SimpleJob *KIO::mount( bool ro, const char *fstype, const QString& dev, const QString& point, bool showProgressInfo )
00611 {
00612 KIO_ARGS << int(1) << Q_INT8( ro ? 1 : 0 )
00613 << QString::fromLatin1(fstype) << dev << point;
00614 SimpleJob *job = special( KURL("file:/"), packedArgs, showProgressInfo );
00615 if ( showProgressInfo )
00616 Observer::self()->mounting( job, dev, point );
00617 return job;
00618 }
00619
00620 SimpleJob *KIO::unmount( const QString& point, bool showProgressInfo )
00621 {
00622 KIO_ARGS << int(2) << point;
00623 SimpleJob *job = special( KURL("file:/"), packedArgs, showProgressInfo );
00624 if ( showProgressInfo )
00625 Observer::self()->unmounting( job, point );
00626 return job;
00627 }
00628
00630
00631 StatJob::StatJob( const KURL& url, int command,
00632 const QByteArray &packedArgs, bool showProgressInfo )
00633 : SimpleJob(url, command, packedArgs, showProgressInfo),
00634 m_bSource(true), m_details(2)
00635 {
00636 }
00637
00638 void StatJob::start(Slave *slave)
00639 {
00640 m_outgoingMetaData.replace( "statSide", m_bSource ? "source" : "dest" );
00641 m_outgoingMetaData.replace( "details", QString::number(m_details) );
00642
00643 SimpleJob::start(slave);
00644
00645 connect( m_slave, SIGNAL( statEntry( const KIO::UDSEntry& ) ),
00646 SLOT( slotStatEntry( const KIO::UDSEntry & ) ) );
00647 connect( slave, SIGNAL( redirection(const KURL &) ),
00648 SLOT( slotRedirection(const KURL &) ) );
00649 }
00650
00651 void StatJob::slotStatEntry( const KIO::UDSEntry & entry )
00652 {
00653
00654 m_statResult = entry;
00655 }
00656
00657
00658 void StatJob::slotRedirection( const KURL &url)
00659 {
00660 kdDebug(7007) << "StatJob::slotRedirection(" << url.prettyURL() << ")" << endl;
00661 if (!kapp->authorizeURLAction("redirect", m_url, url))
00662 {
00663 kdWarning(7007) << "StatJob: Redirection from " << m_url.prettyURL() << " to " << url.prettyURL() << " REJECTED!" << endl;
00664 return;
00665 }
00666 m_redirectionURL = url;
00667 if (m_url.hasUser() && !url.hasUser() && (m_url.host().lower() == url.host().lower()))
00668 m_redirectionURL.setUser(m_url.user());
00669
00670 emit redirection(this, m_redirectionURL);
00671 }
00672
00673 void StatJob::slotFinished()
00674 {
00675 if ( m_redirectionURL.isEmpty() || m_redirectionURL.isMalformed())
00676 {
00677
00678 SimpleJob::slotFinished();
00679 } else {
00680
00681 if (queryMetaData("permanent-redirect")=="true")
00682 emit permanentRedirection(this, m_url, m_redirectionURL);
00683 m_url = m_redirectionURL;
00684 m_redirectionURL = KURL();
00685 m_packedArgs.truncate(0);
00686 QDataStream stream( m_packedArgs, IO_WriteOnly );
00687 stream << m_url;
00688
00689
00690 slaveDone();
00691 Scheduler::doJob(this);
00692 }
00693 }
00694
00695 StatJob *KIO::stat(const KURL& url, bool showProgressInfo)
00696 {
00697
00698 return stat( url, true, 2, showProgressInfo );
00699 }
00700
00701 StatJob *KIO::stat(const KURL& url, bool sideIsSource, short int details, bool showProgressInfo)
00702 {
00703 kdDebug(7007) << "stat " << url.prettyURL() << endl;
00704 KIO_ARGS << url;
00705 StatJob * job = new StatJob(url, CMD_STAT, packedArgs, showProgressInfo );
00706 job->setSide( sideIsSource );
00707 job->setDetails( details );
00708 if ( showProgressInfo )
00709 Observer::self()->stating( job, url );
00710 return job;
00711 }
00712
00713 SimpleJob *KIO::http_update_cache( const KURL& url, bool no_cache, time_t expireDate)
00714 {
00715 assert( (url.protocol() == "http") || (url.protocol() == "https") );
00716
00717 KIO_ARGS << (int)2 << url << no_cache << expireDate;
00718 SimpleJob * job = new SimpleJob( url, CMD_SPECIAL, packedArgs, false );
00719 Scheduler::scheduleJob(job);
00720 return job;
00721 }
00722
00724
00725 TransferJob::TransferJob( const KURL& url, int command,
00726 const QByteArray &packedArgs,
00727 const QByteArray &_staticData,
00728 bool showProgressInfo)
00729 : SimpleJob(url, command, packedArgs, showProgressInfo), staticData( _staticData)
00730 {
00731 m_suspended = false;
00732 m_errorPage = false;
00733 m_subJob = 0L;
00734 if ( showProgressInfo )
00735 Observer::self()->slotTransferring( this, url );
00736 }
00737
00738
00739 void TransferJob::slotData( const QByteArray &_data)
00740 {
00741 if(m_redirectionURL.isEmpty() || m_redirectionURL.isMalformed() || m_error)
00742 emit data( this, _data);
00743 }
00744
00745
00746 void TransferJob::slotRedirection( const KURL &url)
00747 {
00748 kdDebug(7007) << "TransferJob::slotRedirection(" << url.prettyURL() << ")" << endl;
00749 if (!kapp->authorizeURLAction("redirect", m_url, url))
00750 {
00751 kdWarning(7007) << "TransferJob: Redirection from " << m_url.prettyURL() << " to " << url.prettyURL() << " REJECTED!" << endl;
00752 return;
00753 }
00754
00755
00756
00757
00758 if (m_redirectionList.contains(url) > 5)
00759 {
00760 kdDebug(7007) << "TransferJob::slotRedirection: CYCLIC REDIRECTION!" << endl;
00761 m_error = ERR_CYCLIC_LINK;
00762 m_errorText = m_url.prettyURL();
00763 }
00764 else
00765 {
00766 m_redirectionURL = url;
00767 if (m_url.hasUser() && !url.hasUser() && (m_url.host().lower() == url.host().lower()))
00768 m_redirectionURL.setUser(m_url.user());
00769 m_redirectionList.append(url);
00770 m_outgoingMetaData["ssl_was_in_use"] = m_incomingMetaData["ssl_in_use"];
00771
00772 emit redirection(this, m_redirectionURL);
00773 }
00774 }
00775
00776 void TransferJob::slotFinished()
00777 {
00778
00779 if (m_redirectionURL.isEmpty() || m_redirectionURL.isMalformed())
00780 SimpleJob::slotFinished();
00781 else {
00782
00783 if (queryMetaData("permanent-redirect")=="true")
00784 emit permanentRedirection(this, m_url, m_redirectionURL);
00785
00786
00787
00788
00789 staticData.truncate(0);
00790 m_incomingMetaData.clear();
00791 if (queryMetaData("cache") != "reload");
00792 addMetaData("cache","refresh");
00793 m_suspended = false;
00794 m_url = m_redirectionURL;
00795 m_redirectionURL = KURL();
00796
00797 QString dummyStr;
00798 KURL dummyUrl;
00799 QDataStream istream( m_packedArgs, IO_ReadOnly );
00800 switch( m_command ) {
00801 case CMD_GET: {
00802 m_packedArgs.truncate(0);
00803 QDataStream stream( m_packedArgs, IO_WriteOnly );
00804 stream << m_url;
00805 break;
00806 }
00807 case CMD_PUT: {
00808 int permissions;
00809 Q_INT8 iOverwrite, iResume;
00810 istream >> dummyUrl >> iOverwrite >> iResume >> permissions;
00811 m_packedArgs.truncate(0);
00812 QDataStream stream( m_packedArgs, IO_WriteOnly );
00813 stream << m_url << iOverwrite << iResume << permissions;
00814 break;
00815 }
00816 case CMD_SPECIAL: {
00817 int specialcmd;
00818 istream >> specialcmd;
00819 assert(specialcmd == 1);
00820 if (specialcmd == 1)
00821 {
00822 addMetaData("cache","reload");
00823 m_packedArgs.truncate(0);
00824 QDataStream stream( m_packedArgs, IO_WriteOnly );
00825 stream << m_url;
00826 m_command = CMD_GET;
00827 }
00828 break;
00829 }
00830 }
00831
00832
00833 slaveDone();
00834 Scheduler::doJob(this);
00835 }
00836 }
00837
00838
00839 void TransferJob::slotDataReq()
00840 {
00841 QByteArray dataForSlave;
00842 if (!staticData.isEmpty())
00843 {
00844 dataForSlave = staticData;
00845 staticData = QByteArray();
00846 }
00847 else
00848 emit dataReq( this, dataForSlave);
00849
00850 static const size_t max_size = 14 * 1024 * 1024;
00851 if (dataForSlave.size() > max_size) {
00852 kdDebug() << "send " << dataForSlave.size() / 1024 / 1024 << "MB of data in TransferJob::dataReq. This needs to be splitted, which requires a copy. Fix the application.\n";
00853 staticData.duplicate(dataForSlave.data() + max_size , dataForSlave.size() - max_size);
00854 dataForSlave.truncate(max_size);
00855 }
00856
00857 m_slave->connection()->send( MSG_DATA, dataForSlave );
00858 if (m_subJob)
00859 {
00860
00861 suspend();
00862 m_subJob->resume();
00863 }
00864 }
00865
00866 void TransferJob::slotMimetype( const QString& type )
00867 {
00868 m_mimetype = type;
00869 emit mimetype( this, m_mimetype);
00870 }
00871
00872
00873 void TransferJob::suspend()
00874 {
00875 m_suspended = true;
00876 if (m_slave)
00877 m_slave->connection()->suspend();
00878 }
00879
00880 void TransferJob::resume()
00881 {
00882 m_suspended = false;
00883 if (m_slave)
00884 m_slave->connection()->resume();
00885 }
00886
00887 void TransferJob::start(Slave *slave)
00888 {
00889 assert(slave);
00890 connect( slave, SIGNAL( data( const QByteArray & ) ),
00891 SLOT( slotData( const QByteArray & ) ) );
00892
00893 connect( slave, SIGNAL( dataReq() ),
00894 SLOT( slotDataReq() ) );
00895
00896 connect( slave, SIGNAL( redirection(const KURL &) ),
00897 SLOT( slotRedirection(const KURL &) ) );
00898
00899 connect( slave, SIGNAL(mimeType( const QString& ) ),
00900 SLOT( slotMimetype( const QString& ) ) );
00901
00902 connect( slave, SIGNAL(errorPage() ),
00903 SLOT( slotErrorPage() ) );
00904
00905 connect( slave, SIGNAL( needSubURLData() ),
00906 SLOT( slotNeedSubURLData() ) );
00907
00908 connect( slave, SIGNAL(canResume( KIO::filesize_t ) ),
00909 SLOT( slotCanResume( KIO::filesize_t ) ) );
00910
00911 if (slave->suspended())
00912 {
00913 m_mimetype = "unknown";
00914
00915 slave->resume();
00916 }
00917
00918 SimpleJob::start(slave);
00919 if (m_suspended)
00920 slave->connection()->suspend();
00921 }
00922
00923 void TransferJob::slotNeedSubURLData()
00924 {
00925
00926 m_subJob = KIO::get( m_subUrl, false, false);
00927 suspend();
00928 connect(m_subJob, SIGNAL( data(KIO::Job*,const QByteArray &)),
00929 SLOT( slotSubURLData(KIO::Job*,const QByteArray &)));
00930 addSubjob(m_subJob);
00931 }
00932
00933 void TransferJob::slotSubURLData(KIO::Job*, const QByteArray &data)
00934 {
00935
00936 staticData = data;
00937 m_subJob->suspend();
00938 resume();
00939 }
00940
00941 void TransferJob::slotErrorPage()
00942 {
00943 m_errorPage = true;
00944 }
00945
00946 void TransferJob::slotCanResume( KIO::filesize_t offset )
00947 {
00948 emit canResume(this, offset);
00949 }
00950
00951 void TransferJob::slotResult( KIO::Job *job)
00952 {
00953
00954 assert(job == m_subJob);
00955
00956 if ( job->error() )
00957 {
00958 m_error = job->error();
00959 m_errorText = job->errorText();
00960
00961 emitResult();
00962 return;
00963 }
00964
00965 if (job == m_subJob)
00966 {
00967 m_subJob = 0;
00968 resume();
00969 }
00970 subjobs.remove(job);
00971 }
00972
00973 TransferJob *KIO::get( const KURL& url, bool reload, bool showProgressInfo )
00974 {
00975
00976 KIO_ARGS << url;
00977 TransferJob * job = new TransferJob( url, CMD_GET, packedArgs, QByteArray(), showProgressInfo );
00978 if (reload)
00979 job->addMetaData("cache", "reload");
00980 return job;
00981 }
00982
00983 class PostErrorJob : public TransferJob
00984 {
00985 public:
00986
00987 PostErrorJob(QString url, const QByteArray &packedArgs, const QByteArray &postData, bool showProgressInfo) : TransferJob("", CMD_SPECIAL, packedArgs, postData, showProgressInfo)
00988 {
00989 m_error = KIO::ERR_POST_DENIED;
00990 m_errorText = url;
00991 }
00992
00993 };
00994
00995 TransferJob *KIO::http_post( const KURL& url, const QByteArray &postData, bool showProgressInfo )
00996 {
00997 bool valid = true;
00998
00999
01000 if ((url.protocol() != "http") && (url.protocol() != "https" ))
01001 valid = false;
01002
01003
01004 static const int bad_ports[] = {
01005 1,
01006 7,
01007 9,
01008 11,
01009 13,
01010 15,
01011 17,
01012 19,
01013 20,
01014 21,
01015 22,
01016 23,
01017 25,
01018 37,
01019 42,
01020 43,
01021 53,
01022 77,
01023 79,
01024 87,
01025 95,
01026 101,
01027 102,
01028 103,
01029 104,
01030 109,
01031 110,
01032 111,
01033 113,
01034 115,
01035 117,
01036 119,
01037 123,
01038 135,
01039 139,
01040 143,
01041 179,
01042 389,
01043 512,
01044 513,
01045 514,
01046 515,
01047 526,
01048 530,
01049 531,
01050 532,
01051 540,
01052 556,
01053 587,
01054 601,
01055 989,
01056 990,
01057 992,
01058 993,
01059 995,
01060 1080,
01061 2049,
01062 4045,
01063 6000,
01064 6667,
01065 0};
01066 for (int cnt=0; bad_ports[cnt]; ++cnt)
01067 if (url.port() == bad_ports[cnt])
01068 {
01069 valid = false;
01070 break;
01071 }
01072
01073 if( !valid )
01074 {
01075 static bool override_loaded = false;
01076 static QValueList< int >* overriden_ports = NULL;
01077 if( !override_loaded )
01078 {
01079 KConfig cfg( "kio_httprc", true );
01080 overriden_ports = new QValueList< int >;
01081 *overriden_ports = cfg.readIntListEntry( "OverriddenPorts" );
01082 override_loaded = true;
01083 }
01084 for( QValueList< int >::ConstIterator it = overriden_ports->begin();
01085 it != overriden_ports->end();
01086 ++it )
01087 if( overriden_ports->contains( url.port()))
01088 valid = true;
01089 }
01090
01091
01092 if (!valid)
01093 {
01094 KIO_ARGS << (int)1 << url;
01095 TransferJob * job = new PostErrorJob(url.url(), packedArgs, postData, showProgressInfo);
01096 return job;
01097 }
01098
01099
01100 KIO_ARGS << (int)1 << url;
01101 TransferJob * job = new TransferJob( url, CMD_SPECIAL,
01102 packedArgs, postData, showProgressInfo );
01103 return job;
01104 }
01105
01106
01107
01108 TransferJob *KIO::put( const KURL& url, int permissions,
01109 bool overwrite, bool resume, bool showProgressInfo )
01110 {
01111 KIO_ARGS << url << Q_INT8( overwrite ? 1 : 0 ) << Q_INT8( resume ? 1 : 0 ) << permissions;
01112 TransferJob * job = new TransferJob( url, CMD_PUT, packedArgs, QByteArray(), showProgressInfo );
01113 return job;
01114 }
01115
01117
01118 MimetypeJob::MimetypeJob( const KURL& url, int command,
01119 const QByteArray &packedArgs, bool showProgressInfo )
01120 : TransferJob(url, command, packedArgs, QByteArray(), showProgressInfo)
01121 {
01122 }
01123
01124 void MimetypeJob::start(Slave *slave)
01125 {
01126 TransferJob::start(slave);
01127 }
01128
01129
01130 void MimetypeJob::slotFinished( )
01131 {
01132 kdDebug(7007) << "MimetypeJob::slotFinished()" << endl;
01133 if ( m_error == KIO::ERR_IS_DIRECTORY )
01134 {
01135
01136
01137
01138 kdDebug(7007) << "It is in fact a directory!" << endl;
01139 m_mimetype = QString::fromLatin1("inode/directory");
01140 emit TransferJob::mimetype( this, m_mimetype );
01141 m_error = 0;
01142 }
01143 if ( m_redirectionURL.isEmpty() || m_redirectionURL.isMalformed() || m_error )
01144 {
01145
01146 TransferJob::slotFinished();
01147 } else {
01148
01149 if (queryMetaData("permanent-redirect")=="true")
01150 emit permanentRedirection(this, m_url, m_redirectionURL);
01151 staticData.truncate(0);
01152 m_suspended = false;
01153 m_url = m_redirectionURL;
01154 m_redirectionURL = KURL();
01155 m_packedArgs.truncate(0);
01156 QDataStream stream( m_packedArgs, IO_WriteOnly );
01157 stream << m_url;
01158
01159
01160 slaveDone();
01161 Scheduler::doJob(this);
01162 }
01163 }
01164
01165 MimetypeJob *KIO::mimetype(const KURL& url, bool showProgressInfo )
01166 {
01167 KIO_ARGS << url;
01168 MimetypeJob * job = new MimetypeJob(url, CMD_MIMETYPE, packedArgs, showProgressInfo);
01169 if ( showProgressInfo )
01170 Observer::self()->stating( job, url );
01171 return job;
01172 }
01173
01175
01176
01177 class FileCopyJob::FileCopyJobPrivate
01178 {
01179 public:
01180 off_t m_sourceSize;
01181 SimpleJob *m_delJob;
01182 };
01183
01184
01185
01186
01187
01188
01189
01190
01191 FileCopyJob::FileCopyJob( const KURL& src, const KURL& dest, int permissions,
01192 bool move, bool overwrite, bool resume, bool showProgressInfo)
01193 : Job(showProgressInfo), m_src(src), m_dest(dest),
01194 m_permissions(permissions), m_move(move), m_overwrite(overwrite), m_resume(resume),
01195 m_totalSize(0)
01196 {
01197 if (showProgressInfo && !move)
01198 Observer::self()->slotCopying( this, src, dest );
01199 else if (showProgressInfo && move)
01200 Observer::self()->slotMoving( this, src, dest );
01201
01202
01203 m_moveJob = 0;
01204 m_copyJob = 0;
01205 m_getJob = 0;
01206 m_putJob = 0;
01207 d = new FileCopyJobPrivate;
01208 d->m_delJob = 0;
01209 d->m_sourceSize = (off_t) -1;
01210 QTimer::singleShot(0, this, SLOT(slotStart()));
01211 }
01212
01213 void FileCopyJob::slotStart()
01214 {
01215 if ((m_src.protocol() == m_dest.protocol()) &&
01216 (m_src.host() == m_dest.host()) &&
01217 (m_src.port() == m_dest.port()) &&
01218 (m_src.user() == m_dest.user()) &&
01219 (m_src.pass() == m_dest.pass()))
01220 {
01221 if (m_move)
01222 {
01223 m_moveJob = KIO::rename( m_src, m_dest, m_overwrite );
01224 addSubjob( m_moveJob );
01225 connectSubjob( m_moveJob );
01226 }
01227 else
01228 {
01229 startCopyJob();
01230 }
01231 }
01232 else
01233 {
01234 if (!m_move &&
01235 (m_src.isLocalFile() && KProtocolInfo::canCopyFromFile(m_dest))
01236 )
01237 {
01238 startCopyJob(m_dest);
01239 }
01240 else if (!m_move &&
01241 (m_dest.isLocalFile() && KProtocolInfo::canCopyToFile(m_src))
01242 )
01243 {
01244 startCopyJob(m_src);
01245 }
01246 else
01247 {
01248 startDataPump();
01249 }
01250 }
01251 }
01252
01253 FileCopyJob::~FileCopyJob()
01254 {
01255 delete d;
01256 }
01257
01258 void FileCopyJob::setSourceSize( off_t size )
01259 {
01260 d->m_sourceSize = size;
01261 m_totalSize = size;
01262 }
01263
01264 void FileCopyJob::startCopyJob()
01265 {
01266 startCopyJob(m_src);
01267 }
01268
01269 void FileCopyJob::startCopyJob(const KURL &slave_url)
01270 {
01271
01272 KIO_ARGS << m_src << m_dest << m_permissions << (Q_INT8) m_overwrite;
01273 m_copyJob = new SimpleJob(slave_url, CMD_COPY, packedArgs, false);
01274 addSubjob( m_copyJob );
01275 connectSubjob( m_copyJob );
01276 }
01277
01278 void FileCopyJob::connectSubjob( SimpleJob * job )
01279 {
01280 connect( job, SIGNAL(totalSize( KIO::Job*, KIO::filesize_t )),
01281 this, SLOT( slotTotalSize(KIO::Job*, KIO::filesize_t)) );
01282
01283 connect( job, SIGNAL(processedSize( KIO::Job*, KIO::filesize_t )),
01284 this, SLOT( slotProcessedSize(KIO::Job*, KIO::filesize_t)) );
01285
01286 connect( job, SIGNAL(percent( KIO::Job*, unsigned long )),
01287 this, SLOT( slotPercent(KIO::Job*, unsigned long)) );
01288
01289 }
01290
01291 void FileCopyJob::slotProcessedSize( KIO::Job *, KIO::filesize_t size )
01292 {
01293 emit processedSize( this, size );
01294 if ( size > m_totalSize ) {
01295 slotTotalSize( this, size );
01296 }
01297 emitPercent( size, m_totalSize );
01298 }
01299
01300 void FileCopyJob::slotTotalSize( KIO::Job*, KIO::filesize_t size )
01301 {
01302 m_totalSize = size;
01303 emit totalSize( this, m_totalSize );
01304 }
01305
01306 void FileCopyJob::slotPercent( KIO::Job*, unsigned long pct )
01307 {
01308 if ( pct > m_percent )
01309 {
01310 m_percent = pct;
01311 emit percent( this, m_percent );
01312 }
01313 }
01314
01315 void FileCopyJob::startDataPump()
01316 {
01317
01318
01319 m_canResume = false;
01320 m_resumeAnswerSent = false;
01321 m_getJob = 0L;
01322 m_putJob = put( m_dest, m_permissions, m_overwrite, m_resume, false );
01323
01324
01325
01326
01327 connect( m_putJob, SIGNAL(canResume(KIO::Job *, KIO::filesize_t)),
01328 SLOT( slotCanResume(KIO::Job *, KIO::filesize_t)));
01329 connect( m_putJob, SIGNAL(dataReq(KIO::Job *, QByteArray&)),
01330 SLOT( slotDataReq(KIO::Job *, QByteArray&)));
01331 addSubjob( m_putJob );
01332 }
01333
01334 void FileCopyJob::slotCanResume( KIO::Job* job, KIO::filesize_t offset )
01335 {
01336 if ( job == m_putJob )
01337 {
01338 kdDebug(7007) << "FileCopyJob::slotCanResume from PUT job. offset=" << KIO::number(offset) << endl;
01339 if (offset)
01340 {
01341 RenameDlg_Result res = R_RESUME;
01342
01343 if (!KProtocolManager::autoResume())
01344 {
01345 QString newPath;
01346 KIO::Job* job = ( !m_progressId && parentJob() ) ? parentJob() : this;
01347
01348 res = Observer::self()->open_RenameDlg(
01349 job, i18n("File Already Exists"),
01350 m_src.prettyURL(0, KURL::StripFileProtocol),
01351 m_dest.prettyURL(0, KURL::StripFileProtocol),
01352 (RenameDlg_Mode) (M_OVERWRITE | M_RESUME | M_NORENAME), newPath,
01353 d->m_sourceSize, offset );
01354 }
01355
01356 if ( res == R_OVERWRITE )
01357 offset = 0;
01358 else if ( res == R_CANCEL )
01359 {
01360 m_putJob->kill(true);
01361 m_error = ERR_USER_CANCELED;
01362 emitResult();
01363 return;
01364 }
01365 }
01366 else
01367 m_resumeAnswerSent = true;
01368
01369 m_getJob = get( m_src, false, false );
01370
01371 m_getJob->addMetaData( "errorPage", "false" );
01372 m_getJob->addMetaData( "AllowCompressedPage", "false" );
01373
01374 if ( d->m_sourceSize != (off_t)-1 )
01375 m_getJob->slotTotalSize( d->m_sourceSize );
01376 if (offset)
01377 {
01378 kdDebug(7007) << "Setting metadata for resume to " << (unsigned long) offset << endl;
01379 m_getJob->addMetaData( "resume", KIO::number(offset) );
01380
01381
01382 connect( m_getJob, SIGNAL(canResume(KIO::Job *, KIO::filesize_t)),
01383 SLOT( slotCanResume(KIO::Job *, KIO::filesize_t)));
01384 }
01385 m_putJob->slave()->setOffset( offset );
01386
01387 m_putJob->suspend();
01388 addSubjob( m_getJob );
01389 connectSubjob( m_getJob );
01390 m_getJob->resume();
01391
01392 connect( m_getJob, SIGNAL(data(KIO::Job *, const QByteArray&)),
01393 SLOT( slotData(KIO::Job *, const QByteArray&)));
01394 }
01395 else if ( job == m_getJob )
01396 {
01397
01398 m_canResume = true;
01399 kdDebug(7007) << "FileCopyJob::slotCanResume from the GET job -> we can resume" << endl;
01400
01401 m_getJob->slave()->setOffset( m_putJob->slave()->offset() );
01402 }
01403 else
01404 kdWarning(7007) << "FileCopyJob::slotCanResume from unknown job=" << job
01405 << " m_getJob=" << m_getJob << " m_putJob=" << m_putJob << endl;
01406 }
01407
01408 void FileCopyJob::slotData( KIO::Job * , const QByteArray &data)
01409 {
01410
01411
01412 assert(m_putJob);
01413 m_getJob->suspend();
01414 m_putJob->resume();
01415 m_buffer = data;
01416
01417
01418
01419 if (!m_resumeAnswerSent)
01420 {
01421 m_resumeAnswerSent = true;
01422 kdDebug(7007) << "FileCopyJob::slotData (first time) -> send resume answer " << m_canResume << endl;
01423 m_putJob->slave()->sendResumeAnswer( m_canResume );
01424 }
01425 }
01426
01427 void FileCopyJob::slotDataReq( KIO::Job * , QByteArray &data)
01428 {
01429
01430 if (!m_resumeAnswerSent && !m_getJob)
01431 {
01432
01433 m_error = ERR_INTERNAL;
01434 m_errorText = "'Put' job didn't send canResume or 'Get' job didn't send data!";
01435 m_putJob->kill(true);
01436 emitResult();
01437 return;
01438 }
01439 if (m_getJob)
01440 {
01441 m_getJob->resume();
01442 m_putJob->suspend();
01443 }
01444 data = m_buffer;
01445 m_buffer = QByteArray();
01446 }
01447
01448 void FileCopyJob::slotResult( KIO::Job *job)
01449 {
01450
01451
01452 if ( job->error() )
01453 {
01454 if ((job == m_moveJob) && (job->error() == ERR_UNSUPPORTED_ACTION))
01455 {
01456 m_moveJob = 0;
01457 startCopyJob();
01458 removeSubjob(job);
01459 return;
01460 }
01461 else if ((job == m_copyJob) && (job->error() == ERR_UNSUPPORTED_ACTION))
01462 {
01463 m_copyJob = 0;
01464 startDataPump();
01465 removeSubjob(job);
01466 return;
01467 }
01468 else if (job == m_getJob)
01469 {
01470 m_getJob = 0L;
01471 if (m_putJob)
01472 m_putJob->kill(true);
01473 }
01474 else if (job == m_putJob)
01475 {
01476 m_putJob = 0L;
01477 if (m_getJob)
01478 m_getJob->kill(true);
01479 }
01480 m_error = job->error();
01481 m_errorText = job->errorText();
01482 emitResult();
01483 return;
01484 }
01485
01486 if (job == m_moveJob)
01487 {
01488 m_moveJob = 0;
01489 }
01490
01491 if (job == m_copyJob)
01492 {
01493 m_copyJob = 0;
01494 if (m_move)
01495 {
01496 d->m_delJob = file_delete( m_src, false );
01497 addSubjob(d->m_delJob);
01498 }
01499 }
01500
01501 if (job == m_getJob)
01502 {
01503 m_getJob = 0;
01504 if (m_putJob)
01505 m_putJob->resume();
01506 }
01507
01508 if (job == m_putJob)
01509 {
01510
01511 m_putJob = 0;
01512 if (m_getJob)
01513 {
01514 kdWarning(7007) << "WARNING ! Get still going on..." << endl;
01515 m_getJob->resume();
01516 }
01517 if (m_move)
01518 {
01519 d->m_delJob = file_delete( m_src, false );
01520 addSubjob(d->m_delJob);
01521 }
01522 }
01523
01524 if (job == d->m_delJob)
01525 {
01526 d->m_delJob = 0;
01527 }
01528 removeSubjob(job);
01529 }
01530
01531 FileCopyJob *KIO::file_copy( const KURL& src, const KURL& dest, int permissions,
01532 bool overwrite, bool resume, bool showProgressInfo)
01533 {
01534 return new FileCopyJob( src, dest, permissions, false, overwrite, resume, showProgressInfo );
01535 }
01536
01537 FileCopyJob *KIO::file_move( const KURL& src, const KURL& dest, int permissions,
01538 bool overwrite, bool resume, bool showProgressInfo)
01539 {
01540 return new FileCopyJob( src, dest, permissions, true, overwrite, resume, showProgressInfo );
01541 }
01542
01543 SimpleJob *KIO::file_delete( const KURL& src, bool showProgressInfo)
01544 {
01545 KIO_ARGS << src << Q_INT8(true);
01546 return new SimpleJob(src, CMD_DEL, packedArgs, showProgressInfo );
01547 }
01548
01550
01551 ListJob::ListJob(const KURL& u, bool showProgressInfo, bool _recursive, QString _prefix, bool _includeHidden) :
01552 SimpleJob(u, CMD_LISTDIR, QByteArray(), showProgressInfo),
01553 recursive(_recursive), includeHidden(_includeHidden), prefix(_prefix), m_processedEntries(0)
01554 {
01555
01556
01557 QDataStream stream( m_packedArgs, IO_WriteOnly );
01558 stream << u;
01559 }
01560
01561 void ListJob::slotListEntries( const KIO::UDSEntryList& list )
01562 {
01563
01564 m_processedEntries += list.count();
01565 slotProcessedSize( m_processedEntries );
01566
01567 if (recursive) {
01568 UDSEntryListConstIterator it = list.begin();
01569 UDSEntryListConstIterator end = list.end();
01570
01571 for (; it != end; ++it) {
01572 bool isDir = false;
01573 bool isLink = false;
01574 QString filename;
01575
01576 UDSEntry::ConstIterator it2 = (*it).begin();
01577 UDSEntry::ConstIterator end2 = (*it).end();
01578 for( ; it2 != end2; it2++ ) {
01579 switch( (*it2).m_uds ) {
01580 case UDS_FILE_TYPE:
01581 isDir = S_ISDIR((*it2).m_long);
01582 break;
01583 case UDS_NAME:
01584 filename = (*it2).m_str;
01585 break;
01586 case UDS_LINK_DEST:
01587
01588 isLink = !(*it2).m_str.isEmpty();
01589 break;
01590 default:
01591 break;
01592 }
01593 }
01594 if (isDir && !isLink) {
01595
01596 if (filename != ".." && filename != "." && (includeHidden || filename[0] != '.')) {
01597 KURL newone = url();
01598 newone.addPath(filename);
01599 ListJob *job = new ListJob(newone, m_progressId!=0, true, prefix + filename + "/",includeHidden);
01600 Scheduler::scheduleJob(job);
01601 connect(job, SIGNAL(entries( KIO::Job *,
01602 const KIO::UDSEntryList& )),
01603 SLOT( gotEntries( KIO::Job*,
01604 const KIO::UDSEntryList& )));
01605 addSubjob(job);
01606 }
01607 }
01608 }
01609 }
01610
01611
01612
01613
01614 if (prefix.isNull() && includeHidden) {
01615 emit entries(this, list);
01616 } else {
01617
01618 UDSEntryList newlist;
01619
01620 UDSEntryListConstIterator it = list.begin();
01621 UDSEntryListConstIterator end = list.end();
01622 for (; it != end; ++it) {
01623
01624 UDSEntry newone = *it;
01625 UDSEntry::Iterator it2 = newone.begin();
01626 QString filename;
01627 for( ; it2 != newone.end(); it2++ ) {
01628 if ((*it2).m_uds == UDS_NAME) {
01629 filename = (*it2).m_str;
01630 (*it2).m_str = prefix + filename;
01631 }
01632 }
01633
01634
01635 if ( (prefix.isNull() || (filename != ".." && filename != ".") )
01636 && (includeHidden || (filename[0] != '.') ) )
01637 newlist.append(newone);
01638 }
01639
01640 emit entries(this, newlist);
01641 }
01642 }
01643
01644 void ListJob::gotEntries(KIO::Job *, const KIO::UDSEntryList& list )
01645 {
01646
01647 emit entries(this, list);
01648 }
01649
01650 void ListJob::slotResult( KIO::Job * job )
01651 {
01652
01653
01654 removeSubjob( job );
01655 }
01656
01657 void ListJob::slotRedirection( const KURL & url )
01658 {
01659 if (!kapp->authorizeURLAction("redirect", m_url, url))
01660 {
01661 kdWarning(7007) << "ListJob: Redirection from " << m_url.prettyURL() << " to " << url.prettyURL() << " REJECTED!" << endl;
01662 return;
01663 }
01664 m_redirectionURL = url;
01665 if (m_url.hasUser() && !url.hasUser() && (m_url.host().lower() == url.host().lower()))
01666 m_redirectionURL.setUser(m_url.user());
01667 emit redirection( this, url );
01668 }
01669
01670 void ListJob::slotFinished()
01671 {
01672 if ( m_redirectionURL.isEmpty() || m_redirectionURL.isMalformed() || m_error )
01673 {
01674
01675 SimpleJob::slotFinished();
01676 } else {
01677 kdDebug(7007) << "ListJob: Redirection to " << m_redirectionURL.prettyURL() << endl;
01678 if (queryMetaData("permanent-redirect")=="true")
01679 emit permanentRedirection(this, m_url, m_redirectionURL);
01680 m_url = m_redirectionURL;
01681 m_redirectionURL = KURL();
01682 m_packedArgs.truncate(0);
01683 QDataStream stream( m_packedArgs, IO_WriteOnly );
01684 stream << m_url;
01685
01686
01687 slaveDone();
01688 Scheduler::doJob(this);
01689 }
01690 }
01691
01692 ListJob *KIO::listDir( const KURL& url, bool showProgressInfo, bool includeHidden )
01693 {
01694 ListJob * job = new ListJob(url, showProgressInfo,false,QString::null,includeHidden);
01695 return job;
01696 }
01697
01698 ListJob *KIO::listRecursive( const KURL& url, bool showProgressInfo, bool includeHidden )
01699 {
01700 ListJob * job = new ListJob(url, showProgressInfo, true,QString::null,includeHidden);
01701 return job;
01702 }
01703
01704 void ListJob::start(Slave *slave)
01705 {
01706 connect( slave, SIGNAL( listEntries( const KIO::UDSEntryList& )),
01707 SLOT( slotListEntries( const KIO::UDSEntryList& )));
01708 connect( slave, SIGNAL( totalSize( KIO::filesize_t ) ),
01709 SLOT( slotTotalSize( KIO::filesize_t ) ) );
01710 connect( slave, SIGNAL( redirection(const KURL &) ),
01711 SLOT( slotRedirection(const KURL &) ) );
01712
01713 SimpleJob::start(slave);
01714 }
01715
01716
01717 CopyJob::CopyJob( const KURL::List& src, const KURL& dest, CopyMode mode, bool asMethod, bool showProgressInfo )
01718 : Job(showProgressInfo), m_mode(mode), m_asMethod(asMethod),
01719 destinationState(DEST_NOT_STATED), state(STATE_STATING),
01720 m_totalSize(0), m_processedSize(0), m_fileProcessedSize(0),
01721 m_processedFiles(0), m_processedDirs(0),
01722 m_srcList(src), m_currentStatSrc(m_srcList.begin()),
01723 m_bCurrentOperationIsLink(false), m_bSingleFileCopy(false), m_bOnlyRenames(mode==Move),
01724 m_dest(dest), m_bAutoSkip( false ), m_bOverwriteAll( false ),
01725 m_conflictError(0), m_reportTimer(0)
01726 {
01727 if ( showProgressInfo ) {
01728 connect( this, SIGNAL( totalFiles( KIO::Job*, unsigned long ) ),
01729 Observer::self(), SLOT( slotTotalFiles( KIO::Job*, unsigned long ) ) );
01730
01731 connect( this, SIGNAL( totalDirs( KIO::Job*, unsigned long ) ),
01732 Observer::self(), SLOT( slotTotalDirs( KIO::Job*, unsigned long ) ) );
01733 }
01734 QTimer::singleShot(0, this, SLOT(slotStart()));
01735 }
01736
01737 void CopyJob::slotStart()
01738 {
01744 m_reportTimer = new QTimer(this);
01745
01746 connect(m_reportTimer,SIGNAL(timeout()),this,SLOT(slotReport()));
01747 m_reportTimer->start(REPORT_TIMEOUT,false);
01748
01749
01750 KIO::Job * job = KIO::stat( m_dest, false, 2, false );
01751
01752 addSubjob(job);
01753 }
01754
01755 void CopyJob::slotResultStating( Job *job )
01756 {
01757
01758
01759 if (job->error() && destinationState != DEST_NOT_STATED )
01760 {
01761 KURL srcurl = ((SimpleJob*)job)->url();
01762 if ( !srcurl.isLocalFile() )
01763 {
01764
01765
01766
01767 kdDebug(7007) << "Error while stating source. Activating hack" << endl;
01768 subjobs.remove( job );
01769 assert ( subjobs.isEmpty() );
01770 struct CopyInfo info;
01771 info.permissions = (mode_t) -1;
01772 info.mtime = (time_t) -1;
01773 info.ctime = (time_t) -1;
01774 info.size = (off_t)-1;
01775 info.uSource = srcurl;
01776 info.uDest = m_dest;
01777
01778 if ( destinationState == DEST_IS_DIR && !m_asMethod )
01779 info.uDest.addPath( srcurl.fileName() );
01780
01781 files.append( info );
01782 ++m_currentStatSrc;
01783 statNextSrc();
01784 return;
01785 }
01786
01787 Job::slotResult( job );
01788 return;
01789 }
01790
01791
01792 UDSEntry entry = ((StatJob*)job)->statResult();
01793 bool bDir = false;
01794 bool bLink = false;
01795 UDSEntry::ConstIterator it2 = entry.begin();
01796 for( ; it2 != entry.end(); it2++ ) {
01797 if ( ((*it2).m_uds) == UDS_FILE_TYPE )
01798 bDir = S_ISDIR( (mode_t)(*it2).m_long );
01799 else if ( ((*it2).m_uds) == UDS_LINK_DEST )
01800 bLink = !((*it2).m_str.isEmpty());
01801 }
01802
01803 if ( destinationState == DEST_NOT_STATED )
01804
01805 {
01806 if (job->error())
01807 destinationState = DEST_DOESNT_EXIST;
01808 else {
01809
01810 destinationState = bDir ? DEST_IS_DIR : DEST_IS_FILE;
01811
01812 }
01813 subjobs.remove( job );
01814 assert ( subjobs.isEmpty() );
01815
01816
01817 statNextSrc();
01818 return;
01819 }
01820
01821 m_currentDest = m_dest;
01822
01823 UDSEntryList lst;
01824 lst.append(entry);
01825
01826
01827
01828
01829
01830
01831
01832
01833
01834
01835
01836
01837
01838 m_bCurrentSrcIsDir = false;
01839 slotEntries(job, lst);
01840
01841 KURL srcurl = ((SimpleJob*)job)->url();
01842
01843 subjobs.remove( job );
01844 assert ( subjobs.isEmpty() );
01845
01846 if ( bDir
01847 && !bLink
01848 && m_mode != Link )
01849 {
01850
01851
01852 m_bCurrentSrcIsDir = true;
01853 if ( destinationState == DEST_IS_DIR )
01854
01855 m_currentDest.addPath( srcurl.fileName() );
01856 else if ( destinationState == DEST_IS_FILE )
01857 {
01858 m_error = ERR_IS_FILE;
01859 m_errorText = m_dest.prettyURL();
01860 emitResult();
01861 return;
01862 }
01863 else
01864 {
01865
01866
01867
01868
01869 destinationState = DEST_IS_DIR;
01870 }
01871
01872 startListing( srcurl );
01873 }
01874 else
01875 {
01876
01877 ++m_currentStatSrc;
01878 statNextSrc();
01879 }
01880 }
01881
01882 void CopyJob::slotReport()
01883 {
01884
01885 Observer * observer = m_progressId ? Observer::self() : 0L;
01886 switch (state) {
01887 case STATE_COPYING_FILES:
01888 emit processedFiles( this, m_processedFiles );
01889 if (observer) observer->slotProcessedFiles(this,m_processedFiles);
01890 if (m_mode==Move)
01891 {
01892 if (observer) observer->slotMoving( this, m_currentSrcURL,m_currentDestURL);
01893 emit moving( this, m_currentSrcURL, m_currentDestURL);
01894 }
01895 else if (m_mode==Link)
01896 {
01897 if (observer) observer->slotCopying( this, m_currentSrcURL, m_currentDestURL );
01898 emit linking( this, m_currentSrcURL.path(), m_currentDestURL );
01899 }
01900 else
01901 {
01902 if (observer) observer->slotCopying( this, m_currentSrcURL, m_currentDestURL );
01903 emit copying( this, m_currentSrcURL, m_currentDestURL );
01904 };
01905 break;
01906
01907 case STATE_CREATING_DIRS:
01908 if (observer) {
01909 observer->slotProcessedDirs( this, m_processedDirs );
01910 observer->slotCreatingDir( this,m_currentDestURL);
01911 }
01912 emit processedDirs( this, m_processedDirs );
01913 emit creatingDir( this, m_currentDestURL );
01914 break;
01915
01916 case STATE_STATING:
01917 case STATE_LISTING:
01918 if (observer) observer->slotCopying( this, m_currentSrcURL, m_currentDestURL );
01919 emit totalSize( this, m_totalSize );
01920 emit totalFiles( this, files.count() );
01921 emit totalDirs( this, dirs.count() );
01922 break;
01923
01924 default:
01925 break;
01926 }
01927 };
01928
01929 void CopyJob::slotEntries(KIO::Job* job, const UDSEntryList& list)
01930 {
01931 UDSEntryListConstIterator it = list.begin();
01932 UDSEntryListConstIterator end = list.end();
01933 for (; it != end; ++it) {
01934 UDSEntry::ConstIterator it2 = (*it).begin();
01935 struct CopyInfo info;
01936 info.permissions = -1;
01937 info.mtime = (time_t) -1;
01938 info.ctime = (time_t) -1;
01939 info.size = (off_t)-1;
01940 QString relName;
01941 bool isDir = false;
01942 for( ; it2 != (*it).end(); it2++ ) {
01943 switch ((*it2).m_uds) {
01944 case UDS_FILE_TYPE:
01945
01946 isDir = S_ISDIR( (mode_t)((*it2).m_long) );
01947 break;
01948 case UDS_NAME:
01949 relName = (*it2).m_str;
01950 break;
01951 case UDS_LINK_DEST:
01952 info.linkDest = (*it2).m_str;
01953 break;
01954 case UDS_ACCESS:
01955 info.permissions = ((*it2).m_long);
01956 break;
01957 case UDS_SIZE:
01958 info.size = (off_t)((*it2).m_long);
01959 m_totalSize += info.size;
01960 break;
01961 case UDS_MODIFICATION_TIME:
01962 info.mtime = (time_t)((*it2).m_long);
01963 break;
01964 case UDS_CREATION_TIME:
01965 info.ctime = (time_t)((*it2).m_long);
01966 default:
01967 break;
01968 }
01969 }
01970 if (relName != ".." && relName != ".")
01971 {
01972
01973 info.uSource = ((SimpleJob *)job)->url();
01974 if ( m_bCurrentSrcIsDir )
01975 info.uSource.addPath( relName );
01976 info.uDest = m_currentDest;
01977
01978
01979 if ( destinationState == DEST_IS_DIR && !m_asMethod )
01980 {
01981
01982
01983
01984 if ( relName.isEmpty() )
01985 info.uDest.addPath( KIO::encodeFileName( info.uSource.prettyURL() ) );
01986 else
01987 info.uDest.addPath( relName );
01988 }
01989
01990 if ( info.linkDest.isEmpty() && (isDir ) && m_mode != Link )
01991 {
01992 dirs.append( info );
01993 if (m_mode == Move)
01994 dirsToRemove.append( info.uSource );
01995 }
01996 else {
01997 files.append( info );
01998 }
01999 }
02000 }
02001 }
02002
02003 void CopyJob::statNextSrc()
02004 {
02005 if ( m_currentStatSrc != m_srcList.end() )
02006 {
02007 m_currentSrcURL = (*m_currentStatSrc);
02008 if ( m_mode == Link )
02009 {
02010
02011 m_currentDest = m_dest;
02012 struct CopyInfo info;
02013 info.permissions = -1;
02014 info.mtime = (time_t) -1;
02015 info.ctime = (time_t) -1;
02016 info.size = (off_t)-1;
02017 info.uSource = m_currentSrcURL;
02018 info.uDest = m_currentDest;
02019
02020 if ( destinationState == DEST_IS_DIR && !m_asMethod )
02021 {
02022 if (
02023 (m_currentSrcURL.protocol() == info.uDest.protocol()) &&
02024 (m_currentSrcURL.host() == info.uDest.host()) &&
02025 (m_currentSrcURL.port() == info.uDest.port()) &&
02026 (m_currentSrcURL.user() == info.uDest.user()) &&
02027 (m_currentSrcURL.pass() == info.uDest.pass()) )
02028 {
02029
02030 info.uDest.addPath( m_currentSrcURL.fileName() );
02031 }
02032 else
02033 {
02034
02035
02036
02037 info.uDest.addPath( KIO::encodeFileName( m_currentSrcURL.prettyURL() )+".desktop" );
02038 }
02039 }
02040 files.append( info );
02041 ++m_currentStatSrc;
02042 statNextSrc();
02043 }
02044
02045 else if ( m_mode == Move &&
02046 (m_currentSrcURL.protocol() == m_dest.protocol()) &&
02047 (m_currentSrcURL.host() == m_dest.host()) &&
02048 (m_currentSrcURL.port() == m_dest.port()) &&
02049 (m_currentSrcURL.user() == m_dest.user()) &&
02050 (m_currentSrcURL.pass() == m_dest.pass()) )
02051 {
02052 KURL dest = m_dest;
02053
02054 if ( destinationState == DEST_IS_DIR && !m_asMethod )
02055 dest.addPath( m_currentSrcURL.fileName() );
02056 kdDebug(7007) << "This seems to be a suitable case for trying to rename before stat+[list+]copy+del" << endl;
02057 state = STATE_RENAMING;
02058 SimpleJob * newJob = KIO::rename( m_currentSrcURL, dest, false );
02059 Scheduler::scheduleJob(newJob);
02060 addSubjob( newJob );
02061 if ( m_currentSrcURL.directory() != dest.directory() )
02062 m_bOnlyRenames = false;
02063 }
02064 else
02065 {
02066
02067 if (m_mode == Move && !KProtocolInfo::supportsDeleting(m_currentSrcURL)) {
02068 KMessageBox::information( 0, buildErrorString(ERR_CANNOT_DELETE, m_currentSrcURL.prettyURL()));
02069 ++m_currentStatSrc;
02070 statNextSrc();
02071 return;
02072 }
02073
02074 Job * job = KIO::stat( m_currentSrcURL, true, 2, false );
02075
02076 state = STATE_STATING;
02077 addSubjob(job);
02078 m_currentDestURL=m_dest;
02079 m_bOnlyRenames = false;
02080 }
02081 } else
02082 {
02083
02084
02085 state = STATE_STATING;
02086 slotReport();
02087
02088 m_bSingleFileCopy = ( files.count() == 1 && dirs.isEmpty() );
02089
02090 state = STATE_CREATING_DIRS;
02091 createNextDir();
02092 }
02093 }
02094
02095
02096 void CopyJob::startListing( const KURL & src )
02097 {
02098 state = STATE_LISTING;
02099 ListJob * newjob = listRecursive( src, false );
02100 connect(newjob, SIGNAL(entries( KIO::Job *,
02101 const KIO::UDSEntryList& )),
02102 SLOT( slotEntries( KIO::Job*,
02103 const KIO::UDSEntryList& )));
02104 addSubjob( newjob );
02105 }
02106
02107 void CopyJob::skip( const KURL & sourceUrl )
02108 {
02109
02110
02111
02112 KURL::List::Iterator sit = m_srcList.find( sourceUrl );
02113 if ( sit != m_srcList.end() )
02114 {
02115
02116 m_srcList.remove( sit );
02117 }
02118 dirsToRemove.remove( sourceUrl );
02119 }
02120
02121 void CopyJob::slotResultCreatingDirs( Job * job )
02122 {
02123
02124 QValueList<CopyInfo>::Iterator it = dirs.begin();
02125
02126 if ( job->error() )
02127 {
02128 m_conflictError = job->error();
02129 if ( (m_conflictError == ERR_DIR_ALREADY_EXIST)
02130 || (m_conflictError == ERR_FILE_ALREADY_EXIST) )
02131 {
02132 KURL oldURL = ((SimpleJob*)job)->url();
02133
02134 if ( m_bAutoSkip ) {
02135
02136 m_skipList.append( oldURL.path( 1 ) );
02137 skip( oldURL );
02138 dirs.remove( it );
02139 } else if ( m_bOverwriteAll ) {
02140 dirs.remove( it );
02141 } else
02142 {
02143 assert( ((SimpleJob*)job)->url().url() == (*it).uDest.url() );
02144 subjobs.remove( job );
02145 assert ( subjobs.isEmpty() );
02146
02147
02148 KURL existingDest( (*it).uDest );
02149 SimpleJob * newJob = KIO::stat( existingDest, false, 2, false );
02150 Scheduler::scheduleJob(newJob);
02151 kdDebug(7007) << "KIO::stat for resolving conflict on " << existingDest.prettyURL() << endl;
02152 state = STATE_CONFLICT_CREATING_DIRS;
02153 addSubjob(newJob);
02154 return;
02155 }
02156 }
02157 else
02158 {
02159
02160 Job::slotResult( job );
02161 return;
02162 }
02163 }
02164 else
02165 {
02166
02167 emit copyingDone( this, (*it).uSource, (*it).uDest, true, false );
02168 dirs.remove( it );
02169 }
02170
02171 m_processedDirs++;
02172
02173 subjobs.remove( job );
02174 assert ( subjobs.isEmpty() );
02175 createNextDir();
02176 }
02177
02178 void CopyJob::slotResultConflictCreatingDirs( KIO::Job * job )
02179 {
02180
02181
02182
02183 QValueList<CopyInfo>::Iterator it = dirs.begin();
02184
02185 time_t destmtime = (time_t)-1;
02186 time_t destctime = (time_t)-1;
02187 KIO::filesize_t destsize = 0;
02188 UDSEntry entry = ((KIO::StatJob*)job)->statResult();
02189 KIO::UDSEntry::ConstIterator it2 = entry.begin();
02190 for( ; it2 != entry.end(); it2++ ) {
02191 switch ((*it2).m_uds) {
02192 case UDS_MODIFICATION_TIME:
02193 destmtime = (time_t)((*it2).m_long);
02194 break;
02195 case UDS_CREATION_TIME:
02196 destctime = (time_t)((*it2).m_long);
02197 break;
02198 case UDS_SIZE:
02199 destsize = (*it2).m_long;
02200 break;
02201 }
02202 }
02203 subjobs.remove( job );
02204 assert ( subjobs.isEmpty() );
02205
02206
02207 RenameDlg_Mode mode = (RenameDlg_Mode)( M_MULTI | M_SKIP );
02208
02209 if ( m_conflictError == ERR_DIR_ALREADY_EXIST )
02210 mode = (RenameDlg_Mode)( mode | M_OVERWRITE );
02211
02212 QString existingDest = (*it).uDest.path();
02213 QString newPath;
02214 if (m_reportTimer)
02215 m_reportTimer->stop();
02216 RenameDlg_Result r = Observer::self()->open_RenameDlg( this, i18n("Directory Already Exists"),
02217 (*it).uSource.prettyURL(0, KURL::StripFileProtocol),
02218 (*it).uDest.prettyURL(0, KURL::StripFileProtocol),
02219 mode, newPath,
02220 (*it).size, destsize,
02221 (*it).ctime, destctime,
02222 (*it).mtime, destmtime );
02223 if (m_reportTimer)
02224 m_reportTimer->start(REPORT_TIMEOUT,false);
02225 switch ( r ) {
02226 case R_CANCEL:
02227 m_error = ERR_USER_CANCELED;
02228 emitResult();
02229 return;
02230 case R_RENAME:
02231 {
02232 QString oldPath = (*it).uDest.path( 1 );
02233 KURL newUrl( (*it).uDest );
02234 newUrl.setPath( newPath );
02235 emit renamed( this, (*it).uDest, newUrl );
02236
02237
02238 (*it).uDest = newUrl.path( -1 );
02239 newPath = newUrl.path( 1 );
02240 QValueList<CopyInfo>::Iterator renamedirit = it;
02241 renamedirit++;
02242
02243 for( ; renamedirit != dirs.end() ; ++renamedirit )
02244 {
02245 QString path = (*renamedirit).uDest.path();
02246 if ( path.left(oldPath.length()) == oldPath )
02247 (*renamedirit).uDest.setPath( path.replace( 0, oldPath.length(), newPath ) );
02248 }
02249
02250 QValueList<CopyInfo>::Iterator renamefileit = files.begin();
02251 for( ; renamefileit != files.end() ; ++renamefileit )
02252 {
02253 QString path = (*renamefileit).uDest.path();
02254 if ( path.left(oldPath.length()) == oldPath )
02255 (*renamefileit).uDest.setPath( path.replace( 0, oldPath.length(), newPath ) );
02256 }
02257 }
02258 break;
02259 case R_AUTO_SKIP:
02260 m_bAutoSkip = true;
02261
02262 case R_SKIP:
02263 m_skipList.append( existingDest );
02264 skip( (*it).uSource );
02265
02266 dirs.remove( it );
02267 break;
02268 case R_OVERWRITE:
02269 m_overwriteList.append( existingDest );
02270
02271 dirs.remove( it );
02272 break;
02273 case R_OVERWRITE_ALL:
02274 m_bOverwriteAll = true;
02275
02276 dirs.remove( it );
02277 break;
02278 default:
02279 assert( 0 );
02280 }
02281 state = STATE_CREATING_DIRS;
02282 m_processedDirs++;
02283
02284 createNextDir();
02285 }
02286
02287 void CopyJob::createNextDir()
02288 {
02289 KURL udir;
02290 if ( !dirs.isEmpty() )
02291 {
02292
02293 QValueList<CopyInfo>::Iterator it = dirs.begin();
02294
02295 while( it != dirs.end() && udir.isEmpty() )
02296 {
02297 QString dir = (*it).uDest.path();
02298 bool bCreateDir = true;
02299
02300 QStringList::Iterator sit = m_skipList.begin();
02301 for( ; sit != m_skipList.end() && bCreateDir; sit++ )
02302
02303 if ( *sit == dir.left( (*sit).length() ) )
02304 bCreateDir = false;
02305
02306 if ( !bCreateDir ) {
02307 dirs.remove( it );
02308 it = dirs.begin();
02309 } else
02310 udir = (*it).uDest;
02311 }
02312 }
02313 if ( !udir.isEmpty() )
02314 {
02315
02316
02317 KIO::SimpleJob *newjob = KIO::mkdir( udir, -1 );
02318 Scheduler::scheduleJob(newjob);
02319
02320 m_currentDestURL = udir;
02321
02322 addSubjob(newjob);
02323 return;
02324 }
02325 else
02326 {
02327 state = STATE_COPYING_FILES;
02328 m_processedFiles++;
02329 copyNextFile();
02330 }
02331 }
02332
02333 void CopyJob::slotResultCopyingFiles( Job * job )
02334 {
02335
02336 QValueList<CopyInfo>::Iterator it = files.begin();
02337 if ( job->error() )
02338 {
02339
02340 if ( m_bAutoSkip )
02341 {
02342 skip( (*it).uSource );
02343 files.remove( it );
02344 }
02345 else
02346 {
02347 m_conflictError = job->error();
02348
02349 if ( ( m_conflictError == ERR_FILE_ALREADY_EXIST )
02350 || ( m_conflictError == ERR_DIR_ALREADY_EXIST ) )
02351 {
02352 subjobs.remove( job );
02353 assert ( subjobs.isEmpty() );
02354
02355 KURL existingFile( (*it).uDest );
02356 SimpleJob * newJob = KIO::stat( existingFile, false, 2, false );
02357 Scheduler::scheduleJob(newJob);
02358 kdDebug(7007) << "KIO::stat for resolving conflict on " << existingFile.prettyURL() << endl;
02359 state = STATE_CONFLICT_COPYING_FILES;
02360 addSubjob(newJob);
02361 return;
02362 }
02363 else
02364 {
02365 if ( m_bCurrentOperationIsLink && job->inherits( "KIO::DeleteJob" ) )
02366 {
02367
02368
02369 files.remove( it );
02370 } else {
02371
02372 slotResultConflictCopyingFiles( job );
02373 return;
02374 }
02375 }
02376 }
02377 } else
02378 {
02379
02380 if ( m_bCurrentOperationIsLink && m_mode == Move
02381 && !job->inherits( "KIO::DeleteJob" )
02382 )
02383 {
02384 subjobs.remove( job );
02385 assert ( subjobs.isEmpty() );
02386
02387
02388 KIO::Job * newjob = KIO::del( (*it).uSource, false , false );
02389 addSubjob( newjob );
02390 return;
02391 }
02392
02393 if ( m_bCurrentOperationIsLink )
02394 {
02395 QString target = ( m_mode == Link ? (*it).uSource.path() : (*it).linkDest );
02396
02397 emit copyingLinkDone( this, (*it).uSource, target, (*it).uDest );
02398 }
02399 else
02400
02401 emit copyingDone( this, (*it).uSource, (*it).uDest, false, false );
02402
02403 files.remove( it );
02404 }
02405 m_processedFiles++;
02406
02407
02408 m_processedSize += m_fileProcessedSize;
02409 m_fileProcessedSize = 0;
02410
02411
02412 subjobs.remove( job );
02413 assert ( subjobs.isEmpty() );
02414 copyNextFile();
02415 }
02416
02417 void CopyJob::slotResultConflictCopyingFiles( KIO::Job * job )
02418 {
02419
02420
02421 QValueList<CopyInfo>::Iterator it = files.begin();
02422
02423 RenameDlg_Result res;
02424 QString newPath;
02425
02426 if (m_reportTimer)
02427 m_reportTimer->stop();
02428
02429 if ( ( m_conflictError == ERR_FILE_ALREADY_EXIST )
02430 || ( m_conflictError == ERR_DIR_ALREADY_EXIST ) )
02431 {
02432
02433 time_t destmtime = (time_t)-1;
02434 time_t destctime = (time_t)-1;
02435 KIO::filesize_t destsize = 0;
02436 UDSEntry entry = ((KIO::StatJob*)job)->statResult();
02437 KIO::UDSEntry::ConstIterator it2 = entry.begin();
02438 for( ; it2 != entry.end(); it2++ ) {
02439 switch ((*it2).m_uds) {
02440 case UDS_MODIFICATION_TIME:
02441 destmtime = (time_t)((*it2).m_long);
02442 break;
02443 case UDS_CREATION_TIME:
02444 destctime = (time_t)((*it2).m_long);
02445 break;
02446 case UDS_SIZE:
02447 destsize = (*it2).m_long;
02448 break;
02449 }
02450 }
02451
02452
02453
02454 RenameDlg_Mode mode = (RenameDlg_Mode)
02455 ( ( m_conflictError == ERR_DIR_ALREADY_EXIST ? 0 :
02456 ( (*it).uSource == (*it).uDest ) ? M_OVERWRITE_ITSELF : M_OVERWRITE ) );
02457 if ( files.count() > 0 )
02458 mode = (RenameDlg_Mode) ( mode | M_MULTI | M_SKIP );
02459 else
02460 mode = (RenameDlg_Mode) ( mode | M_SINGLE );
02461 res = Observer::self()->open_RenameDlg( this, m_conflictError == ERR_FILE_ALREADY_EXIST ?
02462 i18n("File Already Exists") : i18n("Already Exists as a Directory"),
02463 (*it).uSource.prettyURL(0, KURL::StripFileProtocol),
02464 (*it).uDest.prettyURL(0, KURL::StripFileProtocol),
02465 mode, newPath,
02466 (*it).size, destsize,
02467 (*it).ctime, destctime,
02468 (*it).mtime, destmtime );
02469
02470 }
02471 else
02472 {
02473 if ( job->error() == ERR_USER_CANCELED )
02474 res = R_CANCEL;
02475 else
02476 {
02477 SkipDlg_Result skipResult = Observer::self()->open_SkipDlg( this, files.count() > 0,
02478 job->errorString() );
02479
02480
02481 res = ( skipResult == S_SKIP ) ? R_SKIP :
02482 ( skipResult == S_AUTO_SKIP ) ? R_AUTO_SKIP :
02483 R_CANCEL;
02484 }
02485 }
02486
02487 if (m_reportTimer)
02488 m_reportTimer->start(REPORT_TIMEOUT,false);
02489
02490 subjobs.remove( job );
02491 assert ( subjobs.isEmpty() );
02492 switch ( res ) {
02493 case R_CANCEL:
02494 m_error = ERR_USER_CANCELED;
02495 emitResult();
02496 return;
02497 case R_RENAME:
02498 {
02499 KURL newUrl( (*it).uDest );
02500 newUrl.setPath( newPath );
02501 emit renamed( this, (*it).uDest, newUrl );
02502 (*it).uDest = newUrl;
02503 }
02504 break;
02505 case R_AUTO_SKIP:
02506 m_bAutoSkip = true;
02507
02508 case R_SKIP:
02509
02510 skip( (*it).uSource );
02511 files.remove( it );
02512 break;
02513 case R_OVERWRITE_ALL:
02514 m_bOverwriteAll = true;
02515 break;
02516 case R_OVERWRITE:
02517
02518 m_overwriteList.append( (*it).uDest.path() );
02519 break;
02520 default:
02521 assert( 0 );
02522 }
02523 state = STATE_COPYING_FILES;
02524 m_processedFiles++;
02525
02526 copyNextFile();
02527 }
02528
02529 void CopyJob::copyNextFile()
02530 {
02531 bool bCopyFile = false;
02532
02533
02534 QValueList<CopyInfo>::Iterator it = files.begin();
02535
02536 while (it != files.end() && !bCopyFile)
02537 {
02538 bCopyFile = true;
02539 QString destFile = (*it).uDest.path();
02540
02541 QStringList::Iterator sit = m_skipList.begin();
02542 for( ; sit != m_skipList.end() && bCopyFile; sit++ )
02543
02544 if ( *sit == destFile.left( (*sit).length() ) )
02545 bCopyFile = false;
02546
02547 if (!bCopyFile) {
02548 files.remove( it );
02549 it = files.begin();
02550 }
02551 }
02552
02553 if (bCopyFile)
02554 {
02555
02556 bool bOverwrite = m_bOverwriteAll;
02557 QString destFile = (*it).uDest.path();
02558 if ( (*it).uDest == (*it).uSource )
02559 bOverwrite = false;
02560 else
02561 {
02562
02563 QStringList::Iterator sit = m_overwriteList.begin();
02564 for( ; sit != m_overwriteList.end() && !bOverwrite; sit++ )
02565 if ( *sit == destFile.left( (*sit).length() ) )
02566 bOverwrite = true;
02567 }
02568
02569 m_bCurrentOperationIsLink = false;
02570 KIO::Job * newjob = 0L;
02571 if ( m_mode == Link )
02572 {
02573
02574 if (
02575 ((*it).uSource.protocol() == (*it).uDest.protocol()) &&
02576 ((*it).uSource.host() == (*it).uDest.host()) &&
02577 ((*it).uSource.port() == (*it).uDest.port()) &&
02578 ((*it).uSource.user() == (*it).uDest.user()) &&
02579 ((*it).uSource.pass() == (*it).uDest.pass()) )
02580 {
02581
02582 KIO::SimpleJob *newJob = KIO::symlink( (*it).uSource.path(), (*it).uDest, bOverwrite, false );
02583 newjob = newJob;
02584 Scheduler::scheduleJob(newJob);
02585 kdDebug(7007) << "CopyJob::copyNextFile : Linking target=" << (*it).uSource.path() << " link=" << (*it).uDest.prettyURL() << endl;
02586
02587 m_bCurrentOperationIsLink = true;
02588 m_currentSrcURL=(*it).uSource;
02589 m_currentDestURL=(*it).uDest;
02590
02591 } else {
02592 kdDebug(7007) << "CopyJob::copyNextFile : Linking URL=" << (*it).uSource.prettyURL() << " link=" << (*it).uDest.prettyURL() << endl;
02593 if ( (*it).uDest.isLocalFile() )
02594 {
02595 bool devicesOk=false;
02596
02597
02598 if ((*it).uSource.protocol()==QString::fromLatin1("devices"))
02599 {
02600 QByteArray data;
02601 QByteArray param;
02602 QCString retType;
02603 QDataStream streamout(param,IO_WriteOnly);
02604 streamout<<(*it).uSource;
02605 streamout<<(*it).uDest;
02606 if ( kapp->dcopClient()->call( "kded",
02607 "mountwatcher", "createLink(KURL, KURL)", param,retType,data,false ) )
02608 {
02609 QDataStream streamin(data,IO_ReadOnly);
02610 streamin>>devicesOk;
02611 }
02612 if (devicesOk)
02613 {
02614 files.remove( it );
02615 m_processedFiles++;
02616
02617 copyNextFile();
02618 return;
02619 }
02620 }
02621
02622 if (!devicesOk)
02623 {
02624 QString path = (*it).uDest.path();
02625 kdDebug(7007) << "CopyJob::copyNextFile path=" << path << endl;
02626 QFile f( path );
02627 if ( f.open( IO_ReadWrite ) )
02628 {
02629 f.close();
02630 KSimpleConfig config( path );
02631 config.setDesktopGroup();
02632 config.writeEntry( QString::fromLatin1("URL"), (*it).uSource.url() );
02633 config.writeEntry( QString::fromLatin1("Type"), QString::fromLatin1("Link") );
02634 QString protocol = (*it).uSource.protocol();
02635 if ( protocol == QString::fromLatin1("ftp") )
02636 config.writeEntry( QString::fromLatin1("Icon"), QString::fromLatin1("ftp") );
02637 else if ( protocol == QString::fromLatin1("http") )
02638 config.writeEntry( QString::fromLatin1("Icon"), QString::fromLatin1("www") );
02639 else if ( protocol == QString::fromLatin1("info") )
02640 config.writeEntry( QString::fromLatin1("Icon"), QString::fromLatin1("info") );
02641 else if ( protocol == QString::fromLatin1("mailto") )
02642 config.writeEntry( QString::fromLatin1("Icon"), QString::fromLatin1("kmail") );
02643 else
02644 config.writeEntry( QString::fromLatin1("Icon"), QString::fromLatin1("unknown") );
02645 config.sync();
02646 files.remove( it );
02647 m_processedFiles++;
02648
02649 copyNextFile();
02650 return;
02651 }
02652 else
02653 {
02654 kdDebug(7007) << "CopyJob::copyNextFile ERR_CANNOT_OPEN_FOR_WRITING" << endl;
02655 m_error = ERR_CANNOT_OPEN_FOR_WRITING;
02656 m_errorText = (*it).uDest.path();
02657 emitResult();
02658 return;
02659 }
02660 }
02661 } else {
02662
02663 m_error = ERR_CANNOT_SYMLINK;
02664 m_errorText = (*it).uDest.prettyURL();
02665 emitResult();
02666 return;
02667 }
02668 }
02669 }
02670 else if ( !(*it).linkDest.isEmpty() &&
02671 ((*it).uSource.protocol() == (*it).uDest.protocol()) &&
02672 ((*it).uSource.host() == (*it).uDest.host()) &&
02673 ((*it).uSource.port() == (*it).uDest.port()) &&
02674 ((*it).uSource.user() == (*it).uDest.user()) &&
02675 ((*it).uSource.pass() == (*it).uDest.pass()))
02676
02677 {
02678 KIO::SimpleJob *newJob = KIO::symlink( (*it).linkDest, (*it).uDest, bOverwrite, false );
02679 Scheduler::scheduleJob(newJob);
02680 newjob = newJob;
02681 kdDebug(7007) << "CopyJob::copyNextFile : Linking target=" << (*it).linkDest << " link=" << (*it).uDest.prettyURL() << endl;
02682
02683 m_currentSrcURL=(*it).linkDest;
02684 m_currentDestURL=(*it).uDest;
02685
02686 m_bCurrentOperationIsLink = true;
02687
02688 } else if (m_mode == Move)
02689 {
02690 KIO::FileCopyJob * moveJob = KIO::file_move( (*it).uSource, (*it).uDest, (*it).permissions, bOverwrite, false, false );
02691 moveJob->setSourceSize( (*it).size );
02692 newjob = moveJob;
02693
02694
02695 m_currentSrcURL=(*it).uSource;
02696 m_currentDestURL=(*it).uDest;
02697
02698 }
02699 else
02700 {
02701
02702
02703
02704
02705 bool remoteSource = !(*it).uSource.isLocalFile() && ((*it).uSource.protocol() != "tar");
02706 int permissions = ( remoteSource && (*it).uDest.isLocalFile() ) ? -1 : (*it).permissions;
02707 KIO::FileCopyJob * copyJob = KIO::file_copy( (*it).uSource, (*it).uDest, permissions, bOverwrite, false, false );
02708 copyJob->setParentJob( this );
02709 copyJob->setSourceSize( (*it).size );
02710 newjob = copyJob;
02711
02712 m_currentSrcURL=(*it).uSource;
02713 m_currentDestURL=(*it).uDest;
02714 }
02715 addSubjob(newjob);
02716 connect( newjob, SIGNAL( processedSize( KIO::Job*, KIO::filesize_t ) ),
02717 this, SLOT( slotProcessedSize( KIO::Job*, KIO::filesize_t ) ) );
02718 connect( newjob, SIGNAL( totalSize( KIO::Job*, KIO::filesize_t ) ),
02719 this, SLOT( slotTotalSize( KIO::Job*, KIO::filesize_t ) ) );
02720 }
02721 else
02722 {
02723
02724
02725 deleteNextDir();
02726 }
02727 }
02728
02729 void CopyJob::deleteNextDir()
02730 {
02731 if ( m_mode == Move && !dirsToRemove.isEmpty() )
02732 {
02733 state = STATE_DELETING_DIRS;
02734
02735 KURL::List::Iterator it = dirsToRemove.fromLast();
02736 SimpleJob *job = KIO::rmdir( *it );
02737 Scheduler::scheduleJob(job);
02738 dirsToRemove.remove(it);
02739 addSubjob( job );
02740 }
02741 else
02742 {
02743
02744 if ( !m_bOnlyRenames )
02745 {
02746 KDirNotify_stub allDirNotify("*", "KDirNotify*");
02747 KURL url( m_dest );
02748 if ( destinationState != DEST_IS_DIR || m_asMethod )
02749 url.setPath( url.directory() );
02750
02751 allDirNotify.FilesAdded( url );
02752
02753 if ( m_mode == Move && !m_srcList.isEmpty() )
02754 allDirNotify.FilesRemoved( m_srcList );
02755 }
02756 if (m_reportTimer!=0)
02757 m_reportTimer->stop();
02758 emitResult();
02759 }
02760 }
02761
02762 void CopyJob::slotProcessedSize( KIO::Job*, KIO::filesize_t data_size )
02763 {
02764
02765 m_fileProcessedSize = data_size;
02766
02767 if ( m_processedSize + m_fileProcessedSize > m_totalSize )
02768 {
02769 m_totalSize = m_processedSize + m_fileProcessedSize;
02770
02771 emit totalSize( this, m_totalSize );
02772 }
02773
02774 emit processedSize( this, m_processedSize + m_fileProcessedSize );
02775 emitPercent( m_processedSize + m_fileProcessedSize, m_totalSize );
02776 }
02777
02778 void CopyJob::slotTotalSize( KIO::Job*, KIO::filesize_t size )
02779 {
02780
02781
02782
02783
02784 if ( m_bSingleFileCopy )
02785 {
02786
02787 m_totalSize = size;
02788 emit totalSize( this, size );
02789 }
02790 }
02791
02792 void CopyJob::slotResultDeletingDirs( Job * job )
02793 {
02794 if (job->error())
02795 {
02796
02797
02798
02799 }
02800 subjobs.remove( job );
02801 assert ( subjobs.isEmpty() );
02802 deleteNextDir();
02803 }
02804
02805 void CopyJob::slotResult( Job *job )
02806 {
02807
02808
02809
02810
02811
02812
02813 switch ( state ) {
02814 case STATE_STATING:
02815 slotResultStating( job );
02816 break;
02817 case STATE_RENAMING:
02818 {
02819 int err = job->error();
02820 subjobs.remove( job );
02821 assert ( subjobs.isEmpty() );
02822 if ( err )
02823 {
02824
02825 KURL dest = m_dest;
02826 if ( destinationState == DEST_IS_DIR && !m_asMethod )
02827 dest.addPath( m_currentSrcURL.fileName() );
02828
02829
02830
02831 if ( m_currentSrcURL.isLocalFile() &&
02832 m_currentSrcURL.url(-1).lower() == dest.url(-1).lower() &&
02833 ( job->error() == ERR_FILE_ALREADY_EXIST || job->error() == ERR_DIR_ALREADY_EXIST ) )
02834 {
02835 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;
02836 QCString _src( QFile::encodeName(m_currentSrcURL.path()) );
02837 QCString _dest( QFile::encodeName(dest.path()) );
02838 KTempFile tmpFile( m_currentSrcURL.directory() );
02839 QCString _tmp( QFile::encodeName(tmpFile.name()) );
02840 kdDebug() << "CopyJob::slotResult KTempFile status:" << tmpFile.status() << " using " << _tmp << " as intermediary" << endl;
02841 tmpFile.unlink();
02842 if ( ::rename( _src, _tmp ) == 0 )
02843 {
02844 if ( ::rename( _tmp, _dest ) == 0 )
02845 {
02846 kdDebug(7007) << "Success." << endl;
02847 err = 0;
02848 }
02849 else
02850 {
02851
02852 bool b = ::rename( QFile::encodeName(tmpFile.name()), _src );
02853 if (!b) {
02854 kdError(7007) << "Couldn't rename " << tmpFile.name() << " back to " << _src << " !" << endl;
02855
02856 Job::slotResult( job );
02857 return;
02858 }
02859 }
02860 }
02861 }
02862 }
02863 if ( err )
02864 {
02865 m_currentSrcURL=*m_currentStatSrc;
02866 m_currentDestURL=m_dest;
02867 kdDebug(7007) << "Couldn't rename, reverting to normal way, starting with stat" << endl;
02868 Job * job = KIO::stat( m_currentSrcURL, true, 2, false );
02869
02870 state = STATE_STATING;
02871 addSubjob(job);
02872 m_bOnlyRenames = false;
02873 }
02874 else
02875 {
02876 kdDebug(7007) << "Renaming succeeded, move on" << endl;
02877 emit copyingDone( this, *m_currentStatSrc, m_currentDest, true, true );
02878 ++m_currentStatSrc;
02879 statNextSrc();
02880 }
02881 }
02882 break;
02883 case STATE_LISTING:
02884
02885
02886 if (job->error())
02887 {
02888 Job::slotResult( job );
02889 return;
02890 }
02891
02892 subjobs.remove( job );
02893 assert ( subjobs.isEmpty() );
02894
02895 ++m_currentStatSrc;
02896 statNextSrc();
02897 break;
02898 case STATE_CREATING_DIRS:
02899 slotResultCreatingDirs( job );
02900 break;
02901 case STATE_CONFLICT_CREATING_DIRS:
02902 slotResultConflictCreatingDirs( job );
02903 break;
02904 case STATE_COPYING_FILES:
02905 slotResultCopyingFiles( job );
02906 break;
02907 case STATE_CONFLICT_COPYING_FILES:
02908 slotResultConflictCopyingFiles( job );
02909 break;
02910 case STATE_DELETING_DIRS:
02911 slotResultDeletingDirs( job );
02912 break;
02913 default:
02914 assert( 0 );
02915 }
02916 }
02917
02918 CopyJob *KIO::copy(const KURL& src, const KURL& dest, bool showProgressInfo )
02919 {
02920
02921 KURL::List srcList;
02922 srcList.append( src );
02923 return new CopyJob( srcList, dest, CopyJob::Copy, false, showProgressInfo );
02924 }
02925
02926 CopyJob *KIO::copyAs(const KURL& src, const KURL& dest, bool showProgressInfo )
02927 {
02928
02929 KURL::List srcList;
02930 srcList.append( src );
02931 return new CopyJob( srcList, dest, CopyJob::Copy, true, showProgressInfo );
02932 }
02933
02934 CopyJob *KIO::copy( const KURL::List& src, const KURL& dest, bool showProgressInfo )
02935 {
02936 return new CopyJob( src, dest, CopyJob::Copy, false, showProgressInfo );
02937 }
02938
02939 CopyJob *KIO::move(const KURL& src, const KURL& dest, bool showProgressInfo )
02940 {
02941 KURL::List srcList;
02942 srcList.append( src );
02943 return new CopyJob( srcList, dest, CopyJob::Move, false, showProgressInfo );
02944 }
02945
02946 CopyJob *KIO::moveAs(const KURL& src, const KURL& dest, bool showProgressInfo )
02947 {
02948 KURL::List srcList;
02949 srcList.append( src );
02950 return new CopyJob( srcList, dest, CopyJob::Move, true, showProgressInfo );
02951 }
02952
02953 CopyJob *KIO::move( const KURL::List& src, const KURL& dest, bool showProgressInfo )
02954 {
02955 return new CopyJob( src, dest, CopyJob::Move, false, showProgressInfo );
02956 }
02957
02958 CopyJob *KIO::link(const KURL& src, const KURL& destDir, bool showProgressInfo )
02959 {
02960 KURL::List srcList;
02961 srcList.append( src );
02962 return new CopyJob( srcList, destDir, CopyJob::Link, false, showProgressInfo );
02963 }
02964
02965 CopyJob *KIO::link(const KURL::List& srcList, const KURL& destDir, bool showProgressInfo )
02966 {
02967 return new CopyJob( srcList, destDir, CopyJob::Link, false, showProgressInfo );
02968 }
02969
02970 CopyJob *KIO::linkAs(const KURL& src, const KURL& destDir, bool showProgressInfo )
02971 {
02972 KURL::List srcList;
02973 srcList.append( src );
02974 return new CopyJob( srcList, destDir, CopyJob::Link, false, showProgressInfo );
02975 }
02976
02978
02979 DeleteJob::DeleteJob( const KURL::List& src, bool shred, bool showProgressInfo )
02980 : Job(showProgressInfo), m_totalSize( 0 ), m_processedSize( 0 ), m_fileProcessedSize( 0 ),
02981 m_processedFiles( 0 ), m_processedDirs( 0 ), m_totalFilesDirs( 0 ),
02982 m_srcList(src), m_currentStat(m_srcList.begin()), m_shred(shred), m_reportTimer(0)
02983 {
02984 if ( showProgressInfo ) {
02985
02986 connect( this, SIGNAL( totalFiles( KIO::Job*, unsigned long ) ),
02987 Observer::self(), SLOT( slotTotalFiles( KIO::Job*, unsigned long ) ) );
02988
02989 connect( this, SIGNAL( totalDirs( KIO::Job*, unsigned long ) ),
02990 Observer::self(), SLOT( slotTotalDirs( KIO::Job*, unsigned long ) ) );
02991
02992
02993
02994
02995
02996
02997
02998
02999
03000
03001
03002 m_reportTimer=new QTimer(this);
03003 connect(m_reportTimer,SIGNAL(timeout()),this,SLOT(slotReport()));
03004
03005 m_reportTimer->start(REPORT_TIMEOUT,false);
03006 }
03007
03008 QTimer::singleShot(0, this, SLOT(slotStart()));
03009 }
03010
03011 void DeleteJob::slotStart()
03012 {
03013 statNextSrc();
03014 }
03015
03016
03017
03018
03019 void DeleteJob::slotReport()
03020 {
03021 if (m_progressId==0)
03022 return;
03023
03024 Observer * observer = Observer::self();
03025
03026 emit deleting( this, m_currentURL );
03027 observer->slotDeleting(this,m_currentURL);
03028
03029 switch( state ) {
03030 case STATE_STATING:
03031 case STATE_LISTING:
03032 emit totalSize( this, m_totalSize );
03033 emit totalFiles( this, files.count() );
03034 emit totalDirs( this, dirs.count() );
03035 break;
03036 case STATE_DELETING_DIRS:
03037 emit processedDirs( this, m_processedDirs );
03038 observer->slotProcessedDirs(this,m_processedDirs);
03039 emitPercent( m_processedFiles + m_processedDirs, m_totalFilesDirs );
03040 break;
03041 case STATE_DELETING_FILES:
03042 observer->slotProcessedFiles(this,m_processedFiles);
03043 emit processedFiles( this, m_processedFiles );
03044 if (!m_shred)
03045 emitPercent( m_processedFiles, m_totalFilesDirs );
03046 break;
03047 }
03048 }
03049
03050
03051 void DeleteJob::slotEntries(KIO::Job* job, const UDSEntryList& list)
03052 {
03053 UDSEntryListConstIterator it = list.begin();
03054 UDSEntryListConstIterator end = list.end();
03055 for (; it != end; ++it)
03056 {
03057 UDSEntry::ConstIterator it2 = (*it).begin();
03058 bool bDir = false;
03059 bool bLink = false;
03060 QString relName;
03061 int atomsFound(0);
03062 for( ; it2 != (*it).end(); it2++ )
03063 {
03064 switch ((*it2).m_uds)
03065 {
03066 case UDS_FILE_TYPE:
03067 bDir = S_ISDIR((*it2).m_long);
03068 atomsFound++;
03069 break;
03070 case UDS_NAME:
03071 relName = ((*it2).m_str);
03072 atomsFound++;
03073 break;
03074 case UDS_LINK_DEST:
03075 bLink = !(*it2).m_str.isEmpty();
03076 atomsFound++;
03077 break;
03078 case UDS_SIZE:
03079 m_totalSize += (off_t)((*it2).m_long);
03080 atomsFound++;
03081 break;
03082 default:
03083 break;
03084 }
03085 if (atomsFound==4) break;
03086 }
03087 assert(!relName.isEmpty());
03088 if (relName != ".." && relName != ".")
03089 {
03090 KURL url = ((SimpleJob *)job)->url();
03091 url.addPath( relName );
03092
03093 if ( bLink )
03094 symlinks.append( url );
03095 else if ( bDir )
03096 dirs.append( url );
03097 else
03098 files.append( url );
03099 }
03100 }
03101 }
03102
03103
03104 void DeleteJob::statNextSrc()
03105 {
03106
03107 if ( m_currentStat != m_srcList.end() )
03108 {
03109 m_currentURL = (*m_currentStat);
03110
03111
03112 if (!KProtocolInfo::supportsDeleting(m_currentURL)) {
03113 KMessageBox::information( 0, buildErrorString(ERR_CANNOT_DELETE, m_currentURL.prettyURL()));
03114 ++m_currentStat;
03115 statNextSrc();
03116 return;
03117 }
03118
03119 state = STATE_STATING;
03120 KIO::SimpleJob * job = KIO::stat( m_currentURL, true, 1, false );
03121 Scheduler::scheduleJob(job);
03122
03123 addSubjob(job);
03124
03125
03126 } else
03127 {
03128 m_totalFilesDirs = files.count()+symlinks.count() + dirs.count();
03129 slotReport();
03130
03131
03132
03133
03134 for ( QStringList::Iterator it = m_parentDirs.begin() ; it != m_parentDirs.end() ; ++it )
03135 KDirWatch::self()->stopDirScan( *it );
03136 state = STATE_DELETING_FILES;
03137 deleteNextFile();
03138 }
03139 }
03140
03141 void DeleteJob::deleteNextFile()
03142 {
03143
03144 if ( !files.isEmpty() || !symlinks.isEmpty() )
03145 {
03146 SimpleJob *job;
03147 do {
03148
03149 KURL::List::Iterator it = files.begin();
03150 bool isLink = false;
03151 if ( it == files.end() )
03152 {
03153 it = symlinks.begin();
03154 isLink = true;
03155 }
03156
03157 if ( m_shred && (*it).isLocalFile() && !isLink )
03158 {
03159
03160 KIO_ARGS << int(3) << (*it).path();
03161 job = KIO::special(KURL("file:/"), packedArgs, false );
03162 Scheduler::scheduleJob(job);
03163 m_currentURL=(*it);
03164 connect( job, SIGNAL( processedSize( KIO::Job*, KIO::filesize_t ) ),
03165 this, SLOT( slotProcessedSize( KIO::Job*, KIO::filesize_t ) ) );
03166 } else
03167 {
03168
03169
03170 if ( (*it).isLocalFile() && unlink( QFile::encodeName((*it).path()) ) == 0 ) {
03171 job = 0;
03172 m_processedFiles++;
03173 if ( m_processedFiles % 300 == 0 ) {
03174 m_currentURL = *it;
03175 slotReport();
03176 }
03177 } else
03178 {
03179 job = KIO::file_delete( *it, false );
03180 Scheduler::scheduleJob(job);
03181 m_currentURL=(*it);
03182 }
03183 }
03184 if ( isLink )
03185 symlinks.remove(it);
03186 else
03187 files.remove(it);
03188 if ( job ) {
03189 addSubjob(job);
03190 return;
03191 }
03192
03193 } while (!job && (!files.isEmpty() || !symlinks.isEmpty()));
03194 }
03195 state = STATE_DELETING_DIRS;
03196 deleteNextDir();
03197 }
03198
03199 void DeleteJob::deleteNextDir()
03200 {
03201 if ( !dirs.isEmpty() )
03202 {
03203 do {
03204
03205 KURL::List::Iterator it = dirs.fromLast();
03206
03207 if ( (*it).isLocalFile() && ::rmdir( QFile::encodeName((*it).path()) ) == 0 ) {
03208
03209 m_processedDirs++;
03210 if ( m_processedDirs % 100 == 0 ) {
03211 m_currentURL = *it;
03212 slotReport();
03213 }
03214 } else
03215 {
03216 SimpleJob *job = KIO::rmdir( *it );
03217 Scheduler::scheduleJob(job);
03218 dirs.remove(it);
03219 addSubjob( job );
03220 return;
03221 }
03222 dirs.remove(it);
03223 } while ( !dirs.isEmpty() );
03224 }
03225
03226
03227 for ( QStringList::Iterator it = m_parentDirs.begin() ; it != m_parentDirs.end() ; ++it )
03228 KDirWatch::self()->restartDirScan( *it );
03229
03230
03231 if ( !m_srcList.isEmpty() )
03232 {
03233 KDirNotify_stub allDirNotify("*", "KDirNotify*");
03234 allDirNotify.FilesRemoved( m_srcList );
03235 }
03236 if (m_reportTimer!=0)
03237 m_reportTimer->stop();
03238 emitResult();
03239 }
03240
03241 void DeleteJob::slotProcessedSize( KIO::Job*, KIO::filesize_t data_size )
03242 {
03243
03244
03245
03246
03247 m_fileProcessedSize = data_size;
03248
03249
03250
03251 emit processedSize( this, m_processedSize + m_fileProcessedSize );
03252
03253
03254 unsigned long ipercent = m_percent;
03255
03256 if ( m_totalSize == 0 )
03257 m_percent = 100;
03258 else
03259 m_percent = (unsigned long)(( (float)(m_processedSize + m_fileProcessedSize) / (float)m_totalSize ) * 100.0);
03260
03261 if ( m_percent > ipercent )
03262 {
03263 emit percent( this, m_percent );
03264
03265 }
03266
03267 }
03268
03269 void DeleteJob::slotResult( Job *job )
03270 {
03271 switch ( state )
03272 {
03273 case STATE_STATING:
03274 {
03275
03276 if (job->error() )
03277 {
03278
03279 Job::slotResult( job );
03280 return;
03281 }
03282
03283
03284 UDSEntry entry = ((StatJob*)job)->statResult();
03285 bool bDir = false;
03286 bool bLink = false;
03287 KIO::filesize_t size = (KIO::filesize_t)-1;
03288 UDSEntry::ConstIterator it2 = entry.begin();
03289 int atomsFound(0);
03290 for( ; it2 != entry.end(); it2++ )
03291 {
03292 if ( ((*it2).m_uds) == UDS_FILE_TYPE )
03293 {
03294 bDir = S_ISDIR( (mode_t)(*it2).m_long );
03295 atomsFound++;
03296 }
03297 else if ( ((*it2).m_uds) == UDS_LINK_DEST )
03298 {
03299 bLink = !((*it2).m_str.isEmpty());
03300 atomsFound++;
03301 }
03302 else if ( ((*it2).m_uds) == UDS_SIZE )
03303 {
03304 size = (*it2).m_long;
03305 atomsFound++;
03306 };
03307 if (atomsFound==3) break;
03308 }
03309
03310 KURL url = ((SimpleJob*)job)->url();
03311
03312 subjobs.remove( job );
03313 assert( subjobs.isEmpty() );
03314
03315 if (bDir && !bLink)
03316 {
03317
03318 dirs.append( url );
03319 if ( url.isLocalFile() && !m_parentDirs.contains( url.path(-1) ) )
03320 m_parentDirs.append( url.path(-1) );
03321
03322
03323
03324 state = STATE_LISTING;
03325 ListJob *newjob = listRecursive( url, false );
03326 Scheduler::scheduleJob(newjob);
03327 connect(newjob, SIGNAL(entries( KIO::Job *,
03328 const KIO::UDSEntryList& )),
03329 SLOT( slotEntries( KIO::Job*,
03330 const KIO::UDSEntryList& )));
03331 addSubjob(newjob);
03332 }
03333 else
03334 {
03335 if ( bLink ) {
03336
03337 symlinks.append( url );
03338 } else {
03339
03340 files.append( url );
03341 }
03342 if ( url.isLocalFile() && !m_parentDirs.contains( url.directory(-1) ) )
03343 m_parentDirs.append( url.directory(-1) );
03344 ++m_currentStat;
03345 statNextSrc();
03346 }
03347 }
03348 break;
03349 case STATE_LISTING:
03350 if ( job->error() )
03351 {
03352
03353 }
03354 subjobs.remove( job );
03355 assert( subjobs.isEmpty() );
03356 ++m_currentStat;
03357 statNextSrc();
03358 break;
03359 case STATE_DELETING_FILES:
03360 if ( job->error() )
03361 {
03362 Job::slotResult( job );
03363 return;
03364 }
03365 subjobs.remove( job );
03366 assert( subjobs.isEmpty() );
03367 m_processedFiles++;
03368
03369 deleteNextFile();
03370 break;
03371 case STATE_DELETING_DIRS:
03372 if ( job->error() )
03373 {
03374 Job::slotResult( job );
03375 return;
03376 }
03377 subjobs.remove( job );
03378 assert( subjobs.isEmpty() );
03379 m_processedDirs++;
03380
03381
03382
03383
03384 deleteNextDir();
03385 break;
03386 default:
03387 assert(0);
03388 }
03389 }
03390
03391 DeleteJob *KIO::del( const KURL& src, bool shred, bool showProgressInfo )
03392 {
03393 KURL::List srcList;
03394 srcList.append( src );
03395 DeleteJob *job = new DeleteJob( srcList, shred, showProgressInfo );
03396 return job;
03397 }
03398
03399 DeleteJob *KIO::del( const KURL::List& src, bool shred, bool showProgressInfo )
03400 {
03401 DeleteJob *job = new DeleteJob( src, shred, showProgressInfo );
03402 return job;
03403 }
03404
03405 MultiGetJob::MultiGetJob(const KURL& url,
03406 bool showProgressInfo)
03407 : TransferJob(url, 0, QByteArray(), QByteArray(), showProgressInfo)
03408 {
03409 m_waitQueue.setAutoDelete(true);
03410 m_activeQueue.setAutoDelete(true);
03411 m_currentEntry = 0;
03412 }
03413
03414 void MultiGetJob::get(long id, const KURL &url, const MetaData &metaData)
03415 {
03416 GetRequest *entry = new GetRequest(id, url, metaData);
03417 entry->metaData["request-id"] = QString("%1").arg(id);
03418 m_waitQueue.append(entry);
03419 }
03420
03421 void MultiGetJob::flushQueue(QPtrList<GetRequest> &queue)
03422 {
03423 GetRequest *entry;
03424
03425
03426 for(entry = m_waitQueue.first(); entry; )
03427 {
03428 if ((m_url.protocol() == entry->url.protocol()) &&
03429 (m_url.host() == entry->url.host()) &&
03430 (m_url.port() == entry->url.port()) &&
03431 (m_url.user() == entry->url.user()))
03432 {
03433 m_waitQueue.take();
03434 queue.append(entry);
03435 entry = m_waitQueue.current();
03436 }
03437 else
03438 {
03439 entry = m_waitQueue.next();
03440 }
03441 }
03442
03443 KIO_ARGS << (Q_INT32) queue.count();
03444 for(entry = queue.first(); entry; entry = queue.next())
03445 {
03446 stream << entry->url << entry->metaData;
03447 }
03448 m_packedArgs = packedArgs;
03449 m_command = CMD_MULTI_GET;
03450 m_outgoingMetaData.clear();
03451 }
03452
03453 void MultiGetJob::start(Slave *slave)
03454 {
03455
03456 GetRequest *entry = m_waitQueue.take(0);
03457 m_activeQueue.append(entry);
03458
03459 m_url = entry->url;
03460
03461 if (!entry->url.protocol().startsWith("http"))
03462 {
03463
03464 KIO_ARGS << entry->url;
03465 m_packedArgs = packedArgs;
03466 m_outgoingMetaData = entry->metaData;
03467 m_command = CMD_GET;
03468 b_multiGetActive = false;
03469 }
03470 else
03471 {
03472 flushQueue(m_activeQueue);
03473 b_multiGetActive = true;
03474 }
03475
03476 TransferJob::start(slave);
03477 }
03478
03479 bool MultiGetJob::findCurrentEntry()
03480 {
03481 if (b_multiGetActive)
03482 {
03483 long id = m_incomingMetaData["request-id"].toLong();
03484 for(GetRequest *entry = m_activeQueue.first(); entry; entry = m_activeQueue.next())
03485 {
03486 if (entry->id == id)
03487 {
03488 m_currentEntry = entry;
03489 return true;
03490 }
03491 }
03492 m_currentEntry = 0;
03493 return false;
03494 }
03495 else
03496 {
03497 m_currentEntry = m_activeQueue.first();
03498 return (m_currentEntry != 0);
03499 }
03500 }
03501
03502 void MultiGetJob::slotRedirection( const KURL &url)
03503 {
03504 if (!findCurrentEntry()) return;
03505 if (!kapp->authorizeURLAction("redirect", m_url, url))
03506 {
03507 kdWarning(7007) << "MultiGetJob: Redirection from " << m_currentEntry->url.prettyURL() << " to " << url.prettyURL() << " REJECTED!" << endl;
03508 return;
03509 }
03510 m_redirectionURL = url;
03511 if (m_currentEntry->url.hasUser() && !url.hasUser() && (m_currentEntry->url.host().lower() == url.host().lower()))
03512 m_redirectionURL.setUser(m_currentEntry->url.user());
03513 get(m_currentEntry->id, m_redirectionURL, m_currentEntry->metaData);
03514 }
03515
03516
03517 void MultiGetJob::slotFinished()
03518 {
03519 if (!findCurrentEntry()) return;
03520 if (m_redirectionURL.isEmpty())
03521 {
03522
03523 emit result(m_currentEntry->id);
03524 }
03525 m_redirectionURL = KURL();
03526 m_error = 0;
03527 m_incomingMetaData.clear();
03528 m_activeQueue.removeRef(m_currentEntry);
03529 if (m_activeQueue.count() == 0)
03530 {
03531 if (m_waitQueue.count() == 0)
03532 {
03533
03534 TransferJob::slotFinished();
03535 }
03536 else
03537 {
03538
03539
03540
03541 GetRequest *entry = m_waitQueue.at(0);
03542 m_url = entry->url;
03543 slaveDone();
03544 Scheduler::doJob(this);
03545 }
03546 }
03547 }
03548
03549 void MultiGetJob::slotData( const QByteArray &_data)
03550 {
03551 if(!m_currentEntry) return;
03552 if(m_redirectionURL.isEmpty() || m_redirectionURL.isMalformed() || m_error)
03553 emit data(m_currentEntry->id, _data);
03554 }
03555
03556 void MultiGetJob::slotMimetype( const QString &_mimetype )
03557 {
03558 if (b_multiGetActive)
03559 {
03560 QPtrList<GetRequest> newQueue;
03561 flushQueue(newQueue);
03562 if (!newQueue.isEmpty())
03563 {
03564 while(!newQueue.isEmpty())
03565 m_activeQueue.append(newQueue.take(0));
03566 m_slave->connection()->send( m_command, m_packedArgs );
03567 }
03568 }
03569 if (!findCurrentEntry()) return;
03570 emit mimetype(m_currentEntry->id, _mimetype);
03571 }
03572
03573 MultiGetJob *KIO::multi_get(long id, const KURL &url, const MetaData &metaData)
03574 {
03575 MultiGetJob * job = new MultiGetJob( url, false );
03576 job->get(id, url, metaData);
03577 return job;
03578 }
03579
03580
03581 #ifdef CACHE_INFO
03582 CacheInfo::CacheInfo(const KURL &url)
03583 {
03584 m_url = url;
03585 }
03586
03587 QString CacheInfo::cachedFileName()
03588 {
03589 const QChar seperator = '_';
03590
03591 QString CEF = m_url.path();
03592
03593 int p = CEF.find('/');
03594
03595 while(p != -1)
03596 {
03597 CEF[p] = seperator;
03598 p = CEF.find('/', p);
03599 }
03600
03601 QString host = m_url.host().lower();
03602 CEF = host + CEF + '_';
03603
03604 QString dir = KProtocolManager::cacheDir();
03605 if (dir[dir.length()-1] != '/')
03606 dir += "/";
03607
03608 int l = m_url.host().length();
03609 for(int i = 0; i < l; i++)
03610 {
03611 if (host[i].isLetter() && (host[i] != 'w'))
03612 {
03613 dir += host[i];
03614 break;
03615 }
03616 }
03617 if (dir[dir.length()-1] == '/')
03618 dir += "0";
03619
03620 unsigned long hash = 0x00000000;
03621 QCString u = m_url.url().latin1();
03622 for(int i = u.length(); i--;)
03623 {
03624 hash = (hash * 12211 + u[i]) % 2147483563;
03625 }
03626
03627 QString hashString;
03628 hashString.sprintf("%08lx", hash);
03629
03630 CEF = CEF + hashString;
03631
03632 CEF = dir + "/" + CEF;
03633
03634 return CEF;
03635 }
03636
03637 QFile *CacheInfo::cachedFile()
03638 {
03639 const char *mode = (readWrite ? "r+" : "r");
03640
03641 FILE *fs = fopen( CEF.latin1(), mode);
03642 if (!fs)
03643 return 0;
03644
03645 char buffer[401];
03646 bool ok = true;
03647
03648
03649 if (ok && (!fgets(buffer, 400, fs)))
03650 ok = false;
03651 if (ok && (strcmp(buffer, CACHE_REVISION) != 0))
03652 ok = false;
03653
03654 time_t date;
03655 time_t currentDate = time(0);
03656
03657
03658 if (ok && (!fgets(buffer, 400, fs)))
03659 ok = false;
03660 if (ok)
03661 {
03662 int l = strlen(buffer);
03663 if (l>0)
03664 buffer[l-1] = 0;
03665 if (m_.url.url() != buffer)
03666 {
03667 ok = false;
03668 }
03669 }
03670
03671
03672 if (ok && (!fgets(buffer, 400, fs)))
03673 ok = false;
03674 if (ok)
03675 {
03676 date = (time_t) strtoul(buffer, 0, 10);
03677 if (m_maxCacheAge && (difftime(currentDate, date) > m_maxCacheAge))
03678 {
03679 m_bMustRevalidate = true;
03680 m_expireDate = currentDate;
03681 }
03682 }
03683
03684
03685 m_cacheExpireDateOffset = ftell(fs);
03686 if (ok && (!fgets(buffer, 400, fs)))
03687 ok = false;
03688 if (ok)
03689 {
03690 if (m_request.cache == CC_Verify)
03691 {
03692 date = (time_t) strtoul(buffer, 0, 10);
03693
03694 if (!date || difftime(currentDate, date) >= 0)
03695 m_bMustRevalidate = true;
03696 m_expireDate = date;
03697 }
03698 }
03699
03700
03701 if (ok && (!fgets(buffer, 400, fs)))
03702 ok = false;
03703 if (ok)
03704 {
03705 m_etag = QString(buffer).stripWhiteSpace();
03706 }
03707
03708
03709 if (ok && (!fgets(buffer, 400, fs)))
03710 ok = false;
03711 if (ok)
03712 {
03713 m_lastModified = QString(buffer).stripWhiteSpace();
03714 }
03715
03716 fclose(fs);
03717
03718 if (ok)
03719 return fs;
03720
03721 unlink( CEF.latin1());
03722 return 0;
03723
03724 }
03725
03726 void CacheInfo::flush()
03727 {
03728 cachedFile().remove();
03729 }
03730
03731 void CacheInfo::touch()
03732 {
03733
03734 }
03735 void CacheInfo::setExpireDate(int);
03736 void CacheInfo::setExpireTimeout(int);
03737
03738
03739 int CacheInfo::creationDate();
03740 int CacheInfo::expireDate();
03741 int CacheInfo::expireTimeout();
03742 #endif
03743
03744 void Job::virtual_hook( int, void* )
03745 { }
03746
03747 void SimpleJob::virtual_hook( int id, void* data )
03748 { KIO::Job::virtual_hook( id, data ); }
03749
03750 void StatJob::virtual_hook( int id, void* data )
03751 { SimpleJob::virtual_hook( id, data ); }
03752
03753 void TransferJob::virtual_hook( int id, void* data )
03754 { SimpleJob::virtual_hook( id, data ); }
03755
03756 void MultiGetJob::virtual_hook( int id, void* data )
03757 { TransferJob::virtual_hook( id, data ); }
03758
03759 void MimetypeJob::virtual_hook( int id, void* data )
03760 { TransferJob::virtual_hook( id, data ); }
03761
03762 void FileCopyJob::virtual_hook( int id, void* data )
03763 { Job::virtual_hook( id, data ); }
03764
03765 void ListJob::virtual_hook( int id, void* data )
03766 { SimpleJob::virtual_hook( id, data ); }
03767
03768 void CopyJob::virtual_hook( int id, void* data )
03769 { Job::virtual_hook( id, data ); }
03770
03771 void DeleteJob::virtual_hook( int id, void* data )
03772 { Job::virtual_hook( id, data ); }
03773
03774
03775 #include "jobclasses.moc"