diff --git a/src/commands/decryptverifyfilescommand.cpp b/src/commands/decryptverifyfilescommand.cpp index 2207f2de9..424a7b924 100644 --- a/src/commands/decryptverifyfilescommand.cpp +++ b/src/commands/decryptverifyfilescommand.cpp @@ -1,193 +1,191 @@ /* -*- 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 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() override; 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()) { + if (!forceManualMode && 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, &Controller::done, q, [this]() { slotControllerDone(); }); connect(mController, &Controller::error, q, [this](int err, const QString &details) { slotControllerError(err, details); }); } 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/conf/cryptooperationsconfigwidget.cpp b/src/conf/cryptooperationsconfigwidget.cpp index 37fdc9b2e..2da9c3df4 100644 --- a/src/conf/cryptooperationsconfigwidget.cpp +++ b/src/conf/cryptooperationsconfigwidget.cpp @@ -1,418 +1,411 @@ /* cryptooperationsconfigwidget.cpp This file is part of kleopatra, the KDE key manager SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-FileCopyrightText: 2022 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "cryptooperationsconfigwidget.h" #include "kleopatra_debug.h" #include "emailoperationspreferences.h" #include "fileoperationspreferences.h" #include "settings.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Config; CryptoOperationsConfigWidget::CryptoOperationsConfigWidget(QWidget *p, Qt::WindowFlags f) : QWidget{p, f} { setupGui(); } static void resetDefaults() { auto config = QGpgME::cryptoConfig(); if (!config) { qCWarning(KLEOPATRA_LOG) << "Failed to obtain config"; return; } const QStringList componentList = config->componentList(); for (const auto &compName: componentList) { auto comp = config->component(compName); if (!comp) { qCWarning(KLEOPATRA_LOG) << "Failed to find component:" << comp; return; } const QStringList groupList = comp->groupList(); for (const auto &grpName: groupList) { auto grp = comp->group(grpName); if (!grp) { qCWarning(KLEOPATRA_LOG) << "Failed to find group:" << grp << "in component:" << compName; return; } const QStringList entries = grp->entryList(); for (const auto &entryName: entries) { auto entry = grp->entry(entryName); if (!entry) { qCWarning(KLEOPATRA_LOG) << "Failed to find entry:" << entry << "in group:"<< grp << "in component:" << compName; return; } entry->resetToDefault(); } } } config->sync(true); return; } void CryptoOperationsConfigWidget::applyProfile(const QString &profile) { if (profile.isEmpty()) { return; } qCDebug(KLEOPATRA_LOG) << "Applying profile " << profile; if (profile == i18n("default")) { if (KMessageBox::warningYesNo( this, i18n("This means that every configuration option of the GnuPG System will be reset to its default."), i18n("Apply profile"), KStandardGuiItem::apply(), KStandardGuiItem::no()) != KMessageBox::Yes) { return; } resetDefaults(); KeyFilterManager::instance()->reload(); return; } mApplyBtn->setEnabled(false); QDir datadir(QString::fromUtf8(GpgME::dirInfo("datadir")) + QStringLiteral("/../doc/gnupg/examples")); const auto path = datadir.filePath(profile + QStringLiteral(".prf")); auto gpgconf = new QProcess; const auto ei = GpgME::engineInfo(GpgME::GpgConfEngine); Q_ASSERT (ei.fileName()); gpgconf->setProgram(QFile::decodeName(ei.fileName())); gpgconf->setProcessChannelMode(QProcess::MergedChannels); gpgconf->setArguments(QStringList() << QStringLiteral("--runtime") << QStringLiteral("--apply-profile") << path); qDebug() << "Starting" << ei.fileName() << "with args" << gpgconf->arguments(); connect(gpgconf, &QProcess::finished, this, [this, gpgconf, profile] () { mApplyBtn->setEnabled(true); if (gpgconf->exitStatus() != QProcess::NormalExit) { KMessageBox::error(this, QStringLiteral("
%1
").arg(QString::fromUtf8(gpgconf->readAll()))); delete gpgconf; return; } delete gpgconf; KMessageBox::information(this, i18nc("%1 is the name of the profile", "The configuration profile \"%1\" was applied.", profile), i18n("GnuPG Profile - Kleopatra")); auto config = QGpgME::cryptoConfig(); if (config) { config->clear(); } KeyFilterManager::instance()->reload(); }); gpgconf->start(); } // Get a list of available profile files and add a configuration // group if there are any. void CryptoOperationsConfigWidget::setupProfileGui(QBoxLayout *layout) { const Settings settings; if (settings.profilesDisabled() || GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < "2.1.20" || !layout) { // Profile support is new in 2.1.20 qCDebug(KLEOPATRA_LOG) << "Profile settings disabled"; return; } QDir datadir(QString::fromUtf8(GpgME::dirInfo("datadir")) + QStringLiteral("/../doc/gnupg/examples")); if (!datadir.exists()) { qCDebug(KLEOPATRA_LOG) << "Failed to find gnupg's example profile directory" << datadir.path(); return; } const auto profiles = datadir.entryInfoList(QStringList() << QStringLiteral("*.prf"), QDir::Readable | QDir::Files, QDir::Name); if (profiles.isEmpty()) { qCDebug(KLEOPATRA_LOG) << "Failed to find any profiles in: " << datadir.path(); return; } auto genGrp = new QGroupBox(i18nc("@title", "General Operations")); auto profLayout = new QHBoxLayout; genGrp->setLayout(profLayout); layout->addWidget(genGrp); auto profLabel = new QLabel(i18n("Activate GnuPG Profile:")); profLabel->setToolTip(i18n("A profile consists of various settings that can apply to multiple components of the GnuPG system.")); auto combo = new QComboBox; profLabel->setBuddy(combo); // Add an empty Item to avoid the impression that this GUI element // shows the currently selected profile. combo->addItem(QString()); // We don't translate "default" here because the other profile names are // also not translated as they are taken directly from file. combo->addItem(i18n("default")); for (const auto &profile: profiles) { combo->addItem(profile.baseName()); } mApplyBtn = new QPushButton(i18n("Apply")); mApplyBtn->setEnabled(false); profLayout->addWidget(profLabel); profLayout->addWidget(combo); profLayout->addWidget(mApplyBtn); profLayout->addStretch(1); connect(mApplyBtn, &QPushButton::clicked, this, [this, combo] () { applyProfile(combo->currentText()); }); connect(combo, &QComboBox::currentTextChanged, this, [this] (const QString &text) { mApplyBtn->setEnabled(!text.isEmpty()); }); } void CryptoOperationsConfigWidget::setupGui() { auto baseLay = new QVBoxLayout(this); baseLay->setContentsMargins(0, 0, 0, 0); auto mailGrp = new QGroupBox(i18n("EMail Operations")); auto mailGrpLayout = new QVBoxLayout; mQuickSignCB = new QCheckBox(i18n("Don't confirm signing certificate if there is only one valid certificate for the identity")); mQuickEncryptCB = new QCheckBox(i18n("Don't confirm encryption certificates if there is exactly one valid certificate for each recipient")); mailGrpLayout->addWidget(mQuickSignCB); mailGrpLayout->addWidget(mQuickEncryptCB); mailGrp->setLayout(mailGrpLayout); baseLay->addWidget(mailGrp); auto fileGrp = new QGroupBox(i18n("File Operations")); auto fileGrpLay = new QVBoxLayout; mPGPFileExtCB = new QCheckBox(i18n(R"(Create OpenPGP encrypted files with ".pgp" file extensions instead of ".gpg")")); mASCIIArmorCB = new QCheckBox(i18n("Create signed or encrypted files as text files.")); mASCIIArmorCB->setToolTip(i18nc("@info", "Set this option to encode encrypted or signed files as base64 encoded text. " "So that they can be opened with an editor or sent in a mail body. " "This will increase file size by one third.")); mAutoDecryptVerifyCB = new QCheckBox(i18n("Automatically start operation based on input detection for decrypt/verify.")); mAutoExtractArchivesCB = new QCheckBox(i18n("Automatically extract file archives after decryption")); mTmpDirCB = new QCheckBox(i18n("Create temporary decrypted files in the folder of the encrypted file.")); mTmpDirCB->setToolTip(i18nc("@info", "Set this option to avoid using the users temporary directory.")); mSymmetricOnlyCB = new QCheckBox(i18n("Use symmetric encryption only.")); mSymmetricOnlyCB->setToolTip(i18nc("@info", "Set this option to disable public key encryption.")); fileGrpLay->addWidget(mPGPFileExtCB); fileGrpLay->addWidget(mAutoDecryptVerifyCB); fileGrpLay->addWidget(mAutoExtractArchivesCB); fileGrpLay->addWidget(mASCIIArmorCB); fileGrpLay->addWidget(mTmpDirCB); fileGrpLay->addWidget(mSymmetricOnlyCB); auto comboLay = new QGridLayout; mChecksumDefinitionCB.createWidgets(this); mChecksumDefinitionCB.label()->setText(i18n("Checksum program to use when creating checksum files:")); comboLay->addWidget(mChecksumDefinitionCB.label(), 0, 0); comboLay->addWidget(mChecksumDefinitionCB.widget(), 0, 1); mArchiveDefinitionCB.createWidgets(this); mArchiveDefinitionCB.label()->setText(i18n("Archive command to use when archiving files:")); comboLay->addWidget(mArchiveDefinitionCB.label(), 1, 0); comboLay->addWidget(mArchiveDefinitionCB.widget(), 1, 1); fileGrpLay->addLayout(comboLay); fileGrp->setLayout(fileGrpLay); baseLay->addWidget(fileGrp); setupProfileGui(baseLay); baseLay->addStretch(1); - - if (!GpgME::hasFeature(0, GpgME::BinaryAndFineGrainedIdentify)) { - /* Auto handling requires a working identify in GpgME. - * so that classify in kleoaptra can correctly detect the input.*/ - mAutoDecryptVerifyCB->setVisible(false); - } - for (auto cb : findChildren()) { connect(cb, &QCheckBox::toggled, this, &CryptoOperationsConfigWidget::changed); } for (auto combo : findChildren()) { connect(combo, qOverload(&QComboBox::currentIndexChanged), this, &CryptoOperationsConfigWidget::changed); } } CryptoOperationsConfigWidget::~CryptoOperationsConfigWidget() {} void CryptoOperationsConfigWidget::defaults() { EMailOperationsPreferences emailPrefs; emailPrefs.setQuickSignEMail(emailPrefs.findItem(QStringLiteral("QuickSignEMail"))->getDefault().toBool()); emailPrefs.setQuickEncryptEMail(emailPrefs.findItem(QStringLiteral("QuickEncryptEMail"))->getDefault().toBool()); FileOperationsPreferences filePrefs; filePrefs.setUsePGPFileExt(filePrefs.findItem(QStringLiteral("UsePGPFileExt"))->getDefault().toBool()); filePrefs.setAutoDecryptVerify(filePrefs.findItem(QStringLiteral("AutoDecryptVerify"))->getDefault().toBool()); filePrefs.setAutoExtractArchives(filePrefs.findItem(QStringLiteral("AutoExtractArchives"))->getDefault().toBool()); filePrefs.setAddASCIIArmor(filePrefs.findItem(QStringLiteral("AddASCIIArmor"))->getDefault().toBool()); filePrefs.setDontUseTmpDir(filePrefs.findItem(QStringLiteral("DontUseTmpDir"))->getDefault().toBool()); filePrefs.setSymmetricEncryptionOnly(filePrefs.findItem(QStringLiteral("SymmetricEncryptionOnly"))->getDefault().toBool()); filePrefs.setArchiveCommand(filePrefs.findItem(QStringLiteral("ArchiveCommand"))->getDefault().toString()); Settings settings; settings.setChecksumDefinitionId(settings.findItem(QStringLiteral("ChecksumDefinitionId"))->getDefault().toString()); load(emailPrefs, filePrefs, settings); } void CryptoOperationsConfigWidget::load(const Kleo::EMailOperationsPreferences &emailPrefs, const Kleo::FileOperationsPreferences &filePrefs, const Kleo::Settings &settings) { mQuickSignCB->setChecked(emailPrefs.quickSignEMail()); mQuickSignCB->setEnabled(!emailPrefs.isImmutable(QStringLiteral("QuickSignEMail"))); mQuickEncryptCB->setChecked(emailPrefs.quickEncryptEMail()); mQuickEncryptCB->setEnabled(!emailPrefs.isImmutable(QStringLiteral("QuickEncryptEMail"))); mPGPFileExtCB->setChecked(filePrefs.usePGPFileExt()); mPGPFileExtCB->setEnabled(!filePrefs.isImmutable(QStringLiteral("UsePGPFileExt"))); mAutoDecryptVerifyCB->setChecked(filePrefs.autoDecryptVerify()); mAutoDecryptVerifyCB->setEnabled(!filePrefs.isImmutable(QStringLiteral("AutoDecryptVerify"))); mAutoExtractArchivesCB->setChecked(filePrefs.autoExtractArchives()); mAutoExtractArchivesCB->setEnabled(!filePrefs.isImmutable(QStringLiteral("AutoExtractArchives"))); mASCIIArmorCB->setChecked(filePrefs.addASCIIArmor()); mASCIIArmorCB->setEnabled(!filePrefs.isImmutable(QStringLiteral("AddASCIIArmor"))); mTmpDirCB->setChecked(filePrefs.dontUseTmpDir()); mTmpDirCB->setEnabled(!filePrefs.isImmutable(QStringLiteral("DontUseTmpDir"))); mSymmetricOnlyCB->setChecked(filePrefs.symmetricEncryptionOnly()); mSymmetricOnlyCB->setEnabled(!filePrefs.isImmutable(QStringLiteral("SymmetricEncryptionOnly"))); const auto defaultChecksumDefinitionId = settings.checksumDefinitionId(); { const auto index = mChecksumDefinitionCB.widget()->findData(defaultChecksumDefinitionId); if (index >= 0) { mChecksumDefinitionCB.widget()->setCurrentIndex(index); } else { qCWarning(KLEOPATRA_LOG) << "No checksum definition found with id" << defaultChecksumDefinitionId; } } mChecksumDefinitionCB.setEnabled(!settings.isImmutable(QStringLiteral("ChecksumDefinitionId"))); const auto ad_default_id = filePrefs.archiveCommand(); { const auto index = mArchiveDefinitionCB.widget()->findData(ad_default_id); if (index >= 0) { mArchiveDefinitionCB.widget()->setCurrentIndex(index); } else { qCWarning(KLEOPATRA_LOG) << "No archive definition found with id" << ad_default_id; } } mArchiveDefinitionCB.setEnabled(!filePrefs.isImmutable(QStringLiteral("ArchiveCommand"))); } void CryptoOperationsConfigWidget::load() { mChecksumDefinitionCB.widget()->clear(); const auto cds = ChecksumDefinition::getChecksumDefinitions(); for (const std::shared_ptr &cd : cds) { mChecksumDefinitionCB.widget()->addItem(cd->label(), QVariant{cd->id()}); } // This is a weird hack but because we are a KCM we can't link // against ArchiveDefinition which pulls in loads of other classes. // So we do the parsing which archive definitions exist here ourself. mArchiveDefinitionCB.widget()->clear(); if (KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("libkleopatrarc"))) { const QStringList groups = config->groupList().filter(QRegularExpression(QStringLiteral("^Archive Definition #"))); for (const QString &group : groups) { const KConfigGroup cGroup(config, group); const QString id = cGroup.readEntryUntranslated(QStringLiteral("id")); const QString name = cGroup.readEntry("Name"); mArchiveDefinitionCB.widget()->addItem(name, QVariant(id)); } } load(EMailOperationsPreferences{}, FileOperationsPreferences{}, Settings{}); } void CryptoOperationsConfigWidget::save() { EMailOperationsPreferences emailPrefs; emailPrefs.setQuickSignEMail(mQuickSignCB ->isChecked()); emailPrefs.setQuickEncryptEMail(mQuickEncryptCB->isChecked()); emailPrefs.save(); FileOperationsPreferences filePrefs; filePrefs.setUsePGPFileExt(mPGPFileExtCB->isChecked()); filePrefs.setAutoDecryptVerify(mAutoDecryptVerifyCB->isChecked()); filePrefs.setAutoExtractArchives(mAutoExtractArchivesCB->isChecked()); filePrefs.setAddASCIIArmor(mASCIIArmorCB->isChecked()); filePrefs.setDontUseTmpDir(mTmpDirCB->isChecked()); filePrefs.setSymmetricEncryptionOnly(mSymmetricOnlyCB->isChecked()); Settings settings; const int idx = mChecksumDefinitionCB.widget()->currentIndex(); if (idx >= 0) { const auto id = mChecksumDefinitionCB.widget()->itemData(idx).toString(); settings.setChecksumDefinitionId(id); } settings.save(); const int aidx = mArchiveDefinitionCB.widget()->currentIndex(); if (aidx >= 0) { const QString id = mArchiveDefinitionCB.widget()->itemData(aidx).toString(); filePrefs.setArchiveCommand(id); } filePrefs.save(); }