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