certmanager/lib

gnupgprocessbase.cpp

00001 /*
00002     gnupgprocessbase.cpp
00003 
00004     This file is part of libkleopatra, the KDE keymanagement library
00005     Copyright (c) 2004 Klarälvdalens Datakonsult AB
00006 
00007     Libkleopatra is free software; you can redistribute it and/or
00008     modify it under the terms of the GNU General Public License as
00009     published by the Free Software Foundation; either version 2 of the
00010     License, or (at your option) any later version.
00011 
00012     Libkleopatra is distributed in the hope that it will be useful,
00013     but WITHOUT ANY WARRANTY; without even the implied warranty of
00014     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015     General Public License for more details.
00016 
00017     You should have received a copy of the GNU General Public License
00018     along with this program; if not, write to the Free Software
00019     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
00020 
00021     In addition, as a special exception, the copyright holders give
00022     permission to link the code of this program with any edition of
00023     the Qt library by Trolltech AS, Norway (or with modified versions
00024     of Qt that use the same license as Qt), and distribute linked
00025     combinations including the two.  You must obey the GNU General
00026     Public License in all respects for all of the code used other than
00027     Qt.  If you modify this file, you may extend this exception to
00028     your version of the file, but you are not obligated to do so.  If
00029     you do not wish to do so, delete this exception statement from
00030     your version.
00031 */
00032 
00033 #include "gnupgprocessbase.h"
00034 
00035 #include <kdebug.h>
00036 #include <kurl.h>
00037 
00038 #include <qsocketnotifier.h>
00039 #include <qtextcodec.h>
00040 #include <qstringlist.h>
00041 
00042 #include <unistd.h>
00043 #include <fcntl.h>
00044 #include <errno.h>
00045 #include <assert.h>
00046 
00047 struct Kleo::GnuPGProcessBase::Private {
00048   Private() : useStatusFD( false ), statnot( 0 ) {
00049     statusFD[0] = statusFD[1] = -1;
00050   }
00051 
00052   bool useStatusFD;
00053   int statusFD[2];
00054   QSocketNotifier * statnot;
00055   QCString statusBuffer;
00056 };
00057 
00058 
00059 Kleo::GnuPGProcessBase::GnuPGProcessBase( QObject * parent, const char * name )
00060   : KProcess( parent, name )
00061 {
00062   d = new Private();
00063 }
00064 
00065 Kleo::GnuPGProcessBase::~GnuPGProcessBase() {
00066   delete d; d = 0;
00067 }
00068 
00069 void Kleo::GnuPGProcessBase::setUseStatusFD( bool use ) {
00070   assert( d );
00071   d->useStatusFD = use;
00072 }
00073 
00074 bool Kleo::GnuPGProcessBase::start( RunMode runmode, Communication comm ) {
00075   if ( d->useStatusFD ) {
00076     // set up the status-fd. This should be in setupCommunication(),
00077     // but then it's too late: we need the fd of the pipe to pass it
00078     // as argument to the --status-fd option:
00079     // PENDING(marc) find out why KProcess uses both pipe() and socketpair()...
00080     if ( ::pipe( d->statusFD ) < 0 ) {
00081       kdDebug( 5150 ) << "Kleo::GnuPGProcessBase::start: pipe(2) failed: " << perror << endl;
00082       return false;
00083     }
00084     ::fcntl( d->statusFD[0], F_SETFD, FD_CLOEXEC );
00085     ::fcntl( d->statusFD[1], F_SETFD, FD_CLOEXEC );
00086     if ( !arguments.empty() ) {
00087       QValueList<QCString>::iterator it = arguments.begin();
00088       ++it;
00089       arguments.insert( it, "--status-fd" );
00090       char buf[25];
00091       sprintf( buf, "%d", d->statusFD[1] );
00092       arguments.insert( it, buf );
00093       arguments.insert( it, "--no-tty" );
00094       //arguments.insert( it, "--enable-progress-filter" ); // gpgsm doesn't know this
00095     }
00096   }
00097   return KProcess::start( runmode, comm );
00098 }
00099 
00100 int Kleo::GnuPGProcessBase::setupCommunication( Communication comm ) {
00101   if ( int ok = KProcess::setupCommunication( comm ) )
00102     return ok;
00103   if ( d->useStatusFD ) {
00104     // base class impl returned error, so close our fd's, too
00105     ::close( d->statusFD[0] );
00106     ::close( d->statusFD[1] );
00107     d->statusFD[0] = d->statusFD[1] = -1;
00108   }
00109   return 0; // Error
00110 }
00111 
00112 int Kleo::GnuPGProcessBase::commSetupDoneP() {
00113   if ( d->useStatusFD ) {
00114     ::close( d->statusFD[1] ); // close the input end of the pipe, we're the reader
00115     d->statnot = new QSocketNotifier( d->statusFD[0], QSocketNotifier::Read, this );
00116     connect( d->statnot, SIGNAL(activated(int)), SLOT(slotChildStatus(int)) );
00117   }
00118   return KProcess::commSetupDoneP();
00119 }
00120 
00121 int Kleo::GnuPGProcessBase::commSetupDoneC() {
00122   if ( d->useStatusFD )
00123     ::fcntl( d->statusFD[1], F_SETFD, 0 );
00124   return KProcess::commSetupDoneC();
00125 }
00126 
00127 void Kleo::GnuPGProcessBase::slotChildStatus( int fd ) {
00128   if ( !childStatus(fd) )
00129     closeStatus();
00130 }
00131 
00132 bool Kleo::GnuPGProcessBase::closeStatus() {
00133   if ( !d->useStatusFD )
00134     return false;
00135   d->useStatusFD = false;
00136   delete d->statnot; d->statnot = 0;
00137   ::close( d->statusFD[0] ); d->statusFD[0] = -1;
00138   return true;
00139 }
00140 
00141 int Kleo::GnuPGProcessBase::childStatus( int fd ) {
00142   char buf[1024];
00143   const int len = ::read( fd, buf, sizeof(buf)-1 );
00144   if ( len > 0 ) {
00145     buf[len] = 0;
00146     d->statusBuffer += buf;
00147     parseStatusOutput();
00148   }
00149   return len;
00150 }
00151 
00152 static QString fromHexEscapedUtf8( const QCString & str ) {
00153   return KURL::decode_string( str.data(), 106 /* utf-8 */ );
00154 }
00155 
00156 void Kleo::GnuPGProcessBase::parseStatusOutput() {
00157   static const char startToken[] = "[GNUPG:] ";
00158   static const int startTokenLen = sizeof startToken / sizeof *startToken - 1;
00159 
00160   int lineStart = 0;
00161   for ( int lineEnd = d->statusBuffer.find( '\n' ) ; lineEnd >= 0 ; lineEnd = d->statusBuffer.find( '\n', lineStart = lineEnd+1 ) ) {
00162     // get next line:
00163     const QCString line = d->statusBuffer.mid( lineStart, lineEnd - lineStart ).stripWhiteSpace();
00164     if ( line.isEmpty() )
00165       continue;
00166     // check status token
00167     if ( line.left( startTokenLen ) != startToken ) {
00168       kdDebug( 5150 ) << "Kleo::GnuPGProcessBase::childStatus: status-fd protocol error: line doesn't begin with \""
00169               << startToken << "\"" << endl;
00170       continue;
00171     }
00172     // remove status token:
00173     const QCString command = line.mid( startTokenLen ).simplifyWhiteSpace() + ' ';
00174     if ( command == " " ) {
00175       kdDebug( 5150 ) << "Kleo::GnuPGProcessBase::childStatus: status-fd protocol error: line without content." << endl;
00176       continue;
00177     }
00178     // split into base and args
00179     QString cmd;
00180     QStringList args;
00181     int tagStart = 0;
00182     for ( int tagEnd = command.find( ' ' ) ; tagEnd >= 0 ; tagEnd = command.find( ' ', tagStart = tagEnd+1 ) ) {
00183       const QCString tag = command.mid( tagStart, tagEnd - tagStart );
00184       if ( cmd.isNull() )
00185     cmd = fromHexEscapedUtf8( tag );
00186       else
00187     args.push_back( fromHexEscapedUtf8( tag ) );
00188     }
00189     emit status( this, cmd, args );
00190   }
00191   d->statusBuffer = d->statusBuffer.mid( lineStart );
00192 }
00193 
00194 void Kleo::GnuPGProcessBase::virtual_hook( int id, void * data ) {
00195   KProcess::virtual_hook( id, data );
00196 }
00197 
00198 #include "gnupgprocessbase.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys