kio Library API Documentation

krun.cpp

00001 /* This file is part of the KDE libraries
00002     Copyright (C) 2000 Torben Weis <weis@kde.org>
00003 
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 as published by the Free Software Foundation; either
00007     version 2 of the License, or (at your option) any later version.
00008 
00009     This library is distributed in the hope that it will be useful,
00010     but WITHOUT ANY WARRANTY; without even the implied warranty of
00011     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012     Library General Public License for more details.
00013 
00014     You should have received a copy of the GNU Library General Public License
00015     along with this library; see the file COPYING.LIB.  If not, write to
00016     the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00017     Boston, MA 02111-1307, USA.
00018 */
00019 
00020 #include <assert.h>
00021 #include <stdlib.h>
00022 #include <string.h>
00023 #include <unistd.h>
00024 
00025 #include "krun.h"
00026 #include "kuserprofile.h"
00027 #include "kmimetype.h"
00028 #include "kmimemagic.h"
00029 #include "kio/job.h"
00030 #include "kio/global.h"
00031 #include "kio/scheduler.h"
00032 #include "kfile/kopenwith.h"
00033 #include "kfile/krecentdocument.h"
00034 
00035 #include <kdatastream.h>
00036 #include <kmessageboxwrapper.h>
00037 #include <kurl.h>
00038 #include <kapplication.h>
00039 #include <kdebug.h>
00040 #include <klocale.h>
00041 #include <kprotocolinfo.h>
00042 #include <kstandarddirs.h>
00043 #include <kprocess.h>
00044 #include <dcopclient.h>
00045 #include <qfile.h>
00046 #include <qtextstream.h>
00047 #include <qdatetime.h>
00048 #include <qregexp.h>
00049 #include <kwin.h>
00050 #include <kdesktopfile.h>
00051 #include <kstartupinfo.h>
00052 #include <typeinfo>
00053 
00054 class KRun::KRunPrivate
00055 {
00056 public:
00057     KRunPrivate() { m_showingError = false; }
00058     bool m_showingError;
00059     QString m_preferredService;
00060 };
00061 
00062 pid_t KRun::runURL( const KURL& u, const QString& _mimetype )
00063 {
00064     return runURL( u, _mimetype, false );
00065 }
00066 
00067 // This is called by foundMimeType, since it knows the mimetype of the URL
00068 pid_t KRun::runURL( const KURL& u, const QString& _mimetype, bool tempFile )
00069 {
00070 
00071   if ( _mimetype == "inode/directory-locked" )
00072   {
00073     KMessageBoxWrapper::error( 0L,
00074             i18n("<qt>Unable to enter <b>%1</b>.\nYou do not have access rights to this location.</qt>").arg(u.htmlURL()) );
00075     return 0;
00076   }
00077   else if ( _mimetype == "application/x-desktop" )
00078   {
00079     if ( u.isLocalFile() )
00080       return KDEDesktopMimeType::run( u, true );
00081   }
00082   else if ( _mimetype == "application/x-executable"  ||
00083             _mimetype == "application/x-shellscript")
00084   {
00085     if (!kapp->authorize("shell_access"))
00086     {
00087       KMessageBoxWrapper::error( 0L,
00088             i18n("<qt>You do not have permission to run <b>%1</b>.</qt>").arg(u.htmlURL()) );
00089       return 0;
00090     }
00091     if ( u.isLocalFile() )
00092     {
00093       QString path = u.path();
00094       shellQuote( path );
00095       return (KRun::runCommand(path)); // just execute the url as a command
00096       // ## TODO implement deleting the file if tempFile==true
00097     }
00098   }
00099 
00100   KURL::List lst;
00101   lst.append( u );
00102 
00103   static const QString& app_str = KGlobal::staticQString("Application");
00104 
00105   KService::Ptr offer = KServiceTypeProfile::preferredService( _mimetype, app_str );
00106 
00107   if ( !offer )
00108   {
00109     // Open-with dialog
00110     // TODO : pass the mimetype as a parameter, to show it (comment field) in the dialog !
00111     // Hmm, in fact KOpenWithDlg::setServiceType already guesses the mimetype from the first URL of the list...
00112     return displayOpenWithDialog( lst, tempFile );
00113   }
00114 
00115   return KRun::run( *offer, lst, tempFile );
00116 }
00117 
00118 bool KRun::displayOpenWithDialog( const KURL::List& lst )
00119 {
00120     return displayOpenWithDialog( lst, false );
00121 }
00122 
00123 bool KRun::displayOpenWithDialog( const KURL::List& lst, bool tempFiles )
00124 {
00125     KOpenWithDlg l( lst, i18n("Open with:"), QString::null, 0L );
00126     if ( l.exec() )
00127     {
00128       KService::Ptr service = l.service();
00129       if ( !!service )
00130         return KRun::run( *service, lst, tempFiles );
00131 
00132       kdDebug(250) << "No service set, running " << l.text() << endl;
00133       return KRun::run( l.text(), lst ); // TODO handle tempFiles
00134     }
00135     return false;
00136 }
00137 
00138 void KRun::shellQuote( QString &_str )
00139 {
00140     // Credits to Walter, says Bernd G. :)
00141     if (_str.isEmpty()) // Don't create an explicit empty parameter
00142         return;
00143     QString res = "'";
00144     res += _str.replace(QRegExp("'"), "'\"'\"'");
00145     res += "'";
00146     _str = res;
00147 }
00148 
00149 static QStringList breakup(const QString &exec, bool *need_shell = 0)
00150 {
00151   QStringList result;
00152   // This small state machine is used to parse "exec" in order
00153   // to cut arguments at spaces, but also treat "..." and '...'
00154   // as a single argument even if they contain spaces. Those simple
00155   // and double quotes are also removed.
00156   enum { PARSE_ANY, PARSE_QUOTED, PARSE_DBLQUOTED } state = PARSE_ANY;
00157   QString arg;
00158   for ( uint pos = 0; pos < exec.length() ; ++pos )
00159   {
00160       QChar ch = exec[pos];
00161       switch (state) {
00162           case PARSE_ANY:
00163               if ( ch == '\'' && arg.isEmpty() )
00164                   state = PARSE_QUOTED;
00165               else if ( ch == '"' && arg.isEmpty() )
00166                   state = PARSE_DBLQUOTED;
00167               else if ( ch == ' ' ) 
00168               {
00169                   if (!arg.isEmpty())
00170                       result.append(arg);
00171                   arg = QString::null;
00172                   state = PARSE_ANY;
00173               }
00174               else if (( ch == ';' ) || (ch == '|') || (ch == '<'))
00175               {
00176                   if (!arg.isEmpty())
00177                       result.append(arg);
00178                   result.append(QString(ch));
00179                   arg = QString::null;
00180                   state = PARSE_ANY;
00181                   if (need_shell)
00182                      *need_shell = true;
00183               }
00184               else
00185                   arg += ch;
00186               break;
00187           case PARSE_QUOTED:
00188               if ( ch == '\'' )
00189               {
00190                   result.append(arg);
00191                   arg = QString::null;
00192                   state = PARSE_ANY;
00193               }
00194               else
00195                   arg += ch;
00196               break;
00197           case PARSE_DBLQUOTED:
00198               if ( ch == '"' )
00199               {
00200                   result.append(arg);
00201                   arg = QString::null;
00202                   state = PARSE_ANY;
00203               }
00204               else
00205                   arg += ch;
00206               break;
00207       }
00208   }
00209   if (!arg.isEmpty())
00210           result.append(arg);
00211   if (need_shell && !result.isEmpty())
00212   {
00213      if (result[0].contains('='))
00214         *need_shell = true;
00215   }
00216   return result;
00217 }
00218 
00219 static QString conditionalQuote(const QString &s, bool quote)
00220 {
00221    if (!quote) return s;
00222    QString r = s;
00223    KRun::shellQuote(r);
00224    return r;
00225 }
00226 
00227 static QString substitution(int option, const KURL &_url, bool quote)
00228 {
00229    if (option == 'u')
00230       return conditionalQuote(_url.isLocalFile() ? _url.path() : _url.url(), quote);
00231    if (option == 'd')
00232       return conditionalQuote(_url.directory(), quote);
00233    if (option == 'f')
00234       return conditionalQuote(_url.path(), quote);
00235    if (option == 'n')
00236       return conditionalQuote(_url.fileName(), quote);
00237    if (option == 'v')
00238    {
00239       if ( _url.isLocalFile() && QFile::exists( _url.path() ) )
00240       {
00241           KDesktopFile desktopFile(_url.path(), true);
00242           return conditionalQuote(desktopFile.readEntry( "Dev" ), quote);
00243       }
00244    }
00245     return QString::null;
00246 }
00247 
00248 static QStringList substitution(int option, const KService &_service, bool quote)
00249 {
00250    QStringList result;
00251    if (option == 'c')
00252       result << conditionalQuote(_service.name(), quote);
00253    else if (option == 'i')
00254       result << "-icon" << conditionalQuote(_service.icon(), quote);
00255    else if (option == 'm')
00256       result << "-miniicon" << conditionalQuote(_service.icon(), quote);
00257    else if (option == 'k')
00258       result << conditionalQuote(_service.desktopEntryPath(), quote);
00259 
00260    if (result.isEmpty())
00261       result << QString::null;
00262    return result;
00263 }
00264 
00265 static QStringList substitution(int option, const KURL::List &_urls, bool quote)
00266 {
00267    QStringList result;
00268    option = option - 'A' + 'a'; // To lower
00269    for(KURL::List::ConstIterator it = _urls.begin();
00270        it != _urls.end(); ++it)
00271    {
00272        result.append(substitution(option, *it, quote));
00273    }
00274    return result;
00275 }
00276 
00277 static void substitute(QStringList &_list, QStringList::Iterator &it, const KService &_service, const KURL::List &_urls, bool quote, bool service_only=false)
00278 {
00279   QString &arg = *it;
00280   if ((arg.length() == 2) && (arg[0] == '%'))
00281   {
00282      int option = arg[1].unicode();
00283      QStringList subs;
00284      switch(option)
00285      {
00286         case 'U':
00287         case 'F':
00288         case 'D':
00289         case 'N':
00290           if (service_only)
00291              return;
00292           subs = substitution(option, _urls, quote);
00293           break;
00294 
00295         case 'u':
00296         case 'f':
00297         case 'd':
00298         case 'n':
00299         case 'v':
00300           if (service_only)
00301              return;
00302           if (_urls.count())
00303              subs.append(substitution(option, _urls.first(), quote));
00304           break;
00305 
00306         case 'c':
00307         case 'i':
00308         case 'm':
00309         case 'k':
00310           subs = substitution(option, _service, quote);
00311           break;
00312 
00313         case '%':
00314           subs.append("%");
00315           break;
00316      }
00317 
00318      if (subs.count() == 1)
00319      {
00320         arg = subs[0];
00321      }
00322      else
00323      {
00324         for(QStringList::Iterator it_subs = subs.begin();
00325             it_subs != subs.end(); ++it_subs)
00326         {
00327            _list.insert(it, *it_subs);
00328         }
00329         QStringList::Iterator delete_it = it;
00330         --it;
00331         _list.remove(delete_it);
00332      }
00333      return;
00334   }
00335 
00336   QStringList args = breakup(arg);
00337   if (args.isEmpty())
00338   {
00339      arg = QString::null;
00340      return;
00341   }
00342   else if (args.count() != 1)
00343   {
00344      arg = QString::null;
00345      for(QStringList::Iterator it = args.begin();
00346          it != args.end(); ++it)
00347      {
00348         substitute(args, it, _service, _urls, true, service_only);
00349      }
00350      arg = QString::null;
00351      for(QStringList::Iterator it = args.begin();
00352          it != args.end(); ++it)
00353      {
00354         if (!arg.isEmpty())
00355            arg += " ";
00356         arg += *it;
00357      }
00358      if (quote)
00359         KRun::shellQuote(arg);
00360      return;
00361   }
00362   arg = args[0];
00363 
00364   bool need_quote = false;
00365   int l = arg.length();
00366   int p = 0;
00367   while (p < l-1)
00368   {
00369      if (arg[p] == '%')
00370      {
00371         need_quote = true;
00372         int option = arg[++p].unicode();
00373         if (service_only &&
00374             ((option == 'u') || (option == 'f') || (option == 'd') || (option == 'n')))
00375            continue;
00376 
00377         QString sub;
00378         QStringList subs;
00379         switch(option)
00380         {
00381           case 'u':
00382           case 'f':
00383           case 'd':
00384           case 'n':
00385           case 'v':
00386             sub = substitution(option, _urls.first(), false);
00387             break;
00388 
00389           case 'c':
00390           case 'k':
00391             subs = substitution(option, _service, false);
00392             if (!subs.isEmpty())
00393                sub = subs[0];
00394             break;
00395           case '%':
00396             sub = "%";
00397             break;
00398         }
00399 
00400         arg.replace(p-1, 2, sub);
00401         p += sub.length()-2;
00402         l = arg.length();
00403      }
00404      p++;
00405   }
00406   if (quote && need_quote)
00407   {
00408      KRun::shellQuote(arg);
00409   }
00410 }
00411 
00412 // BIC: merge with method below
00413 QStringList KRun::processDesktopExec(const KService &_service, const KURL::List& _urls, bool has_shell) {
00414     return processDesktopExec( _service, _urls, has_shell, false );
00415 }
00416 
00417 QStringList KRun::processDesktopExec(const KService &_service, const KURL::List& _urls, bool has_shell, bool tempFiles)
00418 {
00419   QString exec = _service.exec();
00420   QString user = _service.username();
00421   // Did the user forget to append something like '%f' ?
00422   // If so, then assume that '%f' is the right choice => the application
00423   // accepts only local files.
00424   if ( exec.find( "%f" ) == -1 && exec.find( "%u" ) == -1 && exec.find( "%n" ) == -1 &&
00425        exec.find( "%d" ) == -1 && exec.find( "%F" ) == -1 && exec.find( "%U" ) == -1 &&
00426        exec.find( "%N" ) == -1 && exec.find( "%D" ) == -1 && exec.find( "%v" ) == -1 )
00427     exec += " %f";
00428 
00429   bool terminal_su = false;
00430   bool terminal_sh = false;
00431   bool kdesu = false;
00432 
00433   if (_service.substituteUid() && !user.isEmpty())
00434   {
00435     if (_service.terminal())
00436       terminal_su = true;
00437     else
00438       kdesu = true;
00439   }
00440   else if (_service.terminal())
00441   {
00442     terminal_sh = true;
00443   }
00444 
00445   // Check if we need kfmexec.
00446   bool b_local_app = ( exec.find( "%u" ) == -1 && exec.find( "%U" ) == -1 );
00447   bool b_local_files = true;
00448   KURL::List::ConstIterator it = _urls.begin();
00449   for( ; it != _urls.end(); ++it )
00450     if ( !(*it).isLocalFile() )
00451       b_local_files = false;
00452 
00453   if ( (b_local_app && !b_local_files) || tempFiles )
00454   {
00455      // We need to run the app through kfmexec
00456      QStringList result = breakup(exec);
00457 
00458      // Substitute everything that isn't file-related.
00459      for(QStringList::Iterator it = result.begin();
00460          it != result.end(); ++it)
00461      {
00462          substitute(result, it, _service, _urls, true, true);
00463      }
00464      QString cmd = result.join(" ");
00465      if (has_shell)
00466         shellQuote(cmd);
00467      result.clear();
00468      result << "kfmexec" << cmd;
00469      KURL::List::ConstIterator it = _urls.begin();
00470      for( ; it != _urls.end(); ++it )
00471      {
00472         QString url = (*it).url();
00473         if (has_shell)
00474            shellQuote(url);
00475         result << url;
00476      }
00477      return result;
00478   }
00479 
00480   // Move args to result
00481   bool need_shell = false;
00482   QStringList result = breakup(exec, &need_shell);
00483 
00484   for(QStringList::Iterator it = result.begin();
00485       it != result.end(); ++it)
00486   {
00487       substitute(result, it, _service, _urls, has_shell || need_shell);
00488   }
00489 
00490   if (need_shell && !terminal_su && !kdesu &&
00491       (!has_shell || terminal_sh))
00492   {
00493      QString cmd = result.join(" ");
00494      result.clear();
00495      result << "/bin/sh" << "-c" << cmd;
00496   }
00497 
00498   KConfigGroupSaver gs(KGlobal::config(), "General");
00499   QString terminal = KGlobal::config()->readPathEntry("TerminalApplication");
00500   if( terminal.isEmpty() )
00501   {
00502       if( !KStandardDirs::findExe( "konsole" ).isEmpty() )
00503           terminal = "konsole";
00504       else
00505           terminal = "xvt";
00506   }
00507 
00508   if (terminal == "konsole")
00509     terminal += " -caption=%c %i %m";
00510 
00511   if (terminal_su)
00512   {
00513     QString cmd = result.join(" ");
00514     result = breakup(QString("%1 %2 -e su %3 -c").arg(terminal).arg(_service.terminalOptions()).arg(user));
00515     for(QStringList::Iterator it = result.begin();
00516         it != result.end(); ++it)
00517     {
00518         substitute(result, it, _service, _urls, has_shell);
00519     }
00520     result.append(cmd);
00521   }
00522   else if (terminal_sh)
00523   {
00524      QStringList cmd = result;
00525      result = breakup(QString("%1 %2 -e").arg(terminal).arg(_service.terminalOptions()));
00526      for(QStringList::Iterator it = result.begin();
00527          it != result.end(); ++it)
00528      {
00529          substitute(result, it, _service, _urls, has_shell);
00530      }
00531       result += cmd;
00532   }
00533   else if (kdesu)
00534   {
00535      result = breakup(QString("kdesu -u %1 --").arg(user))+result;
00536   }
00537 
00538   return result;
00539 }
00540 
00541 //static
00542 QString KRun::binaryName( const QString & execLine, bool removePath )
00543 {
00544   // Remove parameters and/or trailing spaces.
00545   QStringList args = breakup( execLine );
00546   QString _bin_name;
00547   do {
00548       if ( args.isEmpty() )
00549          return QString::null;
00550       _bin_name = args.first();
00551       args.pop_front();
00552   } while (_bin_name.contains('='));
00553   // Remove path if wanted
00554   return removePath ? _bin_name.mid(_bin_name.findRev('/') + 1) : _bin_name;
00555 }
00556 
00557 static pid_t runCommandInternal( KProcess* proc, const KService* service, const QString& binName,
00558     const QString &execName_P, const QString & iconName_P )
00559 {
00560   QString bin = KRun::binaryName( binName, false );
00561   QString execName = execName_P;
00562   QString iconName = iconName_P;
00563   if ( service && !KDesktopFile::isAuthorizedDesktopFile( service->desktopEntryPath() ))
00564   {
00565      KMessageBox::sorry(0, i18n("You are not authorized to execute this file."));
00566      return 0;
00567   }
00568 #ifdef Q_WS_X11 // Startup notification doesn't work with QT/E, service isn't needed without Startup notification
00569   bool startup_notify = false;
00570   QCString wmclass;
00571   KStartupInfoId id;
00572   if( service && service->property( "X-KDE-StartupNotify" ).isValid())
00573   {
00574       startup_notify = service->property( "X-KDE-StartupNotify" ).toBool();
00575       wmclass = service->property( "X-KDE-WMClass" ).toString().latin1();
00576   }
00577   else // non-compliant app ( .desktop file )
00578   {
00579       if( service && service->type() == "Application" )
00580       {
00581           startup_notify = true; // doesn't have .desktop entries needed
00582           wmclass = "0";         // start as non-compliant
00583       }
00584   }
00585   if( startup_notify )
00586   {
00587       id.initId();
00588       id.setupStartupEnv();
00589       if( execName.isEmpty())
00590           execName = service->name();
00591       if( iconName.isEmpty())
00592           iconName = service->icon();
00593       KStartupInfoData data;
00594       data.setHostname();
00595       data.setBin( KRun::binaryName( binName, true ));
00596       data.setName( execName );
00597       data.setIcon( iconName );
00598       if( !wmclass.isEmpty())
00599           data.setWMClass( wmclass );
00600       data.setDesktop( KWin::currentDesktop());
00601       KStartupInfo::sendStartup( id, data );
00602   }
00603   pid_t pid = KProcessRunner::run( proc, KRun::binaryName( binName, true ), id );
00604   if( startup_notify )
00605   {
00606       KStartupInfoData data;
00607       if ( pid ) // successfully started
00608       {
00609           data.addPid( pid );
00610           KStartupInfo::sendChange( id, data );
00611       } else // not started (e.g. executable not found)
00612       {
00613           data.setHostname();
00614           KStartupInfo::sendFinish( id, data );
00615       }
00616       KStartupInfo::resetStartupEnv();
00617   }
00618   return pid;
00619 #else
00620   return KProcessRunner::run( proc, KRun::binaryName( binName, true ) );
00621 #endif
00622 }
00623 
00624 static pid_t runTempService( const KService& _service, const KURL::List& _urls, bool tempFiles )
00625 {
00626   if (!_urls.isEmpty()) {
00627     kdDebug(7010) << "runTempService: first url " << _urls.first().url() << endl;
00628   }
00629 
00630   QStringList args;
00631   if ((_urls.count() > 1) && !_service.allowMultipleFiles())
00632   {
00633       // We need to launch the application N times. That sucks.
00634       // We ignore the result for application 2 to N.
00635       // For the first file we launch the application in the
00636       // usual way. The reported result is based on this
00637       // application.
00638       KURL::List::ConstIterator it = _urls.begin();
00639       for(++it; it != _urls.end(); ++it)
00640       {
00641          KURL::List singleUrl;
00642          singleUrl.append(*it);
00643          runTempService( _service, singleUrl, tempFiles );
00644       }
00645       KURL::List singleUrl;
00646       singleUrl.append(_urls.first());
00647       args = KRun::processDesktopExec(_service, singleUrl, false, tempFiles);
00648   }
00649   else
00650   {
00651       args = KRun::processDesktopExec(_service, _urls, false, tempFiles);
00652   }
00653   //kdDebug(7010) << "runTempService: KProcess args=" << args << endl;
00654 
00655   KProcess * proc = new KProcess;
00656   for(QStringList::Iterator it = args.begin();
00657       it != args.end(); ++it)
00658   {
00659      QString arg = *it;
00660      *proc << arg;
00661   }
00662   return runCommandInternal( proc, &_service, _service.exec(), _service.name(), _service.icon() );
00663 }
00664 
00665 // BIC merge with method below
00666 pid_t KRun::run( const KService& _service, const KURL::List& _urls )
00667 {
00668     return run( _service, _urls, false );
00669 }
00670 
00671 pid_t KRun::run( const KService& _service, const KURL::List& _urls, bool tempFiles )
00672 {
00673   if (!_service.desktopEntryPath().isEmpty() &&
00674       !KDesktopFile::isAuthorizedDesktopFile( _service.desktopEntryPath()))
00675   {
00676      KMessageBox::sorry(0, i18n("You are not authorized to execute this service."));
00677      return 0;
00678   }
00679 
00680   if ( !tempFiles )
00681   {
00682   // Remember we opened those urls, for the "recent documents" menu in kicker
00683   KURL::List::ConstIterator it = _urls.begin();
00684   for(; it != _urls.end(); ++it) {
00685      //kdDebug(7010) << "KRecentDocument::adding " << (*it).url() << endl;
00686      KRecentDocument::add( *it, _service.desktopEntryName() );
00687   }
00688   }
00689 
00690   if ( tempFiles || _service.desktopEntryPath().isEmpty())
00691   {
00692      return runTempService(_service, _urls, tempFiles);
00693   }
00694 
00695   kdDebug(7010) << "KRun::run " << _service.desktopEntryPath() << endl;
00696 
00697   if (!_urls.isEmpty()) {
00698     kdDebug(7010) << "First url " << _urls.first().url() << endl;
00699   }
00700 
00701   QString error;
00702   int pid = 0;
00703 
00704   int i = KApplication::startServiceByDesktopPath(
00705         _service.desktopEntryPath(), _urls.toStringList(), &error, 0L, &pid
00706         );
00707 
00708   if (i != 0)
00709   {
00710      kdDebug(7010) << error << endl;
00711      KMessageBox::sorry( 0L, error );
00712      return 0;
00713   }
00714 
00715   kdDebug(7010) << "startServiceByDesktopPath worked fine" << endl;
00716   return (pid_t) pid;
00717 }
00718 
00719 
00720 pid_t KRun::run( const QString& _exec, const KURL::List& _urls, const QString& _name,
00721                 const QString& _icon, const QString&, const QString&)
00722 {
00723   KService::Ptr service = new KService(_name, _exec, _icon);
00724 
00725   return run(*service, _urls);
00726 }
00727 
00728 pid_t KRun::runCommand( QString cmd )
00729 {
00730   return KRun::runCommand( cmd, QString::null, QString::null );
00731 }
00732 
00733 pid_t KRun::runCommand( const QString& cmd, const QString &execName, const QString & iconName )
00734 {
00735   kdDebug(7010) << "runCommand " << cmd << "," << execName << endl;
00736   KProcess * proc = new KProcess;
00737   proc->setUseShell(true);
00738   *proc << cmd;
00739   QString bin = binaryName( cmd, false );
00740   KService::Ptr service = KService::serviceByDesktopName( bin );
00741   return runCommandInternal( proc, service.data(), bin, execName, iconName );
00742 }
00743 
00744 KRun::KRun( const KURL& _url, mode_t _mode, bool _is_local_file, bool _showProgressInfo )
00745   : m_timer(0,"KRun::timer")
00746 {
00747   m_bFault = false;
00748   m_bAutoDelete = true;
00749   m_bProgressInfo = _showProgressInfo;
00750   m_bFinished = false;
00751   m_job = 0L;
00752   m_strURL = _url;
00753   m_bScanFile = false;
00754   m_bIsDirectory = false;
00755   m_bIsLocalFile = _is_local_file;
00756   m_mode = _mode;
00757   d = new KRunPrivate;
00758 
00759   // Start the timer. This means we will return to the event
00760   // loop and do initialization afterwards.
00761   // Reason: We must complete the constructor before we do anything else.
00762   m_bInit = true;
00763   connect( &m_timer, SIGNAL( timeout() ), this, SLOT( slotTimeout() ) );
00764   m_timer.start( 0, true );
00765   kdDebug(7010) << " new KRun " << this << " " << _url.prettyURL() << " timer=" << &m_timer << endl;
00766 
00767   kapp->ref();
00768 }
00769 
00770 void KRun::init()
00771 {
00772   kdDebug(7010) << "INIT called" << endl;
00773   if ( m_strURL.isMalformed() )
00774   {
00775     d->m_showingError = true;
00776     KMessageBoxWrapper::error( 0L, i18n( "Malformed URL\n%1" ).arg( m_strURL.url() ) );
00777     d->m_showingError = false;
00778     m_bFault = true;
00779     m_bFinished = true;
00780     m_timer.start( 0, true );
00781     return;
00782   }
00783   if ( !kapp->authorizeURLAction( "open", KURL(), m_strURL))
00784   {
00785     QString msg = KIO::buildErrorString(KIO::ERR_ACCESS_DENIED, m_strURL.prettyURL());
00786     d->m_showingError = true;
00787     KMessageBoxWrapper::error( 0L, msg );
00788     d->m_showingError = false;
00789     m_bFault = true;
00790     m_bFinished = true;
00791     m_timer.start( 0, true );
00792     return;
00793   }
00794 
00795   if ( !m_bIsLocalFile && m_strURL.isLocalFile() )
00796 
00797     m_bIsLocalFile = true;
00798 
00799   if ( m_bIsLocalFile )
00800   {
00801     if ( m_mode == 0 )
00802     {
00803       struct stat buff;
00804       if ( stat( QFile::encodeName(m_strURL.path()), &buff ) == -1 )
00805       {
00806         d->m_showingError = true;
00807         KMessageBoxWrapper::error( 0L, i18n( "<qt>Unable to run the command specified. The file or directory <b>%1</b> does not exist.</qt>" ).arg( m_strURL.htmlURL() ) );
00808         d->m_showingError = false;
00809         m_bFault = true;
00810         m_bFinished = true;
00811         m_timer.start( 0, true );
00812         return;
00813       }
00814       m_mode = buff.st_mode;
00815     }
00816 
00817     KMimeType::Ptr mime = KMimeType::findByURL( m_strURL, m_mode, m_bIsLocalFile );
00818     assert( mime != 0L );
00819     kdDebug(7010) << "MIME TYPE is " << mime->name() << endl;
00820     foundMimeType( mime->name() );
00821     return;
00822   }
00823   else if ( KProtocolInfo::isHelperProtocol( m_strURL ) ) {
00824     kdDebug(7010) << "Helper protocol" << endl;
00825 
00826     KURL::List urls;
00827     urls.append( m_strURL );
00828     QString exec = KProtocolInfo::exec( m_strURL.protocol() );
00829     run( exec, urls );
00830 
00831     m_bFinished = true;
00832     // will emit the error and autodelete this
00833     m_timer.start( 0, true );
00834     return;
00835   }
00836 
00837   // Did we already get the information that it is a directory ?
00838   if ( S_ISDIR( m_mode ) )
00839   {
00840     foundMimeType( "inode/directory" );
00841     return;
00842   }
00843 
00844   // Let's see whether it is a directory
00845 
00846   if ( !KProtocolInfo::supportsListing( m_strURL ) )
00847   {
00848     //kdDebug(7010) << "Protocol has no support for listing" << endl;
00849     // No support for listing => it can't be a directory (example: http)
00850     scanFile();
00851     return;
00852   }
00853 
00854   kdDebug(7010) << "Testing directory (stating)" << endl;
00855 
00856   // It may be a directory or a file, let's stat
00857   KIO::StatJob *job = KIO::stat( m_strURL, true, 0 /* no details */, m_bProgressInfo );
00858   connect( job, SIGNAL( result( KIO::Job * ) ),
00859            this, SLOT( slotStatResult( KIO::Job * ) ) );
00860   m_job = job;
00861   kdDebug() << " Job " << job << " is about stating " << m_strURL.url() << endl;
00862 }
00863 
00864 KRun::~KRun()
00865 {
00866   kdDebug(7010) << "KRun::~KRun() " << this << endl;
00867   m_timer.stop();
00868   killJob();
00869   kapp->deref();
00870   kdDebug(7010) << "KRun::~KRun() done " << this << endl;
00871   delete d;
00872 }
00873 
00874 void KRun::scanFile()
00875 {
00876   kdDebug(7010) << "###### KRun::scanFile " << m_strURL.url() << endl;
00877   // First, let's check for well-known extensions
00878   // Not when there is a query in the URL, in any case.
00879   if ( m_strURL.query().isEmpty() )
00880   {
00881     KMimeType::Ptr mime = KMimeType::findByURL( m_strURL );
00882     assert( mime != 0L );
00883     if ( mime->name() != "application/octet-stream" || m_bIsLocalFile )
00884     {
00885       kdDebug(7010) << "Scanfile: MIME TYPE is " << mime->name() << endl;
00886       foundMimeType( mime->name() );
00887       return;
00888     }
00889   }
00890 
00891   // No mimetype found, and the URL is not local  (or fast mode not allowed).
00892   // We need to apply the 'KIO' method, i.e. either asking the server or
00893   // getting some data out of the file, to know what mimetype it is.
00894 
00895   if ( !KProtocolInfo::supportsReading( m_strURL ) )
00896   {
00897     kdError(7010) << "#### NO SUPPORT FOR READING!" << endl;
00898     m_bFault = true;
00899     m_bFinished = true;
00900     m_timer.start( 0, true );
00901     return;
00902   }
00903   kdDebug(7010) << this << " Scanning file " << m_strURL.url() << endl;
00904 
00905   KIO::TransferJob *job = KIO::get( m_strURL, false /*reload*/, m_bProgressInfo );
00906   connect(job, SIGNAL( result(KIO::Job *)),
00907           this, SLOT( slotScanFinished(KIO::Job *)));
00908   connect(job, SIGNAL( mimetype(KIO::Job *, const QString &)),
00909           this, SLOT( slotScanMimeType(KIO::Job *, const QString &)));
00910   m_job = job;
00911   kdDebug() << " Job " << job << " is about getting from " << m_strURL.url() << endl;
00912 }
00913 
00914 void KRun::slotTimeout()
00915 {
00916   kdDebug(7010) << this << " slotTimeout called" << endl;
00917   if ( m_bInit )
00918   {
00919     m_bInit = false;
00920     init();
00921     return;
00922   }
00923 
00924   if ( m_bFault ){
00925       emit error();
00926   }
00927   if ( m_bFinished ){
00928       emit finished();
00929   }
00930 
00931   if ( m_bScanFile )
00932   {
00933     m_bScanFile = false;
00934     scanFile();
00935     return;
00936   }
00937   else if ( m_bIsDirectory )
00938   {
00939     m_bIsDirectory = false;
00940     foundMimeType( "inode/directory" );
00941     return;
00942   }
00943 
00944   if ( m_bAutoDelete )
00945   {
00946     delete this;
00947     return;
00948   }
00949 }
00950 
00951 void KRun::slotStatResult( KIO::Job * job )
00952 {
00953   m_job = 0L;
00954   if (job->error())
00955   {
00956     d->m_showingError = true;
00957     kdError(7010) << this << " ERROR " << job->error() << " " << job->errorString() << endl;
00958     job->showErrorDialog();
00959     //kdDebug(7010) << this << " KRun returning from showErrorDialog, starting timer to delete us" << endl;
00960     d->m_showingError = false;
00961 
00962     m_bFault = true;
00963     m_bFinished = true;
00964 
00965     // will emit the error and autodelete this
00966     m_timer.start( 0, true );
00967 
00968   } else {
00969 
00970     kdDebug(7010) << "Finished" << endl;
00971     if(!dynamic_cast<KIO::StatJob*>(job))
00972         kdFatal() << "job is a " << typeid(*job).name() << " should be a StatJob" << endl;
00973 
00974     KIO::UDSEntry entry = ((KIO::StatJob*)job)->statResult();
00975     KIO::UDSEntry::ConstIterator it = entry.begin();
00976     for( ; it != entry.end(); it++ ) {
00977         if ( (*it).m_uds == KIO::UDS_FILE_TYPE )
00978         {
00979             if ( S_ISDIR( (mode_t)((*it).m_long) ) )
00980                 m_bIsDirectory = true; // it's a dir
00981             else
00982                 m_bScanFile = true; // it's a file
00983             break;
00984         }
00985     }
00986     // We should have found something
00987     assert ( m_bScanFile || m_bIsDirectory );
00988 
00989     // Start the timer. Once we get the timer event this
00990     // protocol server is back in the pool and we can reuse it.
00991     // This gives better performance than starting a new slave
00992     m_timer.start( 0, true );
00993   }
00994 }
00995 
00996 void KRun::slotScanMimeType( KIO::Job *, const QString &mimetype )
00997 {
00998   if ( mimetype.isEmpty() )
00999     kdWarning(7010) << "KRun::slotScanFinished : MimetypeJob didn't find a mimetype! Probably a kioslave bug." << endl;
01000   foundMimeType( mimetype );
01001   m_job = 0;
01002 }
01003 
01004 void KRun::slotScanFinished( KIO::Job *job )
01005 {
01006   m_job = 0;
01007   if (job->error())
01008   {
01009     d->m_showingError = true;
01010     kdError(7010) << this << " ERROR (stat) : " << job->error() << " " << job->errorString() << endl;
01011     job->showErrorDialog();
01012     //kdDebug(7010) << this << " KRun returning from showErrorDialog, starting timer to delete us" << endl;
01013     d->m_showingError = false;
01014 
01015     m_bFault = true;
01016     m_bFinished = true;
01017 
01018     // will emit the error and autodelete this
01019     m_timer.start( 0, true );
01020   }
01021 }
01022 
01023 void KRun::foundMimeType( const QString& type )
01024 {
01025   kdDebug(7010) << "Resulting mime type is " << type << endl;
01026 
01027 /*
01028   // Automatically unzip stuff
01029 
01030   // Disabled since the new KIO doesn't have filters yet.
01031 
01032   if ( type == "application/x-gzip"  ||
01033        type == "application/x-bzip"  ||
01034        type == "application/x-bzip2"  )
01035   {
01036     KURL::List lst = KURL::split( m_strURL );
01037     if ( lst.isEmpty() )
01038     {
01039       QString tmp = i18n( "Malformed URL" );
01040       tmp += "\n";
01041       tmp += m_strURL.url();
01042       KMessageBoxWrapper::error( 0L, tmp );
01043       return;
01044     }
01045 
01046     if ( type == "application/x-gzip" )
01047       lst.prepend( KURL( "gzip:/decompress" ) );
01048     else if ( type == "application/x-bzip" )
01049       lst.prepend( KURL( "bzip:/decompress" ) );
01050     else if ( type == "application/x-bzip2" )
01051       lst.prepend( KURL( "bzip2:/decompress" ) );
01052     else if ( type == "application/x-tar" )
01053       lst.prepend( KURL( "tar:/" ) );
01054 
01055     // Move the HTML style reference to the leftmost URL
01056     KURL::List::Iterator it = lst.begin();
01057     ++it;
01058     (*lst.begin()).setRef( (*it).ref() );
01059     (*it).setRef( QString::null );
01060 
01061     // Create the new URL
01062     m_strURL = KURL::join( lst );
01063 
01064     kdDebug(7010) << "Now trying with " << debugString(m_strURL.url()) << endl;
01065 
01066     killJob();
01067 
01068     // We don't know if this is a file or a directory. Let's test this first.
01069     // (For instance a tar.gz is a directory contained inside a file)
01070     // It may be a directory or a file, let's stat
01071     KIO::StatJob *job = KIO::stat( m_strURL, m_bProgressInfo );
01072     connect( job, SIGNAL( result( KIO::Job * ) ),
01073              this, SLOT( slotStatResult( KIO::Job * ) ) );
01074     m_job = job;
01075 
01076     return;
01077   }
01078 */
01079   if (m_job && m_job->inherits("KIO::TransferJob"))
01080   {
01081      KIO::TransferJob *job = static_cast<KIO::TransferJob *>(m_job);
01082      job->putOnHold();
01083      KIO::Scheduler::publishSlaveOnHold();
01084      m_job = 0;
01085   }
01086 
01087   Q_ASSERT( !m_bFinished );
01088 
01089   // Suport for preferred service setting, see setPreferredService
01090   if ( !d->m_preferredService.isEmpty() ) {
01091       kdDebug(7010) << "Attempting to open with preferred service: " << d->m_preferredService << endl;
01092       KService::Ptr serv = KService::serviceByDesktopName( d->m_preferredService );
01093       if ( serv && serv->hasServiceType( type ) )
01094       {
01095           KURL::List lst;
01096           lst.append( m_strURL );
01097           m_bFinished = KRun::run( *serv, lst );
01102       }
01103   }
01104 
01105   if (!m_bFinished && KRun::runURL( m_strURL, type )){
01106     m_bFinished = true;
01107   }
01108   else{
01109     m_bFinished = true;
01110     m_bFault = true;
01111   }
01112 
01113   m_timer.start( 0, true );
01114 }
01115 
01116 void KRun::killJob()
01117 {
01118   if ( m_job )
01119   {
01120     kdDebug(7010) << "KRun::killJob run=" << this << " m_job=" << m_job << endl;
01121     m_job->kill();
01122     m_job = 0L;
01123   }
01124 }
01125 
01126 void KRun::abort()
01127 {
01128   kdDebug() << "KRun::abort " << this << " m_showingError=" << d->m_showingError << endl;
01129   killJob();
01130   // If we're showing an error message box, the rest will be done
01131   // after closing the msgbox -> don't autodelete nor emit signals now.
01132   if ( d->m_showingError )
01133     return;
01134   m_bFault = true;
01135   m_bFinished = true;
01136 
01137   // will emit the error and autodelete this
01138   m_timer.start( 0, true );
01139 }
01140 
01141 void KRun::setPreferredService( const QString& desktopEntryName )
01142 {
01143     d->m_preferredService = desktopEntryName;
01144 }
01145 
01146 /****************/
01147 
01148 pid_t
01149 KProcessRunner::run(KProcess * p, const QString & binName)
01150 {
01151   return (new KProcessRunner(p, binName))->pid();
01152 }
01153 
01154 #ifdef Q_WS_X11
01155 pid_t
01156 KProcessRunner::run(KProcess * p, const QString & binName, const KStartupInfoId& id )
01157 {
01158   return (new KProcessRunner(p, binName, id))->pid();
01159 }
01160 #endif
01161 
01162 KProcessRunner::KProcessRunner(KProcess * p, const QString & _binName )
01163   : QObject(),
01164     process_(p),
01165     binName( _binName )
01166 {
01167   QObject::connect(
01168       process_, SIGNAL(processExited(KProcess *)),
01169       this,     SLOT(slotProcessExited(KProcess *)));
01170 
01171   process_->start();
01172 }
01173 
01174 #ifdef Q_WS_X11
01175 KProcessRunner::KProcessRunner(KProcess * p, const QString & _binName, const KStartupInfoId& id )
01176   : QObject(),
01177     process_(p),
01178     binName( _binName ),
01179     id_( id )
01180 {
01181   QObject::connect(
01182       process_, SIGNAL(processExited(KProcess *)),
01183       this,     SLOT(slotProcessExited(KProcess *)));
01184 
01185   process_->start();
01186 }
01187 #endif
01188 
01189 KProcessRunner::~KProcessRunner()
01190 {
01191   delete process_;
01192 }
01193 
01194   pid_t
01195 KProcessRunner::pid() const
01196 {
01197   return process_->pid();
01198 }
01199 
01200   void
01201 KProcessRunner::slotProcessExited(KProcess * p)
01202 {
01203   if (p != process_)
01204     return; // Eh ?
01205 
01206   kdDebug(7010) << "slotProcessExited " << binName << endl;
01207   kdDebug(7010) << "normalExit " << process_->normalExit() << endl;
01208   kdDebug(7010) << "exitStatus " << process_->exitStatus() << endl;
01209   if ( !binName.isEmpty() && process_->normalExit()
01210           && ( process_->exitStatus() == 127 || process_->exitStatus() == 1 ) )
01211   {
01212     // Often we get 1 (zsh, csh) or 127 (ksh, bash) because the binary doesn't exist.
01213     // We can't just rely on that, but it's a good hint.
01214     // Before assuming its really so, we'll try to find the binName
01215     // relatively to current directory,  and then in the PATH.
01216     if ( !QFile( binName ).exists() && KStandardDirs::findExe( binName ).isEmpty() )
01217     {
01218       kapp->ref();
01219       KMessageBox::sorry( 0L, i18n("Couldn't find the program '%1'").arg( binName ) );
01220       kapp->deref();
01221     }
01222   }
01223 #ifdef Q_WS_X11
01224   if( !id_.none())
01225   {
01226       KStartupInfoData data;
01227       data.addPid( pid()); // announce this pid for the startup notification has finished
01228       data.setHostname();
01229       KStartupInfo::sendFinish( id_, data );
01230   }
01231 #endif
01232   delete this;
01233 }
01234 
01235 void KRun::virtual_hook( int, void* )
01236 { /*BASE::virtual_hook( id, data );*/ }
01237 
01238 #include "krun.moc"
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.4.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Mar 16 18:44:15 2005 by doxygen 1.3.3 written by Dimitri van Heesch, © 1997-2001