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 <klocale.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 <string.h>
59 
60 namespace {
61  class InvarianceChecker {
62  public:
63 #ifdef NDEBUG
64  InvarianceChecker( const Kleo::QGpgMEJob * ) {}
65 #else
66  InvarianceChecker( const Kleo::QGpgMEJob * job )
67  : _this( job )
68  {
69  assert( _this );
70  _this->checkInvariants();
71  }
72  ~InvarianceChecker() {
73  _this->checkInvariants();
74  }
75  private:
76  const Kleo::QGpgMEJob * _this;
77 #endif
78  };
79 }
80 
81 Kleo::QGpgMEJob::QGpgMEJob( Kleo::Job * _this, GpgME::Context * context )
82  : GpgME::ProgressProvider(),
83  GpgME::PassphraseProvider(),
84  mThis( _this ),
85  mCtx( context ),
86  mInData( 0 ),
87  mInDataDataProvider( 0 ),
88  mOutData( 0 ),
89  mOutDataDataProvider( 0 ),
90  mPatterns( 0 ),
91  mReplacedPattern( 0 ),
92  mNumPatterns( 0 ),
93  mChunkSize( 1024 ),
94  mPatternStartIndex( 0 ), mPatternEndIndex( 0 )
95 {
96  InvarianceChecker check( this );
97  assert( context );
98  TQObject::connect( QGpgME::EventLoopInteractor::instance(), TQT_SIGNAL(aboutToDestroy()),
99  _this, TQT_SLOT(slotCancel()) );
100  context->setProgressProvider( this );
101  // (mmutz) work around a gpgme bug in versions at least <= 0.9.0.
102  // These versions will return GPG_ERR_NOT_IMPLEMENTED from
103  // a CMS sign operation when a passphrase callback is set.
104  if ( context->protocol() == GpgME::Context::OpenPGP )
105  context->setPassphraseProvider( this );
106 }
107 
108 void Kleo::QGpgMEJob::checkInvariants() const {
109 #ifndef NDEBUG
110  if ( mPatterns ) {
111  assert( mPatterns[mNumPatterns] == 0 );
112  if ( mPatternEndIndex > 0 ) {
113  assert( mPatternEndIndex > mPatternStartIndex );
114  assert( mPatternEndIndex - mPatternStartIndex == mChunkSize );
115  } else {
116  assert( mPatternEndIndex == mPatternStartIndex );
117  }
118  if ( mPatternEndIndex < mNumPatterns ) {
119  assert( mPatterns[mPatternEndIndex] == 0 );
120  assert( mReplacedPattern != 0 );
121  } else {
122  assert( mReplacedPattern == 0 );
123  }
124  } else {
125  assert( mNumPatterns == 0 );
126  assert( mPatternStartIndex == 0 );
127  assert( mPatternEndIndex == 0 );
128  assert( mReplacedPattern == 0 );
129  }
130 #endif
131 }
132 
133 Kleo::QGpgMEJob::~QGpgMEJob() {
134  InvarianceChecker check( this );
135  delete mCtx; mCtx = 0;
136  delete mInData; mInData = 0;
137  delete mInDataDataProvider; mInDataDataProvider = 0;
138  delete mOutData; mOutData = 0;
139  delete mOutDataDataProvider; mOutDataDataProvider = 0;
140  deleteAllPatterns();
141 }
142 
143 void Kleo::QGpgMEJob::deleteAllPatterns() {
144  if ( mPatterns )
145  for ( unsigned int i = 0 ; i < mNumPatterns ; ++i )
146  free( (void*)mPatterns[i] );
147  free( (void*)mReplacedPattern ); mReplacedPattern = 0;
148  delete[] mPatterns; mPatterns = 0;
149  mPatternEndIndex = mPatternStartIndex = mNumPatterns = 0;
150 }
151 
153  mCtx->setManagedByEventLoopInteractor( true );
154  TQObject::connect( QGpgME::EventLoopInteractor::instance(),
155  TQT_SIGNAL(operationDoneEventSignal(GpgME::Context*,const GpgME::Error&)),
156  mThis, TQT_SLOT(slotOperationDoneEvent(GpgME::Context*,const GpgME::Error&)) );
157 }
158 
159 void Kleo::QGpgMEJob::setPatterns( const TQStringList & sl, bool allowEmpty ) {
160  InvarianceChecker check( this );
161  deleteAllPatterns();
162  // create a new null-terminated C array of char* from patterns:
163  mPatterns = new const char*[ sl.size() + 1 ];
164  const char* * pat_it = mPatterns;
165  mNumPatterns = 0;
166  for ( TQStringList::const_iterator it = sl.begin() ; it != sl.end() ; ++it ) {
167  if ( (*it).isNull() )
168  continue;
169  if ( (*it).isEmpty() && !allowEmpty )
170  continue;
171  *pat_it++ = strdup( (*it).utf8().data() );
172  ++mNumPatterns;
173  }
174  *pat_it++ = 0;
175  mReplacedPattern = 0;
176  mPatternEndIndex = mChunkSize = mNumPatterns;
177 }
178 
179 void Kleo::QGpgMEJob::setChunkSize( unsigned int chunksize ) {
180  InvarianceChecker check( this );
181  if ( mReplacedPattern ) {
182  mPatterns[mPatternEndIndex] = mReplacedPattern;
183  mReplacedPattern = 0;
184  }
185  mChunkSize = std::min( chunksize, mNumPatterns );
186  mPatternStartIndex = 0;
187  mPatternEndIndex = mChunkSize;
188  mReplacedPattern = mPatterns[mPatternEndIndex];
189  mPatterns[mPatternEndIndex] = 0;
190 }
191 
192 const char* * Kleo::QGpgMEJob::nextChunk() {
193  InvarianceChecker check( this );
194  if ( mReplacedPattern ) {
195  mPatterns[mPatternEndIndex] = mReplacedPattern;
196  mReplacedPattern = 0;
197  }
198  mPatternStartIndex += mChunkSize;
199  mPatternEndIndex += mChunkSize;
200  if ( mPatternEndIndex < mNumPatterns ) { // could safely be <=, but the last entry is NULL anyway
201  mReplacedPattern = mPatterns[mPatternEndIndex];
202  mPatterns[mPatternEndIndex] = 0;
203  }
204  return patterns();
205 }
206 
207 const char* * Kleo::QGpgMEJob::patterns() const {
208  InvarianceChecker check( this );
209  if ( mPatternStartIndex < mNumPatterns )
210  return mPatterns + mPatternStartIndex;
211  return 0;
212 }
213 
214 GpgME::Error Kleo::QGpgMEJob::setSigningKeys( const std::vector<GpgME::Key> & signers ) {
215  mCtx->clearSigningKeys();
216  for ( std::vector<GpgME::Key>::const_iterator it = signers.begin() ; it != signers.end() ; ++it ) {
217  if ( (*it).isNull() )
218  continue;
219  if ( const GpgME::Error err = mCtx->addSigningKey( *it ) )
220  return err;
221  }
222  return 0;
223 }
224 
225 void Kleo::QGpgMEJob::createInData( const TQByteArray & in ) {
226  mInDataDataProvider = new QGpgME::TQByteArrayDataProvider( in );
227  mInData = new GpgME::Data( mInDataDataProvider );
228  assert( !mInData->isNull() );
229 }
230 
232  mOutDataDataProvider = new QGpgME::TQByteArrayDataProvider();
233  mOutData = new GpgME::Data( mOutDataDataProvider );
234  assert( !mOutData->isNull() );
235 }
236 
237 static const unsigned int GetAuditLogFlags = GpgME::Context::AuditLogWithHelp|GpgME::Context::HtmlAuditLog;
238 
239 static TQString audit_log_as_html( GpgME::Context * ctx, GpgME::Error & err ) {
240  assert( ctx );
241  QGpgME::TQByteArrayDataProvider dp;
242  GpgME::Data data( &dp );
243  assert( !data.isNull() );
244  if ( ( err = ctx->getAuditLog( data, GetAuditLogFlags ) ) )
245  return TQString();
246  const TQByteArray ba = dp.data();
247  return TQString::fromUtf8( ba.data(), ba.size() );
248 }
249 
250 void Kleo::QGpgMEJob::doSlotOperationDoneEvent( GpgME::Context * context, const GpgME::Error & e ) {
251  if ( context == mCtx ) {
252  doEmitDoneSignal();
253  doOperationDoneEvent( e );
254  mThis->deleteLater();
255  }
256 }
257 
259  if ( !mCtx )
260  return;
261  mAuditLogAsHtml = audit_log_as_html( mCtx, mAuditLogError );
262 }
263 
264 void Kleo::QGpgMEJob::doSlotCancel() {
265  mCtx->cancelPendingOperation();
266 }
267 
268 void Kleo::QGpgMEJob::showProgress( const char * what, int type, int current, int total ) {
269  doEmitProgressSignal( QGpgMEProgressTokenMapper::instance()->map( what, type, current, total ), current, total );
270 }
271 
272 char * Kleo::QGpgMEJob::getPassphrase( const char * useridHint, const char * /*description*/,
273  bool previousWasBad, bool & canceled ) {
274  // DF: here, description is the key fingerprint, twice, then "17 0". Not really descriptive.
275  // So I'm ignoring TQString::fromLocal8Bit( description ) )
276  TQString msg = previousWasBad ?
277  i18n( "You need a passphrase to unlock the secret key for user:<br/> %1 (retry)" ) :
278  i18n( "You need a passphrase to unlock the secret key for user:<br/> %1" );
279  msg = msg.arg( TQString::fromUtf8( useridHint ) ) + "<br/><br/>";
280  msg.prepend( "<qt>" );
281  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/>";
282  const TQString gpgAgent = KStandardDirs::findExe( "gpg-agent" );
283  if ( !gpgAgent.isEmpty() ) {
284  msg += i18n( "gpg-agent was found in %1, but does not appear to be running." )
285  .arg( gpgAgent );
286  } else {
287  msg += i18n( "gpg-agent is part of gnupg-%1, which you can download from %2" )
288  .arg( "1.9" )
289  .arg( "http://www.gnupg.org/download" ); // add #gnupg2 if you can make this a real link
290  }
291  msg += "<br/>";
292  msg += i18n( "For information on how to set up gpg-agent, see %1" )
293  .arg( "http://kmail.kde.org/kmail-pgpmime-howto.html" );
294  msg += "<br/><br/>";
295  msg += i18n( "Enter passphrase:" );
296  Kleo::PassphraseDialog dlg( msg, i18n("Passphrase Dialog") );
297  if ( dlg.exec() != TQDialog::Accepted ) {
298  canceled = true;
299  return 0;
300  }
301  canceled = false;
302  // gpgme++ free()s it, and we need to copy as long as dlg isn't deleted :o
303  return strdup( dlg.passphrase() );
304 }