# kio Library API Documentation

Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Source Files

# 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"

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