qgpgmejob.cpp
1 /*
2  qgpgmejob.cpp
3 
4  This file is part of libkleopatra, the KDE keymanagement library
5  Copyright (c) 2004 Klarälvdalens Datakonsult AB
6 
7  Libkleopatra is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of the
10  License, or (at your option) any later version.
11 
12  Libkleopatra is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 
21  In addition, as a special exception, the copyright holders give
22  permission to link the code of this program with any edition of
23  the TQt library by Trolltech AS, Norway (or with modified versions
24  of TQt that use the same license as TQt), and distribute linked
25  combinations including the two. You must obey the GNU General
26  Public License in all respects for all of the code used other than
27  TQt. If you modify this file, you may extend this exception to
28  your version of the file, but you are not obligated to do so. If
29  you do not wish to do so, delete this exception statement from
30  your version.
31 */
32 
33 #ifdef HAVE_CONFIG_H
34 #include <config.h>
35 #endif
36 
37 #include "qgpgmejob.h"
38 #include "qgpgmeprogresstokenmapper.h"
39 
40 #include <kleo/job.h>
41 #include <ui/passphrasedialog.h>
42 
43 #include <qgpgme/eventloopinteractor.h>
44 #include <qgpgme/dataprovider.h>
45 
46 #include <gpgmepp/context.h>
47 #include <gpgmepp/data.h>
48 
49 #include <tdelocale.h>
50 #include <kstandarddirs.h>
51 
52 #include <tqstring.h>
53 #include <tqstringlist.h>
54 
55 #include <algorithm>
56 
57 #include <assert.h>
58 #include <stdlib.h>
59 #include <string.h>
60 
61 namespace {
62  class InvarianceChecker {
63  public:
64 #ifdef NDEBUG
65  InvarianceChecker( const Kleo::QGpgMEJob * ) {}
66 #else
67  InvarianceChecker( const Kleo::QGpgMEJob * job )
68  : _this( job )
69  {
70  assert( _this );
71  _this->checkInvariants();
72  }
73  ~InvarianceChecker() {
74  _this->checkInvariants();
75  }
76  private:
77  const Kleo::QGpgMEJob * _this;
78 #endif
79  };
80 }
81 
82 Kleo::QGpgMEJob::QGpgMEJob( Kleo::Job * _this, GpgME::Context * context )
83  : GpgME::ProgressProvider(),
84  GpgME::PassphraseProvider(),
85  mThis( _this ),
86  mCtx( context ),
87  mInData( 0 ),
88  mInDataDataProvider( 0 ),
89  mOutData( 0 ),
90  mOutDataDataProvider( 0 ),
91  mPatterns( 0 ),
92  mReplacedPattern( 0 ),
93  mNumPatterns( 0 ),
94  mChunkSize( 1024 ),
95  mPatternStartIndex( 0 ), mPatternEndIndex( 0 )
96 {
97  InvarianceChecker check( this );
98  assert( context );
99  TQObject::connect( QGpgME::EventLoopInteractor::instance(), TQ_SIGNAL(aboutToDestroy()),
100  _this, TQ_SLOT(slotCancel()) );
101  context->setProgressProvider( this );
102  // (mmutz) work around a gpgme bug in versions at least <= 0.9.0.
103  // These versions will return GPG_ERR_NOT_IMPLEMENTED from
104  // a CMS sign operation when a passphrase callback is set.
105  if ( context->protocol() == GpgME::Context::OpenPGP )
106  context->setPassphraseProvider( this );
107 }
108 
109 void Kleo::QGpgMEJob::checkInvariants() const {
110 #ifndef NDEBUG
111  if ( mPatterns ) {
112  assert( mPatterns[mNumPatterns] == 0 );
113  if ( mPatternEndIndex > 0 ) {
114  assert( mPatternEndIndex > mPatternStartIndex );
115  assert( mPatternEndIndex - mPatternStartIndex == mChunkSize );
116  } else {
117  assert( mPatternEndIndex == mPatternStartIndex );
118  }
119  if ( mPatternEndIndex < mNumPatterns ) {
120  assert( mPatterns[mPatternEndIndex] == 0 );
121  assert( mReplacedPattern != 0 );
122  } else {
123  assert( mReplacedPattern == 0 );
124  }
125  } else {
126  assert( mNumPatterns == 0 );
127  assert( mPatternStartIndex == 0 );
128  assert( mPatternEndIndex == 0 );
129  assert( mReplacedPattern == 0 );
130  }
131 #endif
132 }
133 
134 Kleo::QGpgMEJob::~QGpgMEJob() {
135  InvarianceChecker check( this );
136  delete mCtx; mCtx = 0;
137  delete mInData; mInData = 0;
138  delete mInDataDataProvider; mInDataDataProvider = 0;
139  delete mOutData; mOutData = 0;
140  delete mOutDataDataProvider; mOutDataDataProvider = 0;
141  deleteAllPatterns();
142 }
143 
144 void Kleo::QGpgMEJob::deleteAllPatterns() {
145  if ( mPatterns )
146  for ( unsigned int i = 0 ; i < mNumPatterns ; ++i )
147  free( (void*)mPatterns[i] );
148  free( (void*)mReplacedPattern ); mReplacedPattern = 0;
149  delete[] mPatterns; mPatterns = 0;
150  mPatternEndIndex = mPatternStartIndex = mNumPatterns = 0;
151 }
152 
154  mCtx->setManagedByEventLoopInteractor( true );
155  TQObject::connect( QGpgME::EventLoopInteractor::instance(),
156  TQ_SIGNAL(operationDoneEventSignal(GpgME::Context*,const GpgME::Error&)),
157  mThis, TQ_SLOT(slotOperationDoneEvent(GpgME::Context*,const GpgME::Error&)) );
158 }
159 
160 void Kleo::QGpgMEJob::setPatterns( const TQStringList & sl, bool allowEmpty ) {
161  InvarianceChecker check( this );
162  deleteAllPatterns();
163  // create a new null-terminated C array of char* from patterns:
164  mPatterns = new const char*[ sl.size() + 1 ];
165  const char* * pat_it = mPatterns;
166  mNumPatterns = 0;
167  for ( TQStringList::const_iterator it = sl.begin() ; it != sl.end() ; ++it ) {
168  if ( (*it).isNull() )
169  continue;
170  if ( (*it).isEmpty() && !allowEmpty )
171  continue;
172  *pat_it++ = strdup( (*it).utf8().data() );
173  ++mNumPatterns;
174  }
175  *pat_it++ = 0;
176  mReplacedPattern = 0;
177  mPatternEndIndex = mChunkSize = mNumPatterns;
178 }
179 
180 void Kleo::QGpgMEJob::setChunkSize( unsigned int chunksize ) {
181  InvarianceChecker check( this );
182  if ( mReplacedPattern ) {
183  mPatterns[mPatternEndIndex] = mReplacedPattern;
184  mReplacedPattern = 0;
185  }
186  mChunkSize = std::min( chunksize, mNumPatterns );
187  mPatternStartIndex = 0;
188  mPatternEndIndex = mChunkSize;
189  mReplacedPattern = mPatterns[mPatternEndIndex];
190  mPatterns[mPatternEndIndex] = 0;
191 }
192 
193 const char* * Kleo::QGpgMEJob::nextChunk() {
194  InvarianceChecker check( this );
195  if ( mReplacedPattern ) {
196  mPatterns[mPatternEndIndex] = mReplacedPattern;
197  mReplacedPattern = 0;
198  }
199  mPatternStartIndex += mChunkSize;
200  mPatternEndIndex += mChunkSize;
201  if ( mPatternEndIndex < mNumPatterns ) { // could safely be <=, but the last entry is NULL anyway
202  mReplacedPattern = mPatterns[mPatternEndIndex];
203  mPatterns[mPatternEndIndex] = 0;
204  }
205  return patterns();
206 }
207 
208 const char* * Kleo::QGpgMEJob::patterns() const {
209  InvarianceChecker check( this );
210  if ( mPatternStartIndex < mNumPatterns )
211  return mPatterns + mPatternStartIndex;
212  return 0;
213 }
214 
215 GpgME::Error Kleo::QGpgMEJob::setSigningKeys( const std::vector<GpgME::Key> & signers ) {
216  mCtx->clearSigningKeys();
217  for ( std::vector<GpgME::Key>::const_iterator it = signers.begin() ; it != signers.end() ; ++it ) {
218  if ( (*it).isNull() )
219  continue;
220  if ( const GpgME::Error err = mCtx->addSigningKey( *it ) )
221  return err;
222  }
223  return 0;
224 }
225 
226 void Kleo::QGpgMEJob::createInData( const TQByteArray & in ) {
227  mInDataDataProvider = new QGpgME::TQByteArrayDataProvider( in );
228  mInData = new GpgME::Data( mInDataDataProvider );
229  assert( !mInData->isNull() );
230 }
231 
233  mOutDataDataProvider = new QGpgME::TQByteArrayDataProvider();
234  mOutData = new GpgME::Data( mOutDataDataProvider );
235  assert( !mOutData->isNull() );
236 }
237 
238 static const unsigned int GetAuditLogFlags = GpgME::Context::AuditLogWithHelp|GpgME::Context::HtmlAuditLog;
239 
240 static TQString audit_log_as_html( GpgME::Context * ctx, GpgME::Error & err ) {
241  assert( ctx );
242  QGpgME::TQByteArrayDataProvider dp;
243  GpgME::Data data( &dp );
244  assert( !data.isNull() );
245  if ( ( err = ctx->getAuditLog( data, GetAuditLogFlags ) ) )
246  return TQString();
247  const TQByteArray ba = dp.data();
248  return TQString::fromUtf8( ba.data(), ba.size() );
249 }
250 
251 void Kleo::QGpgMEJob::doSlotOperationDoneEvent( GpgME::Context * context, const GpgME::Error & e ) {
252  if ( context == mCtx ) {
253  doEmitDoneSignal();
254  doOperationDoneEvent( e );
255  mThis->deleteLater();
256  }
257 }
258 
260  if ( !mCtx )
261  return;
262  mAuditLogAsHtml = audit_log_as_html( mCtx, mAuditLogError );
263 }
264 
265 void Kleo::QGpgMEJob::doSlotCancel() {
266  mCtx->cancelPendingOperation();
267 }
268 
269 void Kleo::QGpgMEJob::showProgress( const char * what, int type, int current, int total ) {
270  doEmitProgressSignal( QGpgMEProgressTokenMapper::instance()->map( what, type, current, total ), current, total );
271 }
272 
273 char * Kleo::QGpgMEJob::getPassphrase( const char * useridHint, const char * /*description*/,
274  bool previousWasBad, bool & canceled ) {
275  // DF: here, description is the key fingerprint, twice, then "17 0". Not really descriptive.
276  // So I'm ignoring TQString::fromLocal8Bit( description ) )
277  TQString msg = previousWasBad ?
278  i18n( "You need a passphrase to unlock the secret key for user:<br/> %1 (retry)" ) :
279  i18n( "You need a passphrase to unlock the secret key for user:<br/> %1" );
280  msg = msg.arg( TQString::fromUtf8( useridHint ) ) + "<br/><br/>";
281  msg.prepend( "<qt>" );
282  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/>";
283  const TQString gpgAgent = TDEStandardDirs::findExe( "gpg-agent" );
284  if ( !gpgAgent.isEmpty() ) {
285  msg += i18n( "gpg-agent was found in %1, but does not appear to be running." )
286  .arg( gpgAgent );
287  } else {
288  msg += i18n( "gpg-agent is part of gnupg-%1, which you can download from %2" )
289  .arg( "1.9" )
290  .arg( "http://www.gnupg.org/download" ); // add #gnupg2 if you can make this a real link
291  }
292  msg += "<br/>";
293  msg += i18n( "For information on how to set up gpg-agent, see %1" )
294  .arg( "http://userbase.kde.org/KMail/PGP_MIME" );
295  msg += "<br/><br/>";
296  msg += i18n( "Enter passphrase:" );
297  Kleo::PassphraseDialog dlg( msg, i18n("Passphrase Dialog") );
298  if ( dlg.exec() != TQDialog::Accepted ) {
299  canceled = true;
300  return 0;
301  }
302  canceled = false;
303  // gpgme++ free()s it, and we need to copy as long as dlg isn't deleted :o
304  return strdup( dlg.passphrase().utf8() );
305 }
An abstract base class for asynchronous crypto operations.
Definition: job.h:64
This is a hackish helper class to avoid code duplication in this backend's Kleo::Job subclasses.
Definition: qgpgmejob.h:78
void createOutData()
Definition: qgpgmejob.cpp:232
void doSlotOperationDoneEvent(GpgME::Context *context, const GpgME::Error &e)
Definition: qgpgmejob.cpp:251
void getAuditLog()
Definition: qgpgmejob.cpp:259
const char ** nextChunk()
Definition: qgpgmejob.cpp:193
void setPatterns(const TQStringList &sl, bool allowEmpty=false)
Definition: qgpgmejob.cpp:160
GpgME::Error setSigningKeys(const std::vector< GpgME::Key > &signers)
Definition: qgpgmejob.cpp:215
void setChunkSize(unsigned int size)
Definition: qgpgmejob.cpp:180
void hookupContextToEventLoopInteractor()
Definition: qgpgmejob.cpp:153
void createInData(const TQByteArray &in)
Definition: qgpgmejob.cpp:226
const char ** patterns() const
Definition: qgpgmejob.cpp:208