kparts Library API Documentation

browserrun.cpp

00001 /* This file is part of the KDE project
00002  *
00003  * Copyright (C) 2002 David Faure <faure@kde.org>
00004  * This library is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU Library General Public
00006  * License version 2, as published by the Free Software Foundation.
00007  *
00008  * This library is distributed in the hope that it will be useful,
00009  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00010  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00011  * Library General Public License for more details.
00012  *
00013  * You should have received a copy of the GNU Library General Public License
00014  * along with this library; see the file COPYING.LIB.  If not, write to
00015  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00016  * Boston, MA 02111-1307, USA.
00017  */
00018 
00019 #include "browserrun.h"
00020 #include <kmessagebox.h>
00021 #include <kfiledialog.h>
00022 #include <kio/job.h>
00023 #include <kio/scheduler.h>
00024 #include <klocale.h>
00025 #include <kprocess.h>
00026 #include <kstringhandler.h>
00027 #include <kuserprofile.h>
00028 #include <ktempfile.h>
00029 #include <kdebug.h>
00030 #include <kstandarddirs.h>
00031 #include <assert.h>
00032 
00033 using namespace KParts;
00034 
00035 class BrowserRun::BrowserRunPrivate
00036 {
00037 public:
00038   bool m_bHideErrorDialog;
00039 };
00040 
00041 BrowserRun::BrowserRun( const KURL& url, const KParts::URLArgs& args,
00042                         KParts::ReadOnlyPart *part, QWidget* window,
00043                         bool removeReferrer, bool trustedSource )
00044     : KRun( url, window, 0 /*mode*/, false /*is_local_file known*/, false /* no GUI */ ),
00045       m_args( args ), m_part( part ), m_window( window ),
00046       m_bRemoveReferrer( removeReferrer ), m_bTrustedSource( trustedSource )
00047 {
00048   d = new BrowserRunPrivate;
00049   d->m_bHideErrorDialog = false;
00050 }
00051 
00052 // BIC: merge with above ctor
00053 BrowserRun::BrowserRun( const KURL& url, const KParts::URLArgs& args,
00054                         KParts::ReadOnlyPart *part, QWidget* window,
00055                         bool removeReferrer, bool trustedSource, bool hideErrorDialog )
00056     : KRun( url, window, 0 /*mode*/, false /*is_local_file known*/, false /* no GUI */ ),
00057       m_args( args ), m_part( part ), m_window( window ),
00058       m_bRemoveReferrer( removeReferrer ), m_bTrustedSource( trustedSource )
00059 {
00060   d = new BrowserRunPrivate;
00061   d->m_bHideErrorDialog = hideErrorDialog;
00062 }
00063 
00064 BrowserRun::~BrowserRun()
00065 {
00066   delete d;
00067 }
00068 
00069 void BrowserRun::init()
00070 {
00071   if ( d->m_bHideErrorDialog )
00072   {
00073     // ### KRun doesn't call a virtual method when it finds out that the URL
00074     // is either malformed, or points to a non-existing local file...
00075     // So we need to reimplement some of the checks, to handle m_bHideErrorDialog
00076     if ( !m_strURL.isValid() ) {
00077         redirectToError( KIO::ERR_MALFORMED_URL, m_strURL.url() );
00078         return;
00079     }
00080     if ( !m_bIsLocalFile && !m_bFault && m_strURL.isLocalFile() )
00081       m_bIsLocalFile = true;
00082 
00083     if ( m_bIsLocalFile )  {
00084       struct stat buff;
00085       if ( stat( QFile::encodeName(m_strURL.path()), &buff ) == -1 )
00086       {
00087         kdDebug(1000) << "BrowserRun::init : " << m_strURL.prettyURL() << " doesn't exist." << endl;
00088         redirectToError( KIO::ERR_DOES_NOT_EXIST, m_strURL.path() );
00089         return;
00090       }
00091       m_mode = buff.st_mode; // while we're at it, save it for KRun::init() to use it
00092     }
00093   }
00094   KRun::init();
00095 }
00096 
00097 void BrowserRun::scanFile()
00098 {
00099   kdDebug(1000) << "BrowserRun::scanfile " << m_strURL.prettyURL() << endl;
00100 
00101   // Let's check for well-known extensions
00102   // Not when there is a query in the URL, in any case.
00103   // Optimization for http/https, findByURL doesn't trust extensions over http.
00104   if ( m_strURL.query().isEmpty() && !m_strURL.protocol().startsWith("http") )
00105   {
00106     KMimeType::Ptr mime = KMimeType::findByURL( m_strURL );
00107     assert( mime != 0L );
00108     if ( mime->name() != "application/octet-stream" || m_bIsLocalFile )
00109     {
00110       kdDebug(1000) << "Scanfile: MIME TYPE is " << mime->name() << endl;
00111       foundMimeType( mime->name() );
00112       return;
00113     }
00114   }
00115 
00116   if ( m_part )
00117   {
00118       QString proto = m_part->url().protocol().lower();
00119 
00120       if (proto == "https" || proto == "webdavs") {
00121           m_args.metaData().insert("main_frame_request", "TRUE" );
00122           m_args.metaData().insert("ssl_was_in_use", "TRUE" );
00123           m_args.metaData().insert("ssl_activate_warnings", "TRUE" );
00124       } else if (proto == "http" || proto == "webdav") {
00125           m_args.metaData().insert("ssl_activate_warnings", "TRUE" );
00126           m_args.metaData().insert("ssl_was_in_use", "FALSE" );
00127       }
00128 
00129       // Set the PropagateHttpHeader meta-data if it has not already been set...
00130       if (!m_args.metaData().contains("PropagateHttpHeader"))
00131           m_args.metaData().insert("PropagateHttpHeader", "TRUE");
00132   }
00133 
00134   KIO::TransferJob *job;
00135   if ( m_args.doPost() && m_strURL.protocol().startsWith("http"))
00136   {
00137       job = KIO::http_post( m_strURL, m_args.postData, false );
00138       job->addMetaData( "content-type", m_args.contentType() );
00139   }
00140   else
00141       job = KIO::get(m_strURL, m_args.reload, false);
00142 
00143   if ( m_bRemoveReferrer )
00144      m_args.metaData().remove("referrer");
00145 
00146   job->addMetaData( m_args.metaData() );
00147   job->setWindow( m_window );
00148   connect( job, SIGNAL( result( KIO::Job *)),
00149            this, SLOT( slotBrowserScanFinished(KIO::Job *)));
00150   connect( job, SIGNAL( mimetype( KIO::Job *, const QString &)),
00151            this, SLOT( slotBrowserMimetype(KIO::Job *, const QString &)));
00152   m_job = job;
00153 }
00154 
00155 void BrowserRun::slotBrowserScanFinished(KIO::Job *job)
00156 {
00157   kdDebug(1000) << "BrowserRun::slotBrowserScanFinished" << endl;
00158   if ( job->error() == KIO::ERR_IS_DIRECTORY )
00159   {
00160       // It is in fact a directory. This happens when HTTP redirects to FTP.
00161       // Due to the "protocol doesn't support listing" code in BrowserRun, we
00162       // assumed it was a file.
00163       kdDebug(1000) << "It is in fact a directory!" << endl;
00164       // Update our URL in case of a redirection
00165       m_strURL = static_cast<KIO::TransferJob *>(job)->url();
00166       m_job = 0;
00167       foundMimeType( "inode/directory" );
00168   }
00169   else
00170   {
00171       if ( job->error() )
00172           handleError( job );
00173       else
00174           KRun::slotScanFinished(job);
00175   }
00176 }
00177 
00178 void BrowserRun::slotBrowserMimetype( KIO::Job *_job, const QString &type )
00179 {
00180   Q_ASSERT( _job == m_job );
00181   KIO::TransferJob *job = static_cast<KIO::TransferJob *>(m_job);
00182   // Update our URL in case of a redirection
00183   //kdDebug(1000) << "old URL=" << m_strURL.url() << endl;
00184   //kdDebug(1000) << "new URL=" << job->url().url() << endl;
00185   m_strURL = job->url();
00186   kdDebug(1000) << "slotBrowserMimetype: found " << type << " for " << m_strURL.prettyURL() << endl;
00187 
00188   m_suggestedFilename = job->queryMetaData("content-disposition");
00189   //kdDebug(1000) << "m_suggestedFilename=" << m_suggestedFilename << endl;
00190 
00191   // Make a copy to avoid a dead reference
00192   QString _type = type;
00193   job->putOnHold();
00194   m_job = 0;
00195 
00196   foundMimeType( _type );
00197 }
00198 
00199 BrowserRun::NonEmbeddableResult BrowserRun::handleNonEmbeddable( const QString& _mimeType )
00200 {
00201     QString mimeType( _mimeType );
00202     Q_ASSERT( !m_bFinished ); // only come here if the mimetype couldn't be embedded
00203     // Support for saving remote files.
00204     if ( mimeType != "inode/directory" && // dirs can't be saved
00205          !m_strURL.isLocalFile() )
00206     {
00207         if ( isTextExecutable(mimeType) )
00208             mimeType = QString::fromLatin1("text/plain"); // view, don't execute
00209         kdDebug(1000) << "BrowserRun: ask for saving" << endl;
00210         KService::Ptr offer = KServiceTypeProfile::preferredService(mimeType, "Application");
00211         // ... -> ask whether to save
00212         KParts::BrowserRun::AskSaveResult res = askSave( m_strURL, offer, mimeType, m_suggestedFilename );
00213         if ( res == KParts::BrowserRun::Save ) {
00214             save( m_strURL, m_suggestedFilename );
00215             kdDebug(1000) << "BrowserRun::handleNonEmbeddable: Save: returning Handled" << endl;
00216             m_bFinished = true;
00217             return Handled;
00218         }
00219         else if ( res == KParts::BrowserRun::Cancel ) {
00220             // saving done or canceled
00221             kdDebug(1000) << "BrowserRun::handleNonEmbeddable: Cancel: returning Handled" << endl;
00222             m_bFinished = true;
00223             return Handled;
00224         }
00225         else // "Open" chosen (done by KRun::foundMimeType, called when returning NotHandled)
00226         {
00227             // If we were in a POST, we can't just pass a URL to an external application.
00228             // We must save the data to a tempfile first.
00229             if ( m_args.doPost() )
00230             {
00231                 kdDebug(1000) << "BrowserRun: request comes from a POST, can't pass a URL to another app, need to save" << endl;
00232                 m_sMimeType = mimeType;
00233                 QString extension;
00234                 QString fileName = m_suggestedFilename.isEmpty() ? m_strURL.fileName() : m_suggestedFilename;
00235                 int extensionPos = fileName.findRev( '.' );
00236                 if ( extensionPos != -1 )
00237                     extension = fileName.mid( extensionPos ); // keep the '.'
00238                 KTempFile tempFile( QString::null, extension );
00239                 KURL destURL;
00240                 destURL.setPath( tempFile.name() );
00241                 KIO::Job *job = KIO::file_copy( m_strURL, destURL, 0600, true /*overwrite*/, false /*no resume*/, true /*progress info*/ );
00242                 job->setWindow (m_window);
00243                 connect( job, SIGNAL( result( KIO::Job *)),
00244                          this, SLOT( slotCopyToTempFileResult(KIO::Job *)) );
00245                 return Delayed; // We'll continue after the job has finished
00246             }
00247         }
00248     }
00249 
00250     // Check if running is allowed
00251     if ( !m_bTrustedSource && // ... and untrusted source...
00252          !allowExecution( mimeType, m_strURL ) ) // ...and the user said no (for executables etc.)
00253     {
00254         m_bFinished = true;
00255         return Handled;
00256     }
00257 
00258     KIO::SimpleJob::removeOnHold(); // Kill any slave that was put on hold.
00259     return NotHandled;
00260 }
00261 
00262 //static
00263 bool BrowserRun::allowExecution( const QString &serviceType, const KURL &url )
00264 {
00265     if ( !isExecutable( serviceType ) )
00266       return true;
00267 
00268     if ( !url.isLocalFile() ) // Don't permit to execute remote files
00269         return false;
00270 
00271     return ( KMessageBox::warningContinueCancel( 0, i18n( "Do you really want to execute '%1'? " ).arg( url.prettyURL() ),
00272     i18n("Execute File?"), i18n("Execute") ) == KMessageBox::Continue );
00273 }
00274 
00275 static QString makeQuestion( const KURL& url, const QString& mimeType, const QString& suggestedFilename )
00276 {
00277     QString surl = KStringHandler::csqueeze( url.prettyURL() );
00278     KMimeType::Ptr mime = KMimeType::mimeType( mimeType );
00279     QString comment = mimeType;
00280 
00281     // Test if the mimeType is not recognize as octet-stream.
00282     // If so then keep mime-type as comment
00283     if (mime->name() != KMimeType::defaultMimeType()) {
00284         // The mime-type is known so display the comment instead of mime-type
00285         comment = mime->comment();
00286     }
00287     // The strange order in the i18n() calls below is due to the possibility
00288     // of surl containing a '%'
00289     if ( suggestedFilename.isEmpty() )
00290         return i18n("Open '%2'?\nType: %1").arg(comment).arg(surl);
00291     else
00292         return i18n("Open '%3'?\nName: %2\nType: %1").arg(comment).arg(suggestedFilename).arg(surl);
00293 }
00294 
00295 //static
00296 BrowserRun::AskSaveResult BrowserRun::askSave( const KURL & url, KService::Ptr offer, const QString& mimeType, const QString & suggestedFilename )
00297 {
00298     // SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC
00299     // NOTE: Keep this function in sync with kdebase/kcontrol/filetypes/filetypedetails.cpp
00300     //       FileTypeDetails::updateAskSave()
00301 
00302     QString question = makeQuestion( url, mimeType, suggestedFilename );
00303 
00304     // Text used for the open button
00305     QString openText = (offer && !offer->name().isEmpty())
00306                        ? i18n("&Open with '%1'").arg(offer->name())
00307                        : i18n("&Open With...");
00308 
00309     int choice = KMessageBox::questionYesNoCancel(
00310         0L, question, url.host(),
00311         KStdGuiItem::saveAs(), openText,
00312         QString::fromLatin1("askSave")+ mimeType ); // dontAskAgainName, KEEP IN SYNC!!!
00313 
00314     return choice == KMessageBox::Yes ? Save : ( choice == KMessageBox::No ? Open : Cancel );
00315     // SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC
00316 }
00317 
00318 //static
00319 BrowserRun::AskSaveResult BrowserRun::askEmbedOrSave( const KURL & url, const QString& mimeType, const QString & suggestedFilename, int /*flags*/ )
00320 {
00321     // SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC
00322     // NOTE: Keep this funcion in sync with kdebase/kcontrol/filetypes/filetypedetails.cpp
00323     //       FileTypeDetails::updateAskSave()
00324 
00325     KMimeType::Ptr mime = KMimeType::mimeType( mimeType );
00326     // Don't ask for:
00327     // - html (even new tabs would ask, due to about:blank!)
00328     // - dirs obviously (though not common over HTTP :),
00329     // - images (reasoning: no need to save, most of the time, because fast to see)
00330     // e.g. postscript is different, because takes longer to read, so
00331     // it's more likely that the user might want to save it.
00332     // - multipart/* ("server push", see kmultipart)
00333     // - other strange 'internal' mimetypes like print/manager...
00334     // KEEP IN SYNC!!!
00335     if ( mime->is( "text/html" ) ||
00336          mime->is( "text/xml" ) ||
00337          mime->is( "inode/directory" ) ||
00338          mimeType.startsWith( "image" ) ||
00339          mime->is( "multipart/x-mixed-replace" ) ||
00340          mime->is( "multipart/replace" ) ||
00341          mimeType.startsWith( "print" ) )
00342         return Open;
00343 
00344     QString question = makeQuestion( url, mimeType, suggestedFilename );
00345 
00346     int choice = KMessageBox::questionYesNoCancel(
00347         0L, question, url.host(),
00348         KStdGuiItem::saveAs(), KGuiItem( i18n( "&Open" ), "fileopen"),
00349         QString::fromLatin1("askEmbedOrSave")+ mimeType ); // dontAskAgainName, KEEP IN SYNC!!!
00350     return choice == KMessageBox::Yes ? Save : ( choice == KMessageBox::No ? Open : Cancel );
00351     // SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC SYNC
00352 }
00353 
00354 // Default implementation, overridden in KHTMLRun
00355 void BrowserRun::save( const KURL & url, const QString & suggestedFilename )
00356 {
00357     simpleSave( url, suggestedFilename, m_window );
00358 }
00359 
00360 // static
00361 void BrowserRun::simpleSave( const KURL & url, const QString & suggestedFilename )
00362 {
00363     simpleSave (url, suggestedFilename, 0);
00364 }
00365 
00366 void BrowserRun::simpleSave( const KURL & url, const QString & suggestedFilename,
00367                              QWidget* window )
00368 {
00369     // DownloadManager <-> konqueror integration
00370     // find if the integration is enabled
00371     // the empty key  means no integration
00372     // only use the downloadmanager for non-local urls
00373     if ( !url.isLocalFile() )
00374     {
00375         KConfig cfg("konquerorrc", false, false);
00376         cfg.setGroup("HTML Settings");
00377         QString downloadManger = cfg.readPathEntry("DownloadManager");
00378         if (!downloadManger.isEmpty())
00379         {
00380             // then find the download manager location
00381             kdDebug(1000) << "Using: "<<downloadManger <<" as Download Manager" <<endl;
00382             QString cmd=KStandardDirs::findExe(downloadManger);
00383             if (cmd.isEmpty())
00384             {
00385                 QString errMsg=i18n("The Download Manager (%1) could not be found in your $PATH ").arg(downloadManger);
00386                 QString errMsgEx= i18n("Try to reinstall it  \n\nThe integration with Konqueror will be disabled!");
00387                 KMessageBox::detailedSorry(0,errMsg,errMsgEx);
00388                 cfg.writePathEntry("DownloadManager",QString::null);
00389                 cfg.sync ();
00390             }
00391             else
00392             {
00393                 // ### suggestedFilename not taken into account. Fix this (and
00394                 // the duplicated code) with shiny new KDownload class for 3.2 (pfeiffer)
00395                 // Until the shiny new class comes about, send the suggestedFilename
00396                 // along with the actual URL to download. (DA)
00397                 cmd += " " + KProcess::quote(url.url()) + " " + KProcess::quote(suggestedFilename);
00398                 kdDebug(1000) << "Calling command  " << cmd << endl;
00399                 // slave is already on hold (slotBrowserMimetype())
00400                 KIO::Scheduler::publishSlaveOnHold();
00401                 KRun::runCommand(cmd);
00402                 return;
00403             }
00404         }
00405     }
00406 
00407     // no download manager available, let's do it ourself
00408     KFileDialog *dlg = new KFileDialog( QString::null, QString::null /*all files*/,
00409                                         window , "filedialog", true );
00410     dlg->setOperationMode( KFileDialog::Saving );
00411     dlg->setCaption(i18n("Save As"));
00412 
00413     dlg->setSelection( suggestedFilename.isEmpty() ? url.fileName() : suggestedFilename );
00414     if ( dlg->exec() )
00415     {
00416         KURL destURL( dlg->selectedURL() );
00417         if ( destURL.isValid() )
00418         {
00419             KIO::Job *job = KIO::copy( url, destURL );
00420             job->setWindow (window);
00421             job->setAutoErrorHandlingEnabled( true );
00422         }
00423     }
00424     delete dlg;
00425 }
00426 
00427 void BrowserRun::slotStatResult( KIO::Job *job )
00428 {
00429     if ( job->error() ) {
00430         kdDebug(1000) << "BrowserRun::slotStatResult : " << job->errorString() << endl;
00431         handleError( job );
00432     } else
00433         KRun::slotStatResult( job );
00434 }
00435 
00436 void BrowserRun::handleError( KIO::Job * job )
00437 {
00438     if ( !job ) { // Shouldn't happen, see docu.
00439         kdWarning(1000) << "BrowserRun::handleError called with job=0! hideErrorDialog=" << d->m_bHideErrorDialog << endl;
00440         return;
00441     }
00442 
00443     if (d->m_bHideErrorDialog && job->error() != KIO::ERR_NO_CONTENT)
00444     {
00445         redirectToError( job->error(), job->errorText() );
00446         return;
00447     }
00448 
00449     // Reuse code in KRun, to benefit from d->m_showingError etc.
00450     KRun::slotStatResult( job );
00451 }
00452 
00453 void BrowserRun::redirectToError( int error, const QString& errorText )
00454 {
00465     QString errText( errorText );
00466     errText.replace( '#', "%23" ); // a # in the error string would really muck things up...
00467     KURL newURL(QString("error:/?error=%1&errText=%2")
00468                 .arg( error ).arg( errText ), 106 );
00469     m_strURL.setPass( QString::null ); // don't put the password in the error URL
00470 
00471     KURL::List lst;
00472     lst << newURL << m_strURL;
00473     m_strURL = KURL::join( lst );
00474     //kdDebug(1202) << "BrowserRun::handleError m_strURL=" << m_strURL.prettyURL() << endl;
00475 
00476     m_job = 0;
00477     foundMimeType( "text/html" );
00478 }
00479 
00480 void BrowserRun::slotCopyToTempFileResult(KIO::Job *job)
00481 {
00482     if ( job->error() ) {
00483         job->showErrorDialog( m_window );
00484     } else {
00485         // Same as KRun::foundMimeType but with a different URL
00486         (void) (KRun::runURL( static_cast<KIO::FileCopyJob *>(job)->destURL(), m_sMimeType ));
00487     }
00488     m_bFault = true; // see above
00489     m_bFinished = true;
00490     m_timer.start( 0, true );
00491 }
00492 
00493 bool BrowserRun::isTextExecutable( const QString &serviceType )
00494 {
00495     return ( serviceType == "application/x-desktop" ||
00496              serviceType == "application/x-shellscript" );
00497 }
00498 
00499 bool BrowserRun::isExecutable( const QString &serviceType )
00500 {
00501     return KRun::isExecutable( serviceType );
00502 }
00503 
00504 bool BrowserRun::hideErrorDialog() const
00505 {
00506     return d->m_bHideErrorDialog;
00507 }
00508 
00509 #include "browserrun.moc"
KDE Logo
This file is part of the documentation for kparts Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jul 2 13:08:06 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003