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 if (specialcmd == 1)
00820 {
00821 addMetaData("cache","reload");
00822 m_packedArgs.truncate(0);
00823 QDataStream stream( m_packedArgs, IO_WriteOnly );
00824 stream << m_url;
00825 m_command = CMD_GET;
00826 }
00827 break;
00828 }
00829 }
00830
00831
00832 slaveDone();
00833 Scheduler::doJob(this);
00834 }
00835 }
00836
00837
00838 void TransferJob::slotDataReq()
00839 {
00840 QByteArray dataForSlave;
00841 if (!staticData.isEmpty())
00842 {
00843 dataForSlave = staticData;
00844 staticData = QByteArray();
00845 }
00846 else
00847 emit dataReq( this, dataForSlave);
00848
00849 static const size_t max_size = 14 * 1024 * 1024;
00850 if (dataForSlave.size() > max_size) {
00851 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";
00852 staticData.duplicate(dataForSlave.data() + max_size , dataForSlave.size() - max_size);
00853 dataForSlave.truncate(max_size);
00854 }
00855
00856 m_slave->connection()->send( MSG_DATA, dataForSlave );
00857 if (m_subJob)
00858 {
00859
00860 suspend();
00861 m_subJob->resume();
00862 }
00863 }
00864
00865 void TransferJob::slotMimetype( const QString& type )
00866 {
00867 m_mimetype = type;
00868 emit mimetype( this, m_mimetype);
00869 }
00870
00871
00872 void TransferJob::suspend()
00873 {
00874 m_suspended = true;
00875 if (m_slave)
00876 m_slave->connection()->suspend();
00877 }
00878
00879 void TransferJob::resume()
00880 {
00881 m_suspended = false;
00882 if (m_slave)
00883 m_slave->connection()->resume();
00884 }
00885
00886 void TransferJob::start(Slave *slave)
00887 {
00888 assert(slave);
00889 connect( slave, SIGNAL( data( const QByteArray & ) ),
00890 SLOT( slotData( const QByteArray & ) ) );
00891
00892 connect( slave, SIGNAL( dataReq() ),
00893 SLOT( slotDataReq() ) );
00894
00895 connect( slave, SIGNAL( redirection(const KURL &) ),
00896 SLOT( slotRedirection(const KURL &) ) );
00897
00898 connect( slave, SIGNAL(mimeType( const QString& ) ),
00899 SLOT( slotMimetype( const QString& ) ) );
00900
00901 connect( slave, SIGNAL(errorPage() ),
00902 SLOT( slotErrorPage() ) );
00903
00904 connect( slave, SIGNAL( needSubURLData() ),
00905 SLOT( slotNeedSubURLData() ) );
00906
00907 connect( slave, SIGNAL(canResume( KIO::filesize_t ) ),
00908 SLOT( slotCanResume( KIO::filesize_t ) ) );
00909
00910 if (slave->suspended())
00911 {
00912 m_mimetype = "unknown";
00913
00914 slave->resume();
00915 }
00916
00917 SimpleJob::start(slave);
00918 if (m_suspended)
00919 slave->connection()->suspend();
00920 }
00921
00922 void TransferJob::slotNeedSubURLData()
00923 {
00924
00925 m_subJob = KIO::get( m_subUrl, false, false);
00926 suspend();
00927 connect(m_subJob, SIGNAL( data(KIO::Job*,const QByteArray &)),
00928 SLOT( slotSubURLData(KIO::Job*,const QByteArray &)));
00929 addSubjob(m_subJob);
00930 }
00931
00932 void TransferJob::slotSubURLData(KIO::Job*, const QByteArray &data)
00933 {
00934
00935 staticData = data;
00936 m_subJob->suspend();
00937 resume();
00938 }
00939
00940 void TransferJob::slotErrorPage()
00941 {
00942 m_errorPage = true;
00943 }
00944
00945 void TransferJob::slotCanResume( KIO::filesize_t offset )
00946 {
00947 emit canResume(this, offset);
00948 }
00949
00950 void TransferJob::slotResult( KIO::Job *job)
00951 {
00952
00953 assert(job == m_subJob);
00954
00955 if ( job->error() )
00956 {
00957 m_error = job->error();
00958 m_errorText = job->errorText();
00959
00960 emitResult();
00961 return;
00962 }
00963
00964 if (job == m_subJob)
00965 {
00966 m_subJob = 0;
00967 resume();
00968 }
00969 subjobs.remove(job);
00970 }
00971
00972 TransferJob *KIO::get( const KURL& url, bool reload, bool showProgressInfo )
00973 {
00974
00975 KIO_ARGS << url;
00976 TransferJob * job = new TransferJob( url, CMD_GET, packedArgs, QByteArray(), showProgressInfo );
00977 if (reload)
00978 job->addMetaData("cache", "reload");
00979 return job;
00980 }
00981
00982 class PostErrorJob : public TransferJob
00983 {
00984 public:
00985
00986 PostErrorJob(QString url, const QByteArray &packedArgs, const QByteArray &postData, bool showProgressInfo) : TransferJob("", CMD_SPECIAL, packedArgs, postData, showProgressInfo)
00987 {
00988 m_error = KIO::ERR_POST_DENIED;
00989 m_errorText = url;
00990 }
00991
00992 };
00993
00994 TransferJob *KIO::http_post( const KURL& url, const QByteArray &postData, bool showProgressInfo )
00995 {
00996 bool valid = true;
00997
00998
00999 if ((url.protocol() != "http") && (url.protocol() != "https" ))
01000 valid = false;
01001
01002
01003 static const int bad_ports[] = {
01004 1,
01005 7,
01006 9,
01007 11,
01008 13,
01009 15,
01010 17,
01011 19,
01012 20,
01013 21,
01014 22,
01015 23,
01016 25,
01017 37,
01018 42,
01019 43,
01020 53,
01021 77,
01022 79,
01023 87,
01024 95,
01025 101,
01026 102,
01027 103,
01028 104,
01029 109,
01030 110,
01031 111,
01032 113,
01033 115,
01034 117,
01035 119,
01036 123,
01037 135,
01038 139,
01039 143,
01040 179,
01041 389,
01042 512,
01043 513,
01044 514,
01045 515,
01046 526,
01047 530,
01048 531,
01049 532,
01050 540,
01051 556,
01052 587,
01053 601,
01054 989,
01055 990,
01056 992,
01057 993,
01058 995,
01059 1080,
01060 2049,
01061 4045,
01062 6000,
01063 6667,
01064 0};
01065 for (int cnt=0; bad_ports[cnt]; ++cnt)
01066 if (url.port() == bad_ports[cnt])
01067 {
01068 valid = false;
01069 break;
01070 }
01071
01072 if( !valid )
01073 {
01074 static bool override_loaded = false;
01075 static QValueList< int >* overriden_ports = NULL;
01076 if( !override_loaded )
01077 {
01078 KConfig cfg( "kio_httprc", true );
01079 overriden_ports = new QValueList< int >;
01080 *overriden_ports = cfg.readIntListEntry( "OverriddenPorts" );
01081 override_loaded = true;
01082 }
01083 for( QValueList< int >::ConstIterator it = overriden_ports->begin();
01084 it != overriden_ports->end();
01085 ++it )
01086 if( overriden_ports->contains( url.port()))
01087 valid = true;
01088 }
01089
01090
01091 if (!valid)
01092 {
01093 KIO_ARGS << (int)1 << url;
01094 TransferJob * job = new PostErrorJob(url.url(), packedArgs, postData, showProgressInfo);
01095 return job;
01096 }
01097
01098
01099 KIO_ARGS << (int)1 << url;
01100 TransferJob * job = new TransferJob( url, CMD_SPECIAL,
01101 packedArgs, postData, showProgressInfo );
01102 return job;
01103 }
01104
01105
01106
01107 TransferJob *KIO::put( const KURL& url, int permissions,
01108 bool overwrite, bool resume, bool showProgressInfo )
01109 {
01110 KIO_ARGS << url << Q_INT8( overwrite ? 1 : 0 ) << Q_INT8( resume ? 1 : 0 ) << permissions;
01111 TransferJob * job = new TransferJob( url, CMD_PUT, packedArgs, QByteArray(), showProgressInfo );
01112 return job;
01113 }
01114
01116
01117 MimetypeJob::MimetypeJob( const KURL& url, int command,
01118 const QByteArray &packedArgs, bool showProgressInfo )
01119 : TransferJob(url, command, packedArgs, QByteArray(), showProgressInfo)
01120 {
01121 }
01122
01123 void MimetypeJob::start(Slave *slave)
01124 {
01125 TransferJob::start(slave);
01126 }
01127
01128
01129 void MimetypeJob::slotFinished( )
01130 {
01131 kdDebug(7007) << "MimetypeJob::slotFinished()" << endl;
01132 if ( m_error == KIO::ERR_IS_DIRECTORY )
01133 {
01134
01135
01136
01137 kdDebug(7007) << "It is in fact a directory!" << endl;
01138 m_mimetype = QString::fromLatin1("inode/directory");
01139 emit TransferJob::mimetype( this, m_mimetype );
01140 m_error = 0;
01141 }
01142 if ( m_redirectionURL.isEmpty() || m_redirectionURL.isMalformed() || m_error )
01143 {
01144
01145 TransferJob::slotFinished();
01146 } else {
01147
01148 if (queryMetaData("permanent-redirect")=="true")
01149 emit permanentRedirection(this, m_url, m_redirectionURL);
01150 staticData.truncate(0);
01151 m_suspended = false;
01152 m_url = m_redirectionURL;
01153 m_redirectionURL = KURL();
01154 m_packedArgs.truncate(0);
01155 QDataStream stream( m_packedArgs, IO_WriteOnly );
01156 stream << m_url;
01157
01158
01159 slaveDone();
01160 Scheduler::doJob(this);
01161 }
01162 }
01163
01164 MimetypeJob *KIO::mimetype(const KURL& url, bool showProgressInfo )
01165 {
01166 KIO_ARGS << url;
01167 MimetypeJob * job = new MimetypeJob(url, CMD_MIMETYPE, packedArgs, showProgressInfo);
01168 if ( showProgressInfo )
01169 Observer::self()->stating( job, url );
01170 return job;
01171 }
01172
01174
01175
01176 class FileCopyJob::FileCopyJobPrivate
01177 {
01178 public:
01179 off_t m_sourceSize;
01180 SimpleJob *m_delJob;
01181 };
01182
01183
01184
01185
01186
01187
01188
01189
01190 FileCopyJob::FileCopyJob( const KURL& src, const KURL& dest, int permissions,
01191 bool move, bool overwrite, bool resume, bool showProgressInfo)
01192 : Job(showProgressInfo), m_src(src), m_dest(dest),
01193 m_permissions(permissions), m_move(move), m_overwrite(overwrite), m_resume(resume),
01194 m_totalSize(0)
01195 {
01196 if (showProgressInfo && !move)
01197 Observer::self()->slotCopying( this, src, dest );
01198 else if (showProgressInfo && move)
01199 Observer::self()->slotMoving( this, src, dest );
01200
01201
01202 m_moveJob = 0;
01203 m_copyJob = 0;
01204 m_getJob = 0;
01205 m_putJob = 0;
01206 d = new FileCopyJobPrivate;
01207 d->m_delJob = 0;
01208 d->m_sourceSize = (off_t) -1;
01209 QTimer::singleShot(0, this, SLOT(slotStart()));
01210 }
01211
01212 void FileCopyJob::slotStart()
01213 {
01214 if ((m_src.protocol() == m_dest.protocol()) &&
01215 (m_src.host() == m_dest.host()) &&
01216 (m_src.port() == m_dest.port()) &&
01217 (m_src.user() == m_dest.user()) &&
01218 (m_src.pass() == m_dest.pass()))
01219 {
01220 if (m_move)
01221 {
01222 m_moveJob = KIO::rename( m_src, m_dest, m_overwrite );
01223 addSubjob( m_moveJob );
01224 connectSubjob( m_moveJob );
01225 }
01226 else
01227 {
01228 startCopyJob();
01229 }
01230 }
01231 else
01232 {
01233 if (!m_move &&
01234 (m_src.isLocalFile() && KProtocolInfo::canCopyFromFile(m_dest))
01235 )
01236 {
01237 startCopyJob(m_dest);
01238 }
01239 else if (!m_move &&
01240 (m_dest.isLocalFile() && KProtocolInfo::canCopyToFile(m_src))
01241 )
01242 {
01243 startCopyJob(m_src);
01244 }
01245 else
01246 {
01247 startDataPump();
01248 }
01249 }
01250 }
01251
01252 FileCopyJob::~FileCopyJob()
01253 {
01254 delete d;
01255 }
01256
01257 void FileCopyJob::setSourceSize( off_t size )
01258 {
01259 d->m_sourceSize = size;
01260 m_totalSize = size;
01261 }
01262
01263 void FileCopyJob::startCopyJob()
01264 {
01265 startCopyJob(m_src);
01266 }
01267
01268 void FileCopyJob::startCopyJob(const KURL &slave_url)
01269 {
01270
01271 KIO_ARGS << m_src << m_dest << m_permissions << (Q_INT8) m_overwrite;
01272 m_copyJob = new SimpleJob(slave_url, CMD_COPY, packedArgs, false);
01273 addSubjob( m_copyJob );
01274 connectSubjob( m_copyJob );
01275 }
01276
01277 void FileCopyJob::connectSubjob( SimpleJob * job )
01278 {
01279 connect( job, SIGNAL(totalSize( KIO::Job*, KIO::filesize_t )),
01280 this, SLOT( slotTotalSize(KIO::Job*, KIO::filesize_t)) );
01281
01282 connect( job, SIGNAL(processedSize( KIO::Job*, KIO::filesize_t )),
01283 this, SLOT( slotProcessedSize(KIO::Job*, KIO::filesize_t)) );
01284
01285 connect( job, SIGNAL(percent( KIO::Job*, unsigned long )),
01286 this, SLOT( slotPercent(KIO::Job*, unsigned long)) );
01287
01288 }
01289
01290 void FileCopyJob::slotProcessedSize( KIO::Job *, KIO::filesize_t size )
01291 {
01292 emit processedSize( this, size );
01293 if ( size > m_totalSize ) {
01294 slotTotalSize( this, size );
01295 }
01296 emitPercent( size, m_totalSize );
01297 }
01298
01299 void FileCopyJob::slotTotalSize( KIO::Job*, KIO::filesize_t size )
01300 {
01301 m_totalSize = size;
01302 emit totalSize( this, m_totalSize );
01303 }
01304
01305 void FileCopyJob::slotPercent( KIO::Job*, unsigned long pct )
01306 {
01307 if ( pct > m_percent )
01308 {
01309 m_percent = pct;
01310 emit percent( this, m_percent );
01311 }
01312 }
01313
01314 void FileCopyJob::startDataPump()
01315 {
01316
01317
01318 m_canResume = false;
01319 m_resumeAnswerSent = false;
01320 m_getJob = 0L;
01321 m_putJob = put( m_dest, m_permissions, m_overwrite, m_resume, false );
01322
01323
01324
01325
01326 connect( m_putJob, SIGNAL(canResume(KIO::Job *, KIO::filesize_t)),
01327 SLOT( slotCanResume(KIO::Job *, KIO::filesize_t)));
01328 connect( m_putJob, SIGNAL(dataReq(KIO::Job *, QByteArray&)),
01329 SLOT( slotDataReq(KIO::Job *, QByteArray&)));
01330 addSubjob( m_putJob );
01331 }
01332
01333 void FileCopyJob::slotCanResume( KIO::Job* job, KIO::filesize_t offset )
01334 {
01335 if ( job == m_putJob )
01336 {
01337 kdDebug(7007) << "FileCopyJob::slotCanResume from PUT job. offset=" << KIO::number(offset) << endl;
01338 if (offset)
01339 {
01340 RenameDlg_Result res = R_RESUME;
01341
01342 if (!KProtocolManager::autoResume())
01343 {
01344 QString newPath;
01345 KIO::Job* job = ( !m_progressId && parentJob() ) ? parentJob() : this;
01346
01347 res = Observer::self()->open_RenameDlg(
01348 job, i18n("File Already Exists"),
01349 m_src.prettyURL(0, KURL::StripFileProtocol),
01350 m_dest.prettyURL(0, KURL::StripFileProtocol),
01351 (RenameDlg_Mode) (M_OVERWRITE | M_RESUME | M_NORENAME), newPath,
01352 d->m_sourceSize, offset );
01353 }
01354
01355 if ( res == R_OVERWRITE )
01356 offset = 0;
01357 else if ( res == R_CANCEL )
01358 {
01359 m_putJob->kill(true);
01360 m_error = ERR_USER_CANCELED;
01361 emitResult();
01362 return;
01363 }
01364 }
01365 else
01366 m_resumeAnswerSent = true;
01367
01368 m_getJob = get( m_src, false, false );
01369
01370 m_getJob->addMetaData( "errorPage", "false" );
01371 m_getJob->addMetaData( "AllowCompressedPage", "false" );
01372
01373 if ( d->m_sourceSize != (off_t)-1 )
01374 m_getJob->slotTotalSize( d->m_sourceSize );
01375 if (offset)
01376 {
01377 kdDebug(7007) << "Setting metadata for resume to " << (unsigned long) offset << endl;
01378 m_getJob->addMetaData( "resume", KIO::number(offset) );
01379
01380
01381 connect( m_getJob, SIGNAL(canResume(KIO::Job *, KIO::filesize_t)),
01382 SLOT( slotCanResume(KIO::Job *, KIO::filesize_t)));
01383 }
01384 m_putJob->slave()->setOffset( offset );
01385
01386 m_putJob->suspend();
01387 addSubjob( m_getJob );
01388 connectSubjob( m_getJob );
01389 m_getJob->resume();
01390
01391 connect( m_getJob, SIGNAL(data(KIO::Job *, const QByteArray&)),
01392 SLOT( slotData(KIO::Job *, const QByteArray&)));
01393 }
01394 else if ( job == m_getJob )
01395 {
01396
01397 m_canResume = true;
01398 kdDebug(7007) << "FileCopyJob::slotCanResume from the GET job -> we can resume" << endl;
01399
01400 m_getJob->slave()->setOffset( m_putJob->slave()->offset() );
01401 }
01402 else
01403 kdWarning(7007) << "FileCopyJob::slotCanResume from unknown job=" << job
01404 << " m_getJob=" << m_getJob << " m_putJob=" << m_putJob << endl;
01405 }
01406
01407 void FileCopyJob::slotData( KIO::Job * , const QByteArray &data)
01408 {
01409
01410
01411 assert(m_putJob);
01412 m_getJob->suspend();
01413 m_putJob->resume();
01414 m_buffer = data;
01415
01416
01417
01418 if (!m_resumeAnswerSent)
01419 {
01420 m_resumeAnswerSent = true;
01421 kdDebug(7007) << "FileCopyJob::slotData (first time) -> send resume answer " << m_canResume << endl;
01422 m_putJob->slave()->sendResumeAnswer( m_canResume );
01423 }
01424 }
01425
01426 void FileCopyJob::slotDataReq( KIO::Job * , QByteArray &data)
01427 {
01428
01429 if (!m_resumeAnswerSent && !m_getJob)
01430 {
01431
01432 m_error = ERR_INTERNAL;
01433 m_errorText = "'Put' job didn't send canResume or 'Get' job didn't send data!";
01434 m_putJob->kill(true);
01435 emitResult();
01436 return;
01437 }
01438 if (m_getJob)
01439 {
01440 m_getJob->resume();
01441 m_putJob->suspend();
01442 }
01443 data = m_buffer;
01444 m_buffer = QByteArray();
01445 }
01446
01447 void FileCopyJob::slotResult( KIO::Job *job)
01448 {
01449
01450
01451 if ( job->error() )
01452 {
01453 if ((job == m_moveJob) && (job->error() == ERR_UNSUPPORTED_ACTION))
01454 {
01455 m_moveJob = 0;
01456 startCopyJob();
01457 removeSubjob(job);
01458 return;
01459 }
01460 else if ((job == m_copyJob) && (job->error() == ERR_UNSUPPORTED_ACTION))
01461 {
01462 m_copyJob = 0;
01463 startDataPump();
01464 removeSubjob(job);
01465 return;
01466 }
01467 else if (job == m_getJob)
01468 {
01469 m_getJob = 0L;
01470 if (m_putJob)
01471 m_putJob->kill(true);
01472 }
01473 else if (job == m_putJob)
01474 {
01475 m_putJob = 0L;
01476 if (m_getJob)
01477 m_getJob->kill(true);
01478 }
01479 m_error = job->error();
01480 m_errorText = job->errorText();
01481 emitResult();
01482 return;
01483 }
01484
01485 if (job == m_moveJob)
01486 {
01487 m_moveJob = 0;
01488 }
01489
01490 if (job == m_copyJob)
01491 {
01492 m_copyJob = 0;
01493 if (m_move)
01494 {
01495 d->m_delJob = file_delete( m_src, false );
01496 addSubjob(d->m_delJob);
01497 }
01498 }
01499
01500 if (job == m_getJob)
01501 {
01502 m_getJob = 0;
01503 if (m_putJob)
01504 m_putJob->resume();
01505 }
01506
01507 if (job == m_putJob)
01508 {
01509
01510 m_putJob = 0;
01511 if (m_getJob)
01512 {
01513 kdWarning(7007) << "WARNING ! Get still going on..." << endl;
01514 m_getJob->resume();
01515 }
01516 if (m_move)
01517 {
01518 d->m_delJob = file_delete( m_src, false );
01519 addSubjob(d->m_delJob);
01520 }
01521 }
01522
01523 if (job == d->m_delJob)
01524 {
01525 d->m_delJob = 0;
01526 }
01527 removeSubjob(job);
01528 }
01529
01530 FileCopyJob *KIO::file_copy( const KURL& src, const KURL& dest, int permissions,
01531 bool overwrite, bool resume, bool showProgressInfo)
01532 {
01533 return new FileCopyJob( src, dest, permissions, false, overwrite, resume, showProgressInfo );
01534 }
01535
01536 FileCopyJob *KIO::file_move( const KURL& src, const KURL& dest, int permissions,
01537 bool overwrite, bool resume, bool showProgressInfo)
01538 {
01539 return new FileCopyJob( src, dest, permissions, true, overwrite, resume, showProgressInfo );
01540 }
01541
01542 SimpleJob *KIO::file_delete( const KURL& src, bool showProgressInfo)
01543 {
01544 KIO_ARGS << src << Q_INT8(true);
01545 return new SimpleJob(src, CMD_DEL, packedArgs, showProgressInfo );
01546 }
01547
01549
01550 ListJob::ListJob(const KURL& u, bool showProgressInfo, bool _recursive, QString _prefix, bool _includeHidden) :
01551 SimpleJob(u, CMD_LISTDIR, QByteArray(), showProgressInfo),
01552 recursive(_recursive), includeHidden(_includeHidden), prefix(_prefix), m_processedEntries(0)
01553 {
01554
01555
01556 QDataStream stream( m_packedArgs, IO_WriteOnly );
01557 stream << u;
01558 }
01559
01560 void ListJob::slotListEntries( const KIO::UDSEntryList& list )
01561 {
01562
01563 m_processedEntries += list.count();
01564 slotProcessedSize( m_processedEntries );
01565
01566 if (recursive) {
01567 UDSEntryListConstIterator it = list.begin();
01568 UDSEntryListConstIterator end = list.end();
01569
01570 for (; it != end; ++it) {
01571 bool isDir = false;
01572 bool isLink = false;
01573 QString filename;
01574
01575 UDSEntry::ConstIterator it2 = (*it).begin();
01576 UDSEntry::ConstIterator end2 = (*it).end();
01577 for( ; it2 != end2; it2++ ) {
01578 switch( (*it2).m_uds ) {
01579 case UDS_FILE_TYPE:
01580 isDir = S_ISDIR((*it2).m_long);
01581 break;
01582 case UDS_NAME:
01583 filename = (*it2).m_str;
01584 break;
01585 case UDS_LINK_DEST:
01586
01587 isLink = !(*it2).m_str.isEmpty();
01588 break;
01589 default:
01590 break;
01591 }
01592 }
01593 if (isDir && !isLink) {
01594
01595 if (filename != ".." && filename != "." && (includeHidden || filename[0] != '.')) {
01596 KURL newone = url();
01597 newone.addPath(filename);
01598 ListJob *job = new ListJob(newone, m_progressId!=0, true, prefix + filename + "/",includeHidden);
01599 Scheduler::scheduleJob(job);
01600 connect(job, SIGNAL(entries( KIO::Job *,
01601 const KIO::UDSEntryList& )),
01602 SLOT( gotEntries( KIO::Job*,
01603 const KIO::UDSEntryList& )));
01604 addSubjob(job);
01605 }
01606 }
01607 }
01608 }
01609
01610
01611
01612
01613 if (prefix.isNull() && includeHidden) {
01614 emit entries(this, list);
01615 } else {
01616
01617 UDSEntryList newlist;
01618
01619 UDSEntryListConstIterator it = list.begin();
01620 UDSEntryListConstIterator end = list.end();
01621 for (; it != end; ++it) {
01622
01623 UDSEntry newone = *it;
01624 UDSEntry::Iterator it2 = newone.begin();
01625 QString filename;
01626 for( ; it2 != newone.end(); it2++ ) {
01627 if ((*it2).m_uds == UDS_NAME) {
01628 filename = (*it2).m_str;
01629 (*it2).m_str = prefix + filename;
01630 }
01631 }
01632
01633
01634 if ( (prefix.isNull() || (filename != ".." && filename != ".") )
01635 && (includeHidden || (filename[0] != '.') ) )
01636 newlist.append(newone);
01637 }
01638
01639 emit entries(this, newlist);
01640 }
01641 }
01642
01643 void ListJob::gotEntries(KIO::Job *, const KIO::UDSEntryList& list )
01644 {
01645
01646 emit entries(this, list);
01647 }
01648
01649 void ListJob::slotResult( KIO::Job * job )
01650 {
01651
01652
01653 removeSubjob( job );
01654 }
01655
01656 void ListJob::slotRedirection( const KURL & url )
01657 {
01658 if (!kapp->authorizeURLAction("redirect", m_url, url))
01659 {
01660 kdWarning(7007) << "ListJob: Redirection from " << m_url.prettyURL() << " to " << url.prettyURL() << " REJECTED!" << endl;
01661 return;
01662 }
01663 m_redirectionURL = url;
01664 if (m_url.hasUser() && !url.hasUser() && (m_url.host().lower() == url.host().lower()))
01665 m_redirectionURL.setUser(m_url.user());
01666 emit redirection( this, url );
01667 }
01668
01669 void ListJob::slotFinished()
01670 {
01671 if ( m_redirectionURL.isEmpty() || m_redirectionURL.isMalformed() || m_error )
01672 {
01673
01674 SimpleJob::slotFinished();
01675 } else {
01676 kdDebug(7007) << "ListJob: Redirection to " << m_redirectionURL.prettyURL() << endl;
01677 if (queryMetaData("permanent-redirect")=="true")
01678 emit permanentRedirection(this, m_url, m_redirectionURL);
01679 m_url = m_redirectionURL;
01680 m_redirectionURL = KURL();
01681 m_packedArgs.truncate(0);
01682 QDataStream stream( m_packedArgs, IO_WriteOnly );
01683 stream << m_url;
01684
01685
01686 slaveDone();
01687 Scheduler::doJob(this);
01688 }
01689 }
01690
01691 ListJob *KIO::listDir( const KURL& url, bool showProgressInfo, bool includeHidden )
01692 {
01693 ListJob * job = new ListJob(url, showProgressInfo,false,QString::null,includeHidden);
01694 return job;
01695 }
01696
01697 ListJob *KIO::listRecursive( const KURL& url, bool showProgressInfo, bool includeHidden )
01698 {
01699 ListJob * job = new ListJob(url, showProgressInfo, true,QString::null,includeHidden);
01700 return job;
01701 }
01702
01703 void ListJob::start(Slave *slave)
01704 {
01705 connect( slave, SIGNAL( listEntries( const KIO::UDSEntryList& )),
01706 SLOT( slotListEntries( const KIO::UDSEntryList& )));
01707 connect( slave, SIGNAL( totalSize( KIO::filesize_t ) ),
01708 SLOT( slotTotalSize( KIO::filesize_t ) ) );
01709 connect( slave, SIGNAL( redirection(const KURL &) ),
01710 SLOT( slotRedirection(const KURL &) ) );
01711
01712 SimpleJob::start(slave);
01713 }
01714
01715
01716 CopyJob::CopyJob( const KURL::List& src, const KURL& dest, CopyMode mode, bool asMethod, bool showProgressInfo )
01717 : Job(showProgressInfo), m_mode(mode), m_asMethod(asMethod),
01718 destinationState(DEST_NOT_STATED), state(STATE_STATING),
01719 m_totalSize(0), m_processedSize(0), m_fileProcessedSize(0),
01720 m_processedFiles(0), m_processedDirs(0),
01721 m_srcList(src), m_currentStatSrc(m_srcList.begin()),
01722 m_bCurrentOperationIsLink(false), m_bSingleFileCopy(false), m_bOnlyRenames(mode==Move),
01723 m_dest(dest), m_bAutoSkip( false ), m_bOverwriteAll( false ),
01724 m_conflictError(0), m_reportTimer(0)
01725 {
01726 if ( showProgressInfo ) {
01727 connect( this, SIGNAL( totalFiles( KIO::Job*, unsigned long ) ),
01728 Observer::self(), SLOT( slotTotalFiles( KIO::Job*, unsigned long ) ) );
01729
01730 connect( this, SIGNAL( totalDirs( KIO::Job*, unsigned long ) ),
01731 Observer::self(), SLOT( slotTotalDirs( KIO::Job*, unsigned long ) ) );
01732 }
01733 QTimer::singleShot(0, this, SLOT(slotStart()));
01734 }
01735
01736 void CopyJob::slotStart()
01737 {
01743 m_reportTimer = new QTimer(this);
01744
01745 connect(m_reportTimer,SIGNAL(timeout()),this,SLOT(slotReport()));
01746 m_reportTimer->start(REPORT_TIMEOUT,false);
01747
01748
01749 KIO::Job * job = KIO::stat( m_dest, false, 2, false );
01750
01751 addSubjob(job);
01752 }
01753
01754 void CopyJob::slotResultStating( Job *job )
01755 {
01756
01757
01758 if (job->error() && destinationState != DEST_NOT_STATED )
01759 {
01760 KURL srcurl = ((SimpleJob*)job)->url();
01761 if ( !srcurl.isLocalFile() )
01762 {
01763
01764
01765
01766 kdDebug(7007) << "Error while stating source. Activating hack" << endl;
01767 subjobs.remove( job );
01768 assert ( subjobs.isEmpty() );
01769 struct CopyInfo info;
01770 info.permissions = (mode_t) -1;
01771 info.mtime = (time_t) -1;
01772 info.ctime = (time_t) -1;
01773 info.size = (off_t)-1;
01774 info.uSource = srcurl;
01775 info.uDest = m_dest;
01776
01777 if ( destinationState == DEST_IS_DIR && !m_asMethod )
01778 info.uDest.addPath( srcurl.fileName() );
01779
01780 files.append( info );
01781 ++m_currentStatSrc;
01782 statNextSrc();
01783 return;
01784 }
01785
01786 Job::slotResult( job );
01787 return;
01788 }
01789
01790
01791 UDSEntry entry = ((StatJob*)job)->statResult();
01792 bool bDir = false;
01793 bool bLink = false;
01794 UDSEntry::ConstIterator it2 = entry.begin();
01795 for( ; it2 != entry.end(); it2++ ) {
01796 if ( ((*it2).m_uds) == UDS_FILE_TYPE )
01797 bDir = S_ISDIR( (mode_t)(*it2).m_long );
01798 else if ( ((*it2).m_uds) == UDS_LINK_DEST )
01799 bLink = !((*it2).m_str.isEmpty());
01800 }
01801
01802 if ( destinationState == DEST_NOT_STATED )
01803
01804 {
01805 if (job->error())
01806 destinationState = DEST_DOESNT_EXIST;
01807 else {
01808
01809 destinationState = bDir ? DEST_IS_DIR : DEST_IS_FILE;
01810
01811 }
01812 subjobs.remove( job );
01813 assert ( subjobs.isEmpty() );
01814
01815
01816 statNextSrc();
01817 return;
01818 }
01819
01820 m_currentDest = m_dest;
01821
01822 UDSEntryList lst;
01823 lst.append(entry);
01824
01825
01826
01827
01828
01829
01830
01831
01832
01833
01834
01835
01836
01837 m_bCurrentSrcIsDir = false;
01838 slotEntries(job, lst);
01839
01840 KURL srcurl = ((SimpleJob*)job)->url();
01841
01842 subjobs.remove( job );
01843 assert ( subjobs.isEmpty() );
01844
01845 if ( bDir
01846 && !bLink
01847 && m_mode != Link )
01848 {
01849
01850
01851 m_bCurrentSrcIsDir = true;
01852 if ( destinationState == DEST_IS_DIR )
01853
01854 m_currentDest.addPath( srcurl.fileName() );
01855 else if ( destinationState == DEST_IS_FILE )
01856 {
01857 m_error = ERR_IS_FILE;
01858 m_errorText = m_dest.prettyURL();
01859 emitResult();
01860 return;
01861 }
01862 else
01863 {
01864
01865
01866
01867
01868 destinationState = DEST_IS_DIR;
01869 }
01870
01871 startListing( srcurl );
01872 }
01873 else
01874 {
01875
01876 ++m_currentStatSrc;
01877 statNextSrc();
01878 }
01879 }
01880
01881 void CopyJob::slotReport()
01882 {
01883
01884 Observer * observer = m_progressId ? Observer::self() : 0L;
01885 switch (state) {
01886 case STATE_COPYING_FILES:
01887 emit processedFiles( this, m_processedFiles );
01888 if (observer) observer->slotProcessedFiles(this,m_processedFiles);
01889 if (m_mode==Move)
01890 {
01891 if (observer) observer->slotMoving( this, m_currentSrcURL,m_currentDestURL);
01892 emit moving( this, m_currentSrcURL, m_currentDestURL);
01893 }
01894 else if (m_mode==Link)
01895 {
01896 if (observer) observer->slotCopying( this, m_currentSrcURL, m_currentDestURL );
01897 emit linking( this, m_currentSrcURL.path(), m_currentDestURL );
01898 }
01899 else
01900 {
01901 if (observer) observer->slotCopying( this, m_currentSrcURL, m_currentDestURL );
01902 emit copying( this, m_currentSrcURL, m_currentDestURL );
01903 };
01904 break;
01905
01906 case STATE_CREATING_DIRS:
01907 if (observer) {
01908 observer->slotProcessedDirs( this, m_processedDirs );
01909 observer->slotCreatingDir( this,m_currentDestURL);
01910 }
01911 emit processedDirs( this, m_processedDirs );
01912 emit creatingDir( this, m_currentDestURL );
01913 break;
01914
01915 case STATE_STATING:
01916 case STATE_LISTING:
01917 if (observer) observer->slotCopying( this, m_currentSrcURL, m_currentDestURL );
01918 emit totalSize( this, m_totalSize );
01919 emit totalFiles( this, files.count() );
01920 emit totalDirs( this, dirs.count() );
01921 break;
01922
01923 default:
01924 break;
01925 }
01926 }
01927
01928 void CopyJob::slotEntries(KIO::Job* job, const UDSEntryList& list)
01929 {
01930 UDSEntryListConstIterator it = list.begin();
01931 UDSEntryListConstIterator end = list.end();
01932 for (; it != end; ++it) {
01933 UDSEntry::ConstIterator it2 = (*it).begin();
01934 struct CopyInfo info;
01935 info.permissions = -1;
01936 info.mtime = (time_t) -1;
01937 info.ctime = (time_t) -1;
01938 info.size = (off_t)-1;
01939 QString relName;
01940 bool isDir = false;
01941 for( ; it2 != (*it).end(); it2++ ) {
01942 switch ((*it2).m_uds) {
01943 case UDS_FILE_TYPE:
01944
01945 isDir = S_ISDIR( (mode_t)((*it2).m_long) );
01946 break;
01947 case UDS_NAME:
01948 relName = (*it2).m_str;
01949 break;
01950 case UDS_LINK_DEST:
01951 info.linkDest = (*it2).m_str;
01952 break;
01953 case UDS_ACCESS:
01954 info.permissions = ((*it2).m_long);
01955 break;
01956 case UDS_SIZE:
01957 info.size = (off_t)((*it2).m_long);
01958 m_totalSize += info.size;
01959 break;
01960 case UDS_MODIFICATION_TIME:
01961 info.mtime = (time_t)((*it2).m_long);
01962 break;
01963 case UDS_CREATION_TIME:
01964 info.ctime = (time_t)((*it2).m_long);
01965 default:
01966 break;
01967 }
01968 }
01969 if (relName != ".." && relName != ".")
01970 {
01971
01972 info.uSource = ((SimpleJob *)job)->url();
01973 if ( m_bCurrentSrcIsDir )
01974 info.uSource.addPath( relName );
01975 info.uDest = m_currentDest;
01976
01977
01978 if ( destinationState == DEST_IS_DIR && !m_asMethod )
01979 {
01980
01981
01982
01983 if ( relName.isEmpty() )
01984 info.uDest.addPath( KIO::encodeFileName( info.uSource.prettyURL() ) );
01985 else
01986 info.uDest.addPath( relName );
01987 }
01988
01989 if ( info.linkDest.isEmpty() && (isDir ) && m_mode != Link )
01990 {
01991 dirs.append( info );
01992 if (m_mode == Move)
01993 dirsToRemove.append( info.uSource );
01994 }
01995 else {
01996 files.append( info );
01997 }
01998 }
01999 }
02000 }
02001
02002 void CopyJob::statNextSrc()
02003 {
02004 if ( m_currentStatSrc != m_srcList.end() )
02005 {
02006 m_currentSrcURL = (*m_currentStatSrc);
02007 if ( m_mode == Link )
02008 {
02009
02010 m_currentDest = m_dest;
02011 struct CopyInfo info;
02012 info.permissions = -1;
02013 info.mtime = (time_t) -1;
02014 info.ctime = (time_t) -1;
02015 info.size = (off_t)-1;
02016 info.uSource = m_currentSrcURL;
02017 info.uDest = m_currentDest;
02018
02019 if ( destinationState == DEST_IS_DIR && !m_asMethod )
02020 {
02021 if (
02022 (m_currentSrcURL.protocol() == info.uDest.protocol()) &&
02023 (m_currentSrcURL.host() == info.uDest.host()) &&
02024 (m_currentSrcURL.port() == info.uDest.port()) &&
02025 (m_currentSrcURL.user() == info.uDest.user()) &&
02026 (m_currentSrcURL.pass() == info.uDest.pass()) )
02027 {
02028
02029 info.uDest.addPath( m_currentSrcURL.fileName() );
02030 }
02031 else
02032 {
02033
02034
02035
02036 info.uDest.addPath( KIO::encodeFileName( m_currentSrcURL.prettyURL() )+".desktop" );
02037 }
02038 }
02039 files.append( info );
02040 ++m_currentStatSrc;
02041 statNextSrc();
02042 }
02043
02044 else if ( m_mode == Move &&
02045 (m_currentSrcURL.protocol() == m_dest.protocol()) &&
02046 (m_currentSrcURL.host() == m_dest.host()) &&
02047 (m_currentSrcURL.port() == m_dest.port()) &&
02048 (m_currentSrcURL.user() == m_dest.user()) &&
02049 (m_currentSrcURL.pass() == m_dest.pass()) )
02050 {
02051 KURL dest = m_dest;
02052
02053 if ( destinationState == DEST_IS_DIR && !m_asMethod )
02054 dest.addPath( m_currentSrcURL.fileName() );
02055 kdDebug(7007) << "This seems to be a suitable case for trying to rename before stat+[list+]copy+del" << endl;
02056 state = STATE_RENAMING;
02057 SimpleJob * newJob = KIO::rename( m_currentSrcURL, dest, false );
02058 Scheduler::scheduleJob(newJob);
02059 addSubjob( newJob );
02060 if ( m_currentSrcURL.directory() != dest.directory() )
02061 m_bOnlyRenames = false;
02062 }
02063 else
02064 {
02065
02066 if (m_mode == Move && !KProtocolInfo::supportsDeleting(m_currentSrcURL)) {
02067 KMessageBox::information( 0, buildErrorString(ERR_CANNOT_DELETE, m_currentSrcURL.prettyURL()));
02068 ++m_currentStatSrc;
02069 statNextSrc();
02070 return;
02071 }
02072
02073 Job * job = KIO::stat( m_currentSrcURL, true, 2, false );
02074
02075 state = STATE_STATING;
02076 addSubjob(job);
02077 m_currentDestURL=m_dest;
02078 m_bOnlyRenames = false;
02079 }
02080 } else
02081 {
02082
02083
02084 state = STATE_STATING;
02085 slotReport();
02086
02087 m_bSingleFileCopy = ( files.count() == 1 && dirs.isEmpty() );
02088
02089 state = STATE_CREATING_DIRS;
02090 createNextDir();
02091 }
02092 }
02093
02094
02095 void CopyJob::startListing( const KURL & src )
02096 {
02097 state = STATE_LISTING;
02098 ListJob * newjob = listRecursive( src, false );
02099 connect(newjob, SIGNAL(entries( KIO::Job *,
02100 const KIO::UDSEntryList& )),
02101 SLOT( slotEntries( KIO::Job*,
02102 const KIO::UDSEntryList& )));
02103 addSubjob( newjob );
02104 }
02105
02106 void CopyJob::skip( const KURL & sourceUrl )
02107 {
02108
02109
02110
02111 KURL::List::Iterator sit = m_srcList.find( sourceUrl );
02112 if ( sit != m_srcList.end() )
02113 {
02114
02115 m_srcList.remove( sit );
02116 }
02117 dirsToRemove.remove( sourceUrl );
02118 }
02119
02120 void CopyJob::slotResultCreatingDirs( Job * job )
02121 {
02122
02123 QValueList<CopyInfo>::Iterator it = dirs.begin();
02124
02125 if ( job->error() )
02126 {
02127 m_conflictError = job->error();
02128 if ( (m_conflictError == ERR_DIR_ALREADY_EXIST)
02129 || (m_conflictError == ERR_FILE_ALREADY_EXIST) )
02130 {
02131 KURL oldURL = ((SimpleJob*)job)->url();
02132
02133 if ( m_bAutoSkip ) {
02134
02135 m_skipList.append( oldURL.path( 1 ) );
02136 skip( oldURL );
02137 dirs.remove( it );
02138 } else if ( m_bOverwriteAll ) {
02139 dirs.remove( it );
02140 } else
02141 {
02142 assert( ((SimpleJob*)job)->url().url() == (*it).uDest.url() );
02143 subjobs.remove( job );
02144 assert ( subjobs.isEmpty() );
02145
02146
02147 KURL existingDest( (*it).uDest );
02148 SimpleJob * newJob = KIO::stat( existingDest, false, 2, false );
02149 Scheduler::scheduleJob(newJob);
02150 kdDebug(7007) << "KIO::stat for resolving conflict on " << existingDest.prettyURL() << endl;
02151 state = STATE_CONFLICT_CREATING_DIRS;
02152 addSubjob(newJob);
02153 return;
02154 }
02155 }
02156 else
02157 {
02158
02159 Job::slotResult( job );
02160 return;
02161 }
02162 }
02163 else
02164 {
02165
02166 emit copyingDone( this, (*it).uSource, (*it).uDest, true, false );
02167 dirs.remove( it );
02168 }
02169
02170 m_processedDirs++;
02171
02172 subjobs.remove( job );
02173 assert ( subjobs.isEmpty() );
02174 createNextDir();
02175 }
02176
02177 void CopyJob::slotResultConflictCreatingDirs( KIO::Job * job )
02178 {
02179
02180
02181
02182 QValueList<CopyInfo>::Iterator it = dirs.begin();
02183
02184 time_t destmtime = (time_t)-1;
02185 time_t destctime = (time_t)-1;
02186 KIO::filesize_t destsize = 0;
02187 UDSEntry entry = ((KIO::StatJob*)job)->statResult();
02188 KIO::UDSEntry::ConstIterator it2 = entry.begin();
02189 for( ; it2 != entry.end(); it2++ ) {
02190 switch ((*it2).m_uds) {
02191 case UDS_MODIFICATION_TIME:
02192 destmtime = (time_t)((*it2).m_long);
02193 break;
02194 case UDS_CREATION_TIME:
02195 destctime = (time_t)((*it2).m_long);
02196 break;
02197 case UDS_SIZE:
02198 destsize = (*it2).m_long;
02199 break;
02200 }
02201 }
02202 subjobs.remove( job );
02203 assert ( subjobs.isEmpty() );
02204
02205
02206 RenameDlg_Mode mode = (RenameDlg_Mode)( M_MULTI | M_SKIP );
02207
02208 if ( m_conflictError == ERR_DIR_ALREADY_EXIST )
02209 mode = (RenameDlg_Mode)( mode | M_OVERWRITE );
02210
02211 QString existingDest = (*it).uDest.path();
02212 QString newPath;
02213 if (m_reportTimer)
02214 m_reportTimer->stop();
02215 RenameDlg_Result r = Observer::self()->open_RenameDlg( this, i18n("Directory Already Exists"),
02216 (*it).uSource.prettyURL(0, KURL::StripFileProtocol),
02217 (*it).uDest.prettyURL(0, KURL::StripFileProtocol),
02218 mode, newPath,
02219 (*it).size, destsize,
02220 (*it).ctime, destctime,
02221 (*it).mtime, destmtime );
02222 if (m_reportTimer)
02223 m_reportTimer->start(REPORT_TIMEOUT,false);
02224 switch ( r ) {
02225 case R_CANCEL:
02226 m_error = ERR_USER_CANCELED;
02227 emitResult();
02228 return;
02229 case R_RENAME:
02230 {
02231 QString oldPath = (*it).uDest.path( 1 );
02232 KURL newUrl( (*it).uDest );
02233 newUrl.setPath( newPath );
02234 emit renamed( this, (*it).uDest, newUrl );
02235
02236
02237 (*it).uDest = newUrl.path( -1 );
02238 newPath = newUrl.path( 1 );
02239 QValueList<CopyInfo>::Iterator renamedirit = it;
02240 renamedirit++;
02241
02242 for( ; renamedirit != dirs.end() ; ++renamedirit )
02243 {
02244 QString path = (*renamedirit).uDest.path();
02245 if ( path.left(oldPath.length()) == oldPath )
02246 (*renamedirit).uDest.setPath( path.replace( 0, oldPath.length(), newPath ) );
02247 }
02248
02249 QValueList<CopyInfo>::Iterator renamefileit = files.begin();
02250 for( ; renamefileit != files.end() ; ++renamefileit )
02251 {
02252 QString path = (*renamefileit).uDest.path();
02253 if ( path.left(oldPath.length()) == oldPath )
02254 (*renamefileit).uDest.setPath( path.replace( 0, oldPath.length(), newPath ) );
02255 }
02256 }
02257 break;
02258 case R_AUTO_SKIP:
02259 m_bAutoSkip = true;
02260
02261 case R_SKIP:
02262 m_skipList.append( existingDest );
02263 skip( (*it).uSource );
02264
02265 dirs.remove( it );
02266 break;
02267 case R_OVERWRITE:
02268 m_overwriteList.append( existingDest );
02269
02270 dirs.remove( it );
02271 break;
02272 case R_OVERWRITE_ALL:
02273 m_bOverwriteAll = true;
02274
02275 dirs.remove( it );
02276 break;
02277 default:
02278 assert( 0 );
02279 }
02280 state = STATE_CREATING_DIRS;
02281 m_processedDirs++;
02282
02283 createNextDir();
02284 }
02285
02286 void CopyJob::createNextDir()
02287 {
02288 KURL udir;
02289 if ( !dirs.isEmpty() )
02290 {
02291
02292 QValueList<CopyInfo>::Iterator it = dirs.begin();
02293
02294 while( it != dirs.end() && udir.isEmpty() )
02295 {
02296 QString dir = (*it).uDest.path();
02297 bool bCreateDir = true;
02298
02299 QStringList::Iterator sit = m_skipList.begin();
02300 for( ; sit != m_skipList.end() && bCreateDir; sit++ )
02301
02302 if ( *sit == dir.left( (*sit).length() ) )
02303 bCreateDir = false;
02304
02305 if ( !bCreateDir ) {
02306 dirs.remove( it );
02307 it = dirs.begin();
02308 } else
02309 udir = (*it).uDest;
02310 }
02311 }
02312 if ( !udir.isEmpty() )
02313 {
02314
02315
02316 KIO::SimpleJob *newjob = KIO::mkdir( udir, -1 );
02317 Scheduler::scheduleJob(newjob);
02318
02319 m_currentDestURL = udir;
02320
02321 addSubjob(newjob);
02322 return;
02323 }
02324 else
02325 {
02326 state = STATE_COPYING_FILES;
02327 m_processedFiles++;
02328 copyNextFile();
02329 }
02330 }
02331
02332 void CopyJob::slotResultCopyingFiles( Job * job )
02333 {
02334
02335 QValueList<CopyInfo>::Iterator it = files.begin();
02336 if ( job->error() )
02337 {
02338
02339 if ( m_bAutoSkip )
02340 {
02341 skip( (*it).uSource );
02342 files.remove( it );
02343 }
02344 else
02345 {
02346 m_conflictError = job->error();
02347
02348 if ( ( m_conflictError == ERR_FILE_ALREADY_EXIST )
02349 || ( m_conflictError == ERR_DIR_ALREADY_EXIST ) )
02350 {
02351 subjobs.remove( job );
02352 assert ( subjobs.isEmpty() );
02353
02354 KURL existingFile( (*it).uDest );
02355 SimpleJob * newJob = KIO::stat( existingFile, false, 2, false );
02356 Scheduler::scheduleJob(newJob);
02357 kdDebug(7007) << "KIO::stat for resolving conflict on " << existingFile.prettyURL() << endl;
02358 state = STATE_CONFLICT_COPYING_FILES;
02359 addSubjob(newJob);
02360 return;
02361 }
02362 else
02363 {
02364 if ( m_bCurrentOperationIsLink && job->inherits( "KIO::DeleteJob" ) )
02365 {
02366
02367
02368 files.remove( it );
02369 } else {
02370
02371 slotResultConflictCopyingFiles( job );
02372 return;
02373 }
02374 }
02375 }
02376 } else
02377 {
02378
02379 if ( m_bCurrentOperationIsLink && m_mode == Move
02380 && !job->inherits( "KIO::DeleteJob" )
02381 )
02382 {
02383 subjobs.remove( job );
02384 assert ( subjobs.isEmpty() );
02385
02386
02387 KIO::Job * newjob = KIO::del( (*it).uSource, false , false );
02388 addSubjob( newjob );
02389 return;
02390 }
02391
02392 if ( m_bCurrentOperationIsLink )
02393 {
02394 QString target = ( m_mode == Link ? (*it).uSource.path() : (*it).linkDest );
02395
02396 emit copyingLinkDone( this, (*it).uSource, target, (*it).uDest );
02397 }
02398 else
02399
02400 emit copyingDone( this, (*it).uSource, (*it).uDest, false, false );
02401
02402 files.remove( it );
02403 }
02404 m_processedFiles++;
02405
02406
02407 m_processedSize += m_fileProcessedSize;
02408 m_fileProcessedSize = 0;
02409
02410
02411 subjobs.remove( job );
02412 assert ( subjobs.isEmpty() );
02413 copyNextFile();
02414 }
02415
02416 void CopyJob::slotResultConflictCopyingFiles( KIO::Job * job )
02417 {
02418
02419
02420 QValueList<CopyInfo>::Iterator it = files.begin();
02421
02422 RenameDlg_Result res;
02423 QString newPath;
02424
02425 if (m_reportTimer)
02426 m_reportTimer->stop();
02427
02428 if ( ( m_conflictError == ERR_FILE_ALREADY_EXIST )
02429 || ( m_conflictError == ERR_DIR_ALREADY_EXIST ) )
02430 {
02431
02432 time_t destmtime = (time_t)-1;
02433 time_t destctime = (time_t)-1;
02434 KIO::filesize_t destsize = 0;
02435 UDSEntry entry = ((KIO::StatJob*)job)->statResult();
02436 KIO::UDSEntry::ConstIterator it2 = entry.begin();
02437 for( ; it2 != entry.end(); it2++ ) {
02438 switch ((*it2).m_uds) {
02439 case UDS_MODIFICATION_TIME:
02440 destmtime = (time_t)((*it2).m_long);
02441 break;
02442 case UDS_CREATION_TIME:
02443 destctime = (time_t)((*it2).m_long);
02444 break;
02445 case UDS_SIZE:
02446 destsize = (*it2).m_long;
02447 break;
02448 }
02449 }
02450
02451
02452
02453 RenameDlg_Mode mode = (RenameDlg_Mode)
02454 ( ( m_conflictError == ERR_DIR_ALREADY_EXIST ? 0 :
02455 ( (*it).uSource == (*it).uDest ) ? M_OVERWRITE_ITSELF : M_OVERWRITE ) );
02456 if ( files.count() > 0 )
02457 mode = (RenameDlg_Mode) ( mode | M_MULTI | M_SKIP );
02458 else
02459 mode = (RenameDlg_Mode) ( mode | M_SINGLE );
02460 res = Observer::self()->open_RenameDlg( this, m_conflictError == ERR_FILE_ALREADY_EXIST ?
02461 i18n("File Already Exists") : i18n("Already Exists as a Directory"),
02462 (*it).uSource.prettyURL(0, KURL::StripFileProtocol),
02463 (*it).uDest.prettyURL(0, KURL::StripFileProtocol),
02464 mode, newPath,
02465 (*it).size, destsize,
02466 (*it).ctime, destctime,
02467 (*it).mtime, destmtime );
02468
02469 }
02470 else
02471 {
02472 if ( job->error() == ERR_USER_CANCELED )
02473 res = R_CANCEL;
02474 else
02475 {
02476 SkipDlg_Result skipResult = Observer::self()->open_SkipDlg( this, files.count() > 0,
02477 job->errorString() );
02478
02479
02480 res = ( skipResult == S_SKIP ) ? R_SKIP :
02481 ( skipResult == S_AUTO_SKIP ) ? R_AUTO_SKIP :
02482 R_CANCEL;
02483 }
02484 }
02485
02486 if (m_reportTimer)
02487 m_reportTimer->start(REPORT_TIMEOUT,false);
02488
02489 subjobs.remove( job );
02490 assert ( subjobs.isEmpty() );
02491 switch ( res ) {
02492 case R_CANCEL:
02493 m_error = ERR_USER_CANCELED;
02494 emitResult();
02495 return;
02496 case R_RENAME:
02497 {
02498 KURL newUrl( (*it).uDest );
02499 newUrl.setPath( newPath );
02500 emit renamed( this, (*it).uDest, newUrl );
02501 (*it).uDest = newUrl;
02502 }
02503 break;
02504 case R_AUTO_SKIP:
02505 m_bAutoSkip = true;
02506
02507 case R_SKIP:
02508
02509 skip( (*it).uSource );
02510 files.remove( it );
02511 break;
02512 case R_OVERWRITE_ALL:
02513 m_bOverwriteAll = true;
02514 break;
02515 case R_OVERWRITE:
02516
02517 m_overwriteList.append( (*it).uDest.path() );
02518 break;
02519 default:
02520 assert( 0 );
02521 }
02522 state = STATE_COPYING_FILES;
02523 m_processedFiles++;
02524
02525 copyNextFile();
02526 }
02527
02528 void CopyJob::copyNextFile()
02529 {
02530 bool bCopyFile = false;
02531
02532
02533 QValueList<CopyInfo>::Iterator it = files.begin();
02534
02535 while (it != files.end() && !bCopyFile)
02536 {
02537 bCopyFile = true;
02538 QString destFile = (*it).uDest.path();
02539
02540 QStringList::Iterator sit = m_skipList.begin();
02541 for( ; sit != m_skipList.end() && bCopyFile; sit++ )
02542
02543 if ( *sit == destFile.left( (*sit).length() ) )
02544 bCopyFile = false;
02545
02546 if (!bCopyFile) {
02547 files.remove( it );
02548 it = files.begin();
02549 }
02550 }
02551
02552 if (bCopyFile)
02553 {
02554
02555 bool bOverwrite = m_bOverwriteAll;
02556 QString destFile = (*it).uDest.path();
02557 if ( (*it).uDest == (*it).uSource )
02558 bOverwrite = false;
02559 else
02560 {
02561
02562 QStringList::Iterator sit = m_overwriteList.begin();
02563 for( ; sit != m_overwriteList.end() && !bOverwrite; sit++ )
02564 if ( *sit == destFile.left( (*sit).length() ) )
02565 bOverwrite = true;
02566 }
02567
02568 m_bCurrentOperationIsLink = false;
02569 KIO::Job * newjob = 0L;
02570 if ( m_mode == Link )
02571 {
02572
02573 if (
02574 ((*it).uSource.protocol() == (*it).uDest.protocol()) &&
02575 ((*it).uSource.host() == (*it).uDest.host()) &&
02576 ((*it).uSource.port() == (*it).uDest.port()) &&
02577 ((*it).uSource.user() == (*it).uDest.user()) &&
02578 ((*it).uSource.pass() == (*it).uDest.pass()) )
02579 {
02580
02581 KIO::SimpleJob *newJob = KIO::symlink( (*it).uSource.path(), (*it).uDest, bOverwrite, false );
02582 newjob = newJob;
02583 Scheduler::scheduleJob(newJob);
02584 kdDebug(7007) << "CopyJob::copyNextFile : Linking target=" << (*it).uSource.path() << " link=" << (*it).uDest.prettyURL() << endl;
02585
02586 m_bCurrentOperationIsLink = true;
02587 m_currentSrcURL=(*it).uSource;
02588 m_currentDestURL=(*it).uDest;
02589
02590 } else {
02591 kdDebug(7007) << "CopyJob::copyNextFile : Linking URL=" << (*it).uSource.prettyURL() << " link=" << (*it).uDest.prettyURL() << endl;
02592 if ( (*it).uDest.isLocalFile() )
02593 {
02594 bool devicesOk=false;
02595
02596
02597 if ((*it).uSource.protocol()==QString::fromLatin1("devices"))
02598 {
02599 QByteArray data;
02600 QByteArray param;
02601 QCString retType;
02602 QDataStream streamout(param,IO_WriteOnly);
02603 streamout<<(*it).uSource;
02604 streamout<<(*it).uDest;
02605 if ( kapp->dcopClient()->call( "kded",
02606 "mountwatcher", "createLink(KURL, KURL)", param,retType,data,false ) )
02607 {
02608 QDataStream streamin(data,IO_ReadOnly);
02609 streamin>>devicesOk;
02610 }
02611 if (devicesOk)
02612 {
02613 files.remove( it );
02614 m_processedFiles++;
02615
02616 copyNextFile();
02617 return;
02618 }
02619 }
02620
02621 if (!devicesOk)
02622 {
02623 QString path = (*it).uDest.path();
02624 kdDebug(7007) << "CopyJob::copyNextFile path=" << path << endl;
02625 QFile f( path );
02626 if ( f.open( IO_ReadWrite ) )
02627 {
02628 f.close();
02629 KSimpleConfig config( path );
02630 config.setDesktopGroup();
02631 config.writeEntry( QString::fromLatin1("URL"), (*it).uSource.url() );
02632 config.writeEntry( QString::fromLatin1("Type"), QString::fromLatin1("Link") );
02633 QString protocol = (*it).uSource.protocol();
02634 if ( protocol == QString::fromLatin1("ftp") )
02635 config.writeEntry( QString::fromLatin1("Icon"), QString::fromLatin1("ftp") );
02636 else if ( protocol == QString::fromLatin1("http") )
02637 config.writeEntry( QString::fromLatin1("Icon"), QString::fromLatin1("www") );
02638 else if ( protocol == QString::fromLatin1("info") )
02639 config.writeEntry( QString::fromLatin1("Icon"), QString::fromLatin1("info") );
02640 else if ( protocol == QString::fromLatin1("mailto") )
02641 config.writeEntry( QString::fromLatin1("Icon"), QString::fromLatin1("kmail") );
02642 else
02643 config.writeEntry( QString::fromLatin1("Icon"), QString::fromLatin1("unknown") );
02644 config.sync();
02645 files.remove( it );
02646 m_processedFiles++;
02647
02648 copyNextFile();
02649 return;
02650 }
02651 else
02652 {
02653 kdDebug(7007) << "CopyJob::copyNextFile ERR_CANNOT_OPEN_FOR_WRITING" << endl;
02654 m_error = ERR_CANNOT_OPEN_FOR_WRITING;
02655 m_errorText = (*it).uDest.path();
02656 emitResult();
02657 return;
02658 }
02659 }
02660 } else {
02661
02662 m_error = ERR_CANNOT_SYMLINK;
02663 m_errorText = (*it).uDest.prettyURL();
02664 emitResult();
02665 return;
02666 }
02667 }
02668 }
02669 else if ( !(*it).linkDest.isEmpty() &&
02670 ((*it).uSource.protocol() == (*it).uDest.protocol()) &&
02671 ((*it).uSource.host() == (*it).uDest.host()) &&
02672 ((*it).uSource.port() == (*it).uDest.port()) &&
02673 ((*it).uSource.user() == (*it).uDest.user()) &&
02674 ((*it).uSource.pass() == (*it).uDest.pass()))
02675
02676 {
02677 KIO::SimpleJob *newJob = KIO::symlink( (*it).linkDest, (*it).uDest, bOverwrite, false );
02678 Scheduler::scheduleJob(newJob);
02679 newjob = newJob;
02680 kdDebug(7007) << "CopyJob::copyNextFile : Linking target=" << (*it).linkDest << " link=" << (*it).uDest.prettyURL() << endl;
02681
02682 m_currentSrcURL=(*it).linkDest;
02683 m_currentDestURL=(*it).uDest;
02684
02685 m_bCurrentOperationIsLink = true;
02686
02687 } else if (m_mode == Move)
02688 {
02689 KIO::FileCopyJob * moveJob = KIO::file_move( (*it).uSource, (*it).uDest, (*it).permissions, bOverwrite, false, false );
02690 moveJob->setSourceSize( (*it).size );
02691 newjob = moveJob;
02692
02693
02694 m_currentSrcURL=(*it).uSource;
02695 m_currentDestURL=(*it).uDest;
02696
02697 }
02698 else
02699 {
02700
02701
02702
02703
02704 bool remoteSource = !(*it).uSource.isLocalFile() && ((*it).uSource.protocol() != "tar");
02705 int permissions = ( remoteSource && (*it).uDest.isLocalFile() ) ? -1 : (*it).permissions;
02706 KIO::FileCopyJob * copyJob = KIO::file_copy( (*it).uSource, (*it).uDest, permissions, bOverwrite, false, false );
02707 copyJob->setParentJob( this );
02708 copyJob->setSourceSize( (*it).size );
02709 newjob = copyJob;
02710
02711 m_currentSrcURL=(*it).uSource;
02712 m_currentDestURL=(*it).uDest;
02713 }
02714 addSubjob(newjob);
02715 connect( newjob, SIGNAL( processedSize( KIO::Job*, KIO::filesize_t ) ),
02716 this, SLOT( slotProcessedSize( KIO::Job*, KIO::filesize_t ) ) );
02717 connect( newjob, SIGNAL( totalSize( KIO::Job*, KIO::filesize_t ) ),
02718 this, SLOT( slotTotalSize( KIO::Job*, KIO::filesize_t ) ) );
02719 }
02720 else
02721 {
02722
02723
02724 deleteNextDir();
02725 }
02726 }
02727
02728 void CopyJob::deleteNextDir()
02729 {
02730 if ( m_mode == Move && !dirsToRemove.isEmpty() )
02731 {
02732 state = STATE_DELETING_DIRS;
02733
02734 KURL::List::Iterator it = dirsToRemove.fromLast();
02735 SimpleJob *job = KIO::rmdir( *it );
02736 Scheduler::scheduleJob(job);
02737 dirsToRemove.remove(it);
02738 addSubjob( job );
02739 }
02740 else
02741 {
02742
02743 if ( !m_bOnlyRenames )
02744 {
02745 KDirNotify_stub allDirNotify("*", "KDirNotify*");
02746 KURL url( m_dest );
02747 if ( destinationState != DEST_IS_DIR || m_asMethod )
02748 url.setPath( url.directory() );
02749
02750 allDirNotify.FilesAdded( url );
02751
02752 if ( m_mode == Move && !m_srcList.isEmpty() )
02753 allDirNotify.FilesRemoved( m_srcList );
02754 }
02755 if (m_reportTimer!=0)
02756 m_reportTimer->stop();
02757 emitResult();
02758 }
02759 }
02760
02761 void CopyJob::slotProcessedSize( KIO::Job*, KIO::filesize_t data_size )
02762 {
02763
02764 m_fileProcessedSize = data_size;
02765
02766 if ( m_processedSize + m_fileProcessedSize > m_totalSize )
02767 {
02768 m_totalSize = m_processedSize + m_fileProcessedSize;
02769
02770 emit totalSize( this, m_totalSize );
02771 }
02772
02773 emit processedSize( this, m_processedSize + m_fileProcessedSize );
02774 emitPercent( m_processedSize + m_fileProcessedSize, m_totalSize );
02775 }
02776
02777 void CopyJob::slotTotalSize( KIO::Job*, KIO::filesize_t size )
02778 {
02779
02780
02781
02782
02783 if ( m_bSingleFileCopy )
02784 {
02785
02786 m_totalSize = size;
02787 emit totalSize( this, size );
02788 }
02789 }
02790
02791 void CopyJob::slotResultDeletingDirs( Job * job )
02792 {
02793 if (job->error())
02794 {
02795
02796
02797
02798 }
02799 subjobs.remove( job );
02800 assert ( subjobs.isEmpty() );
02801 deleteNextDir();
02802 }
02803
02804 void CopyJob::slotResult( Job *job )
02805 {
02806
02807
02808
02809
02810
02811
02812 switch ( state ) {
02813 case STATE_STATING:
02814 slotResultStating( job );
02815 break;
02816 case STATE_RENAMING:
02817 {
02818 int err = job->error();
02819 subjobs.remove( job );
02820 assert ( subjobs.isEmpty() );
02821
02822 KURL dest = m_dest;
02823 if ( destinationState == DEST_IS_DIR && !m_asMethod )
02824 dest.addPath( m_currentSrcURL.fileName() );
02825 if ( err )
02826 {
02827
02828
02829
02830 if ( m_currentSrcURL.isLocalFile() &&
02831 m_currentSrcURL.url(-1).lower() == dest.url(-1).lower() &&
02832 ( job->error() == ERR_FILE_ALREADY_EXIST || job->error() == ERR_DIR_ALREADY_EXIST ) )
02833 {
02834 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;
02835 QCString _src( QFile::encodeName(m_currentSrcURL.path()) );
02836 QCString _dest( QFile::encodeName(dest.path()) );
02837 KTempFile tmpFile( m_currentSrcURL.directory() );
02838 QCString _tmp( QFile::encodeName(tmpFile.name()) );
02839 kdDebug() << "CopyJob::slotResult KTempFile status:" << tmpFile.status() << " using " << _tmp << " as intermediary" << endl;
02840 tmpFile.unlink();
02841 if ( ::rename( _src, _tmp ) == 0 )
02842 {
02843 if ( ::rename( _tmp, _dest ) == 0 )
02844 {
02845 kdDebug(7007) << "Success." << endl;
02846 err = 0;
02847 }
02848 else
02849 {
02850
02851 bool b = ::rename( QFile::encodeName(tmpFile.name()), _src );
02852 if (!b) {
02853 kdError(7007) << "Couldn't rename " << tmpFile.name() << " back to " << _src << " !" << endl;
02854
02855 Job::slotResult( job );
02856 return;
02857 }
02858 }
02859 }
02860 }
02861 }
02862 if ( err )
02863 {
02864 m_currentSrcURL=*m_currentStatSrc;
02865 m_currentDestURL=m_dest;
02866 kdDebug(7007) << "Couldn't rename, reverting to normal way, starting with stat" << endl;
02867 Job * job = KIO::stat( m_currentSrcURL, true, 2, false );
02868
02869 state = STATE_STATING;
02870 addSubjob(job);
02871 m_bOnlyRenames = false;
02872 }
02873 else
02874 {
02875 kdDebug(7007) << "Renaming succeeded, move on" << endl;
02876 emit copyingDone( this, *m_currentStatSrc, dest, true, true );
02877 ++m_currentStatSrc;
02878 statNextSrc();
02879 }
02880 }
02881 break;
02882 case STATE_LISTING:
02883
02884
02885 if (job->error())
02886 {
02887 Job::slotResult( job );
02888 return;
02889 }
02890
02891 subjobs.remove( job );
02892 assert ( subjobs.isEmpty() );
02893
02894 ++m_currentStatSrc;
02895 statNextSrc();
02896 break;
02897 case STATE_CREATING_DIRS:
02898 slotResultCreatingDirs( job );
02899 break;
02900 case STATE_CONFLICT_CREATING_DIRS:
02901 slotResultConflictCreatingDirs( job );
02902 break;
02903 case STATE_COPYING_FILES:
02904 slotResultCopyingFiles( job );
02905 break;
02906 case STATE_CONFLICT_COPYING_FILES:
02907 slotResultConflictCopyingFiles( job );
02908 break;
02909 case STATE_DELETING_DIRS:
02910 slotResultDeletingDirs( job );
02911 break;
02912 default:
02913 assert( 0 );
02914 }
02915 }
02916
02917 CopyJob *KIO::copy(const KURL& src, const KURL& dest, bool showProgressInfo )
02918 {
02919
02920 KURL::List srcList;
02921 srcList.append( src );
02922 return new CopyJob( srcList, dest, CopyJob::Copy, false, showProgressInfo );
02923 }
02924
02925 CopyJob *KIO::copyAs(const KURL& src, const KURL& dest, bool showProgressInfo )
02926 {
02927
02928 KURL::List srcList;
02929 srcList.append( src );
02930 return new CopyJob( srcList, dest, CopyJob::Copy, true, showProgressInfo );
02931 }
02932
02933 CopyJob *KIO::copy( const KURL::List& src, const KURL& dest, bool showProgressInfo )
02934 {
02935 return new CopyJob( src, dest, CopyJob::Copy, false, showProgressInfo );
02936 }
02937
02938 CopyJob *KIO::move(const KURL& src, const KURL& dest, bool showProgressInfo )
02939 {
02940 KURL::List srcList;
02941 srcList.append( src );
02942 return new CopyJob( srcList, dest, CopyJob::Move, false, showProgressInfo );
02943 }
02944
02945 CopyJob *KIO::moveAs(const KURL& src, const KURL& dest, bool showProgressInfo )
02946 {
02947 KURL::List srcList;
02948 srcList.append( src );
02949 return new CopyJob( srcList, dest, CopyJob::Move, true, showProgressInfo );
02950 }
02951
02952 CopyJob *KIO::move( const KURL::List& src, const KURL& dest, bool showProgressInfo )
02953 {
02954 return new CopyJob( src, dest, CopyJob::Move, false, showProgressInfo );
02955 }
02956
02957 CopyJob *KIO::link(const KURL& src, const KURL& destDir, bool showProgressInfo )
02958 {
02959 KURL::List srcList;
02960 srcList.append( src );
02961 return new CopyJob( srcList, destDir, CopyJob::Link, false, showProgressInfo );
02962 }
02963
02964 CopyJob *KIO::link(const KURL::List& srcList, const KURL& destDir, bool showProgressInfo )
02965 {
02966 return new CopyJob( srcList, destDir, CopyJob::Link, false, showProgressInfo );
02967 }
02968
02969 CopyJob *KIO::linkAs(const KURL& src, const KURL& destDir, bool showProgressInfo )
02970 {
02971 KURL::List srcList;
02972 srcList.append( src );
02973 return new CopyJob( srcList, destDir, CopyJob::Link, false, showProgressInfo );
02974 }
02975
02977
02978 DeleteJob::DeleteJob( const KURL::List& src, bool shred, bool showProgressInfo )
02979 : Job(showProgressInfo), m_totalSize( 0 ), m_processedSize( 0 ), m_fileProcessedSize( 0 ),
02980 m_processedFiles( 0 ), m_processedDirs( 0 ), m_totalFilesDirs( 0 ),
02981 m_srcList(src), m_currentStat(m_srcList.begin()), m_shred(shred), m_reportTimer(0)
02982 {
02983 if ( showProgressInfo ) {
02984
02985 connect( this, SIGNAL( totalFiles( KIO::Job*, unsigned long ) ),
02986 Observer::self(), SLOT( slotTotalFiles( KIO::Job*, unsigned long ) ) );
02987
02988 connect( this, SIGNAL( totalDirs( KIO::Job*, unsigned long ) ),
02989 Observer::self(), SLOT( slotTotalDirs( KIO::Job*, unsigned long ) ) );
02990
02991
02992
02993
02994
02995
02996
02997
02998
02999
03000
03001 m_reportTimer=new QTimer(this);
03002 connect(m_reportTimer,SIGNAL(timeout()),this,SLOT(slotReport()));
03003
03004 m_reportTimer->start(REPORT_TIMEOUT,false);
03005 }
03006
03007 QTimer::singleShot(0, this, SLOT(slotStart()));
03008 }
03009
03010 void DeleteJob::slotStart()
03011 {
03012 statNextSrc();
03013 }
03014
03015
03016
03017
03018 void DeleteJob::slotReport()
03019 {
03020 if (m_progressId==0)
03021 return;
03022
03023 Observer * observer = Observer::self();
03024
03025 emit deleting( this, m_currentURL );
03026 observer->slotDeleting(this,m_currentURL);
03027
03028 switch( state ) {
03029 case STATE_STATING:
03030 case STATE_LISTING:
03031 emit totalSize( this, m_totalSize );
03032 emit totalFiles( this, files.count() );
03033 emit totalDirs( this, dirs.count() );
03034 break;
03035 case STATE_DELETING_DIRS:
03036 emit processedDirs( this, m_processedDirs );
03037 observer->slotProcessedDirs(this,m_processedDirs);
03038 emitPercent( m_processedFiles + m_processedDirs, m_totalFilesDirs );
03039 break;
03040 case STATE_DELETING_FILES:
03041 observer->slotProcessedFiles(this,m_processedFiles);
03042 emit processedFiles( this, m_processedFiles );
03043 if (!m_shred)
03044 emitPercent( m_processedFiles, m_totalFilesDirs );
03045 break;
03046 }
03047 }
03048
03049
03050 void DeleteJob::slotEntries(KIO::Job* job, const UDSEntryList& list)
03051 {
03052 UDSEntryListConstIterator it = list.begin();
03053 UDSEntryListConstIterator end = list.end();
03054 for (; it != end; ++it)
03055 {
03056 UDSEntry::ConstIterator it2 = (*it).begin();
03057 bool bDir = false;
03058 bool bLink = false;
03059 QString relName;
03060 int atomsFound(0);
03061 for( ; it2 != (*it).end(); it2++ )
03062 {
03063 switch ((*it2).m_uds)
03064 {
03065 case UDS_FILE_TYPE:
03066 bDir = S_ISDIR((*it2).m_long);
03067 atomsFound++;
03068 break;
03069 case UDS_NAME:
03070 relName = ((*it2).m_str);
03071 atomsFound++;
03072 break;
03073 case UDS_LINK_DEST:
03074 bLink = !(*it2).m_str.isEmpty();
03075 atomsFound++;
03076 break;
03077 case UDS_SIZE:
03078 m_totalSize += (off_t)((*it2).m_long);
03079 atomsFound++;
03080 break;
03081 default:
03082 break;
03083 }
03084 if (atomsFound==4) break;
03085 }
03086 assert(!relName.isEmpty());
03087 if (relName != ".." && relName != ".")
03088 {
03089 KURL url = ((SimpleJob *)job)->url();
03090 url.addPath( relName );
03091
03092 if ( bLink )
03093 symlinks.append( url );
03094 else if ( bDir )
03095 dirs.append( url );
03096 else
03097 files.append( url );
03098 }
03099 }
03100 }
03101
03102
03103 void DeleteJob::statNextSrc()
03104 {
03105
03106 if ( m_currentStat != m_srcList.end() )
03107 {
03108 m_currentURL = (*m_currentStat);
03109
03110
03111 if (!KProtocolInfo::supportsDeleting(m_currentURL)) {
03112 KMessageBox::information( 0, buildErrorString(ERR_CANNOT_DELETE, m_currentURL.prettyURL()));
03113 ++m_currentStat;
03114 statNextSrc();
03115 return;
03116 }
03117
03118 state = STATE_STATING;
03119 KIO::SimpleJob * job = KIO::stat( m_currentURL, true, 1, false );
03120 Scheduler::scheduleJob(job);
03121
03122 addSubjob(job);
03123
03124
03125 } else
03126 {
03127 m_totalFilesDirs = files.count()+symlinks.count() + dirs.count();
03128 slotReport();
03129
03130
03131
03132
03133 for ( QStringList::Iterator it = m_parentDirs.begin() ; it != m_parentDirs.end() ; ++it )
03134 KDirWatch::self()->stopDirScan( *it );
03135 state = STATE_DELETING_FILES;
03136 deleteNextFile();
03137 }
03138 }
03139
03140 void DeleteJob::deleteNextFile()
03141 {
03142
03143 if ( !files.isEmpty() || !symlinks.isEmpty() )
03144 {
03145 SimpleJob *job;
03146 do {
03147
03148 KURL::List::Iterator it = files.begin();
03149 bool isLink = false;
03150 if ( it == files.end() )
03151 {
03152 it = symlinks.begin();
03153 isLink = true;
03154 }
03155
03156 if ( m_shred && (*it).isLocalFile() && !isLink )
03157 {
03158
03159 KIO_ARGS << int(3) << (*it).path();
03160 job = KIO::special(KURL("file:/"), packedArgs, false );
03161 Scheduler::scheduleJob(job);
03162 m_currentURL=(*it);
03163 connect( job, SIGNAL( processedSize( KIO::Job*, KIO::filesize_t ) ),
03164 this, SLOT( slotProcessedSize( KIO::Job*, KIO::filesize_t ) ) );
03165 } else
03166 {
03167
03168
03169 if ( (*it).isLocalFile() && unlink( QFile::encodeName((*it).path()) ) == 0 ) {
03170 job = 0;
03171 m_processedFiles++;
03172 if ( m_processedFiles % 300 == 0 ) {
03173 m_currentURL = *it;
03174 slotReport();
03175 }
03176 } else
03177 {
03178 job = KIO::file_delete( *it, false );
03179 Scheduler::scheduleJob(job);
03180 m_currentURL=(*it);
03181 }
03182 }
03183 if ( isLink )
03184 symlinks.remove(it);
03185 else
03186 files.remove(it);
03187 if ( job ) {
03188 addSubjob(job);
03189 return;
03190 }
03191
03192 } while (!job && (!files.isEmpty() || !symlinks.isEmpty()));
03193 }
03194 state = STATE_DELETING_DIRS;
03195 deleteNextDir();
03196 }
03197
03198 void DeleteJob::deleteNextDir()
03199 {
03200 if ( !dirs.isEmpty() )
03201 {
03202 do {
03203
03204 KURL::List::Iterator it = dirs.fromLast();
03205
03206 if ( (*it).isLocalFile() && ::rmdir( QFile::encodeName((*it).path()) ) == 0 ) {
03207
03208 m_processedDirs++;
03209 if ( m_processedDirs % 100 == 0 ) {
03210 m_currentURL = *it;
03211 slotReport();
03212 }
03213 } else
03214 {
03215 SimpleJob *job = KIO::rmdir( *it );
03216 Scheduler::scheduleJob(job);
03217 dirs.remove(it);
03218 addSubjob( job );
03219 return;
03220 }
03221 dirs.remove(it);
03222 } while ( !dirs.isEmpty() );
03223 }
03224
03225
03226 for ( QStringList::Iterator it = m_parentDirs.begin() ; it != m_parentDirs.end() ; ++it )
03227 KDirWatch::self()->restartDirScan( *it );
03228
03229
03230 if ( !m_srcList.isEmpty() )
03231 {
03232 KDirNotify_stub allDirNotify("*", "KDirNotify*");
03233 allDirNotify.FilesRemoved( m_srcList );
03234 }
03235 if (m_reportTimer!=0)
03236 m_reportTimer->stop();
03237 emitResult();
03238 }
03239
03240 void DeleteJob::slotProcessedSize( KIO::Job*, KIO::filesize_t data_size )
03241 {
03242
03243
03244
03245
03246 m_fileProcessedSize = data_size;
03247
03248
03249
03250 emit processedSize( this, m_processedSize + m_fileProcessedSize );
03251
03252
03253 unsigned long ipercent = m_percent;
03254
03255 if ( m_totalSize == 0 )
03256 m_percent = 100;
03257 else
03258 m_percent = (unsigned long)(( (float)(m_processedSize + m_fileProcessedSize) / (float)m_totalSize ) * 100.0);
03259
03260 if ( m_percent > ipercent )
03261 {
03262 emit percent( this, m_percent );
03263
03264 }
03265
03266 }
03267
03268 void DeleteJob::slotResult( Job *job )
03269 {
03270 switch ( state )
03271 {
03272 case STATE_STATING:
03273 {
03274
03275 if (job->error() )
03276 {
03277
03278 Job::slotResult( job );
03279 return;
03280 }
03281
03282
03283 UDSEntry entry = ((StatJob*)job)->statResult();
03284 bool bDir = false;
03285 bool bLink = false;
03286 KIO::filesize_t size = (KIO::filesize_t)-1;
03287 UDSEntry::ConstIterator it2 = entry.begin();
03288 int atomsFound(0);
03289 for( ; it2 != entry.end(); it2++ )
03290 {
03291 if ( ((*it2).m_uds) == UDS_FILE_TYPE )
03292 {
03293 bDir = S_ISDIR( (mode_t)(*it2).m_long );
03294 atomsFound++;
03295 }
03296 else if ( ((*it2).m_uds) == UDS_LINK_DEST )
03297 {
03298 bLink = !((*it2).m_str.isEmpty());
03299 atomsFound++;
03300 }
03301 else if ( ((*it2).m_uds) == UDS_SIZE )
03302 {
03303 size = (*it2).m_long;
03304 atomsFound++;
03305 };
03306 if (atomsFound==3) break;
03307 }
03308
03309 KURL url = ((SimpleJob*)job)->url();
03310
03311 subjobs.remove( job );
03312 assert( subjobs.isEmpty() );
03313
03314 if (bDir && !bLink)
03315 {
03316
03317 dirs.append( url );
03318 if ( url.isLocalFile() && !m_parentDirs.contains( url.path(-1) ) )
03319 m_parentDirs.append( url.path(-1) );
03320
03321
03322
03323 state = STATE_LISTING;
03324 ListJob *newjob = listRecursive( url, false );
03325 Scheduler::scheduleJob(newjob);
03326 connect(newjob, SIGNAL(entries( KIO::Job *,
03327 const KIO::UDSEntryList& )),
03328 SLOT( slotEntries( KIO::Job*,
03329 const KIO::UDSEntryList& )));
03330 addSubjob(newjob);
03331 }
03332 else
03333 {
03334 if ( bLink ) {
03335
03336 symlinks.append( url );
03337 } else {
03338
03339 files.append( url );
03340 }
03341 if ( url.isLocalFile() && !m_parentDirs.contains( url.directory(-1) ) )
03342 m_parentDirs.append( url.directory(-1) );
03343 ++m_currentStat;
03344 statNextSrc();
03345 }
03346 }
03347 break;
03348 case STATE_LISTING:
03349 if ( job->error() )
03350 {
03351
03352 }
03353 subjobs.remove( job );
03354 assert( subjobs.isEmpty() );
03355 ++m_currentStat;
03356 statNextSrc();
03357 break;
03358 case STATE_DELETING_FILES:
03359 if ( job->error() )
03360 {
03361 Job::slotResult( job );
03362 return;
03363 }
03364 subjobs.remove( job );
03365 assert( subjobs.isEmpty() );
03366 m_processedFiles++;
03367
03368 deleteNextFile();
03369 break;
03370 case STATE_DELETING_DIRS:
03371 if ( job->error() )
03372 {
03373 Job::slotResult( job );
03374 return;
03375 }
03376 subjobs.remove( job );
03377 assert( subjobs.isEmpty() );
03378 m_processedDirs++;
03379
03380
03381
03382
03383 deleteNextDir();
03384 break;
03385 default:
03386 assert(0);
03387 }
03388 }
03389
03390 DeleteJob *KIO::del( const KURL& src, bool shred, bool showProgressInfo )
03391 {
03392 KURL::List srcList;
03393 srcList.append( src );
03394 DeleteJob *job = new DeleteJob( srcList, shred, showProgressInfo );
03395 return job;
03396 }
03397
03398 DeleteJob *KIO::del( const KURL::List& src, bool shred, bool showProgressInfo )
03399 {
03400 DeleteJob *job = new DeleteJob( src, shred, showProgressInfo );
03401 return job;
03402 }
03403
03404 MultiGetJob::MultiGetJob(const KURL& url,
03405 bool showProgressInfo)
03406 : TransferJob(url, 0, QByteArray(), QByteArray(), showProgressInfo)
03407 {
03408 m_waitQueue.setAutoDelete(true);
03409 m_activeQueue.setAutoDelete(true);
03410 m_currentEntry = 0;
03411 }
03412
03413 void MultiGetJob::get(long id, const KURL &url, const MetaData &metaData)
03414 {
03415 GetRequest *entry = new GetRequest(id, url, metaData);
03416 entry->metaData["request-id"] = QString("%1").arg(id);
03417 m_waitQueue.append(entry);
03418 }
03419
03420 void MultiGetJob::flushQueue(QPtrList<GetRequest> &queue)
03421 {
03422 GetRequest *entry;
03423
03424
03425 for(entry = m_waitQueue.first(); entry; )
03426 {
03427 if ((m_url.protocol() == entry->url.protocol()) &&
03428 (m_url.host() == entry->url.host()) &&
03429 (m_url.port() == entry->url.port()) &&
03430 (m_url.user() == entry->url.user()))
03431 {
03432 m_waitQueue.take();
03433 queue.append(entry);
03434 entry = m_waitQueue.current();
03435 }
03436 else
03437 {
03438 entry = m_waitQueue.next();
03439 }
03440 }
03441
03442 KIO_ARGS << (Q_INT32) queue.count();
03443 for(entry = queue.first(); entry; entry = queue.next())
03444 {
03445 stream << entry->url << entry->metaData;
03446 }
03447 m_packedArgs = packedArgs;
03448 m_command = CMD_MULTI_GET;
03449 m_outgoingMetaData.clear();
03450 }
03451
03452 void MultiGetJob::start(Slave *slave)
03453 {
03454
03455 GetRequest *entry = m_waitQueue.take(0);
03456 m_activeQueue.append(entry);
03457
03458 m_url = entry->url;
03459
03460 if (!entry->url.protocol().startsWith("http"))
03461 {
03462
03463 KIO_ARGS << entry->url;
03464 m_packedArgs = packedArgs;
03465 m_outgoingMetaData = entry->metaData;
03466 m_command = CMD_GET;
03467 b_multiGetActive = false;
03468 }
03469 else
03470 {
03471 flushQueue(m_activeQueue);
03472 b_multiGetActive = true;
03473 }
03474
03475 TransferJob::start(slave);
03476 }
03477
03478 bool MultiGetJob::findCurrentEntry()
03479 {
03480 if (b_multiGetActive)
03481 {
03482 long id = m_incomingMetaData["request-id"].toLong();
03483 for(GetRequest *entry = m_activeQueue.first(); entry; entry = m_activeQueue.next())
03484 {
03485 if (entry->id == id)
03486 {
03487 m_currentEntry = entry;
03488 return true;
03489 }
03490 }
03491 m_currentEntry = 0;
03492 return false;
03493 }
03494 else
03495 {
03496 m_currentEntry = m_activeQueue.first();
03497 return (m_currentEntry != 0);
03498 }
03499 }
03500
03501 void MultiGetJob::slotRedirection( const KURL &url)
03502 {
03503 if (!findCurrentEntry()) return;
03504 if (!kapp->authorizeURLAction("redirect", m_url, url))
03505 {
03506 kdWarning(7007) << "MultiGetJob: Redirection from " << m_currentEntry->url.prettyURL() << " to " << url.prettyURL() << " REJECTED!" << endl;
03507 return;
03508 }
03509 m_redirectionURL = url;
03510 if (m_currentEntry->url.hasUser() && !url.hasUser() && (m_currentEntry->url.host().lower() == url.host().lower()))
03511 m_redirectionURL.setUser(m_currentEntry->url.user());
03512 get(m_currentEntry->id, m_redirectionURL, m_currentEntry->metaData);
03513 }
03514
03515
03516 void MultiGetJob::slotFinished()
03517 {
03518 if (!findCurrentEntry()) return;
03519 if (m_redirectionURL.isEmpty())
03520 {
03521
03522 emit result(m_currentEntry->id);
03523 }
03524 m_redirectionURL = KURL();
03525 m_error = 0;
03526 m_incomingMetaData.clear();
03527 m_activeQueue.removeRef(m_currentEntry);
03528 if (m_activeQueue.count() == 0)
03529 {
03530 if (m_waitQueue.count() == 0)
03531 {
03532
03533 TransferJob::slotFinished();
03534 }
03535 else
03536 {
03537
03538
03539
03540 GetRequest *entry = m_waitQueue.at(0);
03541 m_url = entry->url;
03542 slaveDone();
03543 Scheduler::doJob(this);
03544 }
03545 }
03546 }
03547
03548 void MultiGetJob::slotData( const QByteArray &_data)
03549 {
03550 if(!m_currentEntry) return;
03551 if(m_redirectionURL.isEmpty() || m_redirectionURL.isMalformed() || m_error)
03552 emit data(m_currentEntry->id, _data);
03553 }
03554
03555 void MultiGetJob::slotMimetype( const QString &_mimetype )
03556 {
03557 if (b_multiGetActive)
03558 {
03559 QPtrList<GetRequest> newQueue;
03560 flushQueue(newQueue);
03561 if (!newQueue.isEmpty())
03562 {
03563 while(!newQueue.isEmpty())
03564 m_activeQueue.append(newQueue.take(0));
03565 m_slave->connection()->send( m_command, m_packedArgs );
03566 }
03567 }
03568 if (!findCurrentEntry()) return;
03569 emit mimetype(m_currentEntry->id, _mimetype);
03570 }
03571
03572 MultiGetJob *KIO::multi_get(long id, const KURL &url, const MetaData &metaData)
03573 {
03574 MultiGetJob * job = new MultiGetJob( url, false );
03575 job->get(id, url, metaData);
03576 return job;
03577 }
03578
03579
03580 #ifdef CACHE_INFO
03581 CacheInfo::CacheInfo(const KURL &url)
03582 {
03583 m_url = url;
03584 }
03585
03586 QString CacheInfo::cachedFileName()
03587 {
03588 const QChar seperator = '_';
03589
03590 QString CEF = m_url.path();
03591
03592 int p = CEF.find('/');
03593
03594 while(p != -1)
03595 {
03596 CEF[p] = seperator;
03597 p = CEF.find('/', p);
03598 }
03599
03600 QString host = m_url.host().lower();
03601 CEF = host + CEF + '_';
03602
03603 QString dir = KProtocolManager::cacheDir();
03604 if (dir[dir.length()-1] != '/')
03605 dir += "/";
03606
03607 int l = m_url.host().length();
03608 for(int i = 0; i < l; i++)
03609 {
03610 if (host[i].isLetter() && (host[i] != 'w'))
03611 {
03612 dir += host[i];
03613 break;
03614 }
03615 }
03616 if (dir[dir.length()-1] == '/')
03617 dir += "0";
03618
03619 unsigned long hash = 0x00000000;
03620 QCString u = m_url.url().latin1();
03621 for(int i = u.length(); i--;)
03622 {
03623 hash = (hash * 12211 + u[i]) % 2147483563;
03624 }
03625
03626 QString hashString;
03627 hashString.sprintf("%08lx", hash);
03628
03629 CEF = CEF + hashString;
03630
03631 CEF = dir + "/" + CEF;
03632
03633 return CEF;
03634 }
03635
03636 QFile *CacheInfo::cachedFile()
03637 {
03638 const char *mode = (readWrite ? "r+" : "r");
03639
03640 FILE *fs = fopen( CEF.latin1(), mode);
03641 if (!fs)
03642 return 0;
03643
03644 char buffer[401];
03645 bool ok = true;
03646
03647
03648 if (ok && (!fgets(buffer, 400, fs)))
03649 ok = false;
03650 if (ok && (strcmp(buffer, CACHE_REVISION) != 0))
03651 ok = false;
03652
03653 time_t date;
03654 time_t currentDate = time(0);
03655
03656
03657 if (ok && (!fgets(buffer, 400, fs)))
03658 ok = false;
03659 if (ok)
03660 {
03661 int l = strlen(buffer);
03662 if (l>0)
03663 buffer[l-1] = 0;
03664 if (m_.url.url() != buffer)
03665 {
03666 ok = false;
03667 }
03668 }
03669
03670
03671 if (ok && (!fgets(buffer, 400, fs)))
03672 ok = false;
03673 if (ok)
03674 {
03675 date = (time_t) strtoul(buffer, 0, 10);
03676 if (m_maxCacheAge && (difftime(currentDate, date) > m_maxCacheAge))
03677 {
03678 m_bMustRevalidate = true;
03679 m_expireDate = currentDate;
03680 }
03681 }
03682
03683
03684 m_cacheExpireDateOffset = ftell(fs);
03685 if (ok && (!fgets(buffer, 400, fs)))
03686 ok = false;
03687 if (ok)
03688 {
03689 if (m_request.cache == CC_Verify)
03690 {
03691 date = (time_t) strtoul(buffer, 0, 10);
03692
03693 if (!date || difftime(currentDate, date) >= 0)
03694 m_bMustRevalidate = true;
03695 m_expireDate = date;
03696 }
03697 }
03698
03699
03700 if (ok && (!fgets(buffer, 400, fs)))
03701 ok = false;
03702 if (ok)
03703 {
03704 m_etag = QString(buffer).stripWhiteSpace();
03705 }
03706
03707
03708 if (ok && (!fgets(buffer, 400, fs)))
03709 ok = false;
03710 if (ok)
03711 {
03712 m_lastModified = QString(buffer).stripWhiteSpace();
03713 }
03714
03715 fclose(fs);
03716
03717 if (ok)
03718 return fs;
03719
03720 unlink( CEF.latin1());
03721 return 0;
03722
03723 }
03724
03725 void CacheInfo::flush()
03726 {
03727 cachedFile().remove();
03728 }
03729
03730 void CacheInfo::touch()
03731 {
03732
03733 }
03734 void CacheInfo::setExpireDate(int);
03735 void CacheInfo::setExpireTimeout(int);
03736
03737
03738 int CacheInfo::creationDate();
03739 int CacheInfo::expireDate();
03740 int CacheInfo::expireTimeout();
03741 #endif
03742
03743 void Job::virtual_hook( int, void* )
03744 { }
03745
03746 void SimpleJob::virtual_hook( int id, void* data )
03747 { KIO::Job::virtual_hook( id, data ); }
03748
03749 void StatJob::virtual_hook( int id, void* data )
03750 { SimpleJob::virtual_hook( id, data ); }
03751
03752 void TransferJob::virtual_hook( int id, void* data )
03753 { SimpleJob::virtual_hook( id, data ); }
03754
03755 void MultiGetJob::virtual_hook( int id, void* data )
03756 { TransferJob::virtual_hook( id, data ); }
03757
03758 void MimetypeJob::virtual_hook( int id, void* data )
03759 { TransferJob::virtual_hook( id, data ); }
03760
03761 void FileCopyJob::virtual_hook( int id, void* data )
03762 { Job::virtual_hook( id, data ); }
03763
03764 void ListJob::virtual_hook( int id, void* data )
03765 { SimpleJob::virtual_hook( id, data ); }
03766
03767 void CopyJob::virtual_hook( int id, void* data )
03768 { Job::virtual_hook( id, data ); }
03769
03770 void DeleteJob::virtual_hook( int id, void* data )
03771 { Job::virtual_hook( id, data ); }
03772
03773
03774 #include "jobclasses.moc"