qgpgmejob.cpp
00001 /* 00002 qgpgmejob.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 TQt library by Trolltech AS, Norway (or with modified versions 00024 of TQt that use the same license as TQt), 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 TQt. 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 #ifdef HAVE_CONFIG_H 00034 #include <config.h> 00035 #endif 00036 00037 #include "qgpgmejob.h" 00038 #include "qgpgmeprogresstokenmapper.h" 00039 00040 #include <kleo/job.h> 00041 #include <ui/passphrasedialog.h> 00042 00043 #include <qgpgme/eventloopinteractor.h> 00044 #include <qgpgme/dataprovider.h> 00045 00046 #include <gpgmepp/context.h> 00047 #include <gpgmepp/data.h> 00048 00049 #include <tdelocale.h> 00050 #include <kstandarddirs.h> 00051 00052 #include <tqstring.h> 00053 #include <tqstringlist.h> 00054 00055 #include <algorithm> 00056 00057 #include <assert.h> 00058 #include <stdlib.h> 00059 #include <string.h> 00060 00061 namespace { 00062 class InvarianceChecker { 00063 public: 00064 #ifdef NDEBUG 00065 InvarianceChecker( const Kleo::QGpgMEJob * ) {} 00066 #else 00067 InvarianceChecker( const Kleo::QGpgMEJob * job ) 00068 : _this( job ) 00069 { 00070 assert( _this ); 00071 _this->checkInvariants(); 00072 } 00073 ~InvarianceChecker() { 00074 _this->checkInvariants(); 00075 } 00076 private: 00077 const Kleo::QGpgMEJob * _this; 00078 #endif 00079 }; 00080 } 00081 00082 Kleo::QGpgMEJob::QGpgMEJob( Kleo::Job * _this, GpgME::Context * context ) 00083 : GpgME::ProgressProvider(), 00084 GpgME::PassphraseProvider(), 00085 mThis( _this ), 00086 mCtx( context ), 00087 mInData( 0 ), 00088 mInDataDataProvider( 0 ), 00089 mOutData( 0 ), 00090 mOutDataDataProvider( 0 ), 00091 mPatterns( 0 ), 00092 mReplacedPattern( 0 ), 00093 mNumPatterns( 0 ), 00094 mChunkSize( 1024 ), 00095 mPatternStartIndex( 0 ), mPatternEndIndex( 0 ) 00096 { 00097 InvarianceChecker check( this ); 00098 assert( context ); 00099 TQObject::connect( QGpgME::EventLoopInteractor::instance(), TQT_SIGNAL(aboutToDestroy()), 00100 _this, TQT_SLOT(slotCancel()) ); 00101 context->setProgressProvider( this ); 00102 // (mmutz) work around a gpgme bug in versions at least <= 0.9.0. 00103 // These versions will return GPG_ERR_NOT_IMPLEMENTED from 00104 // a CMS sign operation when a passphrase callback is set. 00105 if ( context->protocol() == GpgME::Context::OpenPGP ) 00106 context->setPassphraseProvider( this ); 00107 } 00108 00109 void Kleo::QGpgMEJob::checkInvariants() const { 00110 #ifndef NDEBUG 00111 if ( mPatterns ) { 00112 assert( mPatterns[mNumPatterns] == 0 ); 00113 if ( mPatternEndIndex > 0 ) { 00114 assert( mPatternEndIndex > mPatternStartIndex ); 00115 assert( mPatternEndIndex - mPatternStartIndex == mChunkSize ); 00116 } else { 00117 assert( mPatternEndIndex == mPatternStartIndex ); 00118 } 00119 if ( mPatternEndIndex < mNumPatterns ) { 00120 assert( mPatterns[mPatternEndIndex] == 0 ); 00121 assert( mReplacedPattern != 0 ); 00122 } else { 00123 assert( mReplacedPattern == 0 ); 00124 } 00125 } else { 00126 assert( mNumPatterns == 0 ); 00127 assert( mPatternStartIndex == 0 ); 00128 assert( mPatternEndIndex == 0 ); 00129 assert( mReplacedPattern == 0 ); 00130 } 00131 #endif 00132 } 00133 00134 Kleo::QGpgMEJob::~QGpgMEJob() { 00135 InvarianceChecker check( this ); 00136 delete mCtx; mCtx = 0; 00137 delete mInData; mInData = 0; 00138 delete mInDataDataProvider; mInDataDataProvider = 0; 00139 delete mOutData; mOutData = 0; 00140 delete mOutDataDataProvider; mOutDataDataProvider = 0; 00141 deleteAllPatterns(); 00142 } 00143 00144 void Kleo::QGpgMEJob::deleteAllPatterns() { 00145 if ( mPatterns ) 00146 for ( unsigned int i = 0 ; i < mNumPatterns ; ++i ) 00147 free( (void*)mPatterns[i] ); 00148 free( (void*)mReplacedPattern ); mReplacedPattern = 0; 00149 delete[] mPatterns; mPatterns = 0; 00150 mPatternEndIndex = mPatternStartIndex = mNumPatterns = 0; 00151 } 00152 00153 void Kleo::QGpgMEJob::hookupContextToEventLoopInteractor() { 00154 mCtx->setManagedByEventLoopInteractor( true ); 00155 TQObject::connect( QGpgME::EventLoopInteractor::instance(), 00156 TQT_SIGNAL(operationDoneEventSignal(GpgME::Context*,const GpgME::Error&)), 00157 mThis, TQT_SLOT(slotOperationDoneEvent(GpgME::Context*,const GpgME::Error&)) ); 00158 } 00159 00160 void Kleo::QGpgMEJob::setPatterns( const TQStringList & sl, bool allowEmpty ) { 00161 InvarianceChecker check( this ); 00162 deleteAllPatterns(); 00163 // create a new null-terminated C array of char* from patterns: 00164 mPatterns = new const char*[ sl.size() + 1 ]; 00165 const char* * pat_it = mPatterns; 00166 mNumPatterns = 0; 00167 for ( TQStringList::const_iterator it = sl.begin() ; it != sl.end() ; ++it ) { 00168 if ( (*it).isNull() ) 00169 continue; 00170 if ( (*it).isEmpty() && !allowEmpty ) 00171 continue; 00172 *pat_it++ = strdup( (*it).utf8().data() ); 00173 ++mNumPatterns; 00174 } 00175 *pat_it++ = 0; 00176 mReplacedPattern = 0; 00177 mPatternEndIndex = mChunkSize = mNumPatterns; 00178 } 00179 00180 void Kleo::QGpgMEJob::setChunkSize( unsigned int chunksize ) { 00181 InvarianceChecker check( this ); 00182 if ( mReplacedPattern ) { 00183 mPatterns[mPatternEndIndex] = mReplacedPattern; 00184 mReplacedPattern = 0; 00185 } 00186 mChunkSize = std::min( chunksize, mNumPatterns ); 00187 mPatternStartIndex = 0; 00188 mPatternEndIndex = mChunkSize; 00189 mReplacedPattern = mPatterns[mPatternEndIndex]; 00190 mPatterns[mPatternEndIndex] = 0; 00191 } 00192 00193 const char* * Kleo::QGpgMEJob::nextChunk() { 00194 InvarianceChecker check( this ); 00195 if ( mReplacedPattern ) { 00196 mPatterns[mPatternEndIndex] = mReplacedPattern; 00197 mReplacedPattern = 0; 00198 } 00199 mPatternStartIndex += mChunkSize; 00200 mPatternEndIndex += mChunkSize; 00201 if ( mPatternEndIndex < mNumPatterns ) { // could safely be <=, but the last entry is NULL anyway 00202 mReplacedPattern = mPatterns[mPatternEndIndex]; 00203 mPatterns[mPatternEndIndex] = 0; 00204 } 00205 return patterns(); 00206 } 00207 00208 const char* * Kleo::QGpgMEJob::patterns() const { 00209 InvarianceChecker check( this ); 00210 if ( mPatternStartIndex < mNumPatterns ) 00211 return mPatterns + mPatternStartIndex; 00212 return 0; 00213 } 00214 00215 GpgME::Error Kleo::QGpgMEJob::setSigningKeys( const std::vector<GpgME::Key> & signers ) { 00216 mCtx->clearSigningKeys(); 00217 for ( std::vector<GpgME::Key>::const_iterator it = signers.begin() ; it != signers.end() ; ++it ) { 00218 if ( (*it).isNull() ) 00219 continue; 00220 if ( const GpgME::Error err = mCtx->addSigningKey( *it ) ) 00221 return err; 00222 } 00223 return 0; 00224 } 00225 00226 void Kleo::QGpgMEJob::createInData( const TQByteArray & in ) { 00227 mInDataDataProvider = new QGpgME::TQByteArrayDataProvider( in ); 00228 mInData = new GpgME::Data( mInDataDataProvider ); 00229 assert( !mInData->isNull() ); 00230 } 00231 00232 void Kleo::QGpgMEJob::createOutData() { 00233 mOutDataDataProvider = new QGpgME::TQByteArrayDataProvider(); 00234 mOutData = new GpgME::Data( mOutDataDataProvider ); 00235 assert( !mOutData->isNull() ); 00236 } 00237 00238 static const unsigned int GetAuditLogFlags = GpgME::Context::AuditLogWithHelp|GpgME::Context::HtmlAuditLog; 00239 00240 static TQString audit_log_as_html( GpgME::Context * ctx, GpgME::Error & err ) { 00241 assert( ctx ); 00242 QGpgME::TQByteArrayDataProvider dp; 00243 GpgME::Data data( &dp ); 00244 assert( !data.isNull() ); 00245 if ( ( err = ctx->getAuditLog( data, GetAuditLogFlags ) ) ) 00246 return TQString(); 00247 const TQByteArray ba = dp.data(); 00248 return TQString::fromUtf8( ba.data(), ba.size() ); 00249 } 00250 00251 void Kleo::QGpgMEJob::doSlotOperationDoneEvent( GpgME::Context * context, const GpgME::Error & e ) { 00252 if ( context == mCtx ) { 00253 doEmitDoneSignal(); 00254 doOperationDoneEvent( e ); 00255 mThis->deleteLater(); 00256 } 00257 } 00258 00259 void Kleo::QGpgMEJob::getAuditLog() { 00260 if ( !mCtx ) 00261 return; 00262 mAuditLogAsHtml = audit_log_as_html( mCtx, mAuditLogError ); 00263 } 00264 00265 void Kleo::QGpgMEJob::doSlotCancel() { 00266 mCtx->cancelPendingOperation(); 00267 } 00268 00269 void Kleo::QGpgMEJob::showProgress( const char * what, int type, int current, int total ) { 00270 doEmitProgressSignal( QGpgMEProgressTokenMapper::instance()->map( what, type, current, total ), current, total ); 00271 } 00272 00273 char * Kleo::QGpgMEJob::getPassphrase( const char * useridHint, const char * /*description*/, 00274 bool previousWasBad, bool & canceled ) { 00275 // DF: here, description is the key fingerprint, twice, then "17 0". Not really descriptive. 00276 // So I'm ignoring TQString::fromLocal8Bit( description ) ) 00277 TQString msg = previousWasBad ? 00278 i18n( "You need a passphrase to unlock the secret key for user:<br/> %1 (retry)" ) : 00279 i18n( "You need a passphrase to unlock the secret key for user:<br/> %1" ); 00280 msg = msg.arg( TQString::fromUtf8( useridHint ) ) + "<br/><br/>"; 00281 msg.prepend( "<qt>" ); 00282 msg += i18n( "This dialog will reappear every time the passphrase is needed. For a more secure solution that also allows caching the passphrase, use gpg-agent." ) + "<br/>"; 00283 const TQString gpgAgent = TDEStandardDirs::findExe( "gpg-agent" ); 00284 if ( !gpgAgent.isEmpty() ) { 00285 msg += i18n( "gpg-agent was found in %1, but does not appear to be running." ) 00286 .arg( gpgAgent ); 00287 } else { 00288 msg += i18n( "gpg-agent is part of gnupg-%1, which you can download from %2" ) 00289 .arg( "1.9" ) 00290 .arg( "http://www.gnupg.org/download" ); // add #gnupg2 if you can make this a real link 00291 } 00292 msg += "<br/>"; 00293 msg += i18n( "For information on how to set up gpg-agent, see %1" ) 00294 .arg( "http://userbase.kde.org/KMail/PGP_MIME" ); 00295 msg += "<br/><br/>"; 00296 msg += i18n( "Enter passphrase:" ); 00297 Kleo::PassphraseDialog dlg( msg, i18n("Passphrase Dialog") ); 00298 if ( dlg.exec() != TQDialog::Accepted ) { 00299 canceled = true; 00300 return 0; 00301 } 00302 canceled = false; 00303 // gpgme++ free()s it, and we need to copy as long as dlg isn't deleted :o 00304 return strdup( dlg.passphrase() ); 00305 }