diff --git a/src/commands/dumpcrlcachecommand.cpp b/src/commands/dumpcrlcachecommand.cpp index b18afaa59..d8522a7a6 100644 --- a/src/commands/dumpcrlcachecommand.cpp +++ b/src/commands/dumpcrlcachecommand.cpp @@ -1,357 +1,357 @@ /* -*- mode: c++; c-basic-offset:4 -*- commands/dumpcrlcachecommand.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "dumpcrlcachecommand.h" #include "command_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const int PROCESS_TERMINATE_TIMEOUT = 5000; // milliseconds namespace { class DumpCrlCacheDialog : public QDialog { Q_OBJECT public: explicit DumpCrlCacheDialog(QWidget *parent = nullptr) : QDialog(parent) , ui(this) , mWithRevocations(false) { readConfig(); } ~DumpCrlCacheDialog() override { writeConfig(); } Q_SIGNALS: void updateRequested(); public Q_SLOTS: void append(const QString &line) { ui.logTextWidget.append(line); ui.logTextWidget.ensureCursorVisible(); } void clear() { ui.logTextWidget.clear(); } public: void setWithRevocations(bool value) { mWithRevocations = value; } [[nodiscard]] bool withRevocations() { return mWithRevocations; } private: void readConfig() { KConfigGroup dialog(KSharedConfig::openStateConfig(), QStringLiteral("DumpCrlCacheDialog")); const QSize size = dialog.readEntry("Size", QSize(600, 400)); if (size.isValid()) { resize(size); } } void writeConfig() { KConfigGroup dialog(KSharedConfig::openStateConfig(), QStringLiteral("DumpCrlCacheDialog")); dialog.writeEntry("Size", size()); dialog.sync(); } struct Ui { QTextEdit logTextWidget; QPushButton updateButton, closeButton, revocationsButton; QVBoxLayout vlay; QHBoxLayout hlay; explicit Ui(DumpCrlCacheDialog *q) : logTextWidget(q) , updateButton(i18nc("@action:button Update the log text widget", "&Update"), q) , closeButton(q) , vlay(q) , hlay() { KGuiItem::assign(&closeButton, KStandardGuiItem::close()); KDAB_SET_OBJECT_NAME(logTextWidget); KDAB_SET_OBJECT_NAME(updateButton); KDAB_SET_OBJECT_NAME(closeButton); KDAB_SET_OBJECT_NAME(vlay); KDAB_SET_OBJECT_NAME(hlay); logTextWidget.setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); logTextWidget.setReadOnly(true); vlay.addWidget(&logTextWidget, 1); vlay.addLayout(&hlay); revocationsButton.setText(i18n("Show Entries")); hlay.addWidget(&updateButton); hlay.addWidget(&revocationsButton); hlay.addStretch(1); hlay.addWidget(&closeButton); connect(&updateButton, &QAbstractButton::clicked, q, &DumpCrlCacheDialog::updateRequested); connect(&closeButton, &QAbstractButton::clicked, q, &QWidget::close); connect(&revocationsButton, &QAbstractButton::clicked, q, [q, this]() { q->mWithRevocations = true; revocationsButton.setEnabled(false); q->updateRequested(); }); } } ui; bool mWithRevocations; }; } using namespace Kleo; using namespace Kleo::Commands; static QByteArray chomped(QByteArray ba) { while (ba.endsWith('\n') || ba.endsWith('\r')) { ba.chop(1); } return ba; } class DumpCrlCacheCommand::Private : Command::Private { friend class ::Kleo::Commands::DumpCrlCacheCommand; DumpCrlCacheCommand *q_func() const { return static_cast(q); } public: explicit Private(DumpCrlCacheCommand *qq, KeyListController *c); ~Private() override; QString errorString() const { return QString::fromLocal8Bit(errorBuffer); } private: void init(); void refreshView(); private: void slotProcessFinished(int, QProcess::ExitStatus); void slotProcessReadyReadStandardOutput() { static int count; while (process.canReadLine()) { if (!dialog) { break; } const QByteArray line = chomped(process.readLine()); - if (!line.size()) { + if (line.isEmpty()) { continue; } if (!dialog->withRevocations() && line.contains("reasons")) { count++; continue; } else if (count) { dialog->append(QLatin1Char(' ') + i18nc("Count of revocations in a CRL", "Entries:") + QStringLiteral("\t\t%1\n").arg(count)); count = 0; } dialog->append(stringFromGpgOutput(line)); } } void slotProcessReadyReadStandardError() { errorBuffer += process.readAllStandardError(); } void slotUpdateRequested() { if (process.state() == QProcess::NotRunning) { refreshView(); } } void slotDialogDestroyed() { dialog = nullptr; if (process.state() != QProcess::NotRunning) { q->cancel(); } else { finished(); } } private: DumpCrlCacheDialog *dialog; KProcess process; QByteArray errorBuffer; bool canceled; }; DumpCrlCacheCommand::Private *DumpCrlCacheCommand::d_func() { return static_cast(d.get()); } const DumpCrlCacheCommand::Private *DumpCrlCacheCommand::d_func() const { return static_cast(d.get()); } #define d d_func() #define q q_func() DumpCrlCacheCommand::Private::Private(DumpCrlCacheCommand *qq, KeyListController *c) : Command::Private(qq, c) , dialog(nullptr) , process() , errorBuffer() , canceled(false) { process.setOutputChannelMode(KProcess::SeparateChannels); process.setReadChannel(KProcess::StandardOutput); process << gpgSmPath() << QStringLiteral("--call-dirmngr") << QStringLiteral("listcrls"); } DumpCrlCacheCommand::Private::~Private() { if (dialog && !dialog->isVisible()) { delete dialog; } } DumpCrlCacheCommand::DumpCrlCacheCommand(KeyListController *c) : Command(new Private(this, c)) { d->init(); } DumpCrlCacheCommand::DumpCrlCacheCommand(QAbstractItemView *v, KeyListController *c) : Command(v, new Private(this, c)) { d->init(); } void DumpCrlCacheCommand::Private::init() { connect(&process, &QProcess::finished, q, [this](int exitCode, QProcess::ExitStatus status) { slotProcessFinished(exitCode, status); }); connect(&process, &QProcess::readyReadStandardError, q, [this]() { slotProcessReadyReadStandardError(); }); connect(&process, &QProcess::readyReadStandardOutput, q, [this] { slotProcessReadyReadStandardOutput(); }); } DumpCrlCacheCommand::~DumpCrlCacheCommand() { } void DumpCrlCacheCommand::doStart() { d->dialog = new DumpCrlCacheDialog; d->dialog->setAttribute(Qt::WA_DeleteOnClose); d->dialog->setWindowTitle(i18nc("@title:window", "CRL Cache Dump")); connect(d->dialog, &DumpCrlCacheDialog::updateRequested, this, [this]() { d->slotUpdateRequested(); }); connect(d->dialog, &QObject::destroyed, this, [this]() { d->slotDialogDestroyed(); }); d->refreshView(); } void DumpCrlCacheCommand::Private::refreshView() { dialog->clear(); process.start(); if (process.waitForStarted()) { dialog->show(); } else { KMessageBox::error(dialog ? static_cast(dialog) : parentWidgetOrView(), i18n("Unable to start process gpgsm. " "Please check your installation."), i18n("Dump CRL Cache Error")); finished(); } } void DumpCrlCacheCommand::doCancel() { d->canceled = true; if (d->process.state() != QProcess::NotRunning) { d->process.terminate(); QTimer::singleShot(PROCESS_TERMINATE_TIMEOUT, &d->process, &QProcess::kill); } if (d->dialog) { d->dialog->close(); } d->dialog = nullptr; } void DumpCrlCacheCommand::Private::slotProcessFinished(int code, QProcess::ExitStatus status) { if (!canceled) { if (status == QProcess::CrashExit) KMessageBox::error(dialog, i18n("The GpgSM process that tried to dump the CRL cache " "ended prematurely because of an unexpected error. " "Please check the output of gpgsm --call-dirmngr listcrls for details."), i18nc("@title:window", "Dump CRL Cache Error")); else if (code) KMessageBox::error(dialog, i18n("An error occurred while trying to dump the CRL cache. " "The output from GpgSM was:\n%1", errorString()), i18nc("@title:window", "Dump CRL Cache Error")); } } #undef d #undef q #include "dumpcrlcachecommand.moc" #include "moc_dumpcrlcachecommand.cpp"