00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "kmultipart.h"
00021
00022
00023 #include <kcomponentdata.h>
00024 #include <kmimetype.h>
00025 #include <klocale.h>
00026 #include <kjobuidelegate.h>
00027 #include <kio/job.h>
00028 #include <QtCore/QFile>
00029 #include <ktemporaryfile.h>
00030 #include <kmessagebox.h>
00031 #include <kparts/componentfactory.h>
00032 #include <kparts/genericfactory.h>
00033 #include <khtml_part.h>
00034 #include <unistd.h>
00035 #include <kxmlguifactory.h>
00036 #include <QtCore/QTimer>
00037 #include <kvbox.h>
00038
00039 typedef KParts::GenericFactory<KMultiPart> KMultiPartFactory;
00040 K_EXPORT_COMPONENT_FACTORY( libkmultipart , KMultiPartFactory )
00041
00042
00043
00044 class KLineParser
00045 {
00046 public:
00047 KLineParser() {
00048 m_lineComplete = false;
00049 }
00050 void addChar( char c, bool storeNewline ) {
00051 if ( !storeNewline && c == '\r' )
00052 return;
00053 Q_ASSERT( !m_lineComplete );
00054 if ( storeNewline || c != '\n' ) {
00055 int sz = m_currentLine.size();
00056 m_currentLine.resize( sz+1 );
00057 m_currentLine[sz] = c;
00058 }
00059 if ( c == '\n' )
00060 m_lineComplete = true;
00061 }
00062 bool isLineComplete() const {
00063 return m_lineComplete;
00064 }
00065 QByteArray currentLine() const {
00066 return m_currentLine;
00067 }
00068 void clearLine() {
00069 Q_ASSERT( m_lineComplete );
00070 reset();
00071 }
00072 void reset() {
00073 m_currentLine.resize( 0 );
00074 m_lineComplete = false;
00075 }
00076 private:
00077 QByteArray m_currentLine;
00078 bool m_lineComplete;
00079 };
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098 KMultiPart::KMultiPart( QWidget *parentWidget,
00099 QObject *parent, const QStringList& )
00100 : KParts::ReadOnlyPart( parent )
00101 {
00102 m_filter = 0L;
00103
00104 setComponentData( KMultiPartFactory::componentData() );
00105
00106 KVBox *box = new KVBox( parentWidget );
00107 setWidget( box );
00108
00109 m_extension = new KParts::BrowserExtension( this );
00110
00111 m_part = 0L;
00112 m_isHTMLPart = false;
00113 m_job = 0L;
00114 m_lineParser = new KLineParser;
00115 m_tempFile = 0L;
00116
00117 m_timer = new QTimer( this );
00118 connect( m_timer, SIGNAL( timeout() ), this, SLOT( slotProgressInfo() ) );
00119 }
00120
00121 KMultiPart::~KMultiPart()
00122 {
00123
00124
00125
00126
00127
00128
00129
00130 if ( m_part )
00131 delete static_cast<KParts::ReadOnlyPart *>( m_part );
00132 delete m_job;
00133 delete m_lineParser;
00134 if ( m_tempFile ) {
00135 m_tempFile->setAutoRemove( true );
00136 delete m_tempFile;
00137 }
00138 delete m_filter;
00139 m_filter = 0L;
00140 }
00141
00142
00143 void KMultiPart::startHeader()
00144 {
00145 m_bParsingHeader = true;
00146 m_bGotAnyHeader = false;
00147 m_gzip = false;
00148
00149 delete m_filter;
00150 m_filter = 0L;
00151 }
00152
00153
00154 bool KMultiPart::openUrl( const KUrl &url )
00155 {
00156 setUrl(url);
00157 m_lineParser->reset();
00158 startHeader();
00159
00160
00161
00162
00163
00164
00165 m_job = KIO::get( url,
00166 arguments().reload() ? KIO::Reload : KIO::NoReload,
00167 KIO::HideProgressInfo );
00168
00169 emit started( 0 );
00170
00171 connect( m_job, SIGNAL( result( KJob * ) ),
00172 this, SLOT( slotJobFinished( KJob * ) ) );
00173 connect( m_job, SIGNAL( data( KIO::Job *, const QByteArray & ) ),
00174 this, SLOT( slotData( KIO::Job *, const QByteArray & ) ) );
00175
00176 m_numberOfFrames = 0;
00177 m_numberOfFramesSkipped = 0;
00178 m_totalNumberOfFrames = 0;
00179 m_qtime.start();
00180 m_timer->start( 1000 );
00181
00182 return true;
00183 }
00184
00185
00186
00187
00188 void KMultiPart::slotData( KIO::Job *job, const QByteArray &data )
00189 {
00190 if (m_boundary.isNull())
00191 {
00192 QString tmp = job->queryMetaData("media-boundary");
00193 kDebug() << "Got Boundary from kio-http '" << tmp << "'";
00194 if ( !tmp.isEmpty() ) {
00195 if (tmp.startsWith(QLatin1String("--")))
00196 m_boundary = tmp.toLatin1();
00197 else
00198 m_boundary = QByteArray("--")+tmp.toLatin1();
00199 m_boundaryLength = m_boundary.length();
00200 }
00201 }
00202
00203 for ( int i = 0; i < data.size() ; ++i )
00204 {
00205
00206 m_lineParser->addChar( data[i], !m_bParsingHeader );
00207 if ( m_lineParser->isLineComplete() )
00208 {
00209 QByteArray lineData = m_lineParser->currentLine();
00210 #ifdef DEBUG_PARSING
00211 kDebug() << "lineData.size()=" << lineData.size();
00212 #endif
00213 QByteArray line( lineData.data(), lineData.size()+1 );
00214
00215
00216 int sz = line.size();
00217 if ( sz > 0 )
00218 line[sz-1] = '\0';
00219 #ifdef DEBUG_PARSING
00220 kDebug() << "[" << m_bParsingHeader << "] line='" << line << "'";
00221 #endif
00222 if ( m_bParsingHeader )
00223 {
00224 if ( !line.isEmpty() )
00225 m_bGotAnyHeader = true;
00226 if ( m_boundary.isNull() )
00227 {
00228 if ( !line.isEmpty() ) {
00229 #ifdef DEBUG_PARSING
00230 kDebug() << "Boundary is " << line;
00231 #endif
00232 m_boundary = line;
00233 m_boundaryLength = m_boundary.length();
00234 }
00235 }
00236 else if ( !qstrnicmp( line.data(), "Content-Encoding:", 17 ) )
00237 {
00238 QString encoding = QString::fromLatin1(line.data()+17).trimmed().toLower();
00239 if (encoding == "gzip" || encoding == "x-gzip") {
00240 m_gzip = true;
00241 } else {
00242 kDebug() << "FIXME: unhandled encoding type in KMultiPart: " << encoding;
00243 }
00244 }
00245
00246 else if ( !qstrnicmp( line.data(), "Content-Type:", 13 ) )
00247 {
00248 Q_ASSERT( m_nextMimeType.isNull() );
00249 m_nextMimeType = QString::fromLatin1( line.data() + 14 ).trimmed();
00250 int semicolon = m_nextMimeType.indexOf( ';' );
00251 if ( semicolon != -1 )
00252 m_nextMimeType = m_nextMimeType.left( semicolon );
00253 kDebug() << "m_nextMimeType=" << m_nextMimeType;
00254 }
00255
00256 else if ( line.isEmpty() && m_bGotAnyHeader )
00257 {
00258 m_bParsingHeader = false;
00259 #ifdef DEBUG_PARSING
00260 kDebug() << "end of headers";
00261 #endif
00262 startOfData();
00263 }
00264
00265 else if ( line == m_boundary )
00266 ;
00267 else if ( !line.isEmpty() )
00268 kDebug() << "Ignoring header " << line;
00269 } else {
00270 if ( !qstrncmp( line, m_boundary, m_boundaryLength ) )
00271 {
00272 #ifdef DEBUG_PARSING
00273 kDebug() << "boundary found!";
00274 kDebug() << "after it is " << line.data() + m_boundaryLength;
00275 #endif
00276
00277 if ( !qstrncmp( line.data() + m_boundaryLength, "--", 2 ) )
00278 {
00279 #ifdef DEBUG_PARSING
00280 kDebug() << "Completed!";
00281 #endif
00282 endOfData();
00283 emit completed();
00284 } else
00285 {
00286 char nextChar = *(line.data() + m_boundaryLength);
00287 #ifdef DEBUG_PARSING
00288 kDebug() << "KMultiPart::slotData nextChar='" << nextChar << "'";
00289 #endif
00290 if ( nextChar == '\n' || nextChar == '\r' ) {
00291 endOfData();
00292 startHeader();
00293 }
00294 else {
00295
00296 sendData( lineData );
00297 }
00298 }
00299 } else {
00300
00301 sendData( lineData );
00302 }
00303 }
00304 m_lineParser->clearLine();
00305 }
00306 }
00307 }
00308
00309 void KMultiPart::setPart( const QString& mimeType )
00310 {
00311 KXMLGUIFactory *guiFactory = factory();
00312 if ( guiFactory )
00313 guiFactory->removeClient( this );
00314 kDebug() << "KMultiPart::setPart " << mimeType;
00315 delete m_part;
00316
00317 m_part = KParts::ComponentFactory::createPartInstanceFromQuery<KParts::ReadOnlyPart>
00318 ( m_mimeType, QString(), widget(), this );
00319 if ( !m_part ) {
00320
00321 KMessageBox::error( widget(), i18n("No handler found for %1.", m_mimeType) );
00322 return;
00323 }
00324
00325 insertChildClient( m_part );
00326 m_part->widget()->show();
00327
00328 connect( m_part, SIGNAL( completed() ),
00329 this, SLOT( slotPartCompleted() ) );
00330
00331 m_isHTMLPart = ( mimeType == "text/html" );
00332 KParts::BrowserExtension* childExtension = KParts::BrowserExtension::childObject( m_part );
00333
00334 if ( childExtension )
00335 {
00336
00337
00338
00339
00340 connect( childExtension, SIGNAL( openURLNotify() ),
00341 m_extension, SIGNAL( openURLNotify() ) );
00342
00343 connect( childExtension, SIGNAL( openUrlRequestDelayed( const KUrl &, const KParts::OpenUrlArguments&, const KParts::BrowserArguments& ) ),
00344 m_extension, SIGNAL( openUrlRequest( const KUrl &, const KParts::OpenUrlArguments&, const KParts::BrowserArguments & ) ) );
00345
00346 connect( childExtension, SIGNAL( createNewWindow( const KUrl &, const KParts::OpenUrlArguments&, const KParts::BrowserArguments&, const KParts::WindowArgs &, KParts::ReadOnlyPart** ) ),
00347 m_extension, SIGNAL( createNewWindow( const KUrl &, const KParts::OpenUrlArguments&, const KParts::BrowserArguments& , const KParts::WindowArgs &, KParts::ReadOnlyPart**) ) );
00348
00349
00350 connect( childExtension, SIGNAL(popupMenu(QPoint,KFileItemList,KParts::OpenUrlArguments,KParts::BrowserArguments,KParts::BrowserExtension::PopupFlags,KParts::BrowserExtension::ActionGroupMap)),
00351 m_extension, SIGNAL(popupMenu(QPoint,KFileItemList,KParts::OpenUrlArguments,KParts::BrowserArguments,KParts::BrowserExtension::PopupFlags,KParts::BrowserExtension::ActionGroupMap)) );
00352 connect( childExtension, SIGNAL(popupMenu(QPoint,KUrl,mode_t,KParts::OpenUrlArguments,KParts::BrowserArguments,KParts::BrowserExtension::PopupFlags,KParts::BrowserExtension::ActionGroupMap)),
00353 m_extension, SIGNAL(popupMenu(QPoint,KUrl,mode_t,KParts::OpenUrlArguments,KParts::BrowserArguments,KParts::BrowserExtension::PopupFlags,KParts::BrowserExtension::ActionGroupMap)) );
00354
00355 if ( m_isHTMLPart )
00356 connect( childExtension, SIGNAL( infoMessage( const QString & ) ),
00357 m_extension, SIGNAL( infoMessage( const QString & ) ) );
00358
00359
00360 childExtension->setBrowserInterface( m_extension->browserInterface() );
00361
00362 connect( childExtension, SIGNAL( enableAction( const char *, bool ) ),
00363 m_extension, SIGNAL( enableAction( const char *, bool ) ) );
00364 connect( childExtension, SIGNAL( setLocationBarURL( const QString& ) ),
00365 m_extension, SIGNAL( setLocationBarURL( const QString& ) ) );
00366 connect( childExtension, SIGNAL( setIconURL( const KUrl& ) ),
00367 m_extension, SIGNAL( setIconURL( const KUrl& ) ) );
00368 connect( childExtension, SIGNAL( loadingProgress( int ) ),
00369 m_extension, SIGNAL( loadingProgress( int ) ) );
00370 if ( m_isHTMLPart )
00371 connect( childExtension, SIGNAL( speedProgress( int ) ),
00372 m_extension, SIGNAL( speedProgress( int ) ) );
00373 connect( childExtension, SIGNAL( selectionInfo( const KFileItemList& ) ),
00374 m_extension, SIGNAL( selectionInfo( const KFileItemList& ) ) );
00375 connect( childExtension, SIGNAL( selectionInfo( const QString& ) ),
00376 m_extension, SIGNAL( selectionInfo( const QString& ) ) );
00377 connect( childExtension, SIGNAL( selectionInfo( const KUrl::List& ) ),
00378 m_extension, SIGNAL( selectionInfo( const KUrl::List& ) ) );
00379 connect( childExtension, SIGNAL( mouseOverInfo( const KFileItem& ) ),
00380 m_extension, SIGNAL( mouseOverInfo( const KFileItem& ) ) );
00381 connect( childExtension, SIGNAL( moveTopLevelWidget( int, int ) ),
00382 m_extension, SIGNAL( moveTopLevelWidget( int, int ) ) );
00383 connect( childExtension, SIGNAL( resizeTopLevelWidget( int, int ) ),
00384 m_extension, SIGNAL( resizeTopLevelWidget( int, int ) ) );
00385 }
00386
00387 m_partIsLoading = false;
00388
00389
00390
00391 loadPlugins( this, m_part, m_part->componentData() );
00392
00393 if ( guiFactory )
00394 guiFactory->addClient( this );
00395 }
00396
00397 void KMultiPart::startOfData()
00398 {
00399 kDebug() << "KMultiPart::startOfData";
00400 Q_ASSERT( !m_nextMimeType.isNull() );
00401 if( m_nextMimeType.isNull() )
00402 return;
00403
00404 if ( m_gzip )
00405 {
00406 m_filter = new HTTPFilterGZip;
00407 connect( m_filter, SIGNAL( output( const QByteArray& ) ), this, SLOT( reallySendData( const QByteArray& ) ) );
00408 }
00409
00410 if ( m_mimeType != m_nextMimeType )
00411 {
00412
00413 m_mimeType = m_nextMimeType;
00414 setPart( m_mimeType );
00415 }
00416 Q_ASSERT( m_part );
00417
00418 m_part->setArguments( arguments() );
00419 KParts::BrowserExtension* childExtension = KParts::BrowserExtension::childObject( m_part );
00420 if ( childExtension )
00421 childExtension->setBrowserArguments( m_extension->browserArguments() );
00422
00423 m_nextMimeType.clear();
00424 if ( m_tempFile ) {
00425 m_tempFile->setAutoRemove( true );
00426 delete m_tempFile;
00427 m_tempFile = 0;
00428 }
00429 if ( m_isHTMLPart )
00430 {
00431 KHTMLPart* htmlPart = static_cast<KHTMLPart *>( static_cast<KParts::ReadOnlyPart *>( m_part ) );
00432 htmlPart->begin( url() );
00433 }
00434 else
00435 {
00436
00437 m_tempFile = new KTemporaryFile;
00438 m_tempFile->open();
00439 }
00440 }
00441
00442 void KMultiPart::sendData( const QByteArray& line )
00443 {
00444 if ( m_filter )
00445 {
00446 m_filter->slotInput( line );
00447 }
00448 else
00449 {
00450 reallySendData( line );
00451 }
00452 }
00453
00454 void KMultiPart::reallySendData( const QByteArray& line )
00455 {
00456 if ( m_isHTMLPart )
00457 {
00458 KHTMLPart* htmlPart = static_cast<KHTMLPart *>( static_cast<KParts::ReadOnlyPart *>( m_part ) );
00459 htmlPart->write( line.data(), line.size() );
00460 }
00461 else if ( m_tempFile )
00462 {
00463 m_tempFile->write( line.data(), line.size() );
00464 }
00465 }
00466
00467 void KMultiPart::endOfData()
00468 {
00469 Q_ASSERT( m_part );
00470 if ( m_isHTMLPart )
00471 {
00472 KHTMLPart* htmlPart = static_cast<KHTMLPart *>( static_cast<KParts::ReadOnlyPart *>( m_part ) );
00473 htmlPart->end();
00474 } else if ( m_tempFile )
00475 {
00476 m_tempFile->close();
00477 if ( m_partIsLoading )
00478 {
00479
00480
00481 kDebug() << "KMultiPart::endOfData part isn't ready, skipping frame";
00482 ++m_numberOfFramesSkipped;
00483 m_tempFile->setAutoRemove( true );
00484 }
00485 else
00486 {
00487 kDebug() << "KMultiPart::endOfData opening " << m_tempFile->fileName();
00488 KUrl url;
00489 url.setPath( m_tempFile->fileName() );
00490 m_partIsLoading = true;
00491 (void) m_part->openUrl( url );
00492 }
00493 delete m_tempFile;
00494 m_tempFile = 0L;
00495 }
00496 }
00497
00498 void KMultiPart::slotPartCompleted()
00499 {
00500 if ( !m_isHTMLPart )
00501 {
00502 Q_ASSERT( m_part );
00503
00504 Q_ASSERT( m_part->url().isLocalFile() );
00505 kDebug() << "slotPartCompleted deleting " << m_part->url().path();
00506 (void) unlink( QFile::encodeName( m_part->url().path() ) );
00507 m_partIsLoading = false;
00508 ++m_numberOfFrames;
00509
00510 }
00511 }
00512
00513 bool KMultiPart::closeUrl()
00514 {
00515 m_timer->stop();
00516 if ( m_part )
00517 return m_part->closeUrl();
00518 return true;
00519 }
00520
00521 void KMultiPart::guiActivateEvent( KParts::GUIActivateEvent * )
00522 {
00523
00524
00525
00526 }
00527
00528 void KMultiPart::slotJobFinished( KJob *job )
00529 {
00530 if ( job->error() )
00531 {
00532
00533 job->uiDelegate()->showErrorMessage();
00534 emit canceled( job->errorString() );
00535 }
00536 else
00537 {
00538
00539
00540
00541
00542
00543
00544 emit completed();
00545
00546
00547 }
00548 m_job = 0L;
00549 }
00550
00551 void KMultiPart::slotProgressInfo()
00552 {
00553 int time = m_qtime.elapsed();
00554 if ( !time ) return;
00555 if ( m_totalNumberOfFrames == m_numberOfFrames + m_numberOfFramesSkipped )
00556 return;
00557
00558 QString str( "%1 frames per second, %2 frames skipped per second" );
00559 str = str.arg( 1000.0 * (double)m_numberOfFrames / (double)time );
00560 str = str.arg( 1000.0 * (double)m_numberOfFramesSkipped / (double)time );
00561 m_totalNumberOfFrames = m_numberOfFrames + m_numberOfFramesSkipped;
00562
00563 emit m_extension->infoMessage( str );
00564 }
00565
00566 KAboutData* KMultiPart::createAboutData()
00567 {
00568 KAboutData* aboutData = new KAboutData( "kmultipart", 0, ki18n("KMultiPart"),
00569 "0.1",
00570 ki18n( "Embeddable component for multipart/mixed" ),
00571 KAboutData::License_GPL,
00572 ki18n("Copyright 2001, David Faure <email>david@mandrakesoft.com</email>"));
00573 return aboutData;
00574 }
00575
00576 #if 0
00577 KMultiPartBrowserExtension::KMultiPartBrowserExtension( KMultiPart *parent, const char *name )
00578 : KParts::BrowserExtension( parent, name )
00579 {
00580 m_imgPart = parent;
00581 }
00582
00583 int KMultiPartBrowserExtension::xOffset()
00584 {
00585 return m_imgPart->doc()->view()->contentsX();
00586 }
00587
00588 int KMultiPartBrowserExtension::yOffset()
00589 {
00590 return m_imgPart->doc()->view()->contentsY();
00591 }
00592
00593 void KMultiPartBrowserExtension::print()
00594 {
00595 static_cast<KHTMLPartBrowserExtension *>( m_imgPart->doc()->browserExtension() )->print();
00596 }
00597
00598 void KMultiPartBrowserExtension::reparseConfiguration()
00599 {
00600 static_cast<KHTMLPartBrowserExtension *>( m_imgPart->doc()->browserExtension() )->reparseConfiguration();
00601 m_imgPart->doc()->setAutoloadImages( true );
00602 }
00603 #endif
00604
00605 #include "kmultipart.moc"