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", "konsole");
00500   
00501   if (terminal == "konsole")
00502     terminal += " -caption=%c %i %m";
00503 
00504   if (terminal_su)
00505   {
00506     QString cmd = result.join(" ");
00507     result = breakup(QString("%1 %2 -e su %3 -c").arg(terminal).arg(_service.terminalOptions()).arg(user));
00508     for(QStringList::Iterator it = result.begin();
00509         it != result.end(); ++it)
00510     {
00511         substitute(result, it, _service, _urls, has_shell);
00512     }
00513     result.append(cmd);
00514   }
00515   else if (terminal_sh)
00516   {
00517      QStringList cmd = result;
00518      result = breakup(QString("%1 %2 -e").arg(terminal).arg(_service.terminalOptions()));
00519      for(QStringList::Iterator it = result.begin();
00520          it != result.end(); ++it)
00521      {
00522          substitute(result, it, _service, _urls, has_shell);
00523      }
00524       result += cmd;
00525   }
00526   else if (kdesu)
00527   {
00528      result = breakup(QString("kdesu -u %1 --").arg(user))+result;
00529   }
00530 
00531   return result;
00532 }
00533 
00534 //static
00535 QString KRun::binaryName( const QString & execLine, bool removePath )
00536 {
00537   // Remove parameters and/or trailing spaces.
00538   QStringList args = breakup( execLine );
00539   QString _bin_name;
00540   do {
00541       if ( args.isEmpty() )
00542          return QString::null;
00543       _bin_name = args.first();
00544       args.pop_front();
00545   } while (_bin_name.contains('='));
00546   // Remove path if wanted
00547   return removePath ? _bin_name.mid(_bin_name.findRev('/') + 1) : _bin_name;
00548 }
00549 
00550 static pid_t runCommandInternal( KProcess* proc, const KService* service, const QString& binName,
00551     const QString &execName_P, const QString & iconName_P )
00552 {
00553   QString bin = KRun::binaryName( binName, false );
00554   QString execName = execName_P;
00555   QString iconName = iconName_P;
00556   if ( service && !KDesktopFile::isAuthorizedDesktopFile( service->desktopEntryPath() ))
00557   {
00558      KMessageBox::sorry(0, i18n("You are not authorized to execute this file."));
00559      return 0;
00560   }
00561 #ifdef Q_WS_X11 // Startup notification doesn't work with QT/E, service isn't needed without Startup notification
00562   bool startup_notify = false;
00563   QCString wmclass;
00564   KStartupInfoId id;
00565   if( service && service->property( "X-KDE-StartupNotify" ).isValid())
00566   {
00567       startup_notify = service->property( "X-KDE-StartupNotify" ).toBool();
00568       wmclass = service->property( "X-KDE-WMClass" ).toString().latin1();
00569   }
00570   else // non-compliant app ( .desktop file )
00571   {
00572       if( service && service->type() == "Application" )
00573       {
00574           startup_notify = true; // doesn't have .desktop entries needed
00575           wmclass = "0";         // start as non-compliant
00576       }
00577   }
00578   if( startup_notify )
00579   {
00580       id.initId();
00581       id.setupStartupEnv();
00582       if( execName.isEmpty())
00583           execName = service->name();
00584       if( iconName.isEmpty())
00585           iconName = service->icon();
00586       KStartupInfoData data;
00587       data.setHostname();
00588       data.setBin( KRun::binaryName( binName, true ));
00589       data.setName( execName );
00590       data.setIcon( iconName );
00591       if( !wmclass.isEmpty())
00592           data.setWMClass( wmclass );
00593       data.setDesktop( KWin::currentDesktop());
00594       KStartupInfo::sendStartup( id, data );
00595   }
00596   pid_t pid = KProcessRunner::run( proc, KRun::binaryName( binName, true ), id );
00597   if( startup_notify )
00598   {
00599       KStartupInfoData data;
00600       if ( pid ) // successfully started
00601       {
00602           data.addPid( pid );
00603           KStartupInfo::sendChange( id, data );
00604       } else // not started (e.g. executable not found)
00605       {
00606           data.setHostname();
00607           KStartupInfo::sendFinish( id, data );
00608       }
00609       KStartupInfo::resetStartupEnv();
00610   }
00611   return pid;
00612 #else
00613   return KProcessRunner::run( proc, KRun::binaryName( binName, true ) );
00614 #endif
00615 }
00616 
00617 static pid_t runTempService( const KService& _service, const KURL::List& _urls, bool tempFiles )
00618 {
00619   if (!_urls.isEmpty()) {
00620     kdDebug(7010) << "runTempService: first url " << _urls.first().url() << endl;
00621   }
00622 
00623   QStringList args;
00624   if ((_urls.count() > 1) && !_service.allowMultipleFiles())
00625   {
00626       // We need to launch the application N times. That sucks.
00627       // We ignore the result for application 2 to N.
00628       // For the first file we launch the application in the
00629       // usual way. The reported result is based on this
00630       // application.
00631       KURL::List::ConstIterator it = _urls.begin();
00632       for(++it; it != _urls.end(); ++it)
00633       {
00634          KURL::List singleUrl;
00635          singleUrl.append(*it);
00636          runTempService( _service, singleUrl, tempFiles );
00637       }
00638       KURL::List singleUrl;
00639       singleUrl.append(_urls.first());
00640       args = KRun::processDesktopExec(_service, singleUrl, false, tempFiles);
00641   }
00642   else
00643   {
00644       args = KRun::processDesktopExec(_service, _urls, false, tempFiles);
00645   }
00646   //kdDebug(7010) << "runTempService: KProcess args=" << args << endl;
00647 
00648   KProcess * proc = new KProcess;
00649   for(QStringList::Iterator it = args.begin();
00650       it != args.end(); ++it)
00651   {
00652      QString arg = *it;
00653      *proc << arg;
00654   }
00655   return runCommandInternal( proc, &_service, _service.exec(), _service.name(), _service.icon() );
00656 }
00657 
00658 // BIC merge with method below
00659 pid_t KRun::run( const KService& _service, const KURL::List& _urls )
00660 {
00661     return run( _service, _urls, false );
00662 }
00663 
00664 pid_t KRun::run( const KService& _service, const KURL::List& _urls, bool tempFiles )
00665 {
00666   if (!_service.desktopEntryPath().isEmpty() &&
00667       !KDesktopFile::isAuthorizedDesktopFile( _service.desktopEntryPath()))
00668   {
00669      KMessageBox::sorry(0, i18n("You are not authorized to execute this service."));
00670      return 0;
00671   }
00672 
00673   if ( !tempFiles )
00674   {
00675   // Remember we opened those urls, for the "recent documents" menu in kicker
00676   KURL::List::ConstIterator it = _urls.begin();
00677   for(; it != _urls.end(); ++it) {
00678      //kdDebug(7010) << "KRecentDocument::adding " << (*it).url() << endl;
00679      KRecentDocument::add( *it, _service.desktopEntryName() );
00680   }
00681   }
00682 
00683   if ( tempFiles || _service.desktopEntryPath().isEmpty())
00684   {
00685      return runTempService(_service, _urls, tempFiles);
00686   }
00687 
00688   kdDebug(7010) << "KRun::run " << _service.desktopEntryPath() << endl;
00689 
00690   if (!_urls.isEmpty()) {
00691     kdDebug(7010) << "First url " << _urls.first().url() << endl;
00692   }
00693 
00694   QString error;
00695   int pid = 0;
00696 
00697   int i = KApplication::startServiceByDesktopPath(
00698         _service.desktopEntryPath(), _urls.toStringList(), &error, 0L, &pid
00699         );
00700 
00701   if (i != 0)
00702   {
00703      kdDebug(7010) << error << endl;
00704      KMessageBox::sorry( 0L, error );
00705      return 0;
00706   }
00707 
00708   kdDebug(7010) << "startServiceByDesktopPath worked fine" << endl;
00709   return (pid_t) pid;
00710 }
00711 
00712 
00713 pid_t KRun::run( const QString& _exec, const KURL::List& _urls, const QString& _name,
00714                 const QString& _icon, const QString&, const QString&)
00715 {
00716   KService::Ptr service = new KService(_name, _exec, _icon);
00717 
00718   return run(*service, _urls);
00719 }
00720 
00721 pid_t KRun::runCommand( QString cmd )
00722 {
00723   return KRun::runCommand( cmd, QString::null, QString::null );
00724 }
00725 
00726 pid_t KRun::runCommand( const QString& cmd, const QString &execName, const QString & iconName )
00727 {
00728   kdDebug(7010) << "runCommand " << cmd << "," << execName << endl;
00729   KProcess * proc = new KProcess;
00730   proc->setUseShell(true);
00731   *proc << cmd;
00732   QString bin = binaryName( cmd, false );
00733   KService::Ptr service = KService::serviceByDesktopName( bin );
00734   return runCommandInternal( proc, service.data(), bin, execName, iconName );
00735 }
00736 
00737 KRun::KRun( const KURL& _url, mode_t _mode, bool _is_local_file, bool _showProgressInfo )
00738   : m_timer(0,"KRun::timer")
00739 {
00740   m_bFault = false;
00741   m_bAutoDelete = true;
00742   m_bProgressInfo = _showProgressInfo;
00743   m_bFinished = false;
00744   m_job = 0L;
00745   m_strURL = _url;
00746   m_bScanFile = false;
00747   m_bIsDirectory = false;
00748   m_bIsLocalFile = _is_local_file;
00749   m_mode = _mode;
00750   d = new KRunPrivate;
00751 
00752   // Start the timer. This means we will return to the event
00753   // loop and do initialization afterwards.
00754   // Reason: We must complete the constructor before we do anything else.
00755   m_bInit = true;
00756   connect( &m_timer, SIGNAL( timeout() ), this, SLOT( slotTimeout() ) );
00757   m_timer.start( 0, true );
00758   kdDebug(7010) << " new KRun " << this << " " << _url.prettyURL() << " timer=" << &m_timer << endl;
00759 
00760   kapp->ref();
00761 }
00762 
00763 void KRun::init()
00764 {
00765   kdDebug(7010) << "INIT called" << endl;
00766   if ( m_strURL.isMalformed() )
00767   {
00768     d->m_showingError = true;
00769     KMessageBoxWrapper::error( 0L, i18n( "Malformed URL\n%1" ).arg( m_strURL.url() ) );
00770     d->m_showingError = false;
00771     m_bFault = true;
00772     m_bFinished = true;
00773     m_timer.start( 0, true );
00774     return;
00775   }
00776   if ( !kapp->authorizeURLAction( "open", KURL(), m_strURL))
00777   {
00778     QString msg = KIO::buildErrorString(KIO::ERR_ACCESS_DENIED, m_strURL.prettyURL());
00779     d->m_showingError = true;
00780     KMessageBoxWrapper::error( 0L, msg );
00781     d->m_showingError = false;
00782     m_bFault = true;
00783     m_bFinished = true;
00784     m_timer.start( 0, true );
00785     return;
00786   }
00787 
00788   if ( !m_bIsLocalFile && m_strURL.isLocalFile() )
00789 
00790     m_bIsLocalFile = true;
00791 
00792   if ( m_bIsLocalFile )
00793   {
00794     if ( m_mode == 0 )
00795     {
00796       struct stat buff;
00797       if ( stat( QFile::encodeName(m_strURL.path()), &buff ) == -1 )
00798       {
00799         d->m_showingError = true;
00800         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() ) );
00801         d->m_showingError = false;
00802         m_bFault = true;
00803         m_bFinished = true;
00804         m_timer.start( 0, true );
00805         return;
00806       }
00807       m_mode = buff.st_mode;
00808     }
00809 
00810     KMimeType::Ptr mime = KMimeType::findByURL( m_strURL, m_mode, m_bIsLocalFile );
00811     assert( mime != 0L );
00812     kdDebug(7010) << "MIME TYPE is " << mime->name() << endl;
00813     foundMimeType( mime->name() );
00814     return;
00815   }
00816   else if ( KProtocolInfo::isHelperProtocol( m_strURL ) ) {
00817     kdDebug(7010) << "Helper protocol" << endl;
00818 
00819     KURL::List urls;
00820     urls.append( m_strURL );
00821     QString exec = KProtocolInfo::exec( m_strURL.protocol() );
00822     run( exec, urls );
00823 
00824     m_bFinished = true;
00825     // will emit the error and autodelete this
00826     m_timer.start( 0, true );
00827     return;
00828   }
00829 
00830   // Did we already get the information that it is a directory ?
00831   if ( S_ISDIR( m_mode ) )
00832   {
00833     foundMimeType( "inode/directory" );
00834     return;
00835   }
00836 
00837   // Let's see whether it is a directory
00838 
00839   if ( !KProtocolInfo::supportsListing( m_strURL ) )
00840   {
00841     //kdDebug(7010) << "Protocol has no support for listing" << endl;
00842     // No support for listing => it can't be a directory (example: http)
00843     scanFile();
00844     return;
00845   }
00846 
00847   kdDebug(7010) << "Testing directory (stating)" << endl;
00848 
00849   // It may be a directory or a file, let's stat
00850   KIO::StatJob *job = KIO::stat( m_strURL, true, 0 /* no details */, m_bProgressInfo );
00851   connect( job, SIGNAL( result( KIO::Job * ) ),
00852            this, SLOT( slotStatResult( KIO::Job * ) ) );
00853   m_job = job;
00854   kdDebug() << " Job " << job << " is about stating " << m_strURL.url() << endl;
00855 }
00856 
00857 KRun::~KRun()
00858 {
00859   kdDebug(7010) << "KRun::~KRun() " << this << endl;
00860   m_timer.stop();
00861   killJob();
00862   kapp->deref();
00863   kdDebug(7010) << "KRun::~KRun() done " << this << endl;
00864   delete d;
00865 }
00866 
00867 void KRun::scanFile()
00868 {
00869   kdDebug(7010) << "###### KRun::scanFile " << m_strURL.url() << endl;
00870   // First, let's check for well-known extensions
00871   // Not when there is a query in the URL, in any case.
00872   if ( m_strURL.query().isEmpty() )
00873   {
00874     KMimeType::Ptr mime = KMimeType::findByURL( m_strURL );
00875     assert( mime != 0L );
00876     if ( mime->name() != "application/octet-stream" || m_bIsLocalFile )
00877     {
00878       kdDebug(7010) << "Scanfile: MIME TYPE is " << mime->name() << endl;
00879       foundMimeType( mime->name() );
00880       return;
00881     }
00882   }
00883 
00884   // No mimetype found, and the URL is not local  (or fast mode not allowed).
00885   // We need to apply the 'KIO' method, i.e. either asking the server or
00886   // getting some data out of the file, to know what mimetype it is.
00887 
00888   if ( !KProtocolInfo::supportsReading( m_strURL ) )
00889   {
00890     kdError(7010) << "#### NO SUPPORT FOR READING!" << endl;
00891     m_bFault = true;
00892     m_bFinished = true;
00893     m_timer.start( 0, true );
00894     return;
00895   }
00896   kdDebug(7010) << this << " Scanning file " << m_strURL.url() << endl;
00897 
00898   KIO::TransferJob *job = KIO::get( m_strURL, false /*reload*/, m_bProgressInfo );
00899   connect(job, SIGNAL( result(KIO::Job *)),
00900           this, SLOT( slotScanFinished(KIO::Job *)));
00901   connect(job, SIGNAL( mimetype(KIO::Job *, const QString &)),
00902           this, SLOT( slotScanMimeType(KIO::Job *, const QString &)));
00903   m_job = job;
00904   kdDebug() << " Job " << job << " is about getting from " << m_strURL.url() << endl;
00905 }
00906 
00907 void KRun::slotTimeout()
00908 {
00909   kdDebug(7010) << this << " slotTimeout called" << endl;
00910   if ( m_bInit )
00911   {
00912     m_bInit = false;
00913     init();
00914     return;
00915   }
00916 
00917   if ( m_bFault ){
00918       emit error();
00919   }
00920   if ( m_bFinished ){
00921       emit finished();
00922   }
00923 
00924   if ( m_bScanFile )
00925   {
00926     m_bScanFile = false;
00927     scanFile();
00928     return;
00929   }
00930   else if ( m_bIsDirectory )
00931   {
00932     m_bIsDirectory = false;
00933     foundMimeType( "inode/directory" );
00934     return;
00935   }
00936 
00937   if ( m_bAutoDelete )
00938   {
00939     delete this;
00940     return;
00941   }
00942 }
00943 
00944 void KRun::slotStatResult( KIO::Job * job )
00945 {
00946   m_job = 0L;
00947   if (job->error())
00948   {
00949     d->m_showingError = true;
00950     kdError(7010) << this << " ERROR " << job->error() << " " << job->errorString() << endl;
00951     job->showErrorDialog();
00952     //kdDebug(7010) << this << " KRun returning from showErrorDialog, starting timer to delete us" << endl;
00953     d->m_showingError = false;
00954 
00955     m_bFault = true;
00956     m_bFinished = true;
00957 
00958     // will emit the error and autodelete this
00959     m_timer.start( 0, true );
00960 
00961   } else {
00962 
00963     kdDebug(7010) << "Finished" << endl;
00964     if(!dynamic_cast<KIO::StatJob*>(job))
00965         kdFatal() << "job is a " << typeid(*job).name() << " should be a StatJob" << endl;
00966 
00967     KIO::UDSEntry entry = ((KIO::StatJob*)job)->statResult();
00968     KIO::UDSEntry::ConstIterator it = entry.begin();
00969     for( ; it != entry.end(); it++ ) {
00970         if ( (*it).m_uds == KIO::UDS_FILE_TYPE )
00971         {
00972             if ( S_ISDIR( (mode_t)((*it).m_long) ) )
00973                 m_bIsDirectory = true; // it's a dir
00974             else
00975                 m_bScanFile = true; // it's a file
00976             break;
00977         }
00978     }
00979     // We should have found something
00980     assert ( m_bScanFile || m_bIsDirectory );
00981 
00982     // Start the timer. Once we get the timer event this
00983     // protocol server is back in the pool and we can reuse it.
00984     // This gives better performance than starting a new slave
00985     m_timer.start( 0, true );
00986   }
00987 }
00988 
00989 void KRun::slotScanMimeType( KIO::Job *, const QString &mimetype )
00990 {
00991   if ( mimetype.isEmpty() )
00992     kdWarning(7010) << "KRun::slotScanFinished : MimetypeJob didn't find a mimetype! Probably a kioslave bug." << endl;
00993   foundMimeType( mimetype );
00994   m_job = 0;
00995 }
00996 
00997 void KRun::slotScanFinished( KIO::Job *job )
00998 {
00999   m_job = 0;
01000   if (job->error())
01001   {
01002     d->m_showingError = true;
01003     kdError(7010) << this << " ERROR (stat) : " << job->error() << " " << job->errorString() << endl;
01004     job->showErrorDialog();
01005     //kdDebug(7010) << this << " KRun returning from showErrorDialog, starting timer to delete us" << endl;
01006     d->m_showingError = false;
01007 
01008     m_bFault = true;
01009     m_bFinished = true;
01010 
01011     // will emit the error and autodelete this
01012     m_timer.start( 0, true );
01013   }
01014 }
01015 
01016 void KRun::foundMimeType( const QString& type )
01017 {
01018   kdDebug(7010) << "Resulting mime type is " << type << endl;
01019 
01020 /*
01021   // Automatically unzip stuff
01022 
01023   // Disabled since the new KIO doesn't have filters yet.
01024 
01025   if ( type == "application/x-gzip"  ||
01026        type == "application/x-bzip"  ||
01027        type == "application/x-bzip2"  )
01028   {
01029     KURL::List lst = KURL::split( m_strURL );
01030     if ( lst.isEmpty() )
01031     {
01032       QString tmp = i18n( "Malformed URL" );
01033       tmp += "\n";
01034       tmp += m_strURL.url();
01035       KMessageBoxWrapper::error( 0L, tmp );
01036       return;
01037     }
01038 
01039     if ( type == "application/x-gzip" )
01040       lst.prepend( KURL( "gzip:/decompress" ) );
01041     else if ( type == "application/x-bzip" )
01042       lst.prepend( KURL( "bzip:/decompress" ) );
01043     else if ( type == "application/x-bzip2" )
01044       lst.prepend( KURL( "bzip2:/decompress" ) );
01045     else if ( type == "application/x-tar" )
01046       lst.prepend( KURL( "tar:/" ) );
01047 
01048     // Move the HTML style reference to the leftmost URL
01049     KURL::List::Iterator it = lst.begin();
01050     ++it;
01051     (*lst.begin()).setRef( (*it).ref() );
01052     (*it).setRef( QString::null );
01053 
01054     // Create the new URL
01055     m_strURL = KURL::join( lst );
01056 
01057     kdDebug(7010) << "Now trying with " << debugString(m_strURL.url()) << endl;
01058 
01059     killJob();
01060 
01061     // We don't know if this is a file or a directory. Let's test this first.
01062     // (For instance a tar.gz is a directory contained inside a file)
01063     // It may be a directory or a file, let's stat
01064     KIO::StatJob *job = KIO::stat( m_strURL, m_bProgressInfo );
01065     connect( job, SIGNAL( result( KIO::Job * ) ),
01066              this, SLOT( slotStatResult( KIO::Job * ) ) );
01067     m_job = job;
01068 
01069     return;
01070   }
01071 */
01072   if (m_job && m_job->inherits("KIO::TransferJob"))
01073   {
01074      KIO::TransferJob *job = static_cast<KIO::TransferJob *>(m_job);
01075      job->putOnHold();
01076      KIO::Scheduler::publishSlaveOnHold();
01077      m_job = 0;
01078   }
01079 
01080   Q_ASSERT( !m_bFinished );
01081 
01082   // Suport for preferred service setting, see setPreferredService
01083   if ( !d->m_preferredService.isEmpty() ) {
01084       kdDebug(7010) << "Attempting to open with preferred service: " << d->m_preferredService << endl;
01085       KService::Ptr serv = KService::serviceByDesktopName( d->m_preferredService );
01086       if ( serv && serv->hasServiceType( type ) )
01087       {
01088           KURL::List lst;
01089           lst.append( m_strURL );
01090           m_bFinished = KRun::run( *serv, lst );
01095       }
01096   }
01097 
01098   if (!m_bFinished && KRun::runURL( m_strURL, type )){
01099     m_bFinished = true;
01100   }
01101   else{
01102     m_bFinished = true;
01103     m_bFault = true;
01104   }
01105 
01106   m_timer.start( 0, true );
01107 }
01108 
01109 void KRun::killJob()
01110 {
01111   if ( m_job )
01112   {
01113     kdDebug(7010) << "KRun::killJob run=" << this << " m_job=" << m_job << endl;
01114     m_job->kill();
01115     m_job = 0L;
01116   }
01117 }
01118 
01119 void KRun::abort()
01120 {
01121   kdDebug() << "KRun::abort " << this << " m_showingError=" << d->m_showingError << endl;
01122   killJob();
01123   // If we're showing an error message box, the rest will be done
01124   // after closing the msgbox -> don't autodelete nor emit signals now.
01125   if ( d->m_showingError )
01126     return;
01127   m_bFault = true;
01128   m_bFinished = true;
01129 
01130   // will emit the error and autodelete this
01131   m_timer.start( 0, true );
01132 }
01133 
01134 void KRun::setPreferredService( const QString& desktopEntryName )
01135 {
01136     d->m_preferredService = desktopEntryName;
01137 }
01138 
01139 /****************/
01140 
01141 pid_t
01142 KProcessRunner::run(KProcess * p, const QString & binName)
01143 {
01144   return (new KProcessRunner(p, binName))->pid();
01145 }
01146 
01147 #ifdef Q_WS_X11
01148 pid_t
01149 KProcessRunner::run(KProcess * p, const QString & binName, const KStartupInfoId& id )
01150 {
01151   return (new KProcessRunner(p, binName, id))->pid();
01152 }
01153 #endif
01154 
01155 KProcessRunner::KProcessRunner(KProcess * p, const QString & _binName )
01156   : QObject(),
01157     process_(p),
01158     binName( _binName )
01159 {
01160   QObject::connect(
01161       process_, SIGNAL(processExited(KProcess *)),
01162       this,     SLOT(slotProcessExited(KProcess *)));
01163 
01164   process_->start();
01165 }
01166 
01167 #ifdef Q_WS_X11
01168 KProcessRunner::KProcessRunner(KProcess * p, const QString & _binName, const KStartupInfoId& id )
01169   : QObject(),
01170     process_(p),
01171     binName( _binName ),
01172     id_( id )
01173 {
01174   QObject::connect(
01175       process_, SIGNAL(processExited(KProcess *)),
01176       this,     SLOT(slotProcessExited(KProcess *)));
01177 
01178   process_->start();
01179 }
01180 #endif
01181 
01182 KProcessRunner::~KProcessRunner()
01183 {
01184   delete process_;
01185 }
01186 
01187   pid_t
01188 KProcessRunner::pid() const
01189 {
01190   return process_->pid();
01191 }
01192 
01193   void
01194 KProcessRunner::slotProcessExited(KProcess * p)
01195 {
01196   if (p != process_)
01197     return; // Eh ?
01198 
01199   kdDebug(7010) << "slotProcessExited " << binName << endl;
01200   kdDebug(7010) << "normalExit " << process_->normalExit() << endl;
01201   kdDebug(7010) << "exitStatus " << process_->exitStatus() << endl;
01202   if ( !binName.isEmpty() && process_->normalExit()
01203           && ( process_->exitStatus() == 127 || process_->exitStatus() == 1 ) )
01204   {
01205     // Often we get 1 (zsh, csh) or 127 (ksh, bash) because the binary doesn't exist.
01206     // We can't just rely on that, but it's a good hint.
01207     // Before assuming its really so, we'll try to find the binName
01208     // relatively to current directory,  and then in the PATH.
01209     if ( !QFile( binName ).exists() && KStandardDirs::findExe( binName ).isEmpty() )
01210     {
01211       kapp->ref();
01212       KMessageBox::sorry( 0L, i18n("Couldn't find the program '%1'").arg( binName ) );
01213       kapp->deref();
01214     }
01215   }
01216 #ifdef Q_WS_X11
01217   if( !id_.none())
01218   {
01219       KStartupInfoData data;
01220       data.addPid( pid()); // announce this pid for the startup notification has finished
01221       data.setHostname();
01222       KStartupInfo::sendFinish( id_, data );
01223   }
01224 #endif
01225   delete this;
01226 }
01227 
01228 void KRun::virtual_hook( int, void* )
01229 { /*BASE::virtual_hook( id, data );*/ }
01230 
01231 #include "krun.moc"
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 13:14:15 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001