00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 #include <config-kleopatra.h>
00034
00035 #include "dumpcrlcachecommand.h"
00036
00037 #include "command_p.h"
00038
00039 #include <utils/kdlogtextwidget.h>
00040 #include <utils/gnupg-helper.h>
00041
00042 #include <KProcess>
00043 #include <KMessageBox>
00044 #include <KLocale>
00045 #include <KPushButton>
00046 #include <KStandardGuiItem>
00047 #include <KGlobalSettings>
00048
00049 #include <QString>
00050 #include <QByteArray>
00051 #include <QTimer>
00052 #include <QLayout>
00053
00054
00055 static const int PROCESS_TERMINATE_TIMEOUT = 5000;
00056
00057 namespace {
00058 class DumpCrlCacheDialog : public QDialog {
00059 Q_OBJECT
00060 public:
00061 explicit DumpCrlCacheDialog( QWidget * parent=0 )
00062 : QDialog( parent ), ui( this )
00063 {
00064
00065 }
00066
00067 Q_SIGNALS:
00068 void updateRequested();
00069
00070 public Q_SLOTS:
00071 void append( const QString & line ) {
00072 ui.logTextWidget.message( line );
00073 }
00074 void clear() {
00075 ui.logTextWidget.clear();
00076 }
00077
00078 private:
00079 struct Ui {
00080 KDLogTextWidget logTextWidget;
00081 KPushButton updateButton, closeButton;
00082 QVBoxLayout vlay;
00083 QHBoxLayout hlay;
00084
00085 explicit Ui( DumpCrlCacheDialog * q )
00086 : logTextWidget( q ),
00087 updateButton( i18n("&Update"), q ),
00088 closeButton( KStandardGuiItem::close(), q ),
00089 vlay( q ),
00090 hlay()
00091 {
00092 KDAB_SET_OBJECT_NAME( logTextWidget );
00093 KDAB_SET_OBJECT_NAME( updateButton );
00094 KDAB_SET_OBJECT_NAME( closeButton );
00095 KDAB_SET_OBJECT_NAME( vlay );
00096 KDAB_SET_OBJECT_NAME( hlay );
00097
00098 logTextWidget.setFont( KGlobalSettings::fixedFont() );
00099 logTextWidget.setMinimumVisibleLines( 25 );
00100 logTextWidget.setMinimumVisibleColumns( 80 );
00101
00102 vlay.addWidget( &logTextWidget, 1 );
00103 vlay.addLayout( &hlay );
00104
00105 hlay.addWidget( &updateButton );
00106 hlay.addStretch( 1 );
00107 hlay.addWidget( &closeButton );
00108
00109 connect( &updateButton, SIGNAL(clicked()),
00110 q, SIGNAL(updateRequested()) );
00111 connect( &closeButton, SIGNAL(clicked()),
00112 q, SLOT(close()) );
00113 }
00114 } ui;
00115 };
00116 }
00117
00118 using namespace Kleo;
00119 using namespace Kleo::Commands;
00120
00121 static QByteArray chomped( QByteArray ba ) {
00122 while ( ba.endsWith( '\n' ) || ba.endsWith( '\r' ) )
00123 ba.chop( 1 );
00124 return ba;
00125 }
00126
00127 class DumpCrlCacheCommand::Private : Command::Private {
00128 friend class ::Kleo::Commands::DumpCrlCacheCommand;
00129 DumpCrlCacheCommand * q_func() const { return static_cast<DumpCrlCacheCommand*>( q ); }
00130 public:
00131 explicit Private( DumpCrlCacheCommand * qq, KeyListController * c );
00132 ~Private();
00133
00134 QString errorString() const {
00135 return QString::fromLocal8Bit( errorBuffer );
00136 }
00137
00138 private:
00139 void init();
00140 void refreshView();
00141
00142 private:
00143 void slotProcessFinished( int, QProcess::ExitStatus );
00144
00145 void slotProcessReadyReadStandardOutput() {
00146 while ( process.canReadLine() ) {
00147 const QByteArray line = chomped( process.readLine() );
00148 if ( dialog )
00149 dialog->append( QString::fromLocal8Bit( line ) );
00150 }
00151 }
00152
00153 void slotProcessReadyReadStandardError() {
00154 errorBuffer += process.readAllStandardError();
00155 }
00156
00157 void slotUpdateRequested() {
00158 if ( process.state() == QProcess::NotRunning )
00159 refreshView();
00160 }
00161
00162 void slotDialogDestroyed() {
00163 dialog = 0;
00164 if ( process.state() != QProcess::NotRunning )
00165 q->cancel();
00166 else
00167 finished();
00168 }
00169
00170 private:
00171 DumpCrlCacheDialog * dialog;
00172 KProcess process;
00173 QByteArray errorBuffer;
00174 bool canceled;
00175 };
00176
00177 DumpCrlCacheCommand::Private * DumpCrlCacheCommand::d_func() { return static_cast<Private*>( d.get() ); }
00178 const DumpCrlCacheCommand::Private * DumpCrlCacheCommand::d_func() const { return static_cast<const Private*>( d.get() ); }
00179
00180 #define d d_func()
00181 #define q q_func()
00182
00183 DumpCrlCacheCommand::Private::Private( DumpCrlCacheCommand * qq, KeyListController * c )
00184 : Command::Private( qq, c ),
00185 process(),
00186 errorBuffer(),
00187 canceled( false )
00188 {
00189 process.setOutputChannelMode( KProcess::SeparateChannels );
00190 process.setReadChannel( KProcess::StandardOutput );
00191 process << gpgSmPath() << "--call-dirmngr" << "listcrls";
00192 }
00193
00194 DumpCrlCacheCommand::Private::~Private() {
00195 if ( dialog && !dialog->isVisible() )
00196 delete dialog;
00197 }
00198
00199 DumpCrlCacheCommand::DumpCrlCacheCommand( KeyListController * c )
00200 : Command( new Private( this, c ) )
00201 {
00202 d->init();
00203 }
00204
00205 DumpCrlCacheCommand::DumpCrlCacheCommand( QAbstractItemView * v, KeyListController * c )
00206 : Command( v, new Private( this, c ) )
00207 {
00208 d->init();
00209 }
00210
00211 void DumpCrlCacheCommand::Private::init() {
00212 connect( &process, SIGNAL(finished(int,QProcess::ExitStatus)),
00213 q, SLOT(slotProcessFinished(int,QProcess::ExitStatus)) );
00214 connect( &process, SIGNAL(readyReadStandardError()),
00215 q, SLOT(slotProcessReadyReadStandardError()) );
00216 connect( &process, SIGNAL(readyReadStandardOutput()),
00217 q, SLOT(slotProcessReadyReadStandardOutput()) );
00218 }
00219
00220 DumpCrlCacheCommand::~DumpCrlCacheCommand() {}
00221
00222 void DumpCrlCacheCommand::doStart() {
00223
00224 d->dialog = new DumpCrlCacheDialog;
00225 d->dialog->setAttribute( Qt::WA_DeleteOnClose );
00226 d->dialog->setWindowTitle( i18n("CRL Cache Dump") );
00227
00228 connect( d->dialog, SIGNAL(updateRequested()),
00229 this, SLOT(slotUpdateRequested()) );
00230 connect( d->dialog, SIGNAL(destroyed()),
00231 this, SLOT(slotDialogDestroyed()) );
00232
00233 d->refreshView();
00234 }
00235
00236 void DumpCrlCacheCommand::Private::refreshView() {
00237
00238 dialog->clear();
00239
00240 process.start();
00241
00242 if ( process.waitForStarted() ) {
00243 dialog->show();
00244 } else {
00245 KMessageBox::error( dialog ? static_cast<QWidget*>( dialog ) : parentWidgetOrView(),
00246 i18n( "Unable to start process gpgsm. "
00247 "Please check your installation." ),
00248 i18n( "Dump CRL Cache Error" ) );
00249 finished();
00250 }
00251 }
00252
00253 void DumpCrlCacheCommand::doCancel() {
00254 d->canceled = true;
00255 if ( d->process.state() != QProcess::NotRunning ) {
00256 d->process.terminate();
00257 QTimer::singleShot( PROCESS_TERMINATE_TIMEOUT, &d->process, SLOT(kill()) );
00258 }
00259 if ( d->dialog )
00260 d->dialog->close();
00261 d->dialog = 0;
00262 }
00263
00264 void DumpCrlCacheCommand::Private::slotProcessFinished( int code, QProcess::ExitStatus status ) {
00265 if ( !canceled ) {
00266 if ( status == QProcess::CrashExit )
00267 KMessageBox::error( dialog,
00268 i18n( "The GpgSM process that tried to dump the CRL cache "
00269 "ended prematurely because of an unexpected error. "
00270 "Please check the output of gpgsm --call-dirmngr listcrls for details." ),
00271 i18n( "Dump CRL Cache Error" ) );
00272 else if ( code )
00273 KMessageBox::error( dialog,
00274 i18n( "An error occurred while trying to dump the CRL cache. "
00275 "The output from GpgSM was:\n%1", errorString() ),
00276 i18n( "Dump CRL Cache Error" ) );
00277 }
00278 }
00279
00280 #undef d
00281 #undef q
00282
00283 #include "moc_dumpcrlcachecommand.cpp"
00284 #include "dumpcrlcachecommand.moc"