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