diff --git a/src/commands/checksumcreatefilescommand.cpp b/src/commands/checksumcreatefilescommand.cpp index c05c365fe..b333b30cf 100644 --- a/src/commands/checksumcreatefilescommand.cpp +++ b/src/commands/checksumcreatefilescommand.cpp @@ -1,171 +1,170 @@ /* -*- mode: c++; c-basic-offset:4 -*- commands/checksumcreatefilescommand.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 "checksumcreatefilescommand.h" #include "command_p.h" #include #include #include #include #include "kleopatra_debug.h" -#include #include using namespace Kleo; using namespace Kleo::Commands; using namespace Kleo::Crypto; class ChecksumCreateFilesCommand::Private : public Command::Private { friend class ::Kleo::Commands::ChecksumCreateFilesCommand; ChecksumCreateFilesCommand *q_func() const { return static_cast(q); } public: explicit Private(ChecksumCreateFilesCommand *qq, KeyListController *c); ~Private(); QStringList selectFiles() const; void init(); private: void slotControllerDone() { finished(); } void slotControllerError(int, const QString &) { finished(); } private: QStringList files; std::shared_ptr shared_qq; CreateChecksumsController controller; }; ChecksumCreateFilesCommand::Private *ChecksumCreateFilesCommand::d_func() { return static_cast(d.get()); } const ChecksumCreateFilesCommand::Private *ChecksumCreateFilesCommand::d_func() const { return static_cast(d.get()); } #define d d_func() #define q q_func() ChecksumCreateFilesCommand::Private::Private(ChecksumCreateFilesCommand *qq, KeyListController *c) : Command::Private(qq, c), files(), shared_qq(qq, [](ChecksumCreateFilesCommand*){}), controller() { controller.setAllowAddition(true); } ChecksumCreateFilesCommand::Private::~Private() { qCDebug(KLEOPATRA_LOG); } ChecksumCreateFilesCommand::ChecksumCreateFilesCommand(KeyListController *c) : Command(new Private(this, c)) { d->init(); } ChecksumCreateFilesCommand::ChecksumCreateFilesCommand(QAbstractItemView *v, KeyListController *c) : Command(v, new Private(this, c)) { d->init(); } ChecksumCreateFilesCommand::ChecksumCreateFilesCommand(const QStringList &files, KeyListController *c) : Command(new Private(this, c)) { d->init(); d->files = files; } ChecksumCreateFilesCommand::ChecksumCreateFilesCommand(const QStringList &files, QAbstractItemView *v, KeyListController *c) : Command(v, new Private(this, c)) { d->init(); d->files = files; } void ChecksumCreateFilesCommand::Private::init() { controller.setExecutionContext(shared_qq); connect(&controller, SIGNAL(done()), q, SLOT(slotControllerDone())); connect(&controller, SIGNAL(error(int,QString)), q, SLOT(slotControllerError(int,QString))); } ChecksumCreateFilesCommand::~ChecksumCreateFilesCommand() { qCDebug(KLEOPATRA_LOG); } void ChecksumCreateFilesCommand::setFiles(const QStringList &files) { d->files = files; } void ChecksumCreateFilesCommand::doStart() { try { if (d->files.empty()) { d->files = d->selectFiles(); } if (d->files.empty()) { d->finished(); return; } d->controller.setFiles(d->files); d->controller.start(); } catch (const std::exception &e) { d->information(i18n("An error occurred: %1", QString::fromLocal8Bit(e.what())), i18n("Create Checksum Files Error")); d->finished(); } } void ChecksumCreateFilesCommand::doCancel() { qCDebug(KLEOPATRA_LOG); d->controller.cancel(); } QStringList ChecksumCreateFilesCommand::Private::selectFiles() const { return FileDialog::getOpenFileNames(parentWidgetOrView(), i18n("Select One or More Files to Create Checksums For"), QStringLiteral("chk")); } #undef d #undef q #include "moc_checksumcreatefilescommand.cpp" diff --git a/src/commands/checksumverifyfilescommand.cpp b/src/commands/checksumverifyfilescommand.cpp index 18e9233bd..4ce95f0cf 100644 --- a/src/commands/checksumverifyfilescommand.cpp +++ b/src/commands/checksumverifyfilescommand.cpp @@ -1,171 +1,170 @@ /* -*- mode: c++; c-basic-offset:4 -*- commands/checksumverifyfilescommand.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 "checksumverifyfilescommand.h" #include "command_p.h" #include #include #include #include #include "kleopatra_debug.h" -#include #include using namespace Kleo; using namespace Kleo::Commands; using namespace Kleo::Crypto; class ChecksumVerifyFilesCommand::Private : public Command::Private { friend class ::Kleo::Commands::ChecksumVerifyFilesCommand; ChecksumVerifyFilesCommand *q_func() const { return static_cast(q); } public: explicit Private(ChecksumVerifyFilesCommand *qq, KeyListController *c); ~Private(); QStringList selectFiles() const; void init(); private: void slotControllerDone() { finished(); } void slotControllerError(int, const QString &) { finished(); } private: QStringList files; std::shared_ptr shared_qq; VerifyChecksumsController controller; }; ChecksumVerifyFilesCommand::Private *ChecksumVerifyFilesCommand::d_func() { return static_cast(d.get()); } const ChecksumVerifyFilesCommand::Private *ChecksumVerifyFilesCommand::d_func() const { return static_cast(d.get()); } #define d d_func() #define q q_func() ChecksumVerifyFilesCommand::Private::Private(ChecksumVerifyFilesCommand *qq, KeyListController *c) : Command::Private(qq, c), files(), shared_qq(qq, [](ChecksumVerifyFilesCommand *){}), controller() { } ChecksumVerifyFilesCommand::Private::~Private() { qCDebug(KLEOPATRA_LOG); } ChecksumVerifyFilesCommand::ChecksumVerifyFilesCommand(KeyListController *c) : Command(new Private(this, c)) { d->init(); } ChecksumVerifyFilesCommand::ChecksumVerifyFilesCommand(QAbstractItemView *v, KeyListController *c) : Command(v, new Private(this, c)) { d->init(); } ChecksumVerifyFilesCommand::ChecksumVerifyFilesCommand(const QStringList &files, KeyListController *c) : Command(new Private(this, c)) { d->init(); d->files = files; } ChecksumVerifyFilesCommand::ChecksumVerifyFilesCommand(const QStringList &files, QAbstractItemView *v, KeyListController *c) : Command(v, new Private(this, c)) { d->init(); d->files = files; } void ChecksumVerifyFilesCommand::Private::init() { controller.setExecutionContext(shared_qq); connect(&controller, SIGNAL(done()), q, SLOT(slotControllerDone())); connect(&controller, SIGNAL(error(int,QString)), q, SLOT(slotControllerError(int,QString))); } ChecksumVerifyFilesCommand::~ChecksumVerifyFilesCommand() { qCDebug(KLEOPATRA_LOG); } void ChecksumVerifyFilesCommand::setFiles(const QStringList &files) { d->files = files; } void ChecksumVerifyFilesCommand::doStart() { try { if (d->files.empty()) { d->files = d->selectFiles(); } if (d->files.empty()) { d->finished(); return; } d->controller.setFiles(d->files); d->controller.start(); } catch (const std::exception &e) { d->information(i18n("An error occurred: %1", QString::fromLocal8Bit(e.what())), i18n("Verify Checksum Files Error")); d->finished(); } } void ChecksumVerifyFilesCommand::doCancel() { qCDebug(KLEOPATRA_LOG); d->controller.cancel(); } QStringList ChecksumVerifyFilesCommand::Private::selectFiles() const { return FileDialog::getOpenFileNames(parentWidgetOrView(), i18n("Select One or More Checksum Files"), QStringLiteral("chk")); } #undef d #undef q #include "moc_checksumverifyfilescommand.cpp" diff --git a/src/commands/decryptverifyfilescommand.cpp b/src/commands/decryptverifyfilescommand.cpp index 6f6e258ff..c90479bb7 100644 --- a/src/commands/decryptverifyfilescommand.cpp +++ b/src/commands/decryptverifyfilescommand.cpp @@ -1,194 +1,193 @@ /* -*- mode: c++; c-basic-offset:4 -*- commands/decryptverifyfilescommand.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 "decryptverifyfilescommand.h" #include "fileoperationspreferences.h" #include "command_p.h" #include "crypto/decryptverifyfilescontroller.h" #include "crypto/autodecryptverifyfilescontroller.h" #include #include #include #include "kleopatra_debug.h" -#include #include using namespace Kleo; using namespace Kleo::Commands; using namespace Kleo::Crypto; class DecryptVerifyFilesCommand::Private : public Command::Private { friend class ::Kleo::Commands::DecryptVerifyFilesCommand; DecryptVerifyFilesCommand *q_func() const { return static_cast(q); } public: explicit Private(DecryptVerifyFilesCommand *qq, KeyListController *c, bool forceManualMode=false); ~Private(); QStringList selectFiles() const; void init(); private: void slotControllerDone() { finished(); } void slotControllerError(int, const QString &msg) { KMessageBox::error(parentWidgetOrView(), msg, i18n("Decrypt/Verify Failed")); finished(); } private: QStringList files; std::shared_ptr shared_qq; DecryptVerifyFilesController *mController; }; DecryptVerifyFilesCommand::Private *DecryptVerifyFilesCommand::d_func() { return static_cast(d.get()); } const DecryptVerifyFilesCommand::Private *DecryptVerifyFilesCommand::d_func() const { return static_cast(d.get()); } #define d d_func() #define q q_func() DecryptVerifyFilesCommand::Private::Private(DecryptVerifyFilesCommand *qq, KeyListController *c, bool forceManualMode) : Command::Private(qq, c), files(), shared_qq(qq, [](DecryptVerifyFilesCommand*){}) { FileOperationsPreferences prefs; if (!forceManualMode && GpgME::hasFeature(0, GpgME::BinaryAndFineGrainedIdentify) && prefs.autoDecryptVerify()) { mController = new AutoDecryptVerifyFilesController(); } else { mController = new DecryptVerifyFilesController(); } } DecryptVerifyFilesCommand::Private::~Private() { qCDebug(KLEOPATRA_LOG); delete mController; } DecryptVerifyFilesCommand::DecryptVerifyFilesCommand(KeyListController *c) : Command(new Private(this, c)) { d->init(); } DecryptVerifyFilesCommand::DecryptVerifyFilesCommand(QAbstractItemView *v, KeyListController *c) : Command(v, new Private(this, c)) { d->init(); } DecryptVerifyFilesCommand::DecryptVerifyFilesCommand(const QStringList &files, KeyListController *c, bool forceManualMode) : Command(new Private(this, c, forceManualMode)) { d->init(); d->files = files; } DecryptVerifyFilesCommand::DecryptVerifyFilesCommand(const QStringList &files, QAbstractItemView *v, KeyListController *c) : Command(v, new Private(this, c)) { d->init(); d->files = files; } void DecryptVerifyFilesCommand::Private::init() { mController->setExecutionContext(shared_qq); connect(mController, SIGNAL(done()), q, SLOT(slotControllerDone())); connect(mController, SIGNAL(error(int,QString)), q, SLOT(slotControllerError(int,QString))); } DecryptVerifyFilesCommand::~DecryptVerifyFilesCommand() { qCDebug(KLEOPATRA_LOG); } void DecryptVerifyFilesCommand::setFiles(const QStringList &files) { d->files = files; } void DecryptVerifyFilesCommand::setOperation(DecryptVerifyOperation op) { try { d->mController->setOperation(op); } catch (...) {} } DecryptVerifyOperation DecryptVerifyFilesCommand::operation() const { return d->mController->operation(); } void DecryptVerifyFilesCommand::doStart() { try { if (d->files.empty()) { d->files = d->selectFiles(); } if (d->files.empty()) { d->finished(); return; } d->mController->setFiles(d->files); d->mController->start(); } catch (const std::exception &e) { d->information(i18n("An error occurred: %1", QString::fromLocal8Bit(e.what())), i18n("Decrypt/Verify Files Error")); d->finished(); } } void DecryptVerifyFilesCommand::doCancel() { qCDebug(KLEOPATRA_LOG); d->mController->cancel(); } QStringList DecryptVerifyFilesCommand::Private::selectFiles() const { return FileDialog::getOpenFileNames(parentWidgetOrView(), i18n("Select One or More Files to Decrypt and/or Verify"), QStringLiteral("enc")); } #undef d #undef q #include "moc_decryptverifyfilescommand.cpp" diff --git a/src/commands/gnupgprocesscommand.cpp b/src/commands/gnupgprocesscommand.cpp index 3efb65d89..879a157b2 100644 --- a/src/commands/gnupgprocesscommand.cpp +++ b/src/commands/gnupgprocesscommand.cpp @@ -1,382 +1,381 @@ /* -*- mode: c++; c-basic-offset:4 -*- commands/gnupgprocesscommand.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 "gnupgprocesscommand.h" #include "command_p.h" #include #include "kleopatra_debug.h" #include #include #include -#include #include #include #include #include #include #include #include #include #include static const int PROCESS_TERMINATE_TIMEOUT = 5000; // milliseconds using namespace Kleo; using namespace Kleo::Commands; namespace { class OutputDialog : public QDialog { Q_OBJECT public: explicit OutputDialog(QWidget *parent = nullptr) : QDialog(parent), vlay(this), logTextWidget(this), buttonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Close, Qt::Horizontal, this) { KDAB_SET_OBJECT_NAME(vlay); KDAB_SET_OBJECT_NAME(logTextWidget); KDAB_SET_OBJECT_NAME(buttonBox); logTextWidget.setReadOnly(true); vlay.addWidget(&logTextWidget, 1); vlay.addWidget(&buttonBox); connect(closeButton(), &QAbstractButton::clicked, this, &QWidget::close); connect(cancelButton(), &QAbstractButton::clicked, this, &OutputDialog::slotCancelClicked); resize(600, 500); } Q_SIGNALS: void cancelRequested(); public Q_SLOTS: void message(const QString &s) { logTextWidget.append(s); logTextWidget.ensureCursorVisible(); } void setComplete(bool complete) { cancelButton()->setVisible(!complete); } private Q_SLOTS: void slotCancelClicked() { cancelButton()->hide(); Q_EMIT cancelRequested(); } private: QAbstractButton *closeButton() const { return buttonBox.button(QDialogButtonBox::Close); } QAbstractButton *cancelButton() const { return buttonBox.button(QDialogButtonBox::Cancel); } private: QVBoxLayout vlay; QTextEdit logTextWidget; QDialogButtonBox buttonBox; }; } class GnuPGProcessCommand::Private : Command::Private { friend class ::Kleo::Commands::GnuPGProcessCommand; GnuPGProcessCommand *q_func() const { return static_cast(q); } public: explicit Private(GnuPGProcessCommand *qq, KeyListController *c); ~Private(); private: void init(); void ensureDialogCreated() { if (!showsOutputWindow) { return; } if (!dialog) { dialog = new OutputDialog; dialog->setAttribute(Qt::WA_DeleteOnClose); applyWindowID(dialog); connect(dialog.data(), &OutputDialog::cancelRequested, q, &Command::cancel); dialog->setWindowTitle(i18nc("@title:window", "Subprocess Diagnostics")); } } void ensureDialogVisible() { if (!showsOutputWindow) { return; } ensureDialogCreated(); if (dialog->isVisible()) { dialog->raise(); } else { dialog->show(); } #ifdef Q_OS_WIN KWindowSystem::forceActiveWindow(dialog->winId()); #endif } void message(const QString &msg) { if (dialog) { dialog->message(msg); } else { qCDebug(KLEOPATRA_LOG) << msg; } } private: void slotProcessFinished(int, QProcess::ExitStatus); void slotProcessReadyReadStandardError(); private: QProcess process; QPointer dialog; QStringList arguments; QByteArray errorBuffer; bool ignoresSuccessOrFailure; bool showsOutputWindow; bool canceled; }; GnuPGProcessCommand::Private *GnuPGProcessCommand::d_func() { return static_cast(d.get()); } const GnuPGProcessCommand::Private *GnuPGProcessCommand::d_func() const { return static_cast(d.get()); } #define d d_func() #define q q_func() GnuPGProcessCommand::Private::Private(GnuPGProcessCommand *qq, KeyListController *c) : Command::Private(qq, c), process(), dialog(), errorBuffer(), ignoresSuccessOrFailure(false), showsOutputWindow(false), canceled(false) { process.setReadChannel(QProcess::StandardError); } GnuPGProcessCommand::Private::~Private() {} GnuPGProcessCommand::GnuPGProcessCommand(KeyListController *c) : Command(new Private(this, c)) { d->init(); } GnuPGProcessCommand::GnuPGProcessCommand(QAbstractItemView *v, KeyListController *c) : Command(v, new Private(this, c)) { d->init(); } GnuPGProcessCommand::GnuPGProcessCommand(const GpgME::Key &key) : Command(key, new Private(this, nullptr)) { d->init(); } void GnuPGProcessCommand::Private::init() { connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), q, SLOT(slotProcessFinished(int,QProcess::ExitStatus))); connect(&process, SIGNAL(readyReadStandardError()), q, SLOT(slotProcessReadyReadStandardError())); } GnuPGProcessCommand::~GnuPGProcessCommand() {} QDialog *GnuPGProcessCommand::dialog() const { return d->dialog; } bool GnuPGProcessCommand::preStartHook(QWidget *) const { return true; } void GnuPGProcessCommand::postSuccessHook(QWidget *) { } void GnuPGProcessCommand::doStart() { if (!preStartHook(d->parentWidgetOrView())) { d->finished(); return; } d->arguments = arguments(); d->process.setProgram(d->arguments.takeFirst()); d->process.setArguments(d->arguments); // Historically code using this expects arguments first to be the program. d->arguments.prepend(d->process.program()); d->process.start(); if (!d->process.waitForStarted()) { d->error(i18n("Unable to start process %1. " "Please check your installation.", d->arguments[0]), errorCaption()); d->finished(); } else { d->ensureDialogVisible(); d->message(i18n("Starting %1...", d->arguments.join(QLatin1Char(' ')))); } } void GnuPGProcessCommand::doCancel() { d->canceled = true; if (d->process.state() != QProcess::NotRunning) { d->process.terminate(); QTimer::singleShot(PROCESS_TERMINATE_TIMEOUT, &d->process, &QProcess::kill); } } void GnuPGProcessCommand::Private::slotProcessFinished(int code, QProcess::ExitStatus status) { if (!canceled) { if (status == QProcess::CrashExit) { const QString msg = q->crashExitMessage(arguments); if (!msg.isEmpty()) { error(msg, q->errorCaption()); } } else if (ignoresSuccessOrFailure) { if (dialog) { message(i18n("Process finished")); } else { ; } } else if (code) { const QString msg = q->errorExitMessage(arguments); if (!msg.isEmpty()) { error(q->errorExitMessage(arguments), q->errorCaption()); } } else { q->postSuccessHook(parentWidgetOrView()); const QString successMessage = q->successMessage(arguments); if (!successMessage.isNull()) { if (dialog) { message(successMessage); } else { information(successMessage, q->successCaption()); } } } } if (dialog) { dialog->setComplete(true); } finished(); } void GnuPGProcessCommand::Private::slotProcessReadyReadStandardError() { auto ba = process.readAllStandardError(); errorBuffer += ba; while (ba.endsWith('\n') || ba.endsWith('\r')) { ba.chop(1); } message(Kleo::stringFromGpgOutput(ba)); } QString GnuPGProcessCommand::errorString() const { return Kleo::stringFromGpgOutput(d->errorBuffer); } void GnuPGProcessCommand::setIgnoresSuccessOrFailure(bool ignores) { d->ignoresSuccessOrFailure = ignores; } bool GnuPGProcessCommand::ignoresSuccessOrFailure() const { return d->ignoresSuccessOrFailure; } void GnuPGProcessCommand::setShowsOutputWindow(bool show) { if (show == d->showsOutputWindow) { return; } d->showsOutputWindow = show; if (show) { d->ensureDialogCreated(); } else { if (d->dialog) { d->dialog->deleteLater(); } d->dialog = nullptr; } } bool GnuPGProcessCommand::showsOutputWindow() const { return d->showsOutputWindow; } QProcess *GnuPGProcessCommand::process() { return &d->process; } QString GnuPGProcessCommand::successCaption() const { return QString(); } QString GnuPGProcessCommand::successMessage(const QStringList &args) const { Q_UNUSED(args) return QString(); } #undef d #undef q #include "moc_gnupgprocesscommand.cpp" #include "gnupgprocesscommand.moc" diff --git a/src/commands/signencryptfilescommand.cpp b/src/commands/signencryptfilescommand.cpp index ab948c3c1..75f69e1a8 100644 --- a/src/commands/signencryptfilescommand.cpp +++ b/src/commands/signencryptfilescommand.cpp @@ -1,292 +1,291 @@ /* -*- mode: c++; c-basic-offset:4 -*- commands/signencryptfilescommand.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 "signencryptfilescommand.h" #include "command_p.h" #include #include #include #include #include "kleopatra_debug.h" -#include #include using namespace Kleo; using namespace Kleo::Commands; using namespace Kleo::Crypto; class SignEncryptFilesCommand::Private : public Command::Private { friend class ::Kleo::Commands::SignEncryptFilesCommand; SignEncryptFilesCommand *q_func() const { return static_cast(q); } public: explicit Private(SignEncryptFilesCommand *qq, KeyListController *c); ~Private(); QStringList selectFiles() const; void init(); private: void slotControllerDone() { finished(); } void slotControllerError(int, const QString &) { finished(); } private: QStringList files; std::shared_ptr shared_qq; SignEncryptFilesController controller; }; SignEncryptFilesCommand::Private *SignEncryptFilesCommand::d_func() { return static_cast(d.get()); } const SignEncryptFilesCommand::Private *SignEncryptFilesCommand::d_func() const { return static_cast(d.get()); } #define d d_func() #define q q_func() SignEncryptFilesCommand::Private::Private(SignEncryptFilesCommand *qq, KeyListController *c) : Command::Private(qq, c), files(), shared_qq(qq, [](SignEncryptFilesCommand*){}), controller() { controller.setOperationMode(SignEncryptFilesController::SignSelected | SignEncryptFilesController::EncryptSelected | SignEncryptFilesController::ArchiveAllowed); } SignEncryptFilesCommand::Private::~Private() { qCDebug(KLEOPATRA_LOG); } SignEncryptFilesCommand::SignEncryptFilesCommand(KeyListController *c) : Command(new Private(this, c)) { d->init(); } SignEncryptFilesCommand::SignEncryptFilesCommand(QAbstractItemView *v, KeyListController *c) : Command(v, new Private(this, c)) { d->init(); } SignEncryptFilesCommand::SignEncryptFilesCommand(const QStringList &files, KeyListController *c) : Command(new Private(this, c)) { d->init(); d->files = files; } SignEncryptFilesCommand::SignEncryptFilesCommand(const QStringList &files, QAbstractItemView *v, KeyListController *c) : Command(v, new Private(this, c)) { d->init(); d->files = files; } void SignEncryptFilesCommand::Private::init() { controller.setExecutionContext(shared_qq); connect(&controller, SIGNAL(done()), q, SLOT(slotControllerDone())); connect(&controller, SIGNAL(error(int,QString)), q, SLOT(slotControllerError(int,QString))); } SignEncryptFilesCommand::~SignEncryptFilesCommand() { qCDebug(KLEOPATRA_LOG); } void SignEncryptFilesCommand::setFiles(const QStringList &files) { d->files = files; } void SignEncryptFilesCommand::setSigningPolicy(Policy policy) { unsigned int mode = d->controller.operationMode(); mode &= ~SignEncryptFilesController::SignMask; switch (policy) { case NoPolicy: case Allow: mode |= SignEncryptFilesController::SignAllowed; break; case Deny: mode |= SignEncryptFilesController::SignDisallowed; break; case Force: mode |= SignEncryptFilesController::SignSelected; break; } try { d->controller.setOperationMode(mode); } catch (...) {} } Policy SignEncryptFilesCommand::signingPolicy() const { const unsigned int mode = d->controller.operationMode(); switch (mode & SignEncryptFilesController::SignMask) { default: Q_ASSERT(!"This should not happen!"); return NoPolicy; case SignEncryptFilesController::SignAllowed: return Allow; case SignEncryptFilesController::SignSelected: return Force; case SignEncryptFilesController::SignDisallowed: return Deny; } } void SignEncryptFilesCommand::setEncryptionPolicy(Policy policy) { unsigned int mode = d->controller.operationMode(); mode &= ~SignEncryptFilesController::EncryptMask; switch (policy) { case NoPolicy: case Allow: mode |= SignEncryptFilesController::EncryptAllowed; break; case Deny: mode |= SignEncryptFilesController::EncryptDisallowed; break; case Force: mode |= SignEncryptFilesController::EncryptSelected; break; } try { d->controller.setOperationMode(mode); } catch (...) {} } Policy SignEncryptFilesCommand::encryptionPolicy() const { const unsigned int mode = d->controller.operationMode(); switch (mode & SignEncryptFilesController::EncryptMask) { default: Q_ASSERT(!"This should not happen!"); return NoPolicy; case SignEncryptFilesController::EncryptAllowed: return Allow; case SignEncryptFilesController::EncryptSelected: return Force; case SignEncryptFilesController::EncryptDisallowed: return Deny; } } void SignEncryptFilesCommand::setArchivePolicy(Policy policy) { unsigned int mode = d->controller.operationMode(); mode &= ~SignEncryptFilesController::ArchiveMask; switch (policy) { case NoPolicy: case Allow: mode |= SignEncryptFilesController::ArchiveAllowed; break; case Deny: mode |= SignEncryptFilesController::ArchiveDisallowed; break; case Force: mode |= SignEncryptFilesController::ArchiveForced; break; } d->controller.setOperationMode(mode); } Policy SignEncryptFilesCommand::archivePolicy() const { const unsigned int mode = d->controller.operationMode(); switch (mode & SignEncryptFilesController::ArchiveMask) { case SignEncryptFilesController::ArchiveAllowed: return Allow; case SignEncryptFilesController::ArchiveForced: return Force; case SignEncryptFilesController::ArchiveDisallowed: return Deny; default: Q_ASSERT(!"This should not happen!"); return NoPolicy; } } void SignEncryptFilesCommand::setProtocol(GpgME::Protocol proto) { d->controller.setProtocol(proto); } GpgME::Protocol SignEncryptFilesCommand::protocol() const { return d->controller.protocol(); } void SignEncryptFilesCommand::doStart() { try { if (d->files.empty()) { d->files = selectFiles(); } if (d->files.empty()) { d->finished(); return; } d->controller.setFiles(d->files); d->controller.start(); } catch (const std::exception &e) { d->information(i18n("An error occurred: %1", QString::fromLocal8Bit(e.what())), i18n("Sign/Encrypt Files Error")); d->finished(); } } void SignEncryptFilesCommand::doCancel() { qCDebug(KLEOPATRA_LOG); d->controller.cancel(); } QStringList SignEncryptFilesCommand::selectFiles() const { return FileDialog::getOpenFileNames(d->parentWidgetOrView(), i18n("Select One or More Files to Sign and/or Encrypt"), QStringLiteral("enc")); } #undef d #undef q #include "moc_signencryptfilescommand.cpp" diff --git a/src/commands/signencryptfoldercommand.cpp b/src/commands/signencryptfoldercommand.cpp index ff3c8ff92..f2c3c8515 100644 --- a/src/commands/signencryptfoldercommand.cpp +++ b/src/commands/signencryptfoldercommand.cpp @@ -1,43 +1,42 @@ /* -*- mode: c++; c-basic-offset:4 -*- commands/signencryptfoldercommand.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "signencryptfoldercommand.h" #include -#include #include #include using namespace Kleo; using namespace Kleo::Commands; SignEncryptFolderCommand::SignEncryptFolderCommand(QAbstractItemView *v, KeyListController *c) : SignEncryptFilesCommand(v, c) { setArchivePolicy(Force); } SignEncryptFolderCommand::SignEncryptFolderCommand(KeyListController *c) : SignEncryptFolderCommand(nullptr, c) { } QStringList SignEncryptFolderCommand::selectFiles() const { const QString dir = QFileDialog::getExistingDirectory(qApp->activeWindow(), i18n("Select Folder to Sign and/or Encrypt")); if (dir.isNull()) { return QStringList(); } return QStringList() << dir; } diff --git a/src/crypto/gui/objectspage.cpp b/src/crypto/gui/objectspage.cpp index 97bb900c1..ae3144cbd 100644 --- a/src/crypto/gui/objectspage.cpp +++ b/src/crypto/gui/objectspage.cpp @@ -1,144 +1,143 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/gui/objectspage.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "objectspage.h" #include #include #include #include #include #include #include -#include #include using namespace Kleo; using namespace Kleo::Crypto::Gui; class ObjectsPage::Private { friend class ::Kleo::Crypto::Gui::ObjectsPage; ObjectsPage *const q; public: explicit Private(ObjectsPage *qq); ~Private(); void add(); void addFile(const QFileInfo &i); void remove(); void listSelectionChanged(); enum Role { AbsoluteFilePathRole = Qt::UserRole }; private: QListWidget *fileListWidget; QPushButton *removeButton; }; ObjectsPage::Private::Private(ObjectsPage *qq) : q(qq) { q->setTitle(i18n("Objects")); auto const top = new QVBoxLayout(q); fileListWidget = new QListWidget; fileListWidget->setSelectionMode(QAbstractItemView::MultiSelection); connect(fileListWidget, SIGNAL(itemSelectionChanged()), q, SLOT(listSelectionChanged())); top->addWidget(fileListWidget); auto const buttonWidget = new QWidget; auto const buttonLayout = new QHBoxLayout(buttonWidget); removeButton = new QPushButton; removeButton->setText(i18n("Remove Selected")); connect(removeButton, SIGNAL(clicked()), q, SLOT(remove())); buttonLayout->addWidget(removeButton); buttonLayout->addStretch(); top->addWidget(buttonWidget); listSelectionChanged(); } ObjectsPage::Private::~Private() {} void ObjectsPage::Private::add() { const QString fname = FileDialog::getOpenFileName(q, i18n("Select File"), QStringLiteral("enc")); if (fname.isEmpty()) { return; } addFile(QFileInfo(fname)); Q_EMIT q->completeChanged(); } void ObjectsPage::Private::remove() { const QList selected = fileListWidget->selectedItems(); Q_ASSERT(!selected.isEmpty()); for (QListWidgetItem *const i : selected) { delete i; } Q_EMIT q->completeChanged(); } void ObjectsPage::Private::listSelectionChanged() { removeButton->setEnabled(!fileListWidget->selectedItems().isEmpty()); } ObjectsPage::ObjectsPage(QWidget *parent, Qt::WindowFlags f) : WizardPage(parent, f), d(new Private(this)) { } ObjectsPage::~ObjectsPage() { } void ObjectsPage::setFiles(const QStringList &list) { d->fileListWidget->clear(); for (const QString &i : list) { d->addFile(QFileInfo(i)); } Q_EMIT completeChanged(); } void ObjectsPage::Private::addFile(const QFileInfo &info) { auto const item = new QListWidgetItem; if (info.isDir()) { item->setIcon(QIcon::fromTheme(QStringLiteral("folder"))); } item->setText(info.fileName()); item->setData(AbsoluteFilePathRole, info.absoluteFilePath()); fileListWidget->addItem(item); } QStringList ObjectsPage::files() const { QStringList list; for (int i = 0; i < d->fileListWidget->count(); ++i) { const QListWidgetItem *const item = d->fileListWidget->item(i); list.push_back(item->data(Private::AbsoluteFilePathRole).toString()); } return list; } bool ObjectsPage::isComplete() const { return d->fileListWidget->count() > 0; } #include "moc_objectspage.cpp" diff --git a/src/smartcard/readerstatus.cpp b/src/smartcard/readerstatus.cpp index fc569441b..51e5d297c 100644 --- a/src/smartcard/readerstatus.cpp +++ b/src/smartcard/readerstatus.cpp @@ -1,1129 +1,1128 @@ /* -*- mode: c++; c-basic-offset:4 -*- smartcard/readerstatus.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2020 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include #include #if GPGMEPP_VERSION >= 0x10E01 // 1.14.1 # define QGPGME_HAS_DEBUG # define GPGME_SUPPORTS_API_FOR_DEVICEINFOWATCHER #endif #include "readerstatus.h" #ifdef GPGME_SUPPORTS_API_FOR_DEVICEINFOWATCHER # include "deviceinfowatcher.h" #endif #include "keypairinfo.h" #include #include #include #ifdef QGPGME_HAS_DEBUG # include #endif #include #include #include #include #include "openpgpcard.h" #include "netkeycard.h" #include "pivcard.h" #include "p15card.h" #include #include #include #include #include #include #include #include #include #include -#include #include #include #include "utils/kdtoolsglobal.h" #include "kleopatra_debug.h" using namespace Kleo; using namespace Kleo::SmartCard; using namespace GpgME; static ReaderStatus *self = nullptr; #define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) #define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) static const char *flags[] = { "NOCARD", "PRESENT", "ACTIVE", "USABLE", }; static_assert(sizeof flags / sizeof * flags == Card::_NumScdStates, ""); static const char *prettyFlags[] = { "NoCard", "CardPresent", "CardActive", "CardUsable", "CardError", }; static_assert(sizeof prettyFlags / sizeof * prettyFlags == Card::NumStates, ""); Q_DECLARE_METATYPE(GpgME::Error) namespace { static bool gpgHasMultiCardMultiAppSupport() { return !(engineInfo(GpgME::GpgEngine).engineVersion() < "2.3.0"); } static QDebug operator<<(QDebug s, const std::string &string) { return s << QString::fromStdString(string); } #ifndef QGPGME_HAS_DEBUG static QDebug operator<<(QDebug s, const GpgME::Error &err) { const bool oldSetting = s.autoInsertSpaces(); s.nospace() << err.asString() << " (code: " << err.code() << ", source: " << err.source() << ")"; s.setAutoInsertSpaces(oldSetting); return s.maybeSpace(); } #endif static QDebug operator<<(QDebug s, const std::vector< std::pair > &v) { using pair = std::pair; s << '('; for (const pair &p : v) { s << "status(" << QString::fromStdString(p.first) << ") =" << QString::fromStdString(p.second) << '\n'; } return s << ')'; } struct CardApp { std::string serialNumber; std::string appName; }; static void logUnexpectedStatusLine(const std::pair &line, const std::string &prefix = std::string(), const std::string &command = std::string()) { qCWarning(KLEOPATRA_LOG) << (!prefix.empty() ? QString::fromStdString(prefix + ": ") : QString()) << "Unexpected status line" << (!command.empty() ? QString::fromStdString(" on " + command + ":") : QLatin1String(":")) << QString::fromStdString(line.first) << QString::fromStdString(line.second); } static int parse_app_version(const std::string &s) { return std::atoi(s.c_str()); } static Card::PinState parse_pin_state(const QString &s) { bool ok; int i = s.toInt(&ok); if (!ok) { qCDebug(KLEOPATRA_LOG) << "Failed to parse pin state" << s; return Card::UnknownPinState; } switch (i) { case -4: return Card::NullPin; case -3: return Card::PinBlocked; case -2: return Card::NoPin; case -1: return Card::UnknownPinState; default: if (i < 0) { return Card::UnknownPinState; } else { return Card::PinOk; } } } template static std::unique_ptr gpgagent_transact(std::shared_ptr &gpgAgent, const char *command, std::unique_ptr transaction, Error &err) { qCDebug(KLEOPATRA_LOG) << "gpgagent_transact(" << command << ")"; err = gpgAgent->assuanTransact(command, std::move(transaction)); if (err.code()) { qCDebug(KLEOPATRA_LOG) << "gpgagent_transact(" << command << "): Error:" << err; if (err.code() >= GPG_ERR_ASS_GENERAL && err.code() <= GPG_ERR_ASS_UNKNOWN_INQUIRE) { qCDebug(KLEOPATRA_LOG) << "Assuan problem, killing context"; gpgAgent.reset(); } return std::unique_ptr(); } std::unique_ptr t = gpgAgent->takeLastAssuanTransaction(); return std::unique_ptr(dynamic_cast(t.release())); } static std::unique_ptr gpgagent_default_transact(std::shared_ptr &gpgAgent, const char *command, Error &err) { return gpgagent_transact(gpgAgent, command, std::make_unique(), err); } static const std::string gpgagent_data(std::shared_ptr gpgAgent, const char *command, Error &err) { const std::unique_ptr t = gpgagent_default_transact(gpgAgent, command, err); if (t.get()) { qCDebug(KLEOPATRA_LOG) << "gpgagent_data(" << command << "): got" << QString::fromStdString(t->data()); return t->data(); } else { qCDebug(KLEOPATRA_LOG) << "gpgagent_data(" << command << "): t == NULL"; return std::string(); } } static const std::vector< std::pair > gpgagent_statuslines(std::shared_ptr gpgAgent, const char *what, Error &err) { const std::unique_ptr t = gpgagent_default_transact(gpgAgent, what, err); if (t.get()) { qCDebug(KLEOPATRA_LOG) << "agent_getattr_status(" << what << "): got" << t->statusLines(); return t->statusLines(); } else { qCDebug(KLEOPATRA_LOG) << "agent_getattr_status(" << what << "): t == NULL"; return std::vector >(); } } static const std::string gpgagent_status(const std::shared_ptr &gpgAgent, const char *what, Error &err) { const auto lines = gpgagent_statuslines (gpgAgent, what, err); // The status is only the last attribute // e.g. for SCD SERIALNO it would only be "SERIALNO" and for SCD GETATTR FOO // it would only be FOO const char *p = strrchr(what, ' '); const char *needle = (p + 1) ? (p + 1) : what; for (const auto &pair: lines) { if (pair.first == needle) { return pair.second; } } return std::string(); } static const std::string scd_getattr_status(std::shared_ptr &gpgAgent, const char *what, Error &err) { std::string cmd = "SCD GETATTR "; cmd += what; return gpgagent_status(gpgAgent, cmd.c_str(), err); } static const std::string getAttribute(std::shared_ptr &gpgAgent, const char *attribute, const char *versionHint) { Error err; const auto result = scd_getattr_status(gpgAgent, attribute, err); if (err) { if (err.code() == GPG_ERR_INV_NAME) { qCDebug(KLEOPATRA_LOG) << "Querying for attribute" << attribute << "not yet supported; needs GnuPG" << versionHint; } else { qCWarning(KLEOPATRA_LOG) << "Running SCD GETATTR " << attribute << " failed:" << err; } return std::string(); } return result; } static std::vector getCardsAndApps(std::shared_ptr &gpgAgent, Error &err) { std::vector result; if (gpgHasMultiCardMultiAppSupport()) { const std::string command = "SCD GETINFO all_active_apps"; const auto statusLines = gpgagent_statuslines(gpgAgent, command.c_str(), err); if (err) { return result; } for (const auto &statusLine: statusLines) { if (statusLine.first == "SERIALNO") { const auto serialNumberAndApps = QByteArray::fromStdString(statusLine.second).split(' '); if (serialNumberAndApps.size() >= 2) { const auto serialNumber = serialNumberAndApps[0]; auto apps = serialNumberAndApps.mid(1); // sort the apps to get a stable order independently of the currently selected application std::sort(apps.begin(), apps.end()); for (const auto &app: apps) { qCDebug(KLEOPATRA_LOG) << "getCardsAndApps(): Found card" << serialNumber << "with app" << app; result.push_back({ serialNumber.toStdString(), app.toStdString() }); } } else { logUnexpectedStatusLine(statusLine, "getCardsAndApps()", command); } } else { logUnexpectedStatusLine(statusLine, "getCardsAndApps()", command); } } } else { // use SCD SERIALNO to get the currently active card const auto serialNumber = gpgagent_status(gpgAgent, "SCD SERIALNO", err); if (err) { return result; } // use SCD GETATTR APPTYPE to find out which app is active auto appName = scd_getattr_status(gpgAgent, "APPTYPE", err); std::transform(appName.begin(), appName.end(), appName.begin(), [](unsigned char c){ return std::tolower(c); }); if (err) { return result; } result.push_back({ serialNumber, appName }); } return result; } static std::string switchCard(std::shared_ptr &gpgAgent, const std::string &serialNumber, Error &err) { const std::string command = "SCD SWITCHCARD " + serialNumber; const auto statusLines = gpgagent_statuslines(gpgAgent, command.c_str(), err); if (err) { return std::string(); } if (statusLines.size() == 1 && statusLines[0].first == "SERIALNO" && statusLines[0].second == serialNumber) { return serialNumber; } qCWarning(KLEOPATRA_LOG) << "switchCard():" << command << "returned" << statusLines << "(expected:" << "SERIALNO " + serialNumber << ")"; return std::string(); } static std::string switchApp(std::shared_ptr &gpgAgent, const std::string &serialNumber, const std::string &appName, Error &err) { const std::string command = "SCD SWITCHAPP " + appName; const auto statusLines = gpgagent_statuslines(gpgAgent, command.c_str(), err); if (err) { return std::string(); } if (statusLines.size() == 1 && statusLines[0].first == "SERIALNO" && statusLines[0].second.find(serialNumber + ' ' + appName) == 0) { return appName; } qCWarning(KLEOPATRA_LOG) << "switchApp():" << command << "returned" << statusLines << "(expected:" << "SERIALNO " + serialNumber + ' ' + appName + "..." << ")"; return std::string(); } static const char * get_openpgp_card_manufacturer_from_serial_number(const std::string &serialno) { qCDebug(KLEOPATRA_LOG) << "get_openpgp_card_manufacturer_from_serial_number(" << serialno.c_str() << ")"; const bool isProperOpenPGPCardSerialNumber = serialno.size() == 32 && serialno.substr(0, 12) == "D27600012401"; if (isProperOpenPGPCardSerialNumber) { const char *sn = serialno.c_str(); const int manufacturerId = xtoi_2(sn + 16)*256 + xtoi_2(sn + 18); switch (manufacturerId) { case 0x0001: return "PPC Card Systems"; case 0x0002: return "Prism"; case 0x0003: return "OpenFortress"; case 0x0004: return "Wewid"; case 0x0005: return "ZeitControl"; case 0x0006: return "Yubico"; case 0x0007: return "OpenKMS"; case 0x0008: return "LogoEmail"; case 0x002A: return "Magrathea"; case 0x1337: return "Warsaw Hackerspace"; case 0xF517: return "FSIJ"; /* 0x0000 and 0xFFFF are defined as test cards per spec, 0xFF00 to 0xFFFE are assigned for use with randomly created serial numbers. */ case 0x0000: case 0xffff: return "test card"; default: return (manufacturerId & 0xff00) == 0xff00 ? "unmanaged S/N range" : "unknown"; } } else { return "unknown"; } } static bool isOpenPGPCardSerialNumber(const std::string &serialNumber) { return serialNumber.size() == 32 && serialNumber.substr(0, 12) == "D27600012401"; } static const std::string getDisplaySerialNumber(std::shared_ptr &gpgAgent, Error &err) { const auto displaySerialNumber = scd_getattr_status(gpgAgent, "$DISPSERIALNO", err); if (err && err.code() != GPG_ERR_INV_NAME) { qCWarning(KLEOPATRA_LOG) << "Running SCD GETATTR $DISPSERIALNO failed:" << err; } return displaySerialNumber; } static void setDisplaySerialNumber(Card *card, std::shared_ptr &gpgAgent) { static const QRegularExpression leadingZeros(QStringLiteral("^0*")); Error err; const QString displaySerialNumber = QString::fromStdString(getDisplaySerialNumber(gpgAgent, err)); if (err) { card->setDisplaySerialNumber(QString::fromStdString(card->serialNumber())); return; } if (isOpenPGPCardSerialNumber(card->serialNumber()) && displaySerialNumber.size() == 12) { // add a space between manufacturer id and card id for OpenPGP cards card->setDisplaySerialNumber(displaySerialNumber.left(4) + QLatin1Char(' ') + displaySerialNumber.right(8)); } else { card->setDisplaySerialNumber(displaySerialNumber); } return; } static void handle_openpgp_card(std::shared_ptr &ci, std::shared_ptr &gpg_agent) { Error err; auto pgpCard = new OpenPGPCard(*ci); const auto info = gpgagent_statuslines(gpg_agent, "SCD LEARN --force", err); if (err.code()) { ci->setStatus(Card::CardError); return; } pgpCard->setCardInfo(info); if (pgpCard->manufacturer().empty()) { // fallback in case MANUFACTURER is not yet included in the card info pgpCard->setManufacturer(get_openpgp_card_manufacturer_from_serial_number(ci->serialNumber())); } setDisplaySerialNumber(pgpCard, gpg_agent); ci.reset(pgpCard); } static void readKeyPairInfoFromPIVCard(const std::string &keyRef, PIVCard *pivCard, const std::shared_ptr &gpg_agent) { Error err; const std::string command = std::string("SCD READKEY --info-only -- ") + keyRef; const auto keyPairInfoLines = gpgagent_statuslines(gpg_agent, command.c_str(), err); if (err) { qCWarning(KLEOPATRA_LOG) << "Running" << command << "failed:" << err; return; } for (const auto &pair: keyPairInfoLines) { if (pair.first == "KEYPAIRINFO") { const KeyPairInfo info = KeyPairInfo::fromStatusLine(pair.second); if (info.grip.empty()) { qCWarning(KLEOPATRA_LOG) << "Invalid KEYPAIRINFO status line" << QString::fromStdString(pair.second); continue; } pivCard->setKeyAlgorithm(keyRef, info.algorithm); } else { logUnexpectedStatusLine(pair, "readKeyPairInfoFromPIVCard()", command); } } } static void readCertificateFromPIVCard(const std::string &keyRef, PIVCard *pivCard, const std::shared_ptr &gpg_agent) { Error err; const std::string command = std::string("SCD READCERT ") + keyRef; const std::string certificateData = gpgagent_data(gpg_agent, command.c_str(), err); if (err && err.code() != GPG_ERR_NOT_FOUND) { qCWarning(KLEOPATRA_LOG) << "Running" << command << "failed:" << err; return; } if (certificateData.empty()) { qCDebug(KLEOPATRA_LOG) << "readCertificateFromPIVCard(" << QString::fromStdString(keyRef) << "): No certificate stored on card"; return; } qCDebug(KLEOPATRA_LOG) << "readCertificateFromPIVCard(" << QString::fromStdString(keyRef) << "): Found certificate stored on card"; pivCard->setCertificateData(keyRef, certificateData); } static void handle_piv_card(std::shared_ptr &ci, std::shared_ptr &gpg_agent) { Error err; auto pivCard = new PIVCard(*ci); const auto info = gpgagent_statuslines(gpg_agent, "SCD LEARN --force", err); if (err) { ci->setStatus(Card::CardError); return; } pivCard->setCardInfo(info); setDisplaySerialNumber(pivCard, gpg_agent); for (const KeyPairInfo &keyInfo : pivCard->keyInfos()) { if (!keyInfo.grip.empty()) { readKeyPairInfoFromPIVCard(keyInfo.keyRef, pivCard, gpg_agent); readCertificateFromPIVCard(keyInfo.keyRef, pivCard, gpg_agent); } } ci.reset(pivCard); } static void handle_p15_card(std::shared_ptr &ci, std::shared_ptr &gpg_agent) { Error err; auto p15Card = new P15Card(*ci); auto info = gpgagent_statuslines(gpg_agent, "SCD LEARN --force", err); if (err) { ci->setStatus(Card::CardError); return; } const auto fprs = gpgagent_statuslines(gpg_agent, "SCD GETATTR KEY-FPR", err); if (!err) { info.insert(info.end(), fprs.begin(), fprs.end()); } /* Create the key stubs */ gpgagent_statuslines(gpg_agent, "READKEY --card --no-data -- $SIGNKEYID", err); gpgagent_statuslines(gpg_agent, "READKEY --card --no-data -- $ENCRKEYID", err); p15Card->setCardInfo(info); setDisplaySerialNumber(p15Card, gpg_agent); ci.reset(p15Card); } static void handle_netkey_card(std::shared_ptr &ci, std::shared_ptr &gpg_agent) { Error err; auto nkCard = new NetKeyCard(*ci); ci.reset(nkCard); ci->setAppVersion(parse_app_version(scd_getattr_status(gpg_agent, "NKS-VERSION", err))); if (err.code()) { qCWarning(KLEOPATRA_LOG) << "Running SCD GETATTR NKS-VERSION failed:" << err; ci->setErrorMsg(QStringLiteral ("NKS-VERSION failed: ") + QString::fromUtf8(err.asString())); return; } if (ci->appVersion() != 3) { qCDebug(KLEOPATRA_LOG) << "not a NetKey v3 card, giving up. Version:" << ci->appVersion(); ci->setErrorMsg(QStringLiteral("NetKey v%1 cards are not supported.").arg(ci->appVersion())); return; } setDisplaySerialNumber(nkCard, gpg_agent); // the following only works for NKS v3... const auto chvStatus = QString::fromStdString( scd_getattr_status(gpg_agent, "CHV-STATUS", err)).split(QLatin1Char(' ')); if (err.code()) { qCDebug(KLEOPATRA_LOG) << "Running SCD GETATTR CHV-STATUS failed:" << err; ci->setErrorMsg(QStringLiteral ("CHV-Status failed: ") + QString::fromUtf8(err.asString())); return; } std::vector states; states.reserve(chvStatus.count()); // CHV Status for NKS v3 is // Pin1 (Normal pin) Pin2 (Normal PUK) // SigG1 SigG PUK. int num = 0; for (const auto &state: chvStatus) { const auto parsed = parse_pin_state (state); states.push_back(parsed); if (parsed == Card::NullPin) { if (num == 0) { ci->setHasNullPin(true); } } ++num; } nkCard->setPinStates(states); const auto info = gpgagent_statuslines(gpg_agent, "SCD LEARN --force", err); if (err) { ci->setStatus(Card::CardError); return; } nkCard->setCardInfo(info); } static std::shared_ptr get_card_status(const std::string &serialNumber, const std::string &appName, std::shared_ptr &gpg_agent) { qCDebug(KLEOPATRA_LOG) << "get_card_status(" << serialNumber << ',' << appName << ',' << gpg_agent.get() << ')'; auto ci = std::shared_ptr(new Card()); if (gpgHasMultiCardMultiAppSupport()) { // select card Error err; const auto result = switchCard(gpg_agent, serialNumber, err); if (err) { if (err.code() == GPG_ERR_CARD_NOT_PRESENT || err.code() == GPG_ERR_CARD_REMOVED) { ci->setStatus(Card::NoCard); } else { ci->setStatus(Card::CardError); } return ci; } if (result.empty()) { qCWarning(KLEOPATRA_LOG) << "get_card_status: switching card failed"; ci->setStatus(Card::CardError); return ci; } ci->setStatus(Card::CardPresent); } else { ci->setStatus(Card::CardPresent); } if (gpgHasMultiCardMultiAppSupport()) { // select app Error err; const auto result = switchApp(gpg_agent, serialNumber, appName, err); if (err) { if (err.code() == GPG_ERR_CARD_NOT_PRESENT || err.code() == GPG_ERR_CARD_REMOVED) { ci->setStatus(Card::NoCard); } else { ci->setStatus(Card::CardError); } return ci; } if (result.empty()) { qCWarning(KLEOPATRA_LOG) << "get_card_status: switching app failed"; ci->setStatus(Card::CardError); return ci; } } ci->setSerialNumber(serialNumber); ci->setSigningKeyRef(getAttribute(gpg_agent, "$SIGNKEYID", "2.2.18")); ci->setEncryptionKeyRef(getAttribute(gpg_agent, "$ENCRKEYID", "2.2.18")); // Handle different card types if (appName == NetKeyCard::AppName) { qCDebug(KLEOPATRA_LOG) << "get_card_status: found Netkey card" << ci->serialNumber().c_str() << "end"; handle_netkey_card(ci, gpg_agent); return ci; } else if (appName == OpenPGPCard::AppName) { qCDebug(KLEOPATRA_LOG) << "get_card_status: found OpenPGP card" << ci->serialNumber().c_str() << "end"; ci->setAuthenticationKeyRef(OpenPGPCard::pgpAuthKeyRef()); handle_openpgp_card(ci, gpg_agent); return ci; } else if (appName == PIVCard::AppName) { qCDebug(KLEOPATRA_LOG) << "get_card_status: found PIV card" << ci->serialNumber().c_str() << "end"; handle_piv_card(ci, gpg_agent); return ci; } else if (appName == P15Card::AppName) { qCDebug(KLEOPATRA_LOG) << "get_card_status: found P15 card" << ci->serialNumber().c_str() << "end"; handle_p15_card(ci, gpg_agent); return ci; } else { qCDebug(KLEOPATRA_LOG) << "get_card_status: unhandled application:" << appName; return ci; } return ci; } static bool isCardNotPresentError(const GpgME::Error &err) { // see fixup_scd_errors() in gpg-card.c return err && ((err.code() == GPG_ERR_CARD_NOT_PRESENT) || ((err.code() == GPG_ERR_ENODEV || err.code() == GPG_ERR_CARD_REMOVED) && (err.sourceID() == GPG_ERR_SOURCE_SCD))); } static std::vector > update_cardinfo(std::shared_ptr &gpgAgent) { qCDebug(KLEOPATRA_LOG) << "update_cardinfo()"; // ensure that a card is present and that all cards are properly set up { Error err; const char *command = (gpgHasMultiCardMultiAppSupport()) ? "SCD SERIALNO --all" : "SCD SERIALNO"; const std::string serialno = gpgagent_status(gpgAgent, command, err); if (err) { if (isCardNotPresentError(err)) { qCDebug(KLEOPATRA_LOG) << "update_cardinfo: No card present"; return std::vector >(); } else { qCWarning(KLEOPATRA_LOG) << "Running" << command << "failed:" << err; auto ci = std::shared_ptr(new Card()); ci->setStatus(Card::CardError); return std::vector >(1, ci); } } } Error err; const std::vector cardApps = getCardsAndApps(gpgAgent, err); if (err) { if (isCardNotPresentError(err)) { qCDebug(KLEOPATRA_LOG) << "update_cardinfo: No card present"; return std::vector >(); } else { qCWarning(KLEOPATRA_LOG) << "Getting active apps on all inserted cards failed:" << err; auto ci = std::shared_ptr(new Card()); ci->setStatus(Card::CardError); return std::vector >(1, ci); } } std::vector > cards; for (const auto &cardApp: cardApps) { const auto card = get_card_status(cardApp.serialNumber, cardApp.appName, gpgAgent); cards.push_back(card); } return cards; } } // namespace struct Transaction { CardApp cardApp; QByteArray command; QPointer receiver; const char *slot; AssuanTransaction* assuanTransaction; }; static const Transaction updateTransaction = { { "__all__", "__all__" }, "__update__", nullptr, nullptr, nullptr }; static const Transaction quitTransaction = { { "__all__", "__all__" }, "__quit__", nullptr, nullptr, nullptr }; namespace { class ReaderStatusThread : public QThread { Q_OBJECT public: explicit ReaderStatusThread(QObject *parent = nullptr) : QThread(parent), m_gnupgHomePath(Kleo::gnupgHomeDirectory()), m_transactions(1, updateTransaction) // force initial scan { connect(this, &ReaderStatusThread::oneTransactionFinished, this, &ReaderStatusThread::slotOneTransactionFinished); } std::vector > cardInfos() const { const QMutexLocker locker(&m_mutex); return m_cardInfos; } Card::Status cardStatus(unsigned int slot) const { const QMutexLocker locker(&m_mutex); if (slot < m_cardInfos.size()) { return m_cardInfos[slot]->status(); } else { return Card::NoCard; } } void addTransaction(const Transaction &t) { const QMutexLocker locker(&m_mutex); m_transactions.push_back(t); m_waitForTransactions.wakeOne(); } Q_SIGNALS: void firstCardWithNullPinChanged(const std::string &serialNumber); void anyCardCanLearnKeysChanged(bool); void cardAdded(const std::string &serialNumber, const std::string &appName); void cardChanged(const std::string &serialNumber, const std::string &appName); void cardRemoved(const std::string &serialNumber, const std::string &appName); void oneTransactionFinished(const GpgME::Error &err); public Q_SLOTS: void deviceStatusChanged(const QByteArray &details) { qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread[GUI]::deviceStatusChanged(" << details << ")"; addTransaction(updateTransaction); } void ping() { qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread[GUI]::ping()"; addTransaction(updateTransaction); } void stop() { const QMutexLocker locker(&m_mutex); m_transactions.push_front(quitTransaction); m_waitForTransactions.wakeOne(); } private Q_SLOTS: void slotOneTransactionFinished(const GpgME::Error &err) { std::list ft; KDAB_SYNCHRONIZED(m_mutex) ft.splice(ft.begin(), m_finishedTransactions); for (const Transaction &t : qAsConst(ft)) if (t.receiver && t.slot && *t.slot) { QMetaObject::invokeMethod(t.receiver, t.slot, Qt::DirectConnection, Q_ARG(GpgME::Error, err)); } } private: void run() override { while (true) { std::shared_ptr gpgAgent; CardApp cardApp; QByteArray command; bool nullSlot = false; AssuanTransaction* assuanTransaction = nullptr; std::list item; std::vector > oldCards; Error err; std::unique_ptr c = Context::createForEngine(AssuanEngine, &err); if (err.code() == GPG_ERR_NOT_SUPPORTED) { return; } gpgAgent = std::shared_ptr(c.release()); KDAB_SYNCHRONIZED(m_mutex) { while (m_transactions.empty()) { // go to sleep waiting for more work: qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread[2nd]: waiting for commands"; m_waitForTransactions.wait(&m_mutex); } // splice off the first transaction without // copying, so we own it without really importing // it into this thread (the QPointer isn't // thread-safe): item.splice(item.end(), m_transactions, m_transactions.begin()); // make local copies of the interesting stuff so // we can release the mutex again: cardApp = item.front().cardApp; command = item.front().command; nullSlot = !item.front().slot; // we take ownership of the assuan transaction std::swap(assuanTransaction, item.front().assuanTransaction); oldCards = m_cardInfos; } qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread[2nd]: new iteration command=" << command << " ; nullSlot=" << nullSlot; // now, let's see what we got: if (nullSlot && command == quitTransaction.command) { return; // quit } if ((nullSlot && command == updateTransaction.command)) { std::vector > newCards = update_cardinfo(gpgAgent); KDAB_SYNCHRONIZED(m_mutex) m_cardInfos = newCards; bool anyLC = false; std::string firstCardWithNullPin; bool anyError = false; for (const auto &newCard: newCards) { const auto serialNumber = newCard->serialNumber(); const auto appName = newCard->appName(); const auto matchingOldCard = std::find_if(oldCards.cbegin(), oldCards.cend(), [serialNumber, appName] (const std::shared_ptr &card) { return card->serialNumber() == serialNumber && card->appName() == appName; }); if (matchingOldCard == oldCards.cend()) { qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread: Card" << serialNumber << "with app" << appName << "was added"; Q_EMIT cardAdded(serialNumber, appName); } else { if (*newCard != **matchingOldCard) { qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread: Card" << serialNumber << "with app" << appName << "changed"; Q_EMIT cardChanged(serialNumber, appName); } oldCards.erase(matchingOldCard); } if (newCard->canLearnKeys()) { anyLC = true; } if (newCard->hasNullPin() && firstCardWithNullPin.empty()) { firstCardWithNullPin = newCard->serialNumber(); } if (newCard->status() == Card::CardError) { anyError = true; } } for (const auto &oldCard: oldCards) { qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread: Card" << oldCard->serialNumber() << "with app" << oldCard->appName() << "was removed"; Q_EMIT cardRemoved(oldCard->serialNumber(), oldCard->appName()); } Q_EMIT firstCardWithNullPinChanged(firstCardWithNullPin); Q_EMIT anyCardCanLearnKeysChanged(anyLC); if (anyError) { gpgAgent.reset(); } } else { GpgME::Error err; if (gpgHasMultiCardMultiAppSupport()) { switchCard(gpgAgent, cardApp.serialNumber, err); if (!err) { switchApp(gpgAgent, cardApp.serialNumber, cardApp.appName, err); } } if (!err) { if (assuanTransaction) { (void)gpgagent_transact(gpgAgent, command.constData(), std::unique_ptr(assuanTransaction), err); } else { (void)gpgagent_default_transact(gpgAgent, command.constData(), err); } } KDAB_SYNCHRONIZED(m_mutex) // splice 'item' into m_finishedTransactions: m_finishedTransactions.splice(m_finishedTransactions.end(), item); Q_EMIT oneTransactionFinished(err); } } } private: mutable QMutex m_mutex; QWaitCondition m_waitForTransactions; const QString m_gnupgHomePath; // protected by m_mutex: std::vector > m_cardInfos; std::list m_transactions, m_finishedTransactions; }; } class ReaderStatus::Private : ReaderStatusThread { friend class Kleo::SmartCard::ReaderStatus; ReaderStatus *const q; public: explicit Private(ReaderStatus *qq) : ReaderStatusThread(qq), q(qq), watcher() { KDAB_SET_OBJECT_NAME(watcher); qRegisterMetaType("Kleo::SmartCard::Card::Status"); qRegisterMetaType("GpgME::Error"); connect(this, &::ReaderStatusThread::cardAdded, q, &ReaderStatus::cardAdded); connect(this, &::ReaderStatusThread::cardChanged, q, &ReaderStatus::cardChanged); connect(this, &::ReaderStatusThread::cardRemoved, q, &ReaderStatus::cardRemoved); connect(this, &::ReaderStatusThread::firstCardWithNullPinChanged, q, &ReaderStatus::firstCardWithNullPinChanged); connect(this, &::ReaderStatusThread::anyCardCanLearnKeysChanged, q, &ReaderStatus::anyCardCanLearnKeysChanged); #ifdef GPGME_SUPPORTS_API_FOR_DEVICEINFOWATCHER if (DeviceInfoWatcher::isSupported()) { qCDebug(KLEOPATRA_LOG) << "ReaderStatus::Private: Using new DeviceInfoWatcher"; connect(&devInfoWatcher, &DeviceInfoWatcher::statusChanged, this, &::ReaderStatusThread::deviceStatusChanged); } else #endif { qCDebug(KLEOPATRA_LOG) << "ReaderStatus::Private: Using deprecated FileSystemWatcher"; watcher.whitelistFiles(QStringList(QStringLiteral("reader_*.status"))); watcher.addPath(Kleo::gnupgHomeDirectory()); watcher.setDelay(100); connect(&watcher, &FileSystemWatcher::triggered, this, &::ReaderStatusThread::ping); } } ~Private() { stop(); if (!wait(100)) { terminate(); wait(); } } private: std::string firstCardWithNullPinImpl() const { const auto cis = cardInfos(); const auto firstWithNullPin = std::find_if(cis.cbegin(), cis.cend(), [](const std::shared_ptr &ci) { return ci->hasNullPin(); }); return firstWithNullPin != cis.cend() ? (*firstWithNullPin)->serialNumber() : std::string(); } bool anyCardCanLearnKeysImpl() const { const auto cis = cardInfos(); return std::any_of(cis.cbegin(), cis.cend(), [](const std::shared_ptr &ci) { return ci->canLearnKeys(); }); } private: FileSystemWatcher watcher; #ifdef GPGME_SUPPORTS_API_FOR_DEVICEINFOWATCHER DeviceInfoWatcher devInfoWatcher; #endif }; ReaderStatus::ReaderStatus(QObject *parent) : QObject(parent), d(new Private(this)) { self = this; qRegisterMetaType("std::string"); } ReaderStatus::~ReaderStatus() { self = nullptr; } // slot void ReaderStatus::startMonitoring() { d->start(); #ifdef GPGME_SUPPORTS_API_FOR_DEVICEINFOWATCHER if (DeviceInfoWatcher::isSupported()) { d->devInfoWatcher.start(); } #endif } // static ReaderStatus *ReaderStatus::mutableInstance() { return self; } // static const ReaderStatus *ReaderStatus::instance() { return self; } Card::Status ReaderStatus::cardStatus(unsigned int slot) const { return d->cardStatus(slot); } std::string ReaderStatus::firstCardWithNullPin() const { return d->firstCardWithNullPinImpl(); } bool ReaderStatus::anyCardCanLearnKeys() const { return d->anyCardCanLearnKeysImpl(); } void ReaderStatus::startSimpleTransaction(const std::shared_ptr &card, const QByteArray &command, QObject *receiver, const char *slot) { const CardApp cardApp = { card->serialNumber(), card->appName() }; const Transaction t = { cardApp, command, receiver, slot, nullptr }; d->addTransaction(t); } void ReaderStatus::startTransaction(const std::shared_ptr &card, const QByteArray &command, QObject *receiver, const char *slot, std::unique_ptr transaction) { const CardApp cardApp = { card->serialNumber(), card->appName() }; const Transaction t = { cardApp, command, receiver, slot, transaction.release() }; d->addTransaction(t); } void ReaderStatus::updateStatus() { d->ping(); } std::vector > ReaderStatus::getCards() const { return d->cardInfos(); } std::shared_ptr ReaderStatus::getCard(const std::string &serialNumber, const std::string &appName) const { for (const auto &card: d->cardInfos()) { if (card->serialNumber() == serialNumber && card->appName() == appName) { qCDebug(KLEOPATRA_LOG) << "ReaderStatus::getCard() - Found card with serial number" << serialNumber << "and app" << appName; return card; } } qCWarning(KLEOPATRA_LOG) << "ReaderStatus::getCard() - Did not find card with serial number" << serialNumber << "and app" << appName; return std::shared_ptr(); } // static std::string ReaderStatus::switchCard(std::shared_ptr& ctx, const std::string& serialNumber, Error& err) { return ::switchCard(ctx, serialNumber, err); } // static std::string ReaderStatus::switchApp(std::shared_ptr& ctx, const std::string& serialNumber, const std::string& appName, Error& err) { return ::switchApp(ctx, serialNumber, appName, err); } // static Error ReaderStatus::switchCardAndApp(const std::string &serialNumber, const std::string &appName) { Error err; if (!(engineInfo(GpgEngine).engineVersion() < "2.3.0")) { std::unique_ptr c = Context::createForEngine(AssuanEngine, &err); if (err.code() == GPG_ERR_NOT_SUPPORTED) { return err; } auto assuanContext = std::shared_ptr(c.release()); const auto resultSerialNumber = switchCard(assuanContext, serialNumber, err); if (err || resultSerialNumber != serialNumber) { qCWarning(KLEOPATRA_LOG) << "Switching to card" << QString::fromStdString(serialNumber) << "failed"; if (!err) { err = Error::fromCode(GPG_ERR_UNEXPECTED); } return err; } const auto resultAppName = switchApp(assuanContext, serialNumber, appName, err); if (err || resultAppName != appName) { qCWarning(KLEOPATRA_LOG) << "Switching card to" << QString::fromStdString(appName) << "app failed"; if (!err) { err = Error::fromCode(GPG_ERR_UNEXPECTED); } return err; } } return err; } #include "readerstatus.moc" diff --git a/src/utils/path-helper.cpp b/src/utils/path-helper.cpp index d4ab5f80b..f26ed511e 100644 --- a/src/utils/path-helper.cpp +++ b/src/utils/path-helper.cpp @@ -1,150 +1,149 @@ /* -*- mode: c++; c-basic-offset:4 -*- utils/path-helper.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "path-helper.h" #include #include #include "kleopatra_debug.h" #include #include -#include #include #include #include #include using namespace Kleo; static QString commonPrefix(const QString &s1, const QString &s2) { return QString(s1.data(), std::mismatch(s1.data(), s1.data() + std::min(s1.size(), s2.size()), s2.data()).first - s1.data()); } static QString longestCommonPrefix(const QStringList &sl) { if (sl.empty()) { return QString(); } QString result = sl.front(); for (const QString &s : sl) { result = commonPrefix(s, result); } return result; } QString Kleo::heuristicBaseDirectory(const QStringList &fileNames) { QStringList dirs; for (const QString &fileName : fileNames) { dirs.push_back(QFileInfo(fileName).path() + QLatin1Char('/')); } qCDebug(KLEOPATRA_LOG) << "dirs" << dirs; const QString candidate = longestCommonPrefix(dirs); const int idx = candidate.lastIndexOf(QLatin1Char('/')); return candidate.left(idx); } QStringList Kleo::makeRelativeTo(const QString &base, const QStringList &fileNames) { if (base.isEmpty()) { return fileNames; } else { return makeRelativeTo(QDir(base), fileNames); } } QStringList Kleo::makeRelativeTo(const QDir &baseDir, const QStringList &fileNames) { QStringList rv; rv.reserve(fileNames.size()); std::transform(fileNames.cbegin(), fileNames.cend(), std::back_inserter(rv), [&baseDir](const QString &file) { return baseDir.relativeFilePath(file); }); return rv; } void Kleo::recursivelyRemovePath(const QString &path) { const QFileInfo fi(path); if (fi.isDir()) { QDir dir(path); Q_FOREACH (const QString &fname, dir.entryList(QDir::AllEntries | QDir::NoDotAndDotDot)) { recursivelyRemovePath(dir.filePath(fname)); } const QString dirName = fi.fileName(); dir.cdUp(); if (!dir.rmdir(dirName)) { throw Exception(GPG_ERR_EPERM, i18n("Cannot remove directory %1", path)); } } else { QFile file(path); if (!file.remove()) { throw Exception(GPG_ERR_EPERM, i18n("Cannot remove file %1: %2", path, file.errorString())); } } } bool Kleo::recursivelyCopy(const QString &src,const QString &dest) { QDir srcDir(src); if(!srcDir.exists()) { return false; } QDir destDir(dest); if(!destDir.exists() && !destDir.mkdir(dest)) { return false; } for(const auto &file: srcDir.entryList(QDir::Files)) { const QString srcName = src + QLatin1Char('/') + file; const QString destName = dest + QLatin1Char('/') + file; if(!QFile::copy(srcName, destName)) { return false; } } for (const auto &dir: srcDir.entryList(QDir::AllDirs | QDir::NoDotAndDotDot)) { const QString srcName = src + QLatin1Char('/') + dir; const QString destName = dest + QLatin1Char('/') + dir; if (!recursivelyCopy(srcName, destName)) { return false; } } return true; } bool Kleo::moveDir(const QString &src, const QString &dest) { if (QStorageInfo(src).device() == QStorageInfo(dest).device()) { // Easy same partition we can use qt. return QFile::rename(src, dest); } // first copy if (!recursivelyCopy(src, dest)) { return false; } // Then delete original recursivelyRemovePath(src); return true; }