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.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Apr 12 23:23:06 2005 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003