qgpgmekeylistjob.cpp
1 /*
2  qgpgmekeylistjob.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 "qgpgmekeylistjob.h"
38 
39 #include <qgpgme/eventloopinteractor.h>
40 
41 #include <gpgmepp/key.h>
42 #include <gpgmepp/context.h>
43 #include <gpgmepp/keylistresult.h>
44 #include <gpg-error.h>
45 
46 #include <tdemessagebox.h>
47 #include <tdelocale.h>
48 #include <kdebug.h>
49 
50 #include <tqstringlist.h>
51 #include <tqtimer.h>
52 
53 #include <algorithm>
54 
55 #include <stdlib.h>
56 #include <string.h>
57 #include <assert.h>
58 
59 Kleo::QGpgMEKeyListJob::QGpgMEKeyListJob( GpgME::Context * context )
60  : KeyListJob( QGpgME::EventLoopInteractor::instance(), "Kleo::QGpgMEKeyListJob" ),
61  QGpgMEJob( this, context ),
62  mResult(), mSecretOnly( false )
63 {
64  assert( context );
65 }
66 
67 Kleo::QGpgMEKeyListJob::~QGpgMEKeyListJob() {
68 }
69 
70 void Kleo::QGpgMEKeyListJob::setup( const TQStringList & pats, bool secretOnly ) {
71  assert( !patterns() );
72 
73  mSecretOnly = secretOnly;
74  setPatterns( pats );
75 }
76 
77 GpgME::Error Kleo::QGpgMEKeyListJob::start( const TQStringList & pats, bool secretOnly ) {
78  setup( pats, secretOnly );
79 
80  hookupContextToEventLoopInteractor();
81  connect( QGpgME::EventLoopInteractor::instance(),
82  TQ_SIGNAL(nextKeyEventSignal(GpgME::Context*,const GpgME::Key&)),
83  TQ_SLOT(slotNextKeyEvent(GpgME::Context*,const GpgME::Key&)) );
84 
85  // The communication channel between gpgme and gpgsm is limited in
86  // the number of patterns that can be transported, but they won't
87  // say to how much, so we need to find out ourselves if we get a
88  // LINE_TOO_LONG error back...
89 
90  // We could of course just feed them single patterns, and that would
91  // probably be easier, but the performance penalty would currently
92  // be noticable.
93 
94  while ( const GpgME::Error err = mCtx->startKeyListing( patterns(), mSecretOnly ) ) {
95  if ( err.code() == GPG_ERR_LINE_TOO_LONG ) {
96  setChunkSize( chunkSize()/2 );
97  if ( chunkSize() >= 1 ) {
98  kdDebug(5150) << "QGpgMEKeyListJob::start(): retrying keylisting with chunksize " << chunkSize() << endl;
99  continue;
100  }
101  } else if ( err.code() == GPG_ERR_EOF ) {
102  kdDebug(5150) << "QGpgMEKeyListJob::start(): early end of keylisting, trying to fake an empty result" << endl;
103  TQTimer::singleShot( 10, this, TQ_SLOT(slotFakeOperationDoneEvent()) );
104  return GpgME::Error();
105  }
106  deleteLater();
107  mResult = GpgME::KeyListResult( 0, err );
108  return err;
109  }
110  mResult = GpgME::KeyListResult( 0, 0 );
111  return 0;
112 }
113 
114 GpgME::KeyListResult Kleo::QGpgMEKeyListJob::exec( const TQStringList & pats, bool secretOnly, std::vector<GpgME::Key> & keys ) {
115  setup( pats, secretOnly );
116 
117  // The communication channel between gpgme and gpgsm is limited in
118  // the number of patterns that can be transported, but they won't
119  // say to how much, so we need to find out ourselves if we get a
120  // LINE_TOO_LONG error back...
121 
122  // We could of course just feed them single patterns, and that would
123  // probably be easier, but the performance penalty would currently
124  // be noticable.
125 
126  for (;;) {
127  keys.clear();
128  mResult = attemptSyncKeyListing( keys );
129  if ( !mResult.error() || mResult.error().code() != GPG_ERR_LINE_TOO_LONG )
130  return mResult;
131  // got LINE_TOO_LONG, try a smaller chunksize:
132  setChunkSize( chunkSize()/2 );
133  if ( chunkSize() < 1 )
134  // chunks smaller than one can't be -> return the error.
135  return mResult;
136  kdDebug(5150) << "QGpgMEKeyListJob::exec(): retrying keylisting with chunksize " << chunkSize() << endl;
137  }
138  kdFatal(5150) << "QGpgMEKeyListJob::exec(): Oops, this is not supposed to happen!" << endl;
139  return GpgME::KeyListResult();
140 }
141 
142 GpgME::KeyListResult Kleo::QGpgMEKeyListJob::attemptSyncKeyListing( std::vector<GpgME::Key> & keys ) {
143  GpgME::KeyListResult result;
144  for ( const char* * chunk = patterns() ; chunk ; chunk = nextChunk() ) {
145 
146  if ( const GpgME::Error err = mCtx->startKeyListing( chunk, mSecretOnly ) )
147  return GpgME::KeyListResult( 0, err );
148 
149  GpgME::Error err;
150  do
151  keys.push_back( mCtx->nextKey( err ) );
152  while ( !err );
153  keys.pop_back();
154  result.mergeWith( mCtx->endKeyListing() );
155  if ( result.error() )
156  break;
157  }
158  return result;
159 }
160 
161 void Kleo::QGpgMEKeyListJob::slotNextKeyEvent( GpgME::Context * context, const GpgME::Key & key ) {
162  if ( context == mCtx )
163  emit nextKey( key );
164 }
165 
166 void Kleo::QGpgMEKeyListJob::slotFakeOperationDoneEvent() {
167  const GpgME::KeyListResult res = mCtx->keyListResult();
168  if ( !res.error().code() == GPG_ERR_EOF )
169  kdDebug(5150) << "QGpgMEKeyListJob::slotFakeOperationDoneEvent: expected EOF, got "
170  << res.error().asString() << endl;
171  mResult = GpgME::KeyListResult();
172  emit done();
173  emit result( mResult );
174  deleteLater();
175 }
176 
177 void Kleo::QGpgMEKeyListJob::slotOperationDoneEvent( GpgME::Context * context, const GpgME::Error & ) {
178  if ( context != mCtx )
179  return;
180  mResult.mergeWith( mCtx->keyListResult() );
181  if ( !mResult.error() )
182  if ( const char* * chunk = nextChunk() ) {
183  if ( const GpgME::Error err = mCtx->startKeyListing( chunk, mSecretOnly ) )
184  mResult.mergeWith( GpgME::KeyListResult( 0, err ) );
185  else
186  return;
187  }
188  emit done();
189  emit result( mResult );
190  deleteLater();
191 }
192 
193 void Kleo::QGpgMEKeyListJob::showErrorDialog( TQWidget * parent, const TQString & caption ) const {
194  if ( !mResult.error() || mResult.error().isCanceled() )
195  return;
196  const TQString msg = i18n( "<qt><p>An error occurred while fetching "
197  "the keys from the backend:</p>"
198  "<p><b>%1</b></p></qt>" )
199  .arg( TQString::fromLocal8Bit( mResult.error().asString() ) );
200  KMessageBox::error( parent, msg, caption );
201 }
202 
203 #include "qgpgmekeylistjob.moc"