/*
qgpgmejob . cpp
This file is part of libkleopatra , the KDE keymanagement library
Copyright ( c ) 2004 Klar <EFBFBD> lvdalens Datakonsult AB
Libkleopatra is free software ; you can redistribute it and / or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation ; either version 2 of the
License , or ( at your option ) any later version .
Libkleopatra is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the GNU
General Public License for more details .
You should have received a copy of the GNU General Public License
along with this program ; if not , write to the Free Software
Foundation , Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
In addition , as a special exception , the copyright holders give
permission to link the code of this program with any edition of
the TQt library by Trolltech AS , Norway ( or with modified versions
of TQt that use the same license as TQt ) , and distribute linked
combinations including the two . You must obey the GNU General
Public License in all respects for all of the code used other than
TQt . If you modify this file , you may extend this exception to
your version of the file , but you are not obligated to do so . If
you do not wish to do so , delete this exception statement from
your version .
*/
# ifdef HAVE_CONFIG_H
# include <config.h>
# endif
# include "qgpgmejob.h"
# include "qgpgmeprogresstokenmapper.h"
# include <kleo/job.h>
# include <ui/passphrasedialog.h>
# include <qgpgme/eventloopinteractor.h>
# include <qgpgme/dataprovider.h>
# include <gpgmepp/context.h>
# include <gpgmepp/data.h>
# include <tdelocale.h>
# include <kstandarddirs.h>
# include <tqstring.h>
# include <tqstringlist.h>
# include <algorithm>
# include <assert.h>
# include <stdlib.h>
# include <string.h>
namespace {
class InvarianceChecker {
public :
# ifdef NDEBUG
InvarianceChecker ( const Kleo : : QGpgMEJob * ) { }
# else
InvarianceChecker ( const Kleo : : QGpgMEJob * job )
: _this ( job )
{
assert ( _this ) ;
_this - > checkInvariants ( ) ;
}
~ InvarianceChecker ( ) {
_this - > checkInvariants ( ) ;
}
private :
const Kleo : : QGpgMEJob * _this ;
# endif
} ;
}
Kleo : : QGpgMEJob : : QGpgMEJob ( Kleo : : Job * _this , GpgME : : Context * context )
: GpgME : : ProgressProvider ( ) ,
GpgME : : PassphraseProvider ( ) ,
mThis ( _this ) ,
mCtx ( context ) ,
mInData ( 0 ) ,
mInDataDataProvider ( 0 ) ,
mOutData ( 0 ) ,
mOutDataDataProvider ( 0 ) ,
mPatterns ( 0 ) ,
mReplacedPattern ( 0 ) ,
mNumPatterns ( 0 ) ,
mChunkSize ( 1024 ) ,
mPatternStartIndex ( 0 ) , mPatternEndIndex ( 0 )
{
InvarianceChecker check ( this ) ;
assert ( context ) ;
TQObject : : connect ( QGpgME : : EventLoopInteractor : : instance ( ) , TQT_SIGNAL ( aboutToDestroy ( ) ) ,
_this , TQT_SLOT ( slotCancel ( ) ) ) ;
context - > setProgressProvider ( this ) ;
// (mmutz) work around a gpgme bug in versions at least <= 0.9.0.
// These versions will return GPG_ERR_NOT_IMPLEMENTED from
// a CMS sign operation when a passphrase callback is set.
if ( context - > protocol ( ) = = GpgME : : Context : : OpenPGP )
context - > setPassphraseProvider ( this ) ;
}
void Kleo : : QGpgMEJob : : checkInvariants ( ) const {
# ifndef NDEBUG
if ( mPatterns ) {
assert ( mPatterns [ mNumPatterns ] = = 0 ) ;
if ( mPatternEndIndex > 0 ) {
assert ( mPatternEndIndex > mPatternStartIndex ) ;
assert ( mPatternEndIndex - mPatternStartIndex = = mChunkSize ) ;
} else {
assert ( mPatternEndIndex = = mPatternStartIndex ) ;
}
if ( mPatternEndIndex < mNumPatterns ) {
assert ( mPatterns [ mPatternEndIndex ] = = 0 ) ;
assert ( mReplacedPattern ! = 0 ) ;
} else {
assert ( mReplacedPattern = = 0 ) ;
}
} else {
assert ( mNumPatterns = = 0 ) ;
assert ( mPatternStartIndex = = 0 ) ;
assert ( mPatternEndIndex = = 0 ) ;
assert ( mReplacedPattern = = 0 ) ;
}
# endif
}
Kleo : : QGpgMEJob : : ~ QGpgMEJob ( ) {
InvarianceChecker check ( this ) ;
delete mCtx ; mCtx = 0 ;
delete mInData ; mInData = 0 ;
delete mInDataDataProvider ; mInDataDataProvider = 0 ;
delete mOutData ; mOutData = 0 ;
delete mOutDataDataProvider ; mOutDataDataProvider = 0 ;
deleteAllPatterns ( ) ;
}
void Kleo : : QGpgMEJob : : deleteAllPatterns ( ) {
if ( mPatterns )
for ( unsigned int i = 0 ; i < mNumPatterns ; + + i )
free ( ( void * ) mPatterns [ i ] ) ;
free ( ( void * ) mReplacedPattern ) ; mReplacedPattern = 0 ;
delete [ ] mPatterns ; mPatterns = 0 ;
mPatternEndIndex = mPatternStartIndex = mNumPatterns = 0 ;
}
void Kleo : : QGpgMEJob : : hookupContextToEventLoopInteractor ( ) {
mCtx - > setManagedByEventLoopInteractor ( true ) ;
TQObject : : connect ( QGpgME : : EventLoopInteractor : : instance ( ) ,
TQT_SIGNAL ( operationDoneEventSignal ( GpgME : : Context * , const GpgME : : Error & ) ) ,
mThis , TQT_SLOT ( slotOperationDoneEvent ( GpgME : : Context * , const GpgME : : Error & ) ) ) ;
}
void Kleo : : QGpgMEJob : : setPatterns ( const TQStringList & sl , bool allowEmpty ) {
InvarianceChecker check ( this ) ;
deleteAllPatterns ( ) ;
// create a new null-terminated C array of char* from patterns:
mPatterns = new const char * [ sl . size ( ) + 1 ] ;
const char * * pat_it = mPatterns ;
mNumPatterns = 0 ;
for ( TQStringList : : const_iterator it = sl . begin ( ) ; it ! = sl . end ( ) ; + + it ) {
if ( ( * it ) . isNull ( ) )
continue ;
if ( ( * it ) . isEmpty ( ) & & ! allowEmpty )
continue ;
* pat_it + + = strdup ( ( * it ) . utf8 ( ) . data ( ) ) ;
+ + mNumPatterns ;
}
* pat_it + + = 0 ;
mReplacedPattern = 0 ;
mPatternEndIndex = mChunkSize = mNumPatterns ;
}
void Kleo : : QGpgMEJob : : setChunkSize ( unsigned int chunksize ) {
InvarianceChecker check ( this ) ;
if ( mReplacedPattern ) {
mPatterns [ mPatternEndIndex ] = mReplacedPattern ;
mReplacedPattern = 0 ;
}
mChunkSize = std : : min ( chunksize , mNumPatterns ) ;
mPatternStartIndex = 0 ;
mPatternEndIndex = mChunkSize ;
mReplacedPattern = mPatterns [ mPatternEndIndex ] ;
mPatterns [ mPatternEndIndex ] = 0 ;
}
const char * * Kleo : : QGpgMEJob : : nextChunk ( ) {
InvarianceChecker check ( this ) ;
if ( mReplacedPattern ) {
mPatterns [ mPatternEndIndex ] = mReplacedPattern ;
mReplacedPattern = 0 ;
}
mPatternStartIndex + = mChunkSize ;
mPatternEndIndex + = mChunkSize ;
if ( mPatternEndIndex < mNumPatterns ) { // could safely be <=, but the last entry is NULL anyway
mReplacedPattern = mPatterns [ mPatternEndIndex ] ;
mPatterns [ mPatternEndIndex ] = 0 ;
}
return patterns ( ) ;
}
const char * * Kleo : : QGpgMEJob : : patterns ( ) const {
InvarianceChecker check ( this ) ;
if ( mPatternStartIndex < mNumPatterns )
return mPatterns + mPatternStartIndex ;
return 0 ;
}
GpgME : : Error Kleo : : QGpgMEJob : : setSigningKeys ( const std : : vector < GpgME : : Key > & signers ) {
mCtx - > clearSigningKeys ( ) ;
for ( std : : vector < GpgME : : Key > : : const_iterator it = signers . begin ( ) ; it ! = signers . end ( ) ; + + it ) {
if ( ( * it ) . isNull ( ) )
continue ;
if ( const GpgME : : Error err = mCtx - > addSigningKey ( * it ) )
return err ;
}
return 0 ;
}
void Kleo : : QGpgMEJob : : createInData ( const TQByteArray & in ) {
mInDataDataProvider = new QGpgME : : TQByteArrayDataProvider ( in ) ;
mInData = new GpgME : : Data ( mInDataDataProvider ) ;
assert ( ! mInData - > isNull ( ) ) ;
}
void Kleo : : QGpgMEJob : : createOutData ( ) {
mOutDataDataProvider = new QGpgME : : TQByteArrayDataProvider ( ) ;
mOutData = new GpgME : : Data ( mOutDataDataProvider ) ;
assert ( ! mOutData - > isNull ( ) ) ;
}
static const unsigned int GetAuditLogFlags = GpgME : : Context : : AuditLogWithHelp | GpgME : : Context : : HtmlAuditLog ;
static TQString audit_log_as_html ( GpgME : : Context * ctx , GpgME : : Error & err ) {
assert ( ctx ) ;
QGpgME : : TQByteArrayDataProvider dp ;
GpgME : : Data data ( & dp ) ;
assert ( ! data . isNull ( ) ) ;
if ( ( err = ctx - > getAuditLog ( data , GetAuditLogFlags ) ) )
return TQString ( ) ;
const TQByteArray ba = dp . data ( ) ;
return TQString : : fromUtf8 ( ba . data ( ) , ba . size ( ) ) ;
}
void Kleo : : QGpgMEJob : : doSlotOperationDoneEvent ( GpgME : : Context * context , const GpgME : : Error & e ) {
if ( context = = mCtx ) {
doEmitDoneSignal ( ) ;
doOperationDoneEvent ( e ) ;
mThis - > deleteLater ( ) ;
}
}
void Kleo : : QGpgMEJob : : getAuditLog ( ) {
if ( ! mCtx )
return ;
mAuditLogAsHtml = audit_log_as_html ( mCtx , mAuditLogError ) ;
}
void Kleo : : QGpgMEJob : : doSlotCancel ( ) {
mCtx - > cancelPendingOperation ( ) ;
}
void Kleo : : QGpgMEJob : : showProgress ( const char * what , int type , int current , int total ) {
doEmitProgressSignal ( QGpgMEProgressTokenMapper : : instance ( ) - > map ( what , type , current , total ) , current , total ) ;
}
char * Kleo : : QGpgMEJob : : getPassphrase ( const char * useridHint , const char * /*description*/ ,
bool previousWasBad , bool & canceled ) {
// DF: here, description is the key fingerprint, twice, then "17 0". Not really descriptive.
// So I'm ignoring TQString::fromLocal8Bit( description ) )
TQString msg = previousWasBad ?
i18n ( " You need a passphrase to unlock the secret key for user:<br/> %1 (retry) " ) :
i18n ( " You need a passphrase to unlock the secret key for user:<br/> %1 " ) ;
msg = msg . arg ( TQString : : fromUtf8 ( useridHint ) ) + " <br/><br/> " ;
msg . prepend ( " <qt> " ) ;
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/> " ;
const TQString gpgAgent = TDEStandardDirs : : findExe ( " gpg-agent " ) ;
if ( ! gpgAgent . isEmpty ( ) ) {
msg + = i18n ( " gpg-agent was found in %1, but does not appear to be running. " )
. arg ( gpgAgent ) ;
} else {
msg + = i18n ( " gpg-agent is part of gnupg-%1, which you can download from %2 " )
. arg ( " 1.9 " )
. arg ( " http://www.gnupg.org/download " ) ; // add #gnupg2 if you can make this a real link
}
msg + = " <br/> " ;
msg + = i18n ( " For information on how to set up gpg-agent, see %1 " )
. arg ( " http://userbase.kde.org/KMail/PGP_MIME " ) ;
msg + = " <br/><br/> " ;
msg + = i18n ( " Enter passphrase: " ) ;
Kleo : : PassphraseDialog dlg ( msg , i18n ( " Passphrase Dialog " ) ) ;
if ( dlg . exec ( ) ! = TQDialog : : Accepted ) {
canceled = true ;
return 0 ;
}
canceled = false ;
// gpgme++ free()s it, and we need to copy as long as dlg isn't deleted :o
return strdup ( dlg . passphrase ( ) ) ;
}