diff --git a/src/commands/dumpcrlcachecommand.cpp b/src/commands/dumpcrlcachecommand.cpp index b6f564a3c..a4f8cc5b1 100644 --- a/src/commands/dumpcrlcachecommand.cpp +++ b/src/commands/dumpcrlcachecommand.cpp @@ -1,351 +1,351 @@ /* -*- 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() { 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; } - bool withRevocations () { + Q_REQUIRED_RESULT bool withRevocations () { return mWithRevocations; } private: void readConfig() { KConfigGroup dialog(KSharedConfig::openConfig(), "DumpCrlCacheDialog"); const QSize size = dialog.readEntry("Size", QSize(600, 400)); if (size.isValid()) { resize(size); } } void writeConfig() { KConfigGroup dialog(KSharedConfig::openConfig(), "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(); 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()) { 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, SIGNAL(finished(int,QProcess::ExitStatus)), q, SLOT(slotProcessFinished(int,QProcess::ExitStatus))); connect(&process, SIGNAL(readyReadStandardError()), q, SLOT(slotProcessReadyReadStandardError())); connect(&process, SIGNAL(readyReadStandardOutput()), q, SLOT(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, SIGNAL(updateRequested()), this, SLOT(slotUpdateRequested())); connect(d->dialog, SIGNAL(destroyed()), this, SLOT(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."), i18n("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()), i18n("Dump CRL Cache Error")); } } #undef d #undef q #include "moc_dumpcrlcachecommand.cpp" #include "dumpcrlcachecommand.moc" diff --git a/src/commands/exportcertificatecommand.cpp b/src/commands/exportcertificatecommand.cpp index c3c44a143..1bc2b6719 100644 --- a/src/commands/exportcertificatecommand.cpp +++ b/src/commands/exportcertificatecommand.cpp @@ -1,335 +1,334 @@ /* -*- mode: c++; c-basic-offset:4 -*- exportcertificatecommand.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 "exportcertificatecommand.h" #include "fileoperationspreferences.h" #include "command_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Dialogs; using namespace GpgME; using namespace QGpgME; class ExportCertificateCommand::Private : public Command::Private { friend class ::ExportCertificateCommand; ExportCertificateCommand *q_func() const { return static_cast(q); } public: explicit Private(ExportCertificateCommand *qq, KeyListController *c); ~Private(); void startExportJob(GpgME::Protocol protocol, const std::vector &keys); void cancelJobs(); void exportResult(const GpgME::Error &, const QByteArray &); void showError(const GpgME::Error &error); bool requestFileNames(GpgME::Protocol prot); void finishedIfLastJob(); private: QMap fileNames; - uint jobsPending; + uint jobsPending = 0; QMap outFileForSender; QPointer cmsJob; QPointer pgpJob; }; ExportCertificateCommand::Private *ExportCertificateCommand::d_func() { return static_cast(d.get()); } const ExportCertificateCommand::Private *ExportCertificateCommand::d_func() const { return static_cast(d.get()); } #define d d_func() #define q q_func() ExportCertificateCommand::Private::Private(ExportCertificateCommand *qq, KeyListController *c) - : Command::Private(qq, c), - jobsPending(0) + : Command::Private(qq, c) { } ExportCertificateCommand::Private::~Private() {} ExportCertificateCommand::ExportCertificateCommand(KeyListController *p) : Command(new Private(this, p)) { } ExportCertificateCommand::ExportCertificateCommand(QAbstractItemView *v, KeyListController *p) : Command(v, new Private(this, p)) { } ExportCertificateCommand::ExportCertificateCommand(const Key &key) : Command(key, new Private(this, nullptr)) { } ExportCertificateCommand::~ExportCertificateCommand() {} void ExportCertificateCommand::setOpenPGPFileName(const QString &fileName) { if (!d->jobsPending) { d->fileNames[OpenPGP] = fileName; } } QString ExportCertificateCommand::openPGPFileName() const { return d->fileNames[OpenPGP]; } void ExportCertificateCommand::setX509FileName(const QString &fileName) { if (!d->jobsPending) { d->fileNames[CMS] = fileName; } } QString ExportCertificateCommand::x509FileName() const { return d->fileNames[CMS]; } void ExportCertificateCommand::doStart() { std::vector keys = d->keys(); if (keys.empty()) { return; } const auto firstCms = std::partition(keys.begin(), keys.end(), [](const GpgME::Key &key) { return key.protocol() != GpgME::CMS; }); std::vector openpgp, cms; std::copy(keys.begin(), firstCms, std::back_inserter(openpgp)); std::copy(firstCms, keys.end(), std::back_inserter(cms)); Q_ASSERT(!openpgp.empty() || !cms.empty()); const bool haveBoth = !cms.empty() && !openpgp.empty(); const GpgME::Protocol prot = haveBoth ? UnknownProtocol : (!cms.empty() ? CMS : OpenPGP); if (!d->requestFileNames(prot)) { Q_EMIT canceled(); d->finished(); } else { if (!openpgp.empty()) { d->startExportJob(GpgME::OpenPGP, openpgp); } if (!cms.empty()) { d->startExportJob(GpgME::CMS, cms); } } } bool ExportCertificateCommand::Private::requestFileNames(GpgME::Protocol protocol) { if (protocol == UnknownProtocol) { if (!fileNames[OpenPGP].isEmpty() && !fileNames[CMS].isEmpty()) { return true; } const QPointer dlg(new ExportCertificatesDialog); applyWindowID(dlg); dlg->setOpenPgpExportFileName(fileNames[OpenPGP]); dlg->setCmsExportFileName(fileNames[CMS]); const bool accepted = dlg->exec() == QDialog::Accepted && dlg; if (accepted) { fileNames[OpenPGP] = dlg->openPgpExportFileName(); fileNames[CMS] = dlg->cmsExportFileName(); } else { fileNames.clear(); } delete dlg; return accepted; } if (!fileNames[protocol].isEmpty()) { return true; } QString proposedFileName; if (keys().size() == 1) { const bool usePGPFileExt = FileOperationsPreferences().usePGPFileExt(); const auto key = keys().front(); auto name = Formatting::prettyName(key); if (name.isEmpty()) { name = Formatting::prettyEMail(key); } /* Not translated so it's better to use in tutorials etc. */ proposedFileName = QStringLiteral("%1_%2_public.%3").arg(name).arg( Formatting::prettyKeyID(key.shortKeyID())).arg( QString::fromLatin1(outputFileExtension(protocol == OpenPGP ? Class::OpenPGP | Class::Ascii | Class::Certificate : Class::CMS | Class::Ascii | Class::Certificate, usePGPFileExt))); } const QString fname = FileDialog::getSaveFileNameEx(parentWidgetOrView(), i18n("Export Certificates"), QStringLiteral("imp"), proposedFileName, protocol == GpgME::OpenPGP ? i18n("OpenPGP Certificates") + QLatin1String(" (*.asc *.gpg *.pgp)") : i18n("S/MIME Certificates") + QLatin1String(" (*.pem *.der)")); fileNames[protocol] = fname; return !fname.isEmpty(); } void ExportCertificateCommand::Private::startExportJob(GpgME::Protocol protocol, const std::vector &keys) { Q_ASSERT(protocol != GpgME::UnknownProtocol); const QGpgME::Protocol *const backend = (protocol == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime(); Q_ASSERT(backend); const QString fileName = fileNames[protocol]; const bool binary = protocol == GpgME::OpenPGP ? fileName.endsWith(QLatin1String(".gpg"), Qt::CaseInsensitive) || fileName.endsWith(QLatin1String(".pgp"), Qt::CaseInsensitive) : fileName.endsWith(QLatin1String(".der"), Qt::CaseInsensitive); std::unique_ptr job(backend->publicKeyExportJob(!binary)); Q_ASSERT(job.get()); connect(job.get(), SIGNAL(result(GpgME::Error,QByteArray)), q, SLOT(exportResult(GpgME::Error,QByteArray))); connect(job.get(), &Job::progress, q, &Command::progress); QStringList fingerprints; fingerprints.reserve(keys.size()); for (const Key &i : keys) { fingerprints << QLatin1String(i.primaryFingerprint()); } const GpgME::Error err = job->start(fingerprints); if (err) { showError(err); finished(); return; } Q_EMIT q->info(i18n("Exporting certificates...")); ++jobsPending; const QPointer exportJob(job.release()); outFileForSender[exportJob.data()] = fileName; (protocol == CMS ? cmsJob : pgpJob) = exportJob; } void ExportCertificateCommand::Private::showError(const GpgME::Error &err) { Q_ASSERT(err); const QString msg = i18n("

An error occurred while trying to export " "the certificate:

" "

%1

", QString::fromLocal8Bit(err.asString())); error(msg, i18n("Certificate Export Failed")); } void ExportCertificateCommand::doCancel() { d->cancelJobs(); } void ExportCertificateCommand::Private::finishedIfLastJob() { if (jobsPending <= 0) { finished(); } } static bool write_complete(QIODevice &iod, const QByteArray &data) { qint64 total = 0; qint64 toWrite = data.size(); while (total < toWrite) { const qint64 written = iod.write(data.data() + total, toWrite); if (written < 0) { return false; } total += written; toWrite -= written; } return true; } void ExportCertificateCommand::Private::exportResult(const GpgME::Error &err, const QByteArray &data) { Q_ASSERT(jobsPending > 0); --jobsPending; Q_ASSERT(outFileForSender.contains(q->sender())); const QString outFile = outFileForSender[q->sender()]; if (err) { showError(err); finishedIfLastJob(); return; } QSaveFile savefile(outFile); //TODO: use KIO const QString writeErrorMsg = i18n("Could not write to file %1.", outFile); const QString errorCaption = i18n("Certificate Export Failed"); if (!savefile.open(QIODevice::WriteOnly)) { error(writeErrorMsg, errorCaption); finishedIfLastJob(); return; } if (!write_complete(savefile, data) || !savefile.commit()) { error(writeErrorMsg, errorCaption); } finishedIfLastJob(); } void ExportCertificateCommand::Private::cancelJobs() { if (cmsJob) { cmsJob->slotCancel(); } if (pgpJob) { pgpJob->slotCancel(); } } #undef d #undef q #include "moc_exportcertificatecommand.cpp" diff --git a/src/commands/exportpaperkeycommand.h b/src/commands/exportpaperkeycommand.h index ec9e74a22..91bd9adbe 100644 --- a/src/commands/exportpaperkeycommand.h +++ b/src/commands/exportpaperkeycommand.h @@ -1,59 +1,59 @@ /* -*- mode: c++; c-basic-offset:4 -*- commands/exportsecretkeycommand.h 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 */ #ifndef __KLEOPATRA_COMMMANDS_EXPORTPAPERKEYCOMMAND_H__ #define __KLEOPATRA_COMMMANDS_EXPORTPAPERKEYCOMMAND_H__ #include #include #include class QWidget; namespace Kleo { namespace Commands { class ExportPaperKeyCommand : public GnuPGProcessCommand { Q_OBJECT public: explicit ExportPaperKeyCommand(QAbstractItemView *view, KeyListController *parent); static Restrictions restrictions() { return OnlyOneKey | NeedSecretKey | MustBeOpenPGP; } protected Q_SLOTS: void pkProcFinished(int code, QProcess::ExitStatus status); private: QStringList arguments() const override; bool preStartHook(QWidget *parentWidget) const override; QString errorCaption() const override; QString crashExitMessage(const QStringList &) const override; QString errorExitMessage(const QStringList &) const override; private: - QWidget *mParent; + QWidget *const mParent; QProcess mPkProc; }; } } #endif // __KLEOPATRA_COMMMANDS_EXPORTPAPERKEYCOMMAND_H__ diff --git a/src/commands/importcrlcommand.cpp b/src/commands/importcrlcommand.cpp index f9255af8d..630694b9f 100644 --- a/src/commands/importcrlcommand.cpp +++ b/src/commands/importcrlcommand.cpp @@ -1,216 +1,216 @@ /* -*- mode: c++; c-basic-offset:4 -*- commands/importcrlcommand.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 "importcrlcommand.h" #include "command_p.h" #include "utils/gnupg-helper.h" #include #include #include #include #include #include static const int PROCESS_TERMINATE_TIMEOUT = 5000; // milliseconds using namespace Kleo; using namespace Kleo::Commands; class ImportCrlCommand::Private : Command::Private { friend class ::Kleo::Commands::ImportCrlCommand; ImportCrlCommand *q_func() const { return static_cast(q); } public: explicit Private(ImportCrlCommand *qq, KeyListController *c); ~Private(); - QString errorString() const + Q_REQUIRED_RESULT QString errorString() const { return stringFromGpgOutput(errorBuffer); } private: void init(); #ifndef QT_NO_FILEDIALOG QStringList getFileNames() { // loadcrl can only work with DER encoded files // (verified with dirmngr 1.0.3) const QString filter = QStringLiteral("%1 (*.crl *.arl *-crl.der *-arl.der)").arg(i18n("Certificate Revocation Lists, DER encoded")); return QFileDialog::getOpenFileNames(parentWidgetOrView(), i18n("Select CRL File to Import"), QString(), filter); } #endif // QT_NO_FILEDIALOG private: void slotProcessFinished(int, QProcess::ExitStatus); void slotProcessReadyReadStandardError(); private: QStringList files; QProcess process; QByteArray errorBuffer; bool canceled; bool firstRun; }; ImportCrlCommand::Private *ImportCrlCommand::d_func() { return static_cast(d.get()); } const ImportCrlCommand::Private *ImportCrlCommand::d_func() const { return static_cast(d.get()); } #define d d_func() #define q q_func() ImportCrlCommand::Private::Private(ImportCrlCommand *qq, KeyListController *c) : Command::Private(qq, c), files(), process(), errorBuffer(), canceled(false), firstRun(true) { process.setProgram (gpgSmPath()); process.setArguments(QStringList() << QStringLiteral("--call-dirmngr") << QStringLiteral("loadcrl")); } ImportCrlCommand::Private::~Private() {} ImportCrlCommand::ImportCrlCommand(KeyListController *c) : Command(new Private(this, c)) { d->init(); } ImportCrlCommand::ImportCrlCommand(QAbstractItemView *v, KeyListController *c) : Command(v, new Private(this, c)) { d->init(); } ImportCrlCommand::ImportCrlCommand(const QStringList &files, KeyListController *c) : Command(new Private(this, c)) { d->init(); d->files = files; } ImportCrlCommand::ImportCrlCommand(const QStringList &files, QAbstractItemView *v, KeyListController *c) : Command(v, new Private(this, c)) { d->init(); d->files = files; } void ImportCrlCommand::Private::init() { connect(&process, SIGNAL(finished(int,QProcess::ExitStatus)), q, SLOT(slotProcessFinished(int,QProcess::ExitStatus))); connect(&process, SIGNAL(readyReadStandardError()), q, SLOT(slotProcessReadyReadStandardError())); } ImportCrlCommand::~ImportCrlCommand() {} void ImportCrlCommand::setFiles(const QStringList &files) { d->files = files; } void ImportCrlCommand::doStart() { #ifndef QT_NO_FILEDIALOG if (d->files.empty()) { d->files = d->getFileNames(); } #endif // QT_NO_FILEDIALOG if (d->files.empty()) { Q_EMIT canceled(); d->finished(); return; } auto args = d->process.arguments(); if (!d->firstRun) { /* remove the last file */ args.pop_back(); } args << d->files.takeFirst(); d->process.setArguments(args); d->process.start(); d->firstRun = false; if (!d->process.waitForStarted()) { d->error(i18n("Unable to start process dirmngr. " "Please check your installation."), i18n("Clear CRL Cache Error")); d->finished(); } } void ImportCrlCommand::doCancel() { d->canceled = true; if (d->process.state() != QProcess::NotRunning) { d->process.terminate(); QTimer::singleShot(PROCESS_TERMINATE_TIMEOUT, &d->process, &QProcess::kill); } } void ImportCrlCommand::Private::slotProcessFinished(int code, QProcess::ExitStatus status) { if (!canceled) { if (status == QProcess::CrashExit) error(i18n("The GpgSM process that tried to import the CRL file " "ended prematurely because of an unexpected error. " "Please check the output of gpgsm --call-dirmngr loadcrl for details."), i18n("Import CRL Error")); else if (code) error(i18n("An error occurred while trying to import the CRL file. " "The output from gpgsm was:\n%1", errorString()), i18n("Import CRL Error")); else if (files.empty()) information(i18n("CRL file imported successfully."), i18n("Import CRL Finished")); } if (!files.empty()) { q->doStart(); return; } finished(); } void ImportCrlCommand::Private::slotProcessReadyReadStandardError() { errorBuffer += process.readAllStandardError(); } #undef d #undef q #include "moc_importcrlcommand.cpp" diff --git a/src/crypto/autodecryptverifyfilescontroller.cpp b/src/crypto/autodecryptverifyfilescontroller.cpp index 7fa515610..f767682cc 100644 --- a/src/crypto/autodecryptverifyfilescontroller.cpp +++ b/src/crypto/autodecryptverifyfilescontroller.cpp @@ -1,528 +1,524 @@ /* -*- mode: c++; c-basic-offset:4 -*- autodecryptverifyfilescontroller.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "autodecryptverifyfilescontroller.h" #include "fileoperationspreferences.h" #include #include #include #include #include "commands/decryptverifyfilescommand.h" #include #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" #include #include #include #include #include #include #include #include using namespace GpgME; using namespace Kleo; using namespace Kleo::Crypto; using namespace Kleo::Crypto::Gui; class AutoDecryptVerifyFilesController::Private { AutoDecryptVerifyFilesController *const q; public: explicit Private(AutoDecryptVerifyFilesController *qq); ~Private() { qCDebug(KLEOPATRA_LOG); delete m_workDir; } void slotDialogCanceled(); void schedule(); void exec(); std::vector > buildTasks(const QStringList &, QStringList &); struct CryptoFile { QString baseName; QString fileName; GpgME::Protocol protocol = GpgME::UnknownProtocol; int classification = 0; std::shared_ptr output; }; QVector classifyAndSortFiles(const QStringList &files); void reportError(int err, const QString &details) { q->setLastError(err, details); q->emitDoneOrError(); } void cancelAllTasks(); QStringList m_passedFiles, m_filesAfterPreparation; std::vector > m_results; std::vector > m_runnableTasks, m_completedTasks; std::shared_ptr m_runningTask; - bool m_errorDetected; - DecryptVerifyOperation m_operation; - DecryptVerifyFilesDialog *m_dialog; - QTemporaryDir *m_workDir; + bool m_errorDetected = false; + DecryptVerifyOperation m_operation = DecryptVerify; + DecryptVerifyFilesDialog *m_dialog = nullptr; + QTemporaryDir *m_workDir = nullptr; }; -AutoDecryptVerifyFilesController::Private::Private(AutoDecryptVerifyFilesController *qq) : q(qq), - m_errorDetected(false), - m_operation(DecryptVerify), - m_dialog(nullptr), - m_workDir(nullptr) +AutoDecryptVerifyFilesController::Private::Private(AutoDecryptVerifyFilesController *qq) : q(qq) { qRegisterMetaType(); } void AutoDecryptVerifyFilesController::Private::slotDialogCanceled() { qCDebug(KLEOPATRA_LOG); } void AutoDecryptVerifyFilesController::Private::schedule() { if (!m_runningTask && !m_runnableTasks.empty()) { const std::shared_ptr t = m_runnableTasks.back(); m_runnableTasks.pop_back(); t->start(); m_runningTask = t; } if (!m_runningTask) { kleo_assert(m_runnableTasks.empty()); for (const std::shared_ptr &i : qAsConst(m_results)) { Q_EMIT q->verificationResult(i->verificationResult()); } } } void AutoDecryptVerifyFilesController::Private::exec() { Q_ASSERT(!m_dialog); QStringList undetected; std::vector > tasks = buildTasks(m_passedFiles, undetected); if (!undetected.isEmpty()) { // Since GpgME 1.7.0 Classification is supposed to be reliable // so we really can't do anything with this data. reportError(makeGnuPGError(GPG_ERR_GENERAL), xi18n("Failed to find encrypted or signed data in one or more files." "You can manually select what to do with the files now." "If they contain signed or encrypted data please report a bug (see Help->Report Bug).")); auto cmd = new Commands::DecryptVerifyFilesCommand(undetected, nullptr, true); cmd->start(); } if (tasks.empty()) { q->emitDoneOrError(); return; } Q_ASSERT(m_runnableTasks.empty()); m_runnableTasks.swap(tasks); std::shared_ptr coll(new TaskCollection); Q_FOREACH (const std::shared_ptr &i, m_runnableTasks) { q->connectTask(i); } coll->setTasks(m_runnableTasks); m_dialog = new DecryptVerifyFilesDialog(coll); m_dialog->setOutputLocation(heuristicBaseDirectory(m_passedFiles)); QTimer::singleShot(0, q, SLOT(schedule())); if (m_dialog->exec() == QDialog::Accepted && m_workDir) { // Without workdir there is nothing to move. const QDir workdir(m_workDir->path()); const QDir outDir(m_dialog->outputLocation()); bool overWriteAll = false; qCDebug(KLEOPATRA_LOG) << workdir.entryList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot); for (const QFileInfo &fi: workdir.entryInfoList(QDir::Files | QDir::Dirs | QDir::NoDotAndDotDot)) { const auto inpath = fi.absoluteFilePath(); if (fi.isDir()) { // A directory. Assume that the input was an archive // and avoid directory merges by trying to find a non // existing directory. auto candidate = fi.baseName(); if (candidate.startsWith(QLatin1Char('-'))) { // Bug in GpgTar Extracts stdout passed archives to a dir named - candidate = QFileInfo(m_passedFiles.first()).baseName(); } QString suffix; QFileInfo ofi; int i = 0; do { ofi = QFileInfo(outDir.absoluteFilePath(candidate + suffix)); if (!ofi.exists()) { break; } suffix = QStringLiteral("_%1").arg(++i); } while (i < 1000); if (!moveDir(inpath, ofi.absoluteFilePath())) { reportError(makeGnuPGError(GPG_ERR_GENERAL), xi18n("Failed to move %1 to %2.", inpath, ofi.absoluteFilePath())); } continue; } const auto outpath = outDir.absoluteFilePath(fi.fileName()); qCDebug(KLEOPATRA_LOG) << "Moving " << inpath << " to " << outpath; const QFileInfo ofi(outpath); if (ofi.exists()) { int sel = KMessageBox::No; if (!overWriteAll) { sel = KMessageBox::questionYesNoCancel(m_dialog, i18n("The file %1 already exists.\n" "Overwrite?", outpath), i18n("Overwrite Existing File?"), KStandardGuiItem::overwrite(), KGuiItem(i18n("Overwrite All")), KStandardGuiItem::cancel()); } if (sel == KMessageBox::Cancel) { qCDebug(KLEOPATRA_LOG) << "Overwriting canceled for: " << outpath; continue; } if (sel == KMessageBox::No) { //Overwrite All overWriteAll = true; } if (!QFile::remove(outpath)) { reportError(makeGnuPGError(GPG_ERR_GENERAL), xi18n("Failed to delete %1.", outpath)); continue; } } if (!QFile::rename(inpath, outpath)) { reportError(makeGnuPGError(GPG_ERR_GENERAL), xi18n("Failed to move %1 to %2.", inpath, outpath)); } } } q->emitDoneOrError(); delete m_dialog; m_dialog = nullptr; } QVector AutoDecryptVerifyFilesController::Private::classifyAndSortFiles(const QStringList &files) { const auto isSignature = [](int classification) -> bool { return mayBeDetachedSignature(classification) || mayBeOpaqueSignature(classification) || (classification & Class::TypeMask) == Class::ClearsignedMessage; }; QVector out; for (const auto &file : files) { CryptoFile cFile; cFile.fileName = file; cFile.baseName = file.left(file.length() - 4); cFile.classification = classify(file); cFile.protocol = findProtocol(cFile.classification); auto it = std::find_if(out.begin(), out.end(), [&cFile](const CryptoFile &other) { return other.protocol == cFile.protocol && other.baseName == cFile.baseName; }); if (it != out.end()) { // If we found a file with the same basename, make sure that encrypted // file is before the signature file, so that we first decrypt and then // verify if (isSignature(cFile.classification) && isCipherText(it->classification)) { out.insert(it + 1, cFile); } else if (isCipherText(cFile.classification) && isSignature(it->classification)) { out.insert(it, cFile); } else { // both are signatures or both are encrypted files, in which // case order does not matter out.insert(it, cFile); } } else { out.push_back(cFile); } } return out; } std::vector< std::shared_ptr > AutoDecryptVerifyFilesController::Private::buildTasks(const QStringList &fileNames, QStringList &undetected) { // sort files so that we make sure we first decrypt and then verify QVector cryptoFiles = classifyAndSortFiles(fileNames); std::vector > tasks; for (auto it = cryptoFiles.begin(), end = cryptoFiles.end(); it != end; ++it) { auto &cFile = (*it); QFileInfo fi(cFile.fileName); qCDebug(KLEOPATRA_LOG) << "classified" << cFile.fileName << "as" << printableClassification(cFile.classification); if (!fi.isReadable()) { reportError(makeGnuPGError(GPG_ERR_ASS_NO_INPUT), xi18n("Cannot open %1 for reading.", cFile.fileName)); continue; } if (mayBeAnyCertStoreType(cFile.classification)) { // Trying to verify a certificate. Possible because extensions are often similar // for PGP Keys. reportError(makeGnuPGError(GPG_ERR_ASS_NO_INPUT), xi18n("The file %1 contains certificates and can't be decrypted or verified.", cFile.fileName)); qCDebug(KLEOPATRA_LOG) << "reported error"; continue; } // We can't reliably detect CMS detached signatures, so we will try to do // our best to use the current file as a detached signature and fallback to // opaque signature otherwise. if (cFile.protocol == GpgME::CMS && mayBeDetachedSignature(cFile.classification)) { // First, see if previous task was a decryption task for the same file // and "pipe" it's output into our input std::shared_ptr input; bool prepend = false; if (it != cryptoFiles.begin()) { const auto prev = it - 1; if (prev->protocol == cFile.protocol && prev->baseName == cFile.baseName) { input = Input::createFromOutput(prev->output); prepend = true; } } if (!input) { if (QFile::exists(cFile.baseName)) { input = Input::createFromFile(cFile.baseName); } } if (input) { qCDebug(KLEOPATRA_LOG) << "Detached CMS verify: " << cFile.fileName; std::shared_ptr t(new VerifyDetachedTask); t->setInput(Input::createFromFile(cFile.fileName)); t->setSignedData(input); t->setProtocol(cFile.protocol); if (prepend) { // Put the verify task BEFORE the decrypt task in the tasks queue, // because the tasks are executed in reverse order! tasks.insert(tasks.end() - 1, t); } else { tasks.push_back(t); } continue; } else { // No signed data, maybe not a detached signature } } if (isDetachedSignature(cFile.classification)) { // Detached signature, try to find data or ask the user. QString signedDataFileName = cFile.baseName; if (signedDataFileName.isEmpty()) { signedDataFileName = QFileDialog::getOpenFileName(nullptr, xi18n("Select the file to verify with \"%1\"", fi.fileName()), fi.dir().dirName()); } if (signedDataFileName.isEmpty()) { qCDebug(KLEOPATRA_LOG) << "No signed data selected. Verify aborted."; } else { qCDebug(KLEOPATRA_LOG) << "Detached verify: " << cFile.fileName << " Data: " << signedDataFileName; std::shared_ptr t(new VerifyDetachedTask); t->setInput(Input::createFromFile(cFile.fileName)); t->setSignedData(Input::createFromFile(signedDataFileName)); t->setProtocol(cFile.protocol); tasks.push_back(t); } continue; } if (!mayBeAnyMessageType(cFile.classification)) { // Not a Message? Maybe there is a signature for this file? const auto signatures = findSignatures(cFile.fileName); bool foundSig = false; if (!signatures.empty()) { for (const QString &sig : signatures) { const auto classification = classify(sig); qCDebug(KLEOPATRA_LOG) << "Guessing: " << sig << " is a signature for: " << cFile.fileName << "Classification: " << classification; const auto proto = findProtocol(classification); if (proto == GpgME::UnknownProtocol) { qCDebug(KLEOPATRA_LOG) << "Could not determine protocol. Skipping guess."; continue; } foundSig = true; std::shared_ptr t(new VerifyDetachedTask); t->setInput(Input::createFromFile(sig)); t->setSignedData(Input::createFromFile(cFile.fileName)); t->setProtocol(proto); tasks.push_back(t); } } if (!foundSig) { undetected << cFile.fileName; qCDebug(KLEOPATRA_LOG) << "Failed detection for: " << cFile.fileName << " adding to undetected."; } } else { // Any Message type so we have input and output. const auto input = Input::createFromFile(cFile.fileName); const auto archiveDefinitions = ArchiveDefinition::getArchiveDefinitions(); const auto ad = q->pick_archive_definition(cFile.protocol, archiveDefinitions, cFile.fileName); if (FileOperationsPreferences().dontUseTmpDir()) { if (!m_workDir) { m_workDir = new QTemporaryDir(heuristicBaseDirectory(fileNames) + QStringLiteral("/kleopatra-XXXXXX")); } if (!m_workDir->isValid()) { qCDebug(KLEOPATRA_LOG) << m_workDir->path() << "not a valid temporary directory."; delete m_workDir; m_workDir = new QTemporaryDir(); } } else if (!m_workDir) { m_workDir = new QTemporaryDir(); } qCDebug(KLEOPATRA_LOG) << "Using:" << m_workDir->path() << "as temporary directory."; const auto wd = QDir(m_workDir->path()); const auto output = ad ? ad->createOutputFromUnpackCommand(cFile.protocol, cFile.fileName, wd) : /*else*/ Output::createFromFile(wd.absoluteFilePath(outputFileName(fi.fileName())), false); // If this might be opaque CMS signature, then try that. We already handled // detached CMS signature above const auto isCMSOpaqueSignature = cFile.protocol == GpgME::CMS && mayBeOpaqueSignature(cFile.classification); if (isOpaqueSignature(cFile.classification) || isCMSOpaqueSignature) { qCDebug(KLEOPATRA_LOG) << "creating a VerifyOpaqueTask"; std::shared_ptr t(new VerifyOpaqueTask); t->setInput(input); t->setOutput(output); t->setProtocol(cFile.protocol); tasks.push_back(t); } else { // Any message. That is not an opaque signature needs to be // decrypted. Verify we always do because we can't know if // an encrypted message is also signed. qCDebug(KLEOPATRA_LOG) << "creating a DecryptVerifyTask"; std::shared_ptr t(new DecryptVerifyTask); t->setInput(input); t->setOutput(output); t->setProtocol(cFile.protocol); cFile.output = output; tasks.push_back(t); } } } return tasks; } void AutoDecryptVerifyFilesController::setFiles(const QStringList &files) { d->m_passedFiles = files; } AutoDecryptVerifyFilesController::AutoDecryptVerifyFilesController(QObject *parent) : DecryptVerifyFilesController(parent), d(new Private(this)) { } AutoDecryptVerifyFilesController::AutoDecryptVerifyFilesController(const std::shared_ptr &ctx, QObject *parent) : DecryptVerifyFilesController(ctx, parent), d(new Private(this)) { } AutoDecryptVerifyFilesController::~AutoDecryptVerifyFilesController() { qCDebug(KLEOPATRA_LOG); } void AutoDecryptVerifyFilesController::start() { d->exec(); } void AutoDecryptVerifyFilesController::setOperation(DecryptVerifyOperation op) { d->m_operation = op; } DecryptVerifyOperation AutoDecryptVerifyFilesController::operation() const { return d->m_operation; } void AutoDecryptVerifyFilesController::Private::cancelAllTasks() { // we just kill all runnable tasks - this will not result in // signal emissions. m_runnableTasks.clear(); // a cancel() will result in a call to if (m_runningTask) { m_runningTask->cancel(); } } void AutoDecryptVerifyFilesController::cancel() { qCDebug(KLEOPATRA_LOG); try { d->m_errorDetected = true; if (d->m_dialog) { d->m_dialog->close(); } d->cancelAllTasks(); } catch (const std::exception &e) { qCDebug(KLEOPATRA_LOG) << "Caught exception: " << e.what(); } } void AutoDecryptVerifyFilesController::doTaskDone(const Task *task, const std::shared_ptr &result) { Q_ASSERT(task); Q_UNUSED(task); // We could just delete the tasks here, but we can't use // Qt::QueuedConnection here (we need sender()) and other slots // might not yet have executed. Therefore, we push completed tasks // into a burial container d->m_completedTasks.push_back(d->m_runningTask); d->m_runningTask.reset(); if (const std::shared_ptr &dvr = std::dynamic_pointer_cast(result)) { d->m_results.push_back(dvr); } QTimer::singleShot(0, this, SLOT(schedule())); } #include "moc_autodecryptverifyfilescontroller.cpp" diff --git a/src/crypto/controller.cpp b/src/crypto/controller.cpp index 02d552dc3..3064bbad8 100644 --- a/src/crypto/controller.cpp +++ b/src/crypto/controller.cpp @@ -1,81 +1,79 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/controller.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 "controller.h" using namespace Kleo; using namespace Kleo::Crypto; class Controller::Private { friend class ::Kleo::Crypto::Controller; Controller *const q; public: explicit Private(Controller *qq) - : q(qq), - lastError(0), - lastErrorString() + : q(qq) { } private: - int lastError; + int lastError = 0; QString lastErrorString; }; Controller::Controller(QObject *parent) : QObject(parent), ExecutionContextUser(), d(new Private(this)) { } Controller::Controller(const std::shared_ptr &ctx, QObject *parent) : QObject(parent), ExecutionContextUser(ctx), d(new Private(this)) { } Controller::~Controller() {} void Controller::taskDone(const std::shared_ptr &result) { const Task *task = qobject_cast(sender()); Q_ASSERT(task); doTaskDone(task, result); } void Controller::doTaskDone(const Task *, const std::shared_ptr &) {} void Controller::connectTask(const std::shared_ptr &task) { Q_ASSERT(task); connect(task.get(), &Task::result, this, &Controller::taskDone); } void Controller::setLastError(int err, const QString &msg) { d->lastError = err; d->lastErrorString = msg; } void Controller::emitDoneOrError() { if (d->lastError) { Q_EMIT error(d->lastError, d->lastErrorString); d->lastError = 0; d->lastErrorString = QString(); } else { done(); } } diff --git a/src/crypto/gui/certificatelineedit.cpp b/src/crypto/gui/certificatelineedit.cpp index ccab0b5c9..d4445c17c 100644 --- a/src/crypto/gui/certificatelineedit.cpp +++ b/src/crypto/gui/certificatelineedit.cpp @@ -1,254 +1,250 @@ /* crypto/gui/certificatelineedit.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 "certificatelineedit.h" #include #include #include #include #include #include #include "kleopatra_debug.h" #include "dialogs/certificateselectiondialog.h" #include "commands/detailscommand.h" #include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Dialogs; using namespace GpgME; Q_DECLARE_METATYPE(GpgME::Key) static QStringList s_lookedUpKeys; namespace { class ProxyModel : public KeyListSortFilterProxyModel { Q_OBJECT public: ProxyModel(QObject *parent = nullptr) : KeyListSortFilterProxyModel(parent) { } QVariant data(const QModelIndex &index, int role) const override { if (!index.isValid()) { return QVariant(); } switch (role) { case Qt::DecorationRole: { const auto key = KeyListSortFilterProxyModel::data(index, Kleo::KeyListModelInterface::KeyRole).value(); Q_ASSERT(!key.isNull()); if (key.isNull()) { return QVariant(); } return Kleo::Formatting::iconForUid(key.userID(0)); } default: return KeyListSortFilterProxyModel::data(index, role); } } }; } // namespace CertificateLineEdit::CertificateLineEdit(AbstractKeyListModel *model, QWidget *parent, KeyFilter *filter) : QLineEdit(parent), mFilterModel(new KeyListSortFilterProxyModel(this)), mFilter(std::shared_ptr(filter)), - mEditStarted(false), - mEditFinished(false), mLineAction(new QAction(this)) { setPlaceholderText(i18n("Please enter a name or email address...")); setClearButtonEnabled(true); addAction(mLineAction, QLineEdit::LeadingPosition); - QFontMetrics fm(font()); - auto *completer = new QCompleter(this); auto *completeFilterModel = new ProxyModel(completer); completeFilterModel->setKeyFilter(mFilter); completeFilterModel->setSourceModel(model); completer->setModel(completeFilterModel); completer->setCompletionColumn(KeyListModelInterface::Summary); completer->setFilterMode(Qt::MatchContains); completer->setCaseSensitivity(Qt::CaseInsensitive); setCompleter(completer); mFilterModel->setSourceModel(model); mFilterModel->setFilterKeyColumn(KeyListModelInterface::Summary); if (filter) { mFilterModel->setKeyFilter(mFilter); } connect(KeyCache::instance().get(), &Kleo::KeyCache::keyListingDone, this, &CertificateLineEdit::updateKey); connect(this, &QLineEdit::editingFinished, this, &CertificateLineEdit::updateKey); connect(this, &QLineEdit::textChanged, this, &CertificateLineEdit::editChanged); connect(mLineAction, &QAction::triggered, this, &CertificateLineEdit::dialogRequested); connect(this, &QLineEdit::editingFinished, this, &CertificateLineEdit::checkLocate); updateKey(); /* Take ownership of the model to prevent double deletion when the * filter models are deleted */ model->setParent(parent ? parent : this); } void CertificateLineEdit::editChanged() { updateKey(); if (!mEditStarted) { Q_EMIT editingStarted(); mEditStarted = true; } mEditFinished = false; } void CertificateLineEdit::checkLocate() { if (!key().isNull()) { // Already have a key return; } // Only check once per mailbox const auto mailText = text(); if (s_lookedUpKeys.contains(mailText)) { return; } s_lookedUpKeys << mailText; qCDebug(KLEOPATRA_LOG) << "Lookup job for" << mailText; QGpgME::KeyForMailboxJob *job = QGpgME::openpgp()->keyForMailboxJob(); job->start(mailText); } void CertificateLineEdit::updateKey() { const auto mailText = text(); auto newKey = Key(); if (mailText.isEmpty()) { mLineAction->setIcon(QIcon::fromTheme(QStringLiteral("resource-group-new"))); mLineAction->setToolTip(i18n("Open selection dialog.")); } else { mFilterModel->setFilterFixedString(mailText); if (mFilterModel->rowCount() > 1) { if (mEditFinished) { mLineAction->setIcon(QIcon::fromTheme(QStringLiteral("question")).pixmap(KIconLoader::SizeSmallMedium)); mLineAction->setToolTip(i18n("Multiple certificates")); } } else if (mFilterModel->rowCount() == 1) { newKey = mFilterModel->data(mFilterModel->index(0, 0), KeyListModelInterface::KeyRole).value(); mLineAction->setToolTip(Formatting::validity(newKey.userID(0)) + QStringLiteral("
Click here for details.")); /* FIXME: This needs to be solved by a multiple UID supporting model */ mLineAction->setIcon(Formatting::iconForUid(newKey.userID(0))); } else { mLineAction->setIcon(QIcon::fromTheme(QStringLiteral("emblem-error"))); mLineAction->setToolTip(i18n("No matching certificates found.
Click here to import a certificate.")); } } mKey = newKey; if (mKey.isNull()) { setToolTip(QString()); } else { setToolTip(Formatting::toolTip(newKey, Formatting::ToolTipOption::AllOptions)); } Q_EMIT keyChanged(); if (mailText.isEmpty()) { Q_EMIT wantsRemoval(this); } } Key CertificateLineEdit::key() const { if (isEnabled()) { return mKey; } else { return Key(); } } void CertificateLineEdit::dialogRequested() { if (!mKey.isNull()) { auto cmd = new Commands::DetailsCommand(mKey, nullptr); cmd->start(); return; } CertificateSelectionDialog *const dlg = new CertificateSelectionDialog(this); dlg->setKeyFilter(mFilter); if (dlg->exec()) { const std::vector keys = dlg->selectedCertificates(); if (!keys.size()) { return; } for (unsigned int i = 0; i < keys.size(); i++) { if (!i) { setKey(keys[i]); } else { Q_EMIT addRequested(keys[i]); } } } delete dlg; updateKey(); } void CertificateLineEdit::setKey(const Key &k) { QSignalBlocker blocky(this); qCDebug(KLEOPATRA_LOG) << "Setting Key. " << Formatting::summaryLine(k); setText(Formatting::summaryLine(k)); updateKey(); } bool CertificateLineEdit::isEmpty() const { return text().isEmpty(); } void CertificateLineEdit::setKeyFilter(const std::shared_ptr &filter) { mFilter = filter; mFilterModel->setKeyFilter(filter); } #include "certificatelineedit.moc" diff --git a/src/crypto/gui/certificatelineedit.h b/src/crypto/gui/certificatelineedit.h index d10b17a1e..2e129df2c 100644 --- a/src/crypto/gui/certificatelineedit.h +++ b/src/crypto/gui/certificatelineedit.h @@ -1,96 +1,96 @@ /* crypto/gui/certificatelineedit.h 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 */ #ifndef CRYPTO_GUI_CERTIFICATELINEEDIT_H #define CRYPTO_GUI_CERTIFICATELINEEDIT_H #include #include #include "dialogs/certificateselectiondialog.h" #include class QLabel; class QAction; namespace Kleo { class AbstractKeyListModel; class KeyFilter; class KeyListSortFilterProxyModel; /** Line edit and completion based Certificate Selection Widget. * * Shows the status of the selection with a status label and icon. * * The widget will use a single line HBox Layout. For larger dialog * see certificateslectiondialog. */ class CertificateLineEdit: public QLineEdit { Q_OBJECT public: /** Create the certificate selection line. * * If parent is not NULL the model is not taken * over but the parent argument used as the parent of the model. * * @param model: The keylistmodel to use. * @param parent: The usual widget parent. * @param filter: The filters to use. See certificateselectiondialog. */ CertificateLineEdit(AbstractKeyListModel *model, QWidget *parent = nullptr, KeyFilter *filter = nullptr); /** Get the selected key */ GpgME::Key key() const; /** Check if the text is empty */ bool isEmpty() const; /** Set the preselected Key for this widget. */ void setKey(const GpgME::Key &key); /** Set the used keyfilter. */ void setKeyFilter(const std::shared_ptr &filter); Q_SIGNALS: /** Emitted when the selected key changed. */ void keyChanged(); /** Emitted when the entry is empty and editing is finished. */ void wantsRemoval(CertificateLineEdit *w); /** Emitted when the entry is no longer empty. */ void editingStarted(); /** Emitted when the certselectiondialog resulted in multiple certificates. */ void addRequested(const GpgME::Key &key); private Q_SLOTS: void updateKey(); void dialogRequested(); void editChanged(); void checkLocate(); private: - KeyListSortFilterProxyModel *mFilterModel; + KeyListSortFilterProxyModel *const mFilterModel; QLabel *mStatusLabel, *mStatusIcon; GpgME::Key mKey; GpgME::Protocol mCurrentProto; std::shared_ptr mFilter; - bool mEditStarted, - mEditFinished; - QAction *mLineAction; + bool mEditStarted = false; + bool mEditFinished = false; + QAction *const mLineAction; }; } #endif diff --git a/src/crypto/gui/certificateselectionline.cpp b/src/crypto/gui/certificateselectionline.cpp index a6abcf1dc..e64363fc4 100644 --- a/src/crypto/gui/certificateselectionline.cpp +++ b/src/crypto/gui/certificateselectionline.cpp @@ -1,320 +1,320 @@ /* crypto/gui/certificateselectionline.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #include "certificateselectionline.h" #include #include #include #include #include #include #include #include "utils/kleo_assert.h" #include #include #include using namespace Kleo; using namespace GpgME; Q_DECLARE_METATYPE(GpgME::Key) namespace { static QString make_initial_text(const std::vector &keys) { if (keys.empty()) { return i18n("(no matching certificates found)"); } else { return i18n("Please select a certificate"); } } // A QComboBox with an initial text (as known from web browsers) // // only works with read-only QComboBoxen, doesn't affect sizeHint // as it should... // class ComboBox : public QComboBox { Q_OBJECT Q_PROPERTY(QString initialText READ initialText WRITE setInitialText) Q_PROPERTY(QIcon initialIcon READ initialIcon WRITE setInitialIcon) public: explicit ComboBox(QWidget *parent = nullptr) : QComboBox(parent), m_initialText(), m_initialIcon() { } explicit ComboBox(const QString &initialText, QWidget *parent = nullptr) : QComboBox(parent), m_initialText(initialText), m_initialIcon() { } explicit ComboBox(const QIcon &initialIcon, const QString &initialText, QWidget *parent = nullptr) : QComboBox(parent), m_initialText(initialText), m_initialIcon(initialIcon) { } - QString initialText() const + Q_REQUIRED_RESULT QString initialText() const { return m_initialText; } - QIcon initialIcon() const + Q_REQUIRED_RESULT QIcon initialIcon() const { return m_initialIcon; } public Q_SLOTS: void setInitialText(const QString &txt) { if (txt == m_initialText) { return; } m_initialText = txt; if (currentIndex() == -1) { update(); } } void setInitialIcon(const QIcon &icon) { if (icon.cacheKey() == m_initialIcon.cacheKey()) { return; } m_initialIcon = icon; if (currentIndex() == -1) { update(); } } protected: void paintEvent(QPaintEvent *) override { QStylePainter p(this); p.setPen(palette().color(QPalette::Text)); QStyleOptionComboBox opt; initStyleOption(&opt); p.drawComplexControl(QStyle::CC_ComboBox, opt); if (currentIndex() == -1) { opt.currentText = m_initialText; opt.currentIcon = m_initialIcon; } p.drawControl(QStyle::CE_ComboBoxLabel, opt); } private: QString m_initialText; QIcon m_initialIcon; }; } // anonymous namespace class Kleo::KeysComboBox : public ComboBox { Q_OBJECT public: explicit KeysComboBox(QWidget *parent = nullptr) : ComboBox(parent) {} explicit KeysComboBox(const QString &initialText, QWidget *parent = nullptr) : ComboBox(initialText, parent) {} explicit KeysComboBox(const std::vector &keys, QWidget *parent = nullptr) : ComboBox(make_initial_text(keys), parent) { setKeys(keys); } void setKeys(const std::vector &keys) { clear(); for (const Key &key : keys) { addItem(Formatting::formatForComboBox(key), QVariant::fromValue(key)); } } std::vector keys() const { std::vector result; result.reserve(count()); for (int i = 0, end = count(); i != end; ++i) { result.push_back(qvariant_cast(itemData(i))); } return result; } int findOrAdd(const Key &key) { for (int i = 0, end = count(); i != end; ++i) if (_detail::ByFingerprint()(key, qvariant_cast(itemData(i)))) { return i; } insertItem(0, Formatting::formatForComboBox(key), QVariant::fromValue(key)); return 0; } void addAndSelectCertificate(const Key &key) { setCurrentIndex(findOrAdd(key)); } Key currentKey() const { return qvariant_cast(itemData(currentIndex())); } }; CertificateSelectionLine::CertificateSelectionLine(const QString &toFrom, const QString &mailbox, const std::vector &pgp, bool pgpAmbig, const std::vector &cms, bool cmsAmbig, QWidget *q, QGridLayout &glay) : pgpAmbiguous(pgpAmbig), cmsAmbiguous(cmsAmbig), mToFromLB(new QLabel(toFrom, q)), mMailboxLB(new QLabel(mailbox, q)), mSbox(new QStackedWidget(q)), mPgpCB(new KeysComboBox(pgp, mSbox)), mCmsCB(new KeysComboBox(cms, mSbox)), noProtocolCB(new KeysComboBox(i18n("(please choose between OpenPGP and S/MIME first)"), mSbox)), mToolTB(new QToolButton(q)) { QFont bold; bold.setBold(true); mToFromLB->setFont(bold); mMailboxLB->setTextFormat(Qt::PlainText); mToolTB->setText(i18n("...")); mPgpCB->setEnabled(!pgp.empty()); mCmsCB->setEnabled(!cms.empty()); noProtocolCB->setEnabled(false); mPgpCB->setKeys(pgp); if (pgpAmbiguous) { mPgpCB->setCurrentIndex(-1); } mCmsCB->setKeys(cms); if (cmsAmbiguous) { mCmsCB->setCurrentIndex(-1); } mSbox->addWidget(mPgpCB); mSbox->addWidget(mCmsCB); mSbox->addWidget(noProtocolCB); mSbox->setCurrentWidget(noProtocolCB); const int row = glay.rowCount(); int col = 0; glay.addWidget(mToFromLB, row, col++); glay.addWidget(mMailboxLB, row, col++); glay.addWidget(mSbox, row, col++); glay.addWidget(mToolTB, row, col++); Q_ASSERT(col == NumColumns); q->connect(mPgpCB, SIGNAL(currentIndexChanged(int)), SLOT(slotCompleteChanged())); q->connect(mCmsCB, SIGNAL(currentIndexChanged(int)), SLOT(slotCompleteChanged())); q->connect(mToolTB, SIGNAL(clicked()), SLOT(slotCertificateSelectionDialogRequested())); } QString CertificateSelectionLine::mailboxText() const { return mMailboxLB->text(); } void CertificateSelectionLine::addAndSelectCertificate(const Key &key) const { if (KeysComboBox *const cb = comboBox(key.protocol())) { cb->addAndSelectCertificate(key); cb->setEnabled(true); } } void CertificateSelectionLine::showHide(Protocol proto, bool &first, bool showAll, bool op) const { if (op && (showAll || wasInitiallyAmbiguous(proto))) { mToFromLB->setVisible(first); first = false; QFont font = mMailboxLB->font(); font.setBold(wasInitiallyAmbiguous(proto)); mMailboxLB->setFont(font); mSbox->setCurrentIndex(proto); mMailboxLB->show(); mSbox->show(); mToolTB->show(); } else { mToFromLB->hide(); mMailboxLB->hide(); mSbox->hide(); mToolTB->hide(); } } bool CertificateSelectionLine::wasInitiallyAmbiguous(Protocol proto) const { return (proto == OpenPGP && pgpAmbiguous) || (proto == CMS && cmsAmbiguous); } bool CertificateSelectionLine::isStillAmbiguous(Protocol proto) const { kleo_assert(proto == OpenPGP || proto == CMS); const KeysComboBox *const cb = comboBox(proto); return cb->currentIndex() == -1; } Key CertificateSelectionLine::key(Protocol proto) const { kleo_assert(proto == OpenPGP || proto == CMS); const KeysComboBox *const cb = comboBox(proto); return cb->currentKey(); } const QToolButton *CertificateSelectionLine::toolButton() const { return mToolTB; } void CertificateSelectionLine::kill() { delete mToFromLB; delete mMailboxLB; delete mSbox; delete mToolTB; } KeysComboBox *CertificateSelectionLine::comboBox(Protocol proto) const { if (proto == OpenPGP) { return mPgpCB; } if (proto == CMS) { return mCmsCB; } return nullptr; } #include "certificateselectionline.moc" diff --git a/src/crypto/gui/decryptverifyfilesdialog.cpp b/src/crypto/gui/decryptverifyfilesdialog.cpp index 63e6c30be..20a7105ab 100644 --- a/src/crypto/gui/decryptverifyfilesdialog.cpp +++ b/src/crypto/gui/decryptverifyfilesdialog.cpp @@ -1,245 +1,245 @@ /* crypto/gui/decryptverifyfilesdialog.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #include "decryptverifyfilesdialog.h" #include "kleopatra_debug.h" #include "crypto/taskcollection.h" #include "crypto/decryptverifytask.h" #include "crypto/gui/resultpage.h" #include "crypto/gui/resultlistwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Crypto; using namespace Kleo::Crypto::Gui; DecryptVerifyFilesDialog::DecryptVerifyFilesDialog(const std::shared_ptr &coll, QWidget *parent) - : QDialog(parent), m_tasks(coll), m_saveButton(QDialogButtonBox::NoButton), m_buttonBox(new QDialogButtonBox) + : QDialog(parent), m_tasks(coll), m_buttonBox(new QDialogButtonBox) { readConfig(); auto vLay = new QVBoxLayout(this); auto labels = new QWidget; auto outputLayout = new QHBoxLayout; m_outputLocationFNR = new FileNameRequester; auto outLabel = new QLabel(i18n("&Output folder:")); outLabel->setBuddy(m_outputLocationFNR); outputLayout->addWidget(outLabel); outputLayout->addWidget(m_outputLocationFNR); m_outputLocationFNR->setFilter(QDir::Dirs); vLay->addLayout(outputLayout); m_progressLabelLayout = new QVBoxLayout(labels); vLay->addWidget(labels); m_progressBar = new QProgressBar; vLay->addWidget(m_progressBar); m_resultList = new ResultListWidget; vLay->addWidget(m_resultList); m_tasks = coll; Q_ASSERT(m_tasks); m_resultList->setTaskCollection(coll); connect(m_tasks.get(), &TaskCollection::progress, this, &DecryptVerifyFilesDialog::progress); connect(m_tasks.get(), &TaskCollection::done, this, &DecryptVerifyFilesDialog::allDone); connect(m_tasks.get(), &TaskCollection::result, this, &DecryptVerifyFilesDialog::result); connect(m_tasks.get(), &TaskCollection::started, this, &DecryptVerifyFilesDialog::started); connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); connect(m_buttonBox, &QDialogButtonBox::clicked, this, &DecryptVerifyFilesDialog::btnClicked); layout()->addWidget(m_buttonBox); bool hasOutputs = false; for (const auto &t: coll->tasks()) { if (!qobject_cast(t.get())) { hasOutputs = true; break; } } if (hasOutputs) { setWindowTitle(i18nc("@title:window", "Decrypt/Verify Files")); m_saveButton = QDialogButtonBox::SaveAll; m_buttonBox->addButton(QDialogButtonBox::Discard); connect(m_buttonBox, &QDialogButtonBox::accepted, this, &DecryptVerifyFilesDialog::checkAccept); } else { outLabel->setVisible(false); m_outputLocationFNR->setVisible(false); setWindowTitle(i18nc("@title:window", "Verify Files")); m_buttonBox->addButton(QDialogButtonBox::Close); connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); } if (m_saveButton) { m_buttonBox->addButton(m_saveButton); m_buttonBox->button(m_saveButton)->setEnabled(false); } } DecryptVerifyFilesDialog::~DecryptVerifyFilesDialog() { qCDebug(KLEOPATRA_LOG); writeConfig(); } void DecryptVerifyFilesDialog::allDone() { qCDebug(KLEOPATRA_LOG) << "All done"; Q_ASSERT(m_tasks); m_progressBar->setRange(0, 100); m_progressBar->setValue(100); for (const auto &i: m_progressLabelByTag.keys()) { if (!i.isEmpty()) { m_progressLabelByTag.value(i)->setText(i18n("%1: All operations completed.", i)); } else { m_progressLabelByTag.value(i)->setText(i18n("All operations completed.")); } } if (m_tasks->allTasksHaveErrors()) { return; } if (m_saveButton != QDialogButtonBox::NoButton) { m_buttonBox->button(m_saveButton)->setEnabled(true); } else { m_buttonBox->removeButton(m_buttonBox->button(QDialogButtonBox::Close)); m_buttonBox->addButton(QDialogButtonBox::Ok); } } void DecryptVerifyFilesDialog::started(const std::shared_ptr &task) { Q_ASSERT(task); const auto tag = task->tag(); auto label = labelForTag(tag); Q_ASSERT(label); if (tag.isEmpty()) { label->setText(i18nc("number, operation description", "Operation %1: %2", m_tasks->numberOfCompletedTasks() + 1, task->label())); } else { label->setText(i18nc("tag( \"OpenPGP\" or \"CMS\"), operation description", "%1: %2", tag, task->label())); } if (m_saveButton != QDialogButtonBox::NoButton) { m_buttonBox->button(m_saveButton)->setEnabled(false); } else if (m_buttonBox->button(QDialogButtonBox::Ok)) { m_buttonBox->removeButton(m_buttonBox->button(QDialogButtonBox::Ok)); m_buttonBox->addButton(QDialogButtonBox::Close); } } QLabel *DecryptVerifyFilesDialog::labelForTag(const QString &tag) { if (QLabel *const label = m_progressLabelByTag.value(tag)) { return label; } auto label = new QLabel; label->setTextFormat(Qt::RichText); label->setWordWrap(true); m_progressLabelLayout->addWidget(label); m_progressLabelByTag.insert(tag, label); return label; } void DecryptVerifyFilesDialog::progress(const QString &msg, int progress, int total) { Q_UNUSED(msg); Q_ASSERT(progress >= 0); Q_ASSERT(total >= 0); m_progressBar->setRange(0, total); m_progressBar->setValue(progress); } void DecryptVerifyFilesDialog::setOutputLocation(const QString &dir) { m_outputLocationFNR->setFileName(dir); } QString DecryptVerifyFilesDialog::outputLocation() const { return m_outputLocationFNR->fileName(); } void DecryptVerifyFilesDialog::btnClicked(QAbstractButton *btn) { if (m_buttonBox->buttonRole(btn) == QDialogButtonBox::DestructiveRole) { close(); } } void DecryptVerifyFilesDialog::checkAccept() { const auto outLoc = outputLocation(); if (outLoc.isEmpty()) { KMessageBox::information(this, i18n("Please select an output folder."), i18n("No output folder.")); return; } const QFileInfo fi(outLoc); if (fi.exists() && fi.isDir() && fi.isWritable()) { accept(); return; } if (!fi.exists()) { qCDebug(KLEOPATRA_LOG) << "Output dir does not exist. Trying to create."; const QDir dir(outLoc); if (!dir.mkdir(outLoc)) { KMessageBox::information(this, i18n("Please select a different output folder."), i18n("Failed to create output folder.")); return; } else { accept(); return; } } KMessageBox::information(this, i18n("Please select a different output folder."), i18n("Invalid output folder.")); } void DecryptVerifyFilesDialog::readConfig() { winId(); // ensure there's a window created // set default window size windowHandle()->resize(640, 480); // restore size from config file KConfigGroup cfgGroup(KSharedConfig::openConfig(), "DecryptVerifyFilesDialog"); KWindowConfig::restoreWindowSize(windowHandle(), cfgGroup); // NOTICE: QWindow::setGeometry() does NOT impact the backing QWidget geometry even if the platform // window was created -> QTBUG-40584. We therefore copy the size here. // TODO: remove once this was resolved in QWidget QPA resize(windowHandle()->size()); } void DecryptVerifyFilesDialog::writeConfig() { KConfigGroup cfgGroup(KSharedConfig::openConfig(), "DecryptVerifyFilesDialog"); KWindowConfig::saveWindowSize(windowHandle(), cfgGroup); cfgGroup.sync(); } diff --git a/src/crypto/gui/decryptverifyfilesdialog.h b/src/crypto/gui/decryptverifyfilesdialog.h index 75dcfa0b5..0e79c76cc 100644 --- a/src/crypto/gui/decryptverifyfilesdialog.h +++ b/src/crypto/gui/decryptverifyfilesdialog.h @@ -1,76 +1,76 @@ /* crypto/gui/decryptverifyfilesdialog.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef CRYPTO_GUI_DECRYPTVERIFYFILESDIALOG_H #define CRYPTO_GUI_DECRYPTVERIFYFILESDIALOG_H #include #include #include #include #include "crypto/task.h" class QVBoxLayout; class QProgressBar; template class QHash; class QLabel; namespace Kleo { class FileNameRequester; namespace Crypto { class TaskCollection; namespace Gui { class ResultListWidget; class DecryptVerifyFilesDialog : public QDialog { Q_OBJECT public: explicit DecryptVerifyFilesDialog(const std::shared_ptr &coll, QWidget *parent = nullptr); ~DecryptVerifyFilesDialog(); void setOutputLocation(const QString &dir); QString outputLocation() const; protected Q_SLOTS: void progress(const QString &msg, int progress, int total); void started(const std::shared_ptr &result); void allDone(); void btnClicked(QAbstractButton *btn); void checkAccept(); protected: void readConfig(); void writeConfig(); protected: QLabel *labelForTag(const QString &tag); private: std::shared_ptr m_tasks; QProgressBar *m_progressBar; QHash m_progressLabelByTag; QVBoxLayout *m_progressLabelLayout; int m_lastErrorItemIndex; ResultListWidget *m_resultList; FileNameRequester *m_outputLocationFNR; - QDialogButtonBox::StandardButton m_saveButton; - QDialogButtonBox *m_buttonBox; + QDialogButtonBox::StandardButton m_saveButton = QDialogButtonBox::NoButton; + QDialogButtonBox *const m_buttonBox; }; } // namespace Gui } //namespace Crypto; } // namespace Kleo #endif // CRYPTO_GUI_DECRYPTVERIFYFILESDIALOG_H diff --git a/src/crypto/gui/encryptemailwizard.cpp b/src/crypto/gui/encryptemailwizard.cpp index b65f07b0a..cb784169c 100644 --- a/src/crypto/gui/encryptemailwizard.cpp +++ b/src/crypto/gui/encryptemailwizard.cpp @@ -1,59 +1,58 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/gui/encryptemailwizard.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 "encryptemailwizard.h" #include #include using namespace Kleo; using namespace Kleo::Crypto; using namespace Kleo::Crypto::Gui; class EncryptEMailWizard::Private { public: - Private() : m_quickMode(false) {} - bool m_quickMode; + bool m_quickMode = false; }; EncryptEMailWizard::EncryptEMailWizard(QWidget *parent, Qt::WindowFlags flags) : SignEncryptWizard(parent, flags), d(new Private) { setWindowTitle(i18nc("@title:window", "Encrypt Mail Message")); std::vector pageOrder; pageOrder.push_back(ResolveRecipientsPage); pageOrder.push_back(ResultPage); setPageOrder(pageOrder); setCommitPage(SignEncryptWizard::ResolveRecipientsPage); } EncryptEMailWizard::~EncryptEMailWizard() { } bool EncryptEMailWizard::quickMode() const { return d->m_quickMode; } void EncryptEMailWizard::setQuickMode(bool quick) { if (quick == d->m_quickMode) { return; } d->m_quickMode = quick; signerResolvePage()->setAutoAdvance(quick); resolveRecipientsPage()->setAutoAdvance(quick); setKeepResultPageOpenWhenDone(!quick); } #include "encryptemailwizard.h" diff --git a/src/crypto/gui/resolverecipientspage.cpp b/src/crypto/gui/resolverecipientspage.cpp index ff8cf8735..f0c04cb50 100644 --- a/src/crypto/gui/resolverecipientspage.cpp +++ b/src/crypto/gui/resolverecipientspage.cpp @@ -1,703 +1,704 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/gui/resolverecipientspage.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 "resolverecipientspage.h" #include "resolverecipientspage_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace GpgME; using namespace Kleo; using namespace Kleo::Dialogs; using namespace Kleo::Crypto; using namespace Kleo::Crypto::Gui; using namespace KMime::Types; ResolveRecipientsPage::ListWidget::ListWidget(QWidget *parent, Qt::WindowFlags flags) : QWidget(parent, flags), m_protocol(UnknownProtocol) { m_listWidget = new QListWidget; m_listWidget->setSelectionMode(QAbstractItemView::MultiSelection); QVBoxLayout *const layout = new QVBoxLayout(this); layout->addWidget(m_listWidget); connect(m_listWidget, &QListWidget::itemSelectionChanged, this, &ListWidget::onSelectionChange); } ResolveRecipientsPage::ListWidget::~ListWidget() { } void ResolveRecipientsPage::ListWidget::onSelectionChange() { - Q_FOREACH (const QString &i, widgets.keys()) { + const auto widgetskeys = widgets.keys(); + for (const QString &i : widgetskeys) { Q_ASSERT(items.contains(i)); widgets[i]->setSelected(items[i]->isSelected()); } Q_EMIT selectionChanged(); } void ResolveRecipientsPage::ListWidget::addEntry(const Mailbox &mbox) { addEntry(mbox.prettyAddress(), mbox.prettyAddress(), mbox); } void ResolveRecipientsPage::ListWidget::addEntry(const QString &id, const QString &name) { addEntry(id, name, Mailbox()); } void ResolveRecipientsPage::ListWidget::addEntry(const QString &id, const QString &name, const Mailbox &mbox) { Q_ASSERT(!widgets.contains(id) && !items.contains(id)); QListWidgetItem *item = new QListWidgetItem; item->setData(IdRole, id); ItemWidget *wid = new ItemWidget(id, name, mbox, this); connect(wid, &ItemWidget::changed, this, &ListWidget::completeChanged); wid->setProtocol(m_protocol); item->setSizeHint(wid->sizeHint()); m_listWidget->addItem(item); m_listWidget->setItemWidget(item, wid); widgets[id] = wid; items[id] = item; } Mailbox ResolveRecipientsPage::ListWidget::mailbox(const QString &id) const { return widgets.contains(id) ? widgets[id]->mailbox() : Mailbox(); } void ResolveRecipientsPage::ListWidget::setCertificates(const QString &id, const std::vector &pgp, const std::vector &cms) { Q_ASSERT(widgets.contains(id)); widgets[id]->setCertificates(pgp, cms); } Key ResolveRecipientsPage::ListWidget::selectedCertificate(const QString &id) const { return widgets.contains(id) ? widgets[id]->selectedCertificate() : Key(); } GpgME::Key ResolveRecipientsPage::ListWidget::selectedCertificate(const QString &id, GpgME::Protocol prot) const { return widgets.contains(id) ? widgets[id]->selectedCertificate(prot) : Key(); } QStringList ResolveRecipientsPage::ListWidget::identifiers() const { return widgets.keys(); } void ResolveRecipientsPage::ListWidget::setProtocol(GpgME::Protocol prot) { if (m_protocol == prot) { return; } m_protocol = prot; Q_FOREACH (ItemWidget *i, widgets) { i->setProtocol(prot); } } void ResolveRecipientsPage::ListWidget::removeEntry(const QString &id) { if (!widgets.contains(id)) { return; } delete items[id]; items.remove(id); delete widgets[id]; widgets.remove(id); } void ResolveRecipientsPage::ListWidget::showSelectionDialog(const QString &id) { if (!widgets.contains(id)) { return; } widgets[id]->showSelectionDialog(); } QStringList ResolveRecipientsPage::ListWidget::selectedEntries() const { QStringList entries; const QList items = m_listWidget->selectedItems(); entries.reserve(items.count()); for (const QListWidgetItem *i : items) { entries.append(i->data(IdRole).toString()); } return entries; } ResolveRecipientsPage::ItemWidget::ItemWidget(const QString &id, const QString &name, const Mailbox &mbox, QWidget *parent, Qt::WindowFlags flags) : QWidget(parent, flags), m_id(id), m_mailbox(mbox), m_protocol(UnknownProtocol), m_selected(false) { Q_ASSERT(!m_id.isEmpty()); setAutoFillBackground(true); QHBoxLayout *layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); layout->addSpacing(15); m_nameLabel = new QLabel; m_nameLabel->setText(name); layout->addWidget(m_nameLabel); layout->addStretch(); m_certLabel = new QLabel; m_certLabel->setText(i18n("No certificate selected")); layout->addWidget(m_certLabel); m_certCombo = new QComboBox; connect(m_certCombo, SIGNAL(currentIndexChanged(int)), this, SIGNAL(changed())); layout->addWidget(m_certCombo); m_selectButton = new QToolButton; m_selectButton->setText(i18n("...")); connect(m_selectButton, &QAbstractButton::clicked, this, &ItemWidget::showSelectionDialog); layout->addWidget(m_selectButton); layout->addSpacing(15); setCertificates(std::vector(), std::vector()); } void ResolveRecipientsPage::ItemWidget::updateVisibility() { m_certLabel->setVisible(m_certCombo->count() == 0); m_certCombo->setVisible(m_certCombo->count() > 0); } ResolveRecipientsPage::ItemWidget::~ItemWidget() { } QString ResolveRecipientsPage::ItemWidget::id() const { return m_id; } void ResolveRecipientsPage::ItemWidget::setSelected(bool selected) { if (m_selected == selected) { return; } m_selected = selected; setBackgroundRole(selected ? QPalette::Highlight : QPalette::Base); const QPalette::ColorRole foreground = selected ? QPalette::HighlightedText : QPalette::Text; setForegroundRole(foreground); m_nameLabel->setForegroundRole(foreground); m_certLabel->setForegroundRole(foreground); } bool ResolveRecipientsPage::ItemWidget::isSelected() const { return m_selected; } static CertificateSelectionDialog::Option protocol2option(GpgME::Protocol proto) { switch (proto) { case OpenPGP: return CertificateSelectionDialog::OpenPGPFormat; case CMS: return CertificateSelectionDialog::CMSFormat; default: return CertificateSelectionDialog::AnyFormat; } } static CertificateSelectionDialog *createCertificateSelectionDialog(QWidget *parent, GpgME::Protocol prot) { CertificateSelectionDialog *const dlg = new CertificateSelectionDialog(parent); const CertificateSelectionDialog::Options options = CertificateSelectionDialog::SingleSelection | CertificateSelectionDialog::EncryptOnly | CertificateSelectionDialog::MultiSelection | protocol2option(prot); dlg->setOptions(options); return dlg; } void ResolveRecipientsPage::ItemWidget::showSelectionDialog() { QPointer dlg = createCertificateSelectionDialog(this, m_protocol); if (dlg->exec() == QDialog::Accepted && dlg /* still with us? */) { const GpgME::Key cert = dlg->selectedCertificate(); if (!cert.isNull()) { addCertificateToComboBox(cert); selectCertificateInComboBox(cert); } } delete dlg; } Mailbox ResolveRecipientsPage::ItemWidget::mailbox() const { return m_mailbox; } void ResolveRecipientsPage::ItemWidget::selectCertificateInComboBox(const Key &key) { m_certCombo->setCurrentIndex(m_certCombo->findData(QLatin1String(key.keyID()))); } void ResolveRecipientsPage::ItemWidget::addCertificateToComboBox(const GpgME::Key &key) { m_certCombo->addItem(Formatting::formatForComboBox(key), QByteArray(key.keyID())); if (m_certCombo->count() == 1) { m_certCombo->setCurrentIndex(0); } updateVisibility(); } void ResolveRecipientsPage::ItemWidget::resetCertificates() { std::vector certs; Key selected; switch (m_protocol) { case OpenPGP: certs = m_pgp; break; case CMS: certs = m_cms; break; case UnknownProtocol: certs = m_cms; certs.insert(certs.end(), m_pgp.begin(), m_pgp.end()); } m_certCombo->clear(); Q_FOREACH (const Key &i, certs) { addCertificateToComboBox(i); } if (!m_selectedCertificates[m_protocol].isNull()) { selectCertificateInComboBox(m_selectedCertificates[m_protocol]); } else if (m_certCombo->count() > 0) { m_certCombo->setCurrentIndex(0); } updateVisibility(); Q_EMIT changed(); } void ResolveRecipientsPage::ItemWidget::setProtocol(Protocol prot) { if (m_protocol == prot) { return; } m_selectedCertificates[m_protocol] = selectedCertificate(); if (m_protocol != UnknownProtocol) { (m_protocol == OpenPGP ? m_pgp : m_cms) = certificates(); } m_protocol = prot; resetCertificates(); } void ResolveRecipientsPage::ItemWidget::setCertificates(const std::vector &pgp, const std::vector &cms) { m_pgp = pgp; m_cms = cms; resetCertificates(); } Key ResolveRecipientsPage::ItemWidget::selectedCertificate() const { return KeyCache::instance()->findByKeyIDOrFingerprint(m_certCombo->itemData(m_certCombo->currentIndex(), ListWidget::IdRole).toString().toStdString()); } GpgME::Key ResolveRecipientsPage::ItemWidget::selectedCertificate(GpgME::Protocol prot) const { return prot == m_protocol ? selectedCertificate() : m_selectedCertificates.value(prot); } std::vector ResolveRecipientsPage::ItemWidget::certificates() const { std::vector certs; for (int i = 0; i < m_certCombo->count(); ++i) { certs.push_back(KeyCache::instance()->findByKeyIDOrFingerprint(m_certCombo->itemData(i, ListWidget::IdRole).toString().toStdString())); } return certs; } class ResolveRecipientsPage::Private { friend class ::Kleo::Crypto::Gui::ResolveRecipientsPage; ResolveRecipientsPage *const q; public: explicit Private(ResolveRecipientsPage *qq); ~Private(); void setSelectedProtocol(Protocol protocol); void selectionChanged(); void removeSelectedEntries(); void addRecipient(); void addRecipient(const Mailbox &mbox); void addRecipient(const QString &id, const QString &name); void updateProtocolRBVisibility(); void protocolSelected(int prot); void writeSelectedCertificatesToPreferences(); void completeChangedInternal(); private: ListWidget *m_listWidget; QPushButton *m_addButton; QPushButton *m_removeButton; QRadioButton *m_pgpRB; QRadioButton *m_cmsRB; QLabel *m_additionalRecipientsLabel; Protocol m_presetProtocol; Protocol m_selectedProtocol; bool m_multipleProtocolsAllowed; std::shared_ptr m_recipientPreferences; }; ResolveRecipientsPage::Private::Private(ResolveRecipientsPage *qq) : q(qq), m_presetProtocol(UnknownProtocol), m_selectedProtocol(m_presetProtocol), m_multipleProtocolsAllowed(false), m_recipientPreferences() { connect(q, SIGNAL(completeChanged()), q, SLOT(completeChangedInternal())); q->setTitle(i18n("Recipients")); QVBoxLayout *const layout = new QVBoxLayout(q); m_listWidget = new ListWidget; connect(m_listWidget, SIGNAL(selectionChanged()), q, SLOT(selectionChanged())); connect(m_listWidget, &ListWidget::completeChanged, q, &WizardPage::completeChanged); layout->addWidget(m_listWidget); m_additionalRecipientsLabel = new QLabel; m_additionalRecipientsLabel->setWordWrap(true); layout->addWidget(m_additionalRecipientsLabel); m_additionalRecipientsLabel->setVisible(false); QWidget *buttonWidget = new QWidget; QHBoxLayout *buttonLayout = new QHBoxLayout(buttonWidget); buttonLayout->setContentsMargins(0, 0, 0, 0); m_addButton = new QPushButton; connect(m_addButton, SIGNAL(clicked()), q, SLOT(addRecipient())); m_addButton->setText(i18n("Add Recipient...")); buttonLayout->addWidget(m_addButton); m_removeButton = new QPushButton; m_removeButton->setEnabled(false); m_removeButton->setText(i18n("Remove Selected")); connect(m_removeButton, SIGNAL(clicked()), q, SLOT(removeSelectedEntries())); buttonLayout->addWidget(m_removeButton); buttonLayout->addStretch(); layout->addWidget(buttonWidget); QWidget *protocolWidget = new QWidget; QHBoxLayout *protocolLayout = new QHBoxLayout(protocolWidget); QButtonGroup *protocolGroup = new QButtonGroup(q); connect(protocolGroup, SIGNAL(buttonClicked(int)), q, SLOT(protocolSelected(int))); m_pgpRB = new QRadioButton; m_pgpRB->setText(i18n("OpenPGP")); protocolGroup->addButton(m_pgpRB, OpenPGP); protocolLayout->addWidget(m_pgpRB); m_cmsRB = new QRadioButton; m_cmsRB->setText(i18n("S/MIME")); protocolGroup->addButton(m_cmsRB, CMS); protocolLayout->addWidget(m_cmsRB); protocolLayout->addStretch(); layout->addWidget(protocolWidget); } ResolveRecipientsPage::Private::~Private() {} void ResolveRecipientsPage::Private::completeChangedInternal() { const bool isComplete = q->isComplete(); const std::vector keys = q->resolvedCertificates(); const bool haveSecret = std::find_if(keys.begin(), keys.end(), [](const Key &key) { return key.hasSecret(); }) != keys.end(); if (isComplete && !haveSecret) { q->setExplanation(i18n("Warning: None of the selected certificates seem to be your own. You will not be able to decrypt the encrypted data again.")); } else { q->setExplanation(QString()); } } void ResolveRecipientsPage::Private::updateProtocolRBVisibility() { const bool visible = !m_multipleProtocolsAllowed && m_presetProtocol == UnknownProtocol; m_cmsRB->setVisible(visible); m_pgpRB->setVisible(visible); if (visible) { if (m_selectedProtocol == CMS) { m_cmsRB->click(); } else { m_pgpRB->click(); } } } bool ResolveRecipientsPage::isComplete() const { const QStringList ids = d->m_listWidget->identifiers(); if (ids.isEmpty()) { return false; } for (const QString &i : ids) { if (d->m_listWidget->selectedCertificate(i).isNull()) { return false; } } return true; } ResolveRecipientsPage::ResolveRecipientsPage(QWidget *parent) : WizardPage(parent), d(new Private(this)) { } ResolveRecipientsPage::~ResolveRecipientsPage() {} Protocol ResolveRecipientsPage::selectedProtocol() const { return d->m_selectedProtocol; } void ResolveRecipientsPage::Private::setSelectedProtocol(Protocol protocol) { if (m_selectedProtocol == protocol) { return; } m_selectedProtocol = protocol; m_listWidget->setProtocol(m_selectedProtocol); Q_EMIT q->selectedProtocolChanged(); } void ResolveRecipientsPage::Private::protocolSelected(int p) { const Protocol protocol = static_cast(p); Q_ASSERT(protocol != UnknownProtocol); setSelectedProtocol(protocol); } void ResolveRecipientsPage::setPresetProtocol(Protocol prot) { if (d->m_presetProtocol == prot) { return; } d->m_presetProtocol = prot; d->setSelectedProtocol(prot); if (prot != UnknownProtocol) { d->m_multipleProtocolsAllowed = false; } d->updateProtocolRBVisibility(); } Protocol ResolveRecipientsPage::presetProtocol() const { return d->m_presetProtocol; } bool ResolveRecipientsPage::multipleProtocolsAllowed() const { return d->m_multipleProtocolsAllowed; } void ResolveRecipientsPage::setMultipleProtocolsAllowed(bool allowed) { if (d->m_multipleProtocolsAllowed == allowed) { return; } d->m_multipleProtocolsAllowed = allowed; if (d->m_multipleProtocolsAllowed) { setPresetProtocol(UnknownProtocol); d->setSelectedProtocol(UnknownProtocol); } d->updateProtocolRBVisibility(); } void ResolveRecipientsPage::Private::addRecipient(const QString &id, const QString &name) { m_listWidget->addEntry(id, name); } void ResolveRecipientsPage::Private::addRecipient(const Mailbox &mbox) { m_listWidget->addEntry(mbox); } void ResolveRecipientsPage::Private::addRecipient() { QPointer dlg = createCertificateSelectionDialog(q, q->selectedProtocol()); if (dlg->exec() != QDialog::Accepted || !dlg /*q already deleted*/) { return; } const std::vector keys = dlg->selectedCertificates(); int i = 0; Q_FOREACH (const Key &key, keys) { const QStringList existing = m_listWidget->identifiers(); QString rec = i18n("Recipient"); while (existing.contains(rec)) { rec = i18nc("%1 == number", "Recipient (%1)", ++i); } addRecipient(rec, rec); const std::vector pgp = key.protocol() == OpenPGP ? std::vector(1, key) : std::vector(); const std::vector cms = key.protocol() == CMS ? std::vector(1, key) : std::vector(); m_listWidget->setCertificates(rec, pgp, cms); } Q_EMIT q->completeChanged(); } namespace { std::vector makeSuggestions(const std::shared_ptr &prefs, const Mailbox &mb, GpgME::Protocol prot) { std::vector suggestions; const Key remembered = prefs ? prefs->preferredCertificate(mb, prot) : Key(); if (!remembered.isNull()) { suggestions.push_back(remembered); } else { suggestions = CertificateResolver::resolveRecipient(mb, prot); } return suggestions; } } static QString listKeysForInfo(const std::vector &keys) { QStringList list; std::transform(keys.begin(), keys.end(), list.begin(), &Formatting::formatKeyLink); return list.join(QLatin1String("
")); } void ResolveRecipientsPage::setAdditionalRecipientsInfo(const std::vector &recipients) { d->m_additionalRecipientsLabel->setVisible(!recipients.empty()); if (recipients.empty()) { return; } d->m_additionalRecipientsLabel->setText( i18n("

Recipients predefined via GnuPG settings:

%1
", listKeysForInfo(recipients))); } void ResolveRecipientsPage::setRecipients(const std::vector &recipients, const std::vector &encryptToSelfRecipients) { uint cmsCount = 0; uint pgpCount = 0; uint senders = 0; Q_FOREACH (const Mailbox &mb, encryptToSelfRecipients) { const QString id = QLatin1String("sender-") + QString::number(++senders); d->m_listWidget->addEntry(id, i18n("Sender"), mb); const std::vector pgp = makeSuggestions(d->m_recipientPreferences, mb, OpenPGP); const std::vector cms = makeSuggestions(d->m_recipientPreferences, mb, CMS); pgpCount += !pgp.empty(); cmsCount += !cms.empty(); d->m_listWidget->setCertificates(id, pgp, cms); } Q_FOREACH (const Mailbox &i, recipients) { //TODO: const QString address = i.prettyAddress(); d->addRecipient(i); const std::vector pgp = makeSuggestions(d->m_recipientPreferences, i, OpenPGP); const std::vector cms = makeSuggestions(d->m_recipientPreferences, i, CMS); pgpCount += pgp.empty() ? 0 : 1; cmsCount += cms.empty() ? 0 : 1; d->m_listWidget->setCertificates(address, pgp, cms); } if (d->m_presetProtocol == UnknownProtocol && !d->m_multipleProtocolsAllowed) { (cmsCount > pgpCount ? d->m_cmsRB : d->m_pgpRB)->click(); } } std::vector ResolveRecipientsPage::resolvedCertificates() const { std::vector certs; Q_FOREACH (const QString &i, d->m_listWidget->identifiers()) { const GpgME::Key cert = d->m_listWidget->selectedCertificate(i); if (!cert.isNull()) { certs.push_back(cert); } } return certs; } void ResolveRecipientsPage::Private::selectionChanged() { m_removeButton->setEnabled(!m_listWidget->selectedEntries().isEmpty()); } void ResolveRecipientsPage::Private::removeSelectedEntries() { Q_FOREACH (const QString &i, m_listWidget->selectedEntries()) { m_listWidget->removeEntry(i); } Q_EMIT q->completeChanged(); } void ResolveRecipientsPage::setRecipientsUserMutable(bool isMutable) { d->m_addButton->setVisible(isMutable); d->m_removeButton->setVisible(isMutable); } bool ResolveRecipientsPage::recipientsUserMutable() const { return d->m_addButton->isVisible(); } std::shared_ptr ResolveRecipientsPage::recipientPreferences() const { return d->m_recipientPreferences; } void ResolveRecipientsPage::setRecipientPreferences(const std::shared_ptr &prefs) { d->m_recipientPreferences = prefs; } void ResolveRecipientsPage::Private::writeSelectedCertificatesToPreferences() { if (!m_recipientPreferences) { return; } Q_FOREACH (const QString &i, m_listWidget->identifiers()) { const Mailbox mbox = m_listWidget->mailbox(i); if (!mbox.hasAddress()) { continue; } const Key pgp = m_listWidget->selectedCertificate(i, OpenPGP); if (!pgp.isNull()) { m_recipientPreferences->setPreferredCertificate(mbox, OpenPGP, pgp); } const Key cms = m_listWidget->selectedCertificate(i, CMS); if (!cms.isNull()) { m_recipientPreferences->setPreferredCertificate(mbox, CMS, cms); } } } void ResolveRecipientsPage::onNext() { d->writeSelectedCertificatesToPreferences(); } #include "moc_resolverecipientspage_p.cpp" #include "moc_resolverecipientspage.cpp" diff --git a/src/crypto/gui/resultitemwidget.cpp b/src/crypto/gui/resultitemwidget.cpp index d178b10d7..8873212ce 100644 --- a/src/crypto/gui/resultitemwidget.cpp +++ b/src/crypto/gui/resultitemwidget.cpp @@ -1,363 +1,363 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/gui/resultitemwidget.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "resultitemwidget.h" #include "utils/auditlog.h" #include "commands/command.h" #include "commands/importcertificatefromfilecommand.h" #include "commands/lookupcertificatescommand.h" #include "crypto/decryptverifytask.h" #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" #include #include #include #include #include #include #if GPGMEPP_VERSION > 0x10B01 // > 1.11.1 # define GPGME_HAS_LEGACY_NOMDC #endif using namespace Kleo; using namespace Kleo::Crypto; using namespace Kleo::Crypto::Gui; namespace { // TODO move out of here static QColor colorForVisualCode(Task::Result::VisualCode code) { switch (code) { case Task::Result::AllGood: return KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::PositiveBackground).color(); case Task::Result::NeutralError: case Task::Result::Warning: return KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::NormalBackground).color(); case Task::Result::Danger: return KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::NegativeBackground).color(); case Task::Result::NeutralSuccess: default: return QColor(0x00, 0x80, 0xFF); // light blue } } static QColor txtColorForVisualCode(Task::Result::VisualCode code) { switch (code) { case Task::Result::AllGood: return KColorScheme(QPalette::Active, KColorScheme::View).foreground(KColorScheme::PositiveText).color(); case Task::Result::NeutralError: case Task::Result::Warning: return KColorScheme(QPalette::Active, KColorScheme::View).foreground(KColorScheme::NormalText).color(); case Task::Result::Danger: return KColorScheme(QPalette::Active, KColorScheme::View).foreground(KColorScheme::NegativeText).color(); case Task::Result::NeutralSuccess: default: return QColor(0xFF, 0xFF, 0xFF); // white } } } class ResultItemWidget::Private { ResultItemWidget *const q; public: - explicit Private(const std::shared_ptr &result, ResultItemWidget *qq) : q(qq), m_result(result), m_detailsLabel(nullptr), m_actionsLabel(nullptr), m_closeButton(nullptr), m_importCanceled(false) + explicit Private(const std::shared_ptr &result, ResultItemWidget *qq) : q(qq), m_result(result) { Q_ASSERT(m_result); } void slotLinkActivated(const QString &); void updateShowDetailsLabel(); void addKeyImportButton(QBoxLayout *lay, bool search); void addIgnoreMDCButton(QBoxLayout *lay); void oneImportFinished(); const std::shared_ptr m_result; - QLabel *m_detailsLabel; - QLabel *m_actionsLabel; - QPushButton *m_closeButton; - bool m_importCanceled; + QLabel *m_detailsLabel = nullptr; + QLabel *m_actionsLabel = nullptr; + QPushButton *m_closeButton = nullptr; + bool m_importCanceled = false; }; void ResultItemWidget::Private::oneImportFinished() { if (m_importCanceled) { return; } if (m_result->parentTask()) { m_result->parentTask()->start(); } q->setVisible(false); } void ResultItemWidget::Private::addIgnoreMDCButton(QBoxLayout *lay) { if (!m_result || !lay) { return; } const auto dvResult = dynamic_cast(m_result.get()); if (!dvResult) { return; } const auto decResult = dvResult->decryptionResult(); #ifdef GPGME_HAS_LEGACY_NOMDC if (decResult.isNull() || !decResult.error() || !decResult.isLegacyCipherNoMDC()) #endif { return; } auto btn = new QPushButton(i18n("Force decryption")); btn->setFixedSize(btn->sizeHint()); connect (btn, &QPushButton::clicked, q, [this] () { if (m_result->parentTask()) { const auto dvTask = dynamic_cast(m_result->parentTask().data()); dvTask->setIgnoreMDCError(true); dvTask->start(); q->setVisible(false); } else { qCWarning(KLEOPATRA_LOG) << "Failed to get parent task"; } }); lay->addWidget(btn); } void ResultItemWidget::Private::addKeyImportButton(QBoxLayout *lay, bool search) { if (!m_result || !lay) { return; } const auto dvResult = dynamic_cast(m_result.get()); if (!dvResult) { return; } const auto verifyResult = dvResult->verificationResult(); if (verifyResult.isNull()) { return; } for (const auto &sig: verifyResult.signatures()) { if (!(sig.summary() & GpgME::Signature::KeyMissing)) { continue; } auto btn = new QPushButton; QString suffix; const auto keyid = QLatin1String(sig.fingerprint()); if (verifyResult.numSignatures() > 1) { suffix = QLatin1Char(' ') + keyid; } btn = new QPushButton(search ? i18nc("1 is optional keyid. No space is intended as it can be empty.", "Search%1", suffix) : i18nc("1 is optional keyid. No space is intended as it can be empty.", "Import%1", suffix)); if (search) { btn->setIcon(QIcon::fromTheme(QStringLiteral("edit-find"))); connect (btn, &QPushButton::clicked, q, [this, btn, keyid] () { btn->setEnabled(false); m_importCanceled = false; auto cmd = new Kleo::Commands::LookupCertificatesCommand(keyid, nullptr); connect(cmd, &Kleo::Commands::LookupCertificatesCommand::canceled, q, [this]() { m_importCanceled = true; }); connect(cmd, &Kleo::Commands::LookupCertificatesCommand::finished, q, [this, btn]() { btn->setEnabled(true); oneImportFinished(); }); cmd->setParentWidget(q); cmd->start(); }); } else { btn->setIcon(QIcon::fromTheme(QStringLiteral("view-certificate-import"))); connect (btn, &QPushButton::clicked, q, [this, btn] () { btn->setEnabled(false); m_importCanceled = false; auto cmd = new Kleo::ImportCertificateFromFileCommand(); connect(cmd, &Kleo::ImportCertificateFromFileCommand::canceled, q, [this]() { m_importCanceled = true; }); connect(cmd, &Kleo::ImportCertificateFromFileCommand::finished, q, [this, btn]() { btn->setEnabled(true); oneImportFinished(); }); cmd->setParentWidget(q); cmd->start(); }); } btn->setFixedSize(btn->sizeHint()); lay->addWidget(btn); } } static QUrl auditlog_url_template() { QUrl url(QStringLiteral("kleoresultitem://showauditlog")); return url; } void ResultItemWidget::Private::updateShowDetailsLabel() { if (!m_actionsLabel || !m_detailsLabel) { return; } const auto parentTask = m_result->parentTask(); QString auditLogLink; if (m_result->hasError()) { auditLogLink = m_result->auditLog().formatLink(auditlog_url_template(), i18n("Diagnostics")); } else { auditLogLink = m_result->auditLog().formatLink(auditlog_url_template()); } m_actionsLabel->setText(auditLogLink); } ResultItemWidget::ResultItemWidget(const std::shared_ptr &result, QWidget *parent, Qt::WindowFlags flags) : QWidget(parent, flags), d(new Private(result, this)) { const QColor color = colorForVisualCode(d->m_result->code()); const QColor txtColor = txtColorForVisualCode(d->m_result->code()); const QString styleSheet = QStringLiteral("QFrame,QLabel { background-color: %1; margin: 0px; }" "QFrame#resultFrame{ border-color: %2; border-style: solid; border-radius: 3px; border-width: 1px }" "QLabel { color: %3; padding: 5px; border-radius: 3px }").arg(color.name()).arg(color.darker(150).name()).arg(txtColor.name()); QVBoxLayout *topLayout = new QVBoxLayout(this); QFrame *frame = new QFrame; frame->setObjectName(QStringLiteral("resultFrame")); frame->setStyleSheet(styleSheet); topLayout->addWidget(frame); QHBoxLayout *layout = new QHBoxLayout(frame); QVBoxLayout *vlay = new QVBoxLayout(); QLabel *overview = new QLabel; overview->setWordWrap(true); overview->setTextFormat(Qt::RichText); overview->setText(d->m_result->overview()); overview->setFocusPolicy(Qt::StrongFocus); overview->setStyleSheet(styleSheet); connect(overview, SIGNAL(linkActivated(QString)), this, SLOT(slotLinkActivated(QString))); vlay->addWidget(overview); layout->addLayout(vlay); const QString details = d->m_result->details(); QVBoxLayout *actionLayout = new QVBoxLayout; layout->addLayout(actionLayout); d->addKeyImportButton(actionLayout, false); // TODO: Only show if auto-key-retrieve is not set. d->addKeyImportButton(actionLayout, true); d->addIgnoreMDCButton(actionLayout); d->m_actionsLabel = new QLabel; connect(d->m_actionsLabel, SIGNAL(linkActivated(QString)), this, SLOT(slotLinkActivated(QString))); actionLayout->addWidget(d->m_actionsLabel); d->m_actionsLabel->setFocusPolicy(Qt::StrongFocus); d->m_actionsLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); d->m_actionsLabel->setStyleSheet(styleSheet); d->m_detailsLabel = new QLabel; d->m_detailsLabel->setWordWrap(true); d->m_detailsLabel->setTextFormat(Qt::RichText); d->m_detailsLabel->setText(details); d->m_detailsLabel->setFocusPolicy(Qt::StrongFocus); d->m_detailsLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); d->m_detailsLabel->setStyleSheet(styleSheet); connect(d->m_detailsLabel, SIGNAL(linkActivated(QString)), this, SLOT(slotLinkActivated(QString))); vlay->addWidget(d->m_detailsLabel); d->m_closeButton = new QPushButton; KGuiItem::assign(d->m_closeButton, KStandardGuiItem::close()); d->m_closeButton->setFixedSize(d->m_closeButton->sizeHint()); connect(d->m_closeButton, &QAbstractButton::clicked, this, &ResultItemWidget::closeButtonClicked); actionLayout->addWidget(d->m_closeButton); d->m_closeButton->setVisible(false); layout->setStretch(0, 1); actionLayout->addStretch(-1); vlay->addStretch(-1); d->updateShowDetailsLabel(); setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Maximum); } ResultItemWidget::~ResultItemWidget() { } void ResultItemWidget::showCloseButton(bool show) { d->m_closeButton->setVisible(show); } bool ResultItemWidget::hasErrorResult() const { return d->m_result->hasError(); } void ResultItemWidget::Private::slotLinkActivated(const QString &link) { Q_ASSERT(m_result); qCDebug(KLEOPATRA_LOG) << "Link activated: " << link; if (link.startsWith(QLatin1String("key:"))) { auto split = link.split(QLatin1Char(':')); auto fpr = split.value(1); if (split.size() == 2 && isFingerprint(fpr)) { /* There might be a security consideration here if somehow * a short keyid is used in a link and it collides with another. * So we additionally check that it really is a fingerprint. */ auto cmd = Command::commandForQuery(fpr); cmd->setParentWId(q->effectiveWinId()); cmd->start(); } else { qCWarning(KLEOPATRA_LOG) << "key link invalid " << link; } return; } const QUrl url(link); if (url.host() == QLatin1String("showauditlog")) { q->showAuditLog(); return; } qCWarning(KLEOPATRA_LOG) << "Unexpected link scheme: " << link; } void ResultItemWidget::showAuditLog() { MessageBox::auditLog(parentWidget(), d->m_result->auditLog().text()); } #include "moc_resultitemwidget.cpp" diff --git a/src/crypto/gui/resultlistwidget.cpp b/src/crypto/gui/resultlistwidget.cpp index e130f06a8..6e019413f 100644 --- a/src/crypto/gui/resultlistwidget.cpp +++ b/src/crypto/gui/resultlistwidget.cpp @@ -1,219 +1,213 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/gui/resultlistwidget.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 "resultlistwidget.h" #include "emailoperationspreferences.h" #include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Crypto; using namespace Kleo::Crypto::Gui; class ResultListWidget::Private { ResultListWidget *const q; public: explicit Private(ResultListWidget *qq); void result(const std::shared_ptr &result); void started(const std::shared_ptr &task); void allTasksDone(); void addResultWidget(ResultItemWidget *widget); void setupSingle(); void setupMulti(); void resizeIfStandalone(); std::vector< std::shared_ptr > m_collections; - bool m_standaloneMode; - int m_lastErrorItemIndex; - ScrollArea *m_scrollArea; - QPushButton *m_closeButton; - QVBoxLayout *m_layout; - QLabel *m_progressLabel; + bool m_standaloneMode = false; + int m_lastErrorItemIndex = 0; + ScrollArea *m_scrollArea = nullptr; + QPushButton *m_closeButton = nullptr; + QVBoxLayout *m_layout = nullptr; + QLabel *m_progressLabel = nullptr; }; ResultListWidget::Private::Private(ResultListWidget *qq) : q(qq), - m_collections(), - m_standaloneMode(false), - m_lastErrorItemIndex(0), - m_scrollArea(nullptr), - m_closeButton(nullptr), - m_layout(nullptr), - m_progressLabel(nullptr) + m_collections() { m_layout = new QVBoxLayout(q); m_layout->setContentsMargins(0, 0, 0, 0); m_layout->setSpacing(0); m_progressLabel = new QLabel; m_progressLabel->setWordWrap(true); m_layout->addWidget(m_progressLabel); m_progressLabel->setVisible(false); m_closeButton = new QPushButton; KGuiItem::assign(m_closeButton, KStandardGuiItem::close()); q->connect(m_closeButton, &QPushButton::clicked, q, &ResultListWidget::close); m_layout->addWidget(m_closeButton); m_closeButton->setVisible(false); } ResultListWidget::ResultListWidget(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f), d(new Private(this)) { } ResultListWidget::~ResultListWidget() { if (!d->m_standaloneMode) { return; } EMailOperationsPreferences prefs; prefs.setDecryptVerifyPopupGeometry(geometry()); prefs.save(); } void ResultListWidget::Private::setupSingle() { m_layout->addStretch(); } void ResultListWidget::Private::resizeIfStandalone() { if (m_standaloneMode) { q->resize(q->size().expandedTo(q->sizeHint())); } } void ResultListWidget::Private::setupMulti() { if (m_scrollArea) { return; // already been here... } m_scrollArea = new ScrollArea; m_scrollArea->setFocusPolicy(Qt::NoFocus); Q_ASSERT(qobject_cast(m_scrollArea->widget()->layout())); static_cast(m_scrollArea->widget()->layout())->setContentsMargins(0, 0, 0, 0); static_cast(m_scrollArea->widget()->layout())->setSpacing(2); static_cast(m_scrollArea->widget()->layout())->addStretch(); m_layout->insertWidget(0, m_scrollArea); } void ResultListWidget::Private::addResultWidget(ResultItemWidget *widget) { Q_ASSERT(widget); Q_ASSERT(std::any_of(m_collections.cbegin(), m_collections.cend(), [](const std::shared_ptr &t) { return !t->isEmpty(); })); Q_ASSERT(m_scrollArea); Q_ASSERT(m_scrollArea->widget()); Q_ASSERT(qobject_cast(m_scrollArea->widget()->layout())); QBoxLayout &blay = *static_cast(m_scrollArea->widget()->layout()); blay.insertWidget(widget->hasErrorResult() ? m_lastErrorItemIndex++ : (blay.count() - 1), widget); widget->show(); resizeIfStandalone(); } void ResultListWidget::Private::allTasksDone() { if (!q->isComplete()) { return; } m_progressLabel->setVisible(false); resizeIfStandalone(); Q_EMIT q->completeChanged(); } void ResultListWidget::Private::result(const std::shared_ptr &result) { Q_ASSERT(result); Q_ASSERT(std::any_of(m_collections.cbegin(), m_collections.cend(), [](const std::shared_ptr &t) { return !t->isEmpty(); })); ResultItemWidget *wid = new ResultItemWidget(result); q->connect(wid, &ResultItemWidget::linkActivated, q, &ResultListWidget::linkActivated); q->connect(wid, &ResultItemWidget::closeButtonClicked, q, &ResultListWidget::close); addResultWidget(wid); } bool ResultListWidget::isComplete() const { return std::all_of(d->m_collections.cbegin(), d->m_collections.cend(), std::mem_fn(&TaskCollection::allTasksCompleted)); } unsigned int ResultListWidget::totalNumberOfTasks() const { return kdtools::accumulate_transform(d->m_collections.cbegin(), d->m_collections.cend(), std::mem_fn(&TaskCollection::size), 0U); } unsigned int ResultListWidget::numberOfCompletedTasks() const { return kdtools::accumulate_transform(d->m_collections.cbegin(), d->m_collections.cend(), std::mem_fn(&TaskCollection::numberOfCompletedTasks), 0U); } void ResultListWidget::setTaskCollection(const std::shared_ptr &coll) { //clear(); ### PENDING(marc) implement addTaskCollection(coll); } void ResultListWidget::addTaskCollection(const std::shared_ptr &coll) { Q_ASSERT(coll); Q_ASSERT(!coll->isEmpty()); d->m_collections.push_back(coll); connect(coll.get(), SIGNAL(result(std::shared_ptr)), this, SLOT(result(std::shared_ptr))); connect(coll.get(), SIGNAL(started(std::shared_ptr)), this, SLOT(started(std::shared_ptr))); connect(coll.get(), SIGNAL(done()), this, SLOT(allTasksDone())); d->setupMulti(); setStandaloneMode(d->m_standaloneMode); } void ResultListWidget::Private::started(const std::shared_ptr &task) { Q_ASSERT(task); Q_ASSERT(m_progressLabel); m_progressLabel->setText(i18nc("number, operation description", "Operation %1: %2", q->numberOfCompletedTasks() + 1, task->label())); resizeIfStandalone(); } void ResultListWidget::setStandaloneMode(bool standalone) { d->m_standaloneMode = standalone; if (totalNumberOfTasks() == 0) { return; } d->m_closeButton->setVisible(standalone); d->m_progressLabel->setVisible(standalone); } #include "moc_resultlistwidget.cpp" diff --git a/src/crypto/gui/resultpage.cpp b/src/crypto/gui/resultpage.cpp index 241568db9..8ce33984d 100644 --- a/src/crypto/gui/resultpage.cpp +++ b/src/crypto/gui/resultpage.cpp @@ -1,181 +1,181 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/gui/resultpage.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 "resultpage.h" #include "resultlistwidget.h" #include "resultitemwidget.h" #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Crypto; using namespace Kleo::Crypto::Gui; class ResultPage::Private { ResultPage *const q; public: explicit Private(ResultPage *qq); void progress(const QString &msg, int progress, int total); void result(const std::shared_ptr &result); void started(const std::shared_ptr &result); void allDone(); void keepOpenWhenDone(bool keep); QLabel *labelForTag(const QString &tag); std::shared_ptr m_tasks; QProgressBar *m_progressBar; QHash m_progressLabelByTag; QVBoxLayout *m_progressLabelLayout; - int m_lastErrorItemIndex; + int m_lastErrorItemIndex = 0; ResultListWidget *m_resultList; QCheckBox *m_keepOpenCB; }; -ResultPage::Private::Private(ResultPage *qq) : q(qq), m_lastErrorItemIndex(0) +ResultPage::Private::Private(ResultPage *qq) : q(qq) { QBoxLayout *const layout = new QVBoxLayout(q); QWidget *const labels = new QWidget; m_progressLabelLayout = new QVBoxLayout(labels); layout->addWidget(labels); m_progressBar = new QProgressBar; layout->addWidget(m_progressBar); m_resultList = new ResultListWidget; layout->addWidget(m_resultList); m_keepOpenCB = new QCheckBox; m_keepOpenCB->setText(i18n("Keep open after operation completed")); m_keepOpenCB->setChecked(true); connect(m_keepOpenCB, &QAbstractButton::toggled, q, &ResultPage::keepOpenWhenDone); layout->addWidget(m_keepOpenCB); } void ResultPage::Private::progress(const QString &msg, int progress, int total) { Q_UNUSED(msg); Q_ASSERT(progress >= 0); Q_ASSERT(total >= 0); m_progressBar->setRange(0, total); m_progressBar->setValue(progress); } void ResultPage::Private::keepOpenWhenDone(bool) { } void ResultPage::Private::allDone() { Q_ASSERT(m_tasks); q->setAutoAdvance(!m_keepOpenCB->isChecked() && !m_tasks->errorOccurred()); m_progressBar->setRange(0, 100); m_progressBar->setValue(100); m_tasks.reset(); Q_FOREACH (const QString &i, m_progressLabelByTag.keys()) { if (!i.isEmpty()) { m_progressLabelByTag.value(i)->setText(i18n("%1: All operations completed.", i)); } else { m_progressLabelByTag.value(i)->setText(i18n("All operations completed.")); } } Q_EMIT q->completeChanged(); } void ResultPage::Private::result(const std::shared_ptr &) { } void ResultPage::Private::started(const std::shared_ptr &task) { Q_ASSERT(task); const QString tag = task->tag(); QLabel *const label = labelForTag(tag); Q_ASSERT(label); if (tag.isEmpty()) { label->setText(i18nc("number, operation description", "Operation %1: %2", m_tasks->numberOfCompletedTasks() + 1, task->label())); } else { label->setText(i18nc("tag( \"OpenPGP\" or \"CMS\"), operation description", "%1: %2", tag, task->label())); } } ResultPage::ResultPage(QWidget *parent, Qt::WindowFlags flags) : WizardPage(parent, flags), d(new Private(this)) { setTitle(i18n("Results")); } ResultPage::~ResultPage() { } bool ResultPage::keepOpenWhenDone() const { return d->m_keepOpenCB->isChecked(); } void ResultPage::setKeepOpenWhenDone(bool keep) { d->m_keepOpenCB->setChecked(keep); } void ResultPage::setTaskCollection(const std::shared_ptr &coll) { Q_ASSERT(!d->m_tasks); if (d->m_tasks == coll) { return; } d->m_tasks = coll; Q_ASSERT(d->m_tasks); d->m_resultList->setTaskCollection(coll); connect(d->m_tasks.get(), SIGNAL(progress(QString,int,int)), this, SLOT(progress(QString,int,int))); connect(d->m_tasks.get(), SIGNAL(done()), this, SLOT(allDone())); connect(d->m_tasks.get(), SIGNAL(result(std::shared_ptr)), this, SLOT(result(std::shared_ptr))); connect(d->m_tasks.get(), SIGNAL(started(std::shared_ptr)), this, SLOT(started(std::shared_ptr))); Q_FOREACH (const std::shared_ptr &i, d->m_tasks->tasks()) { // create labels for all tags in collection Q_ASSERT(i && d->labelForTag(i->tag())); Q_UNUSED(i); } Q_EMIT completeChanged(); } QLabel *ResultPage::Private::labelForTag(const QString &tag) { if (QLabel *const label = m_progressLabelByTag.value(tag)) { return label; } QLabel *label = new QLabel; label->setTextFormat(Qt::RichText); label->setWordWrap(true); m_progressLabelLayout->addWidget(label); m_progressLabelByTag.insert(tag, label); return label; } bool ResultPage::isComplete() const { return d->m_tasks ? d->m_tasks->allTasksCompleted() : true; } #include "moc_resultpage.cpp" diff --git a/src/crypto/gui/signemailwizard.cpp b/src/crypto/gui/signemailwizard.cpp index 5e5ca1e79..6a36643d3 100644 --- a/src/crypto/gui/signemailwizard.cpp +++ b/src/crypto/gui/signemailwizard.cpp @@ -1,144 +1,145 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/gui/signemailwizard.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 "signemailwizard.h" #include "signerresolvepage.h" #include #include #include using namespace Kleo; using namespace Kleo::Crypto::Gui; using namespace GpgME; namespace { class SignerResolveValidator : public SignerResolvePage::Validator { public: explicit SignerResolveValidator(SignerResolvePage *page); bool isComplete() const override; QString explanation() const override; void update() const; QString customWindowTitle() const override { return QString(); } private: SignerResolvePage *m_page; mutable QString expl; mutable bool complete; }; } -SignerResolveValidator::SignerResolveValidator(SignerResolvePage *page) : SignerResolvePage::Validator(), m_page(page), complete(true) +SignerResolveValidator::SignerResolveValidator(SignerResolvePage *page) + : SignerResolvePage::Validator(), m_page(page), complete(true) { Q_ASSERT(m_page); } void SignerResolveValidator::update() const { const bool haveSelected = !m_page->selectedProtocols().empty(); const std::vector missing = m_page->selectedProtocolsWithoutSigningCertificate(); complete = haveSelected && missing.empty(); expl.clear(); if (complete) { return; } if (!haveSelected) { expl = i18n("You need to select a signing certificate to proceed."); return; } Q_ASSERT(missing.size() <= 2); if (missing.size() == 1) { expl = i18n("You need to select an %1 signing certificate to proceed.", Formatting::displayName(missing[0])); } else { expl = i18n("You need to select %1 and %2 signing certificates to proceed.", Formatting::displayName(missing[0]), Formatting::displayName(missing[1])); } } QString SignerResolveValidator::explanation() const { update(); return expl; } bool SignerResolveValidator::isComplete() const { update(); return complete; } class SignEMailWizard::Private { friend class ::Kleo::Crypto::Gui::SignEMailWizard; SignEMailWizard *const q; public: explicit Private(SignEMailWizard *qq); ~Private(); void operationSelected(); bool m_quickMode; }; SignEMailWizard::Private::Private(SignEMailWizard *qq) : q(qq), m_quickMode(false) { q->setWindowTitle(i18nc("@title:window", "Sign Mail Message")); std::vector pageOrder; q->setSignerResolvePageValidator(std::shared_ptr(new SignerResolveValidator(q->signerResolvePage()))); pageOrder.push_back(SignEncryptWizard::ResolveSignerPage); pageOrder.push_back(SignEncryptWizard::ResultPage); q->setPageOrder(pageOrder); q->setCommitPage(SignEncryptWizard::ResolveSignerPage); q->setEncryptionSelected(false); q->setEncryptionUserMutable(false); q->setSigningSelected(true); q->setSigningUserMutable(false); q->signerResolvePage()->setProtocolSelectionUserMutable(false); q->setMultipleProtocolsAllowed(false); } SignEMailWizard::Private::~Private() {} SignEMailWizard::SignEMailWizard(QWidget *parent, Qt::WindowFlags f) : SignEncryptWizard(parent, f), d(new Private(this)) { } bool SignEMailWizard::quickMode() const { return d->m_quickMode; } void SignEMailWizard::setQuickMode(bool quick) { if (quick == d->m_quickMode) { return; } d->m_quickMode = quick; signerResolvePage()->setAutoAdvance(quick); setKeepResultPageOpenWhenDone(!quick); } SignEMailWizard::~SignEMailWizard() {} diff --git a/src/crypto/gui/signencryptfileswizard.cpp b/src/crypto/gui/signencryptfileswizard.cpp index 0ebdf20c3..e0a11cb63 100644 --- a/src/crypto/gui/signencryptfileswizard.cpp +++ b/src/crypto/gui/signencryptfileswizard.cpp @@ -1,519 +1,517 @@ /* crypto/gui/signencryptfileswizard.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "signencryptfileswizard.h" #include "signencryptwidget.h" #include "newresultpage.h" #include #include #include #include #include #include #include "kleopatra_debug.h" #include #include #include #include #include #include #include #include #include #include using namespace GpgME; using namespace Kleo; using namespace Kleo::Crypto::Gui; enum Page { SigEncPageId, ResultPageId, NumPages }; class SigEncPage: public QWizardPage { Q_OBJECT public: explicit SigEncPage(QWidget *parent = nullptr) : QWizardPage(parent), mParent((SignEncryptFilesWizard *) parent), mWidget(new SignEncryptWidget), mOutLayout(new QVBoxLayout), mArchive(false), mUseOutputDir(false) { setTitle(i18nc("@title", "Sign / Encrypt Files")); auto vLay = new QVBoxLayout(this); vLay->setContentsMargins(0, 0, 0, 0); vLay->addWidget(mWidget); connect(mWidget, &SignEncryptWidget::operationChanged, this, &SigEncPage::updateCommitButton); connect(mWidget, &SignEncryptWidget::keysChanged, this, &SigEncPage::updateFileWidgets); updateCommitButton(mWidget->currentOp()); auto outputGrp = new QGroupBox(i18n("Output")); outputGrp->setLayout(mOutLayout); mPlaceholderWidget = new QLabel(i18n("Please select an action.")); mOutLayout->addWidget(mPlaceholderWidget); mUseOutputDirChk = new QCheckBox(i18n("Encrypt / Sign each file separately.")); mUseOutputDirChk->setToolTip(i18nc("@info", "Keep each file separate instead of creating an archive for all.")); mOutLayout->addWidget(mUseOutputDirChk); connect (mUseOutputDirChk, &QCheckBox::toggled, this, [this] (bool state) { mUseOutputDir = state; mArchive = !mUseOutputDir; updateFileWidgets(); }); vLay->addWidget(outputGrp); setMinimumHeight(300); } void setEncryptionPreset(bool value) { mWidget->setEncryptionChecked(value); } void setSigningPreset(bool value) { mWidget->setSigningChecked(value); } bool isComplete() const override { return !mWidget->currentOp().isNull(); } int nextId() const override { return ResultPageId; } void initializePage() override { setCommitPage(true); } void setArchiveForced(bool archive) { mArchive = archive; setArchiveMutable(!archive); } void setArchiveMutable(bool archive) { mUseOutputDirChk->setVisible(archive); if (archive) { const KConfigGroup archCfg(KSharedConfig::openConfig(), "SignEncryptFilesWizard"); mUseOutputDirChk->setChecked(archCfg.readEntry("LastUseOutputDir", false)); } else { mUseOutputDirChk->setChecked(false); } } bool validatePage() override { bool sign = !mWidget->signKey().isNull(); bool encrypt = !mWidget->selfKey().isNull() || !mWidget->recipients().empty(); if (!mWidget->validate()) { return false; } mWidget->saveOwnKeys(); if (mUseOutputDirChk->isVisible()) { KConfigGroup archCfg(KSharedConfig::openConfig(), "SignEncryptFilesWizard"); archCfg.writeEntry("LastUseOutputDir", mUseOutputDir); } if (sign && !encrypt && mArchive) { return KMessageBox::warningContinueCancel(this, xi18nc("@info", "Archiving in combination with sign-only currently requires what are known as opaque signatures - " "unlike detached ones, these embed the content in the signature." "This format is rather unusual. You might want to archive the files separately, " "and then sign the archive as one file with Kleopatra." "Future versions of Kleopatra are expected to also support detached signatures in this case."), i18nc("@title:window", "Unusual Signature Warning"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QStringLiteral("signencryptfileswizard-archive+sign-only-warning")) == KMessageBox::Continue; } else if (sign && !encrypt) { return true; } if (!mWidget->selfKey().isNull()) { return true; } bool hasSecret = false; Q_FOREACH (const Key k, mWidget->recipients()) { if (k.hasSecret()) { hasSecret = true; break; } } if (!hasSecret && !mWidget->encryptSymmetric()) { if (KMessageBox::warningContinueCancel(this, xi18nc("@info", "None of the recipients you are encrypting to seems to be your own." "This means that you will not be able to decrypt the data anymore, once encrypted." "Do you want to continue, or cancel to change the recipient selection?"), i18nc("@title:window", "Encrypt-To-Self Warning"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QStringLiteral("warn-encrypt-to-non-self"), KMessageBox::Notify | KMessageBox::Dangerous) == KMessageBox::Cancel) { return false; } } return true; } QVector recipients() const { return mWidget->recipients(); } /* In the future we might find a usecase for multiple * signers */ QVector signers() const { QVector ret; const Key k = mWidget->signKey(); if (!k.isNull()) { ret << k; } return ret; } private: QWidget *createRequester(int forKind, QBoxLayout *lay) { static const QMap icons = { { SignEncryptFilesWizard::SignatureCMS, QStringLiteral("document-sign") }, { SignEncryptFilesWizard::SignaturePGP, QStringLiteral("document-sign") }, { SignEncryptFilesWizard::CombinedPGP, QStringLiteral("document-edit-sign-encrypt") }, { SignEncryptFilesWizard::EncryptedPGP, QStringLiteral("document-encrypt") }, { SignEncryptFilesWizard::EncryptedCMS, QStringLiteral("document-encrypt") }, { SignEncryptFilesWizard::Directory, QStringLiteral("folder") } }; static const QMap toolTips = { { SignEncryptFilesWizard::SignatureCMS, i18n("The S/MIME signature.") }, { SignEncryptFilesWizard::SignaturePGP, i18n("The signature.") }, { SignEncryptFilesWizard::CombinedPGP, i18n("The signed and encrypted file.") }, { SignEncryptFilesWizard::EncryptedPGP, i18n("The encrypted file.") }, { SignEncryptFilesWizard::EncryptedCMS, i18n("The S/MIME encrypted file.") }, { SignEncryptFilesWizard::Directory, i18n("Output directory.") } }; FileNameRequester *req = new FileNameRequester(forKind == SignEncryptFilesWizard::Directory ? QDir::Dirs : QDir::Files, this); req->setFileName(mOutNames[forKind]); QHBoxLayout *hLay = new QHBoxLayout; QLabel *iconLabel = new QLabel; QWidget *ret = new QWidget; iconLabel->setPixmap(QIcon::fromTheme(icons[forKind]).pixmap(32,32)); hLay->addWidget(iconLabel); iconLabel->setToolTip(toolTips[forKind]); req->setToolTip(toolTips[forKind]); hLay->addWidget(req); ret->setLayout(hLay); lay->addWidget(ret); connect (req, &FileNameRequester::fileNameChanged, this, [this, forKind](const QString &newName) { mOutNames[forKind] = newName; }); return ret; } public: void setOutputNames(const QMap &names) { Q_ASSERT(mOutNames.isEmpty()); mOutNames = names; Q_FOREACH (int i, mOutNames.keys()) { mRequester[i] = createRequester(i, mOutLayout); } updateFileWidgets(); } QMap outputNames() const { if (!mUseOutputDir) { auto ret = mOutNames; ret.remove(SignEncryptFilesWizard::Directory); return ret; } return mOutNames; } bool encryptSymmetric() const { return mWidget->encryptSymmetric(); } private Q_SLOTS: void updateCommitButton(const QString &label) { auto btn = mParent->button(QWizard::CommitButton); if (!label.isEmpty()) { btn->setText(label); if (Kleo::gpgComplianceP("de-vs")) { bool de_vs = mWidget->isDeVsAndValid(); btn->setIcon(QIcon::fromTheme(de_vs ? QStringLiteral("security-high") : QStringLiteral("security-medium"))); btn->setStyleSheet(QStringLiteral("background-color: ") + (de_vs ? KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::PositiveBackground).color().name() : KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::NegativeBackground).color().name())); mParent->setLabelText(de_vs ? i18nc("VS-NfD-conforming is a German standard for restricted documents for which special restrictions about algorithms apply. The string states that all cryptographic operations necessary for the communication are compliant with that.", "VS-NfD-compliant communication possible.") : i18nc("VS-NfD-conforming is a German standard for restricted documents for which special restrictions about algorithms apply. The string states that all cryptographic operations necessary for the communication are compliant with that.", "VS-NfD-compliant communication not possible.")); } } else { btn->setText(i18n("Next")); btn->setIcon(QIcon()); btn->setStyleSheet(QString()); } Q_EMIT completeChanged(); } void updateFileWidgets() { if (mRequester.isEmpty()) { return; } const QVector recipients = mWidget->recipients(); const Key sigKey = mWidget->signKey(); bool pgp = mWidget->encryptSymmetric(); bool cms = false; for (const Key &k : recipients) { if (pgp && cms) { break; } if (k.protocol() == Protocol::OpenPGP) { pgp = true; } else { cms = true; } } mOutLayout->setEnabled(false); mPlaceholderWidget->setVisible(!cms && !pgp && sigKey.isNull()); mRequester[SignEncryptFilesWizard::SignatureCMS]->setVisible(!mUseOutputDir && sigKey.protocol() == Protocol::CMS); mRequester[SignEncryptFilesWizard::EncryptedCMS]->setVisible(!mUseOutputDir && cms); mRequester[SignEncryptFilesWizard::CombinedPGP]->setVisible(!mUseOutputDir && sigKey.protocol() == Protocol::OpenPGP && pgp); mRequester[SignEncryptFilesWizard::EncryptedPGP]->setVisible(!mUseOutputDir && pgp && sigKey.protocol() != Protocol::OpenPGP); mRequester[SignEncryptFilesWizard::SignaturePGP]->setVisible(!mUseOutputDir && sigKey.protocol() == Protocol::OpenPGP && !pgp); mRequester[SignEncryptFilesWizard::Directory]->setVisible(mUseOutputDir && !mPlaceholderWidget->isVisible()); mOutLayout->setEnabled(true); } private: SignEncryptFilesWizard *mParent; SignEncryptWidget *mWidget; QMap mOutNames; QMap mRequester; QVBoxLayout *mOutLayout; QWidget *mPlaceholderWidget; QCheckBox *mUseOutputDirChk; bool mArchive; bool mUseOutputDir; }; class ResultPage : public NewResultPage { Q_OBJECT public: explicit ResultPage(QWidget *parent = nullptr) : NewResultPage(parent), mParent((SignEncryptFilesWizard *) parent) { setTitle(i18nc("@title", "Results")); setSubTitle(i18nc("@title", "Status and progress of the crypto operations is shown here.")); } void initializePage() override { mParent->setLabelText(QString()); } private: SignEncryptFilesWizard *mParent; }; SignEncryptFilesWizard::SignEncryptFilesWizard(QWidget *parent, Qt::WindowFlags f) : QWizard(parent, f) - , mSigningUserMutable(true) - , mEncryptionUserMutable(true) { readConfig(); bool de_vs = Kleo::gpgComplianceP("de-vs"); #ifdef Q_OS_WIN // Enforce modern style to avoid vista style ugliness. setWizardStyle(QWizard::ModernStyle); #endif mSigEncPage = new SigEncPage(this); mResultPage = new ResultPage(this); connect(this, &QWizard::currentIdChanged, this, &SignEncryptFilesWizard::slotCurrentIdChanged); setPage(SigEncPageId, mSigEncPage); setPage(ResultPageId, mResultPage); setOptions(QWizard::IndependentPages | (de_vs ? QWizard::HaveCustomButton1 : (QWizard::WizardOption) 0) | QWizard::NoBackButtonOnLastPage | QWizard::NoBackButtonOnStartPage); if (de_vs) { /* We use a custom button to display a label next to the buttons. */ mLabel = button(QWizard::CustomButton1); /* We style the button so that it looks and acts like a label. */ mLabel->setStyleSheet(QStringLiteral("border: none")); mLabel->setFocusPolicy(Qt::NoFocus); } else { mLabel = nullptr; } } void SignEncryptFilesWizard::setLabelText(const QString &label) const { if (mLabel) { mLabel->setText(label); } } void SignEncryptFilesWizard::slotCurrentIdChanged(int id) { if (id == ResultPageId) { Q_EMIT operationPrepared(); } } SignEncryptFilesWizard::~SignEncryptFilesWizard() { qCDebug(KLEOPATRA_LOG); writeConfig(); } void SignEncryptFilesWizard::setSigningPreset(bool preset) { mSigEncPage->setSigningPreset(preset); } void SignEncryptFilesWizard::setSigningUserMutable(bool mut) { if (mut == mSigningUserMutable) { return; } mSigningUserMutable = mut; } void SignEncryptFilesWizard::setEncryptionPreset(bool preset) { mSigEncPage->setEncryptionPreset(preset); } void SignEncryptFilesWizard::setEncryptionUserMutable(bool mut) { if (mut == mEncryptionUserMutable) { return; } mEncryptionUserMutable = mut; } void SignEncryptFilesWizard::setArchiveForced(bool archive) { mSigEncPage->setArchiveForced(archive); } void SignEncryptFilesWizard::setArchiveMutable(bool archive) { mSigEncPage->setArchiveMutable(archive); } QVector SignEncryptFilesWizard::resolvedRecipients() const { return mSigEncPage->recipients(); } QVector SignEncryptFilesWizard::resolvedSigners() const { return mSigEncPage->signers(); } void SignEncryptFilesWizard::setTaskCollection(const std::shared_ptr &coll) { mResultPage->setTaskCollection(coll); } void SignEncryptFilesWizard::setOutputNames(const QMap &map) const { mSigEncPage->setOutputNames(map); } QMap SignEncryptFilesWizard::outputNames() const { return mSigEncPage->outputNames(); } bool SignEncryptFilesWizard::encryptSymmetric() const { return mSigEncPage->encryptSymmetric(); } void SignEncryptFilesWizard::readConfig() { winId(); // ensure there's a window created // set default window size windowHandle()->resize(640, 480); // restore size from config file KConfigGroup cfgGroup(KSharedConfig::openConfig(), "SignEncryptFilesWizard"); KWindowConfig::restoreWindowSize(windowHandle(), cfgGroup); // NOTICE: QWindow::setGeometry() does NOT impact the backing QWidget geometry even if the platform // window was created -> QTBUG-40584. We therefore copy the size here. // TODO: remove once this was resolved in QWidget QPA resize(windowHandle()->size()); } void SignEncryptFilesWizard::writeConfig() { KConfigGroup cfgGroup(KSharedConfig::openConfig(), "SignEncryptFilesWizard"); KWindowConfig::saveWindowSize(windowHandle(), cfgGroup); cfgGroup.sync(); } #include "signencryptfileswizard.moc" diff --git a/src/crypto/gui/signencryptfileswizard.h b/src/crypto/gui/signencryptfileswizard.h index 09f728fa5..4e182e7e3 100644 --- a/src/crypto/gui/signencryptfileswizard.h +++ b/src/crypto/gui/signencryptfileswizard.h @@ -1,105 +1,105 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/gui/signencryptfileswizard.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef __KLEOPATRA_CRYPTO_GUI_SIGNENCRYPTFILESWIZARD_H__ #define __KLEOPATRA_CRYPTO_GUI_SIGNENCRYPTFILESWIZARD_H__ #include #include #include #include #include #include namespace GpgME { class Key; } namespace Kleo { namespace Crypto { class TaskCollection; } } class ResultPage; class SigEncPage; namespace Kleo { class SignEncryptFilesWizard : public QWizard { Q_OBJECT public: enum KindNames{ SignatureCMS, CombinedPGP, EncryptedPGP, EncryptedCMS, SignaturePGP, Directory }; explicit SignEncryptFilesWizard(QWidget *parent = nullptr, Qt::WindowFlags f = {}); ~SignEncryptFilesWizard(); // Inputs void setSigningPreset(bool preset); void setSigningUserMutable(bool mut); void setEncryptionPreset(bool preset); void setEncryptionUserMutable(bool mut); void setArchiveForced(bool archive); void setArchiveMutable(bool archive); void setOutputNames(const QMap &nameMap) const; QMap outputNames() const; void setTaskCollection(const std::shared_ptr &coll); // Outputs QVector resolvedRecipients() const; QVector resolvedSigners() const; bool encryptSymmetric() const; void setLabelText(const QString &label) const; protected: void readConfig(); void writeConfig(); Q_SIGNALS: void operationPrepared(); private Q_SLOTS: void slotCurrentIdChanged(int); private: - SigEncPage *mSigEncPage; - ResultPage *mResultPage; - QAbstractButton *mLabel; - bool mSigningUserMutable, - mEncryptionUserMutable; + SigEncPage *mSigEncPage = nullptr; + ResultPage *mResultPage = nullptr; + QAbstractButton *mLabel = nullptr; + bool mSigningUserMutable = true; + bool mEncryptionUserMutable = true; }; } #endif /* __KLEOPATRA_CRYPTO_GUI_SIGNENCRYPTFILESWIZARD_H__ */ diff --git a/src/crypto/gui/signencryptwidget.h b/src/crypto/gui/signencryptwidget.h index d8e63f03c..07004f21e 100644 --- a/src/crypto/gui/signencryptwidget.h +++ b/src/crypto/gui/signencryptwidget.h @@ -1,122 +1,122 @@ /* crypto/gui/signencryptwidget.h 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 */ #ifndef CRYPTO_GUI_SIGNENCRYPTWIDGET_H #define CRYPTO_GUI_SIGNENCRYPTWIDGET_H #include #include #include class QGridLayout; class QCheckBox; namespace Kleo { class CertificateLineEdit; class KeySelectionCombo; class AbstractKeyListModel; class UnknownRecipientWidget; class SignEncryptWidget: public QWidget { Q_OBJECT public: /** If cmsSigEncExclusive is true CMS operations can be * done only either as sign or as encrypt */ explicit SignEncryptWidget(QWidget *parent = nullptr, bool cmsSigEncExclusive = false); /** Returns the list of recipients selected in the dialog * or an empty list if encryption is disabled */ QVector recipients() const; /** Returns the selected signing key or a null key if signing * is disabled. */ GpgME::Key signKey() const; /** Returns the selected encrypt to self key or a null key if * encrypt to self is disabled. */ GpgME::Key selfKey() const; /** Returns the operation based on the current selection or * a null string if nothing would happen. */ QString currentOp() const; /** Whether or not symmetric encryption should also be used. */ bool encryptSymmetric() const; /** Save the currently selected signing and encrypt to self keys. */ void saveOwnKeys() const; /** Return whether or not all keys involved in the operation are compliant with CO_DE_VS, and all keys are valid (i.e. all userIDs have Validity >= Full). */ bool isDeVsAndValid() const; /** Set whether or not signing group should be checked */ void setSigningChecked(bool value); /** Set whether or not encryption group should be checked */ void setEncryptionChecked(bool value); /** Filter for a specific protocol. Use UnknownProtocol for both * S/MIME and OpenPGP */ void setProtocol(GpgME::Protocol protocol); /** Add a recipient with the key key */ void addRecipient(const GpgME::Key &key); /** Add a placehoder for an unknown key */ void addUnknownRecipient(const char *keyId); /** Remove all Recipients added by keyId or by key. */ void clearAddedRecipients(); /** Remove a Recipient key */ void removeRecipient(const GpgME::Key &key); /** Validate that each line edit with content has a key. */ bool validate(); protected Q_SLOTS: void updateOp(); void recipientsChanged(); void recpRemovalRequested(CertificateLineEdit *w); void addRecipient(); protected: void loadKeys(); Q_SIGNALS: /* Emitted when the certificate selection changed the operation * with that selection. e.g. "Sign" or "Sign/Encrypt". * If no crypto operation is selected this returns a null string. */ void operationChanged(const QString &op); /* Emitted when the certificate selection might be changed. */ void keysChanged(); private: - KeySelectionCombo *mSigSelect, - *mSelfSelect; + KeySelectionCombo *mSigSelect = nullptr; + KeySelectionCombo *mSelfSelect = nullptr; QVector mRecpWidgets; QVector mUnknownWidgets; QVector mAddedKeys; - QGridLayout *mRecpLayout; + QGridLayout *mRecpLayout = nullptr; QString mOp; - AbstractKeyListModel *mModel; - QCheckBox *mSymmetric, - *mSigChk, - *mEncOtherChk, - *mEncSelfChk; - int mRecpRowCount; + AbstractKeyListModel *mModel = nullptr; + QCheckBox *mSymmetric = nullptr; + QCheckBox *mSigChk = nullptr; + QCheckBox *mEncOtherChk = nullptr; + QCheckBox *mEncSelfChk = nullptr; + int mRecpRowCount = 2; GpgME::Protocol mCurrentProto; - bool mIsExclusive; + const bool mIsExclusive; }; } // namespace Kleo #endif // CRYPTO_GUI_SIGNENCRYPTWIDGET_H diff --git a/src/crypto/gui/signencryptwizard.cpp b/src/crypto/gui/signencryptwizard.cpp index a7d7cecb3..708bfa97d 100644 --- a/src/crypto/gui/signencryptwizard.cpp +++ b/src/crypto/gui/signencryptwizard.cpp @@ -1,283 +1,283 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/gui/signencryptwizard.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 "signencryptwizard.h" #include "objectspage.h" #include "resolverecipientspage.h" #include "signerresolvepage.h" #include "resultpage.h" #include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Crypto; using namespace Kleo::Crypto::Gui; using namespace GpgME; using namespace KMime::Types; class SignEncryptWizard::Private { friend class ::Kleo::Crypto::Gui::SignEncryptWizard; SignEncryptWizard *const q; public: explicit Private(SignEncryptWizard *qq); ~Private(); void setCommitPage(Page page); - Gui::ResolveRecipientsPage *recipientResolvePage; // clashes with enum of same name - SignerResolvePage *signerResolvePage; - Gui::ObjectsPage *objectsPage; // clashes with enum of same name - Gui::ResultPage *resultPage; // clashes with enum of same name + Gui::ResolveRecipientsPage *const recipientResolvePage; // clashes with enum of same name + SignerResolvePage *const signerResolvePage; + Gui::ObjectsPage *const objectsPage; // clashes with enum of same name + Gui::ResultPage *const resultPage; // clashes with enum of same name }; SignEncryptWizard::Private::Private(SignEncryptWizard *qq) : q(qq), recipientResolvePage(new Gui::ResolveRecipientsPage), signerResolvePage(new SignerResolvePage), objectsPage(new Gui::ObjectsPage), resultPage(new Gui::ResultPage) { q->setPage(SignEncryptWizard::ResolveSignerPage, signerResolvePage); q->setPage(SignEncryptWizard::ObjectsPage, objectsPage); q->setPage(SignEncryptWizard::ResolveRecipientsPage, recipientResolvePage); q->setPage(SignEncryptWizard::ResultPage, resultPage); //TODO: move the RecipientPreferences creation out of here, don't create a new instance for each wizard recipientResolvePage->setRecipientPreferences(std::shared_ptr(new KConfigBasedRecipientPreferences(KSharedConfig::openConfig()))); signerResolvePage->setSigningPreferences(std::shared_ptr(new KConfigBasedSigningPreferences(KSharedConfig::openConfig()))); q->resize(QSize(640, 480).expandedTo(q->sizeHint())); } void SignEncryptWizard::onNext(int currentId) { if (currentId == ResolveRecipientsPage) { QTimer::singleShot(0, this, &SignEncryptWizard::recipientsResolved); } if (currentId == ResolveSignerPage) { //FIXME: Sign&Encrypt is only supported by OpenPGP. Remove this when we support this for CMS, too if (encryptionSelected() && signingSelected()) { setPresetProtocol(OpenPGP); } QTimer::singleShot(0, this, &SignEncryptWizard::signersResolved); } if (currentId == ObjectsPage) { QTimer::singleShot(0, this, &SignEncryptWizard::objectsResolved); } } SignEncryptWizard::Private::~Private() {} SignEncryptWizard::SignEncryptWizard(QWidget *p, Qt::WindowFlags f) : Wizard(p, f), d(new Private(this)) { } SignEncryptWizard::~SignEncryptWizard() {} void SignEncryptWizard::setCommitPage(Page page) { d->setCommitPage(page); } void SignEncryptWizard::Private::setCommitPage(Page page) { q->page(ResolveSignerPage)->setCommitPage(false); q->page(ResolveRecipientsPage)->setCommitPage(false); q->page(ObjectsPage)->setCommitPage(false); q->page(ResultPage)->setCommitPage(false); q->page(page)->setCommitPage(true); } void SignEncryptWizard::setPresetProtocol(Protocol proto) { d->signerResolvePage->setPresetProtocol(proto); d->signerResolvePage->setProtocolSelectionUserMutable(proto == UnknownProtocol); d->recipientResolvePage->setPresetProtocol(proto); } GpgME::Protocol SignEncryptWizard::selectedProtocol() const { return d->recipientResolvePage->selectedProtocol(); } GpgME::Protocol SignEncryptWizard::presetProtocol() const { return d->recipientResolvePage->presetProtocol(); } void SignEncryptWizard::setEncryptionSelected(bool selected) { d->signerResolvePage->setEncryptionSelected(selected); } void SignEncryptWizard::setSigningSelected(bool selected) { d->signerResolvePage->setSigningSelected(selected); } bool SignEncryptWizard::isSigningUserMutable() const { return d->signerResolvePage->isSigningUserMutable(); } void SignEncryptWizard::setSigningUserMutable(bool isMutable) { d->signerResolvePage->setSigningUserMutable(isMutable); } bool SignEncryptWizard::isEncryptionUserMutable() const { return d->signerResolvePage->isEncryptionUserMutable(); } bool SignEncryptWizard::isMultipleProtocolsAllowed() const { return d->recipientResolvePage->multipleProtocolsAllowed(); } void SignEncryptWizard::setMultipleProtocolsAllowed(bool allowed) { d->signerResolvePage->setMultipleProtocolsAllowed(allowed); d->recipientResolvePage->setMultipleProtocolsAllowed(allowed); } void SignEncryptWizard::setEncryptionUserMutable(bool isMutable) { d->signerResolvePage->setEncryptionUserMutable(isMutable); } void SignEncryptWizard::setFiles(const QStringList &files) { d->objectsPage->setFiles(files); } QFileInfoList SignEncryptWizard::resolvedFiles() const { const QStringList files = d->objectsPage->files(); QFileInfoList fileInfos; for (const QString &i : files) { fileInfos.push_back(QFileInfo(i)); } return fileInfos; } bool SignEncryptWizard::signingSelected() const { return d->signerResolvePage->signingSelected(); } bool SignEncryptWizard::encryptionSelected() const { return d->signerResolvePage->encryptionSelected(); } void SignEncryptWizard::setRecipients(const std::vector &recipients, const std::vector &encryptToSelfRecipients) { d->recipientResolvePage->setRecipients(recipients, encryptToSelfRecipients); } void SignEncryptWizard::setSignersAndCandidates(const std::vector &signers, const std::vector< std::vector > &keys) { d->signerResolvePage->setSignersAndCandidates(signers, keys); } void SignEncryptWizard::setTaskCollection(const std::shared_ptr &coll) { kleo_assert(coll); d->resultPage->setTaskCollection(coll); } std::vector SignEncryptWizard::resolvedCertificates() const { return d->recipientResolvePage->resolvedCertificates(); } std::vector SignEncryptWizard::resolvedSigners() const { return d->signerResolvePage->resolvedSigners(); } bool SignEncryptWizard::isAsciiArmorEnabled() const { return d->signerResolvePage->isAsciiArmorEnabled(); } void SignEncryptWizard::setAsciiArmorEnabled(bool enabled) { d->signerResolvePage->setAsciiArmorEnabled(enabled); } bool SignEncryptWizard::recipientsUserMutable() const { return d->recipientResolvePage->recipientsUserMutable(); } void SignEncryptWizard::setRecipientsUserMutable(bool isMutable) { d->recipientResolvePage->setRecipientsUserMutable(isMutable); } void SignEncryptWizard::setSignerResolvePageValidator(const std::shared_ptr &validator) { d->signerResolvePage->setValidator(validator); } Gui::SignerResolvePage *SignEncryptWizard::signerResolvePage() { return d->signerResolvePage; } const Gui::SignerResolvePage *SignEncryptWizard::signerResolvePage() const { return d->signerResolvePage; } Gui::ResolveRecipientsPage *SignEncryptWizard::resolveRecipientsPage() { return d->recipientResolvePage; } Gui::ObjectsPage *SignEncryptWizard::objectsPage() { return d->objectsPage; } Gui::ResultPage *SignEncryptWizard::resultPage() { return d->resultPage; } bool SignEncryptWizard::keepResultPageOpenWhenDone() const { return d->resultPage->keepOpenWhenDone(); } void SignEncryptWizard::setKeepResultPageOpenWhenDone(bool keep) { d->resultPage->setKeepOpenWhenDone(keep); } diff --git a/src/crypto/gui/signerresolvepage.cpp b/src/crypto/gui/signerresolvepage.cpp index 8ce1948e3..53905d87f 100644 --- a/src/crypto/gui/signerresolvepage.cpp +++ b/src/crypto/gui/signerresolvepage.cpp @@ -1,666 +1,667 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/gui/signerresolvepage.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 "signerresolvepage.h" #include "signerresolvepage_p.h" #include "signingcertificateselectiondialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace GpgME; using namespace Kleo; using namespace Kleo::Crypto; using namespace Kleo::Crypto::Gui; namespace { static SignerResolvePage::Operation operationFromFlags(bool sign, bool encrypt) { if (!encrypt && sign) { return SignerResolvePage::SignOnly; } if (!sign && encrypt) { return SignerResolvePage::EncryptOnly; } return SignerResolvePage::SignAndEncrypt; } static QString formatLabel(Protocol p, const Key &key) { return i18nc("%1=protocol (S/Mime, OpenPGP), %2=certificate", "Sign using %1: %2", Formatting::displayName(p), !key.isNull() ? Formatting::formatForComboBox(key) : i18n("No certificate selected")); } static std::vector supportedProtocols() { std::vector protocols; protocols.push_back(OpenPGP); protocols.push_back(CMS); return protocols; } } AbstractSigningProtocolSelectionWidget::AbstractSigningProtocolSelectionWidget(QWidget *p, Qt::WindowFlags f) : QWidget(p, f) { } ReadOnlyProtocolSelectionWidget::ReadOnlyProtocolSelectionWidget(QWidget *p, Qt::WindowFlags f) : AbstractSigningProtocolSelectionWidget(p, f) { QVBoxLayout *const layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); - Q_FOREACH (const Protocol i, supportedProtocols()) { //krazy:exclude=foreach + const auto supportedProtocolsLst = supportedProtocols(); + for (const Protocol i: supportedProtocolsLst) { QLabel *const l = new QLabel; l->setText(formatLabel(i, Key())); layout->addWidget(l); m_labels[i] = l; } } void ReadOnlyProtocolSelectionWidget::setProtocolChecked(Protocol protocol, bool checked) { QLabel *const l = label(protocol); Q_ASSERT(l); l->setVisible(checked); } bool ReadOnlyProtocolSelectionWidget::isProtocolChecked(Protocol protocol) const { QLabel *const l = label(protocol); Q_ASSERT(l); return l->isVisible(); } std::vector ReadOnlyProtocolSelectionWidget::checkedProtocols() const { std::vector res; Q_FOREACH (const Protocol i, supportedProtocols()) //krazy:exclude=foreach if (isProtocolChecked(i)) { res.push_back(i); } return res; } SigningProtocolSelectionWidget::SigningProtocolSelectionWidget(QWidget *parent, Qt::WindowFlags f) : AbstractSigningProtocolSelectionWidget(parent, f) { m_buttonGroup = new QButtonGroup(this); connect(m_buttonGroup, static_cast(&QButtonGroup::buttonClicked), this, &SigningProtocolSelectionWidget::userSelectionChanged); QVBoxLayout *const layout = new QVBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); Q_FOREACH (const Protocol i, supportedProtocols()) { //krazy:exclude=foreach QCheckBox *const b = new QCheckBox; b->setText(formatLabel(i, Key())); m_buttons[i] = b; layout->addWidget(b); m_buttonGroup->addButton(b); } setExclusive(true); } void SigningProtocolSelectionWidget::setProtocolChecked(Protocol p, bool checked) { Q_ASSERT(p != UnknownProtocol); QCheckBox *const b = button(p); Q_ASSERT(b); b->setChecked(checked); } bool SigningProtocolSelectionWidget::isProtocolChecked(Protocol p) const { Q_ASSERT(p != UnknownProtocol); const QAbstractButton *const b = button(p); Q_ASSERT(b); return b->isChecked(); } std::vector SigningProtocolSelectionWidget::checkedProtocols() const { std::vector res; for (std::map::const_iterator it = m_buttons.begin(), end = m_buttons.end(); it != end; ++it) if (it->second->isChecked()) { res.push_back(it->first); } return res; } void SigningProtocolSelectionWidget::setExclusive(bool exclusive) { if (exclusive == isExclusive()) { return; } m_buttonGroup->setExclusive(exclusive); Q_EMIT userSelectionChanged(); } QCheckBox *SigningProtocolSelectionWidget::button(Protocol p) const { const std::map::const_iterator it = m_buttons.find(p); return it == m_buttons.end() ? nullptr : it->second; } QLabel *ReadOnlyProtocolSelectionWidget::label(Protocol p) const { const std::map::const_iterator it = m_labels.find(p); return it == m_labels.end() ? nullptr : it->second; } bool SigningProtocolSelectionWidget::isExclusive() const { return m_buttonGroup->exclusive(); } void SigningProtocolSelectionWidget::setCertificate(Protocol prot, const Key &key) { QAbstractButton *const b = button(prot); Q_ASSERT(b); b->setText(formatLabel(prot, key)); } void ReadOnlyProtocolSelectionWidget::setCertificate(Protocol prot, const Key &key) { QLabel *const l = label(prot); l->setText(formatLabel(prot, key)); } namespace { class ValidatorImpl : public SignerResolvePage::Validator { public: QString explanation() const override { return QString(); } bool isComplete() const override { return true; } QString customWindowTitle() const override { return QString(); } }; } class SignerResolvePage::Private { friend class ::Kleo::Crypto::Gui::SignerResolvePage; SignerResolvePage *const q; public: explicit Private(SignerResolvePage *qq); ~Private(); void setOperation(Operation operation); void operationButtonClicked(int operation); void selectCertificates(); void setCertificates(const QMap &certs); void updateModeSelectionWidgets(); void updateUi(); bool protocolSelected(Protocol p) const; bool protocolSelectionActuallyUserMutable() const; private: QButtonGroup *signEncryptGroup; QRadioButton *signAndEncryptRB; QRadioButton *encryptOnlyRB; QRadioButton *signOnlyRB; QGroupBox *signingCertificateBox; QLabel *signerLabelLabel; QLabel *signerLabel; QGroupBox *encryptBox; QCheckBox *textArmorCO; QPushButton *selectCertificatesButton; SigningProtocolSelectionWidget *signingProtocolSelectionWidget; ReadOnlyProtocolSelectionWidget *readOnlyProtocolSelectionWidget; std::vector presetProtocols; bool signingMutable; bool encryptionMutable; bool signingSelected; bool encryptionSelected; bool multipleProtocolsAllowed; bool protocolSelectionUserMutable; QMap certificates; std::shared_ptr validator; std::shared_ptr signingPreferences; }; bool SignerResolvePage::Private::protocolSelectionActuallyUserMutable() const { return (q->protocolSelectionUserMutable() || presetProtocols.empty()) && q->operation() == SignOnly; } SignerResolvePage::Private::Private(SignerResolvePage *qq) : q(qq) , presetProtocols() , signingMutable(true) , encryptionMutable(true) , signingSelected(false) , encryptionSelected(false) , multipleProtocolsAllowed(false) , protocolSelectionUserMutable(true) , validator(new ValidatorImpl) { QVBoxLayout *layout = new QVBoxLayout(q); signEncryptGroup = new QButtonGroup(q); q->connect(signEncryptGroup, SIGNAL(buttonClicked(int)), q, SLOT(operationButtonClicked(int))); signAndEncryptRB = new QRadioButton; signAndEncryptRB->setText(i18n("Sign and encrypt (OpenPGP only)")); signAndEncryptRB->setChecked(true); signEncryptGroup->addButton(signAndEncryptRB, SignAndEncrypt); layout->addWidget(signAndEncryptRB); encryptOnlyRB = new QRadioButton; encryptOnlyRB->setText(i18n("Encrypt only")); signEncryptGroup->addButton(encryptOnlyRB, EncryptOnly); layout->addWidget(encryptOnlyRB); signOnlyRB = new QRadioButton; signOnlyRB->setText(i18n("Sign only")); signEncryptGroup->addButton(signOnlyRB, SignOnly); layout->addWidget(signOnlyRB); encryptBox = new QGroupBox; encryptBox->setTitle(i18n("Encryption Options")); QBoxLayout *const encryptLayout = new QVBoxLayout(encryptBox); textArmorCO = new QCheckBox; textArmorCO->setText(i18n("Text output (ASCII armor)")); encryptLayout->addWidget(textArmorCO); layout->addWidget(encryptBox); signingCertificateBox = new QGroupBox; signingCertificateBox->setTitle(i18n("Signing Options")); QGridLayout *signerLayout = new QGridLayout(signingCertificateBox); signerLayout->setColumnStretch(1, 1); signerLabelLabel = new QLabel; signerLabelLabel->setText(i18n("Signer:")); signerLayout->addWidget(signerLabelLabel, 1, 0); signerLabel = new QLabel; signerLayout->addWidget(signerLabel, 1, 1); signerLabelLabel->setVisible(false); signerLabel->setVisible(false); signingProtocolSelectionWidget = new SigningProtocolSelectionWidget; connect(signingProtocolSelectionWidget, SIGNAL(userSelectionChanged()), q, SLOT(updateUi())); signerLayout->addWidget(signingProtocolSelectionWidget, 2, 0, 1, -1); readOnlyProtocolSelectionWidget = new ReadOnlyProtocolSelectionWidget; signerLayout->addWidget(readOnlyProtocolSelectionWidget, 3, 0, 1, -1); selectCertificatesButton = new QPushButton; selectCertificatesButton->setText(i18n("Change Signing Certificates...")); selectCertificatesButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); signerLayout->addWidget(selectCertificatesButton, 4, 0, 1, -1, Qt::AlignLeft); q->connect(selectCertificatesButton, SIGNAL(clicked()), q, SLOT(selectCertificates())); layout->addWidget(signingCertificateBox); layout->addStretch(); } void SignerResolvePage::setValidator(const std::shared_ptr &validator) { Q_ASSERT(validator); d->validator = validator; d->updateUi(); } std::shared_ptr SignerResolvePage::validator() const { return d->validator; } SignerResolvePage::Private::~Private() {} bool SignerResolvePage::Private::protocolSelected(Protocol p) const { Q_ASSERT(p != UnknownProtocol); return signingProtocolSelectionWidget->isProtocolChecked(p); } void SignerResolvePage::Private::setCertificates(const QMap &certs) { certificates = certs; Q_FOREACH (const Protocol i, certs.keys()) { //krazy:exclude=foreach const Key key = certs.value(i); readOnlyProtocolSelectionWidget->setCertificate(i, key); signingProtocolSelectionWidget->setCertificate(i, key); } updateUi(); } void SignerResolvePage::Private::updateUi() { const bool ismutable = protocolSelectionActuallyUserMutable(); readOnlyProtocolSelectionWidget->setVisible(!ismutable); signingProtocolSelectionWidget->setVisible(ismutable); q->setExplanation(validator->explanation()); Q_EMIT q->completeChanged(); const QString customTitle = validator->customWindowTitle(); if (!customTitle.isEmpty()) { Q_EMIT q->windowTitleChanged(customTitle); } selectCertificatesButton->setEnabled(signingProtocolSelectionWidget->checkedProtocols().size() > 0); } void SignerResolvePage::setProtocolSelectionUserMutable(bool ismutable) { if (d->protocolSelectionUserMutable == ismutable) { return; } d->protocolSelectionUserMutable = ismutable; d->updateModeSelectionWidgets(); } bool SignerResolvePage::protocolSelectionUserMutable() const { return d->protocolSelectionUserMutable; } void SignerResolvePage::setMultipleProtocolsAllowed(bool allowed) { if (d->multipleProtocolsAllowed == allowed) { return; } d->multipleProtocolsAllowed = allowed; d->updateModeSelectionWidgets(); } bool SignerResolvePage::multipleProtocolsAllowed() const { return d->multipleProtocolsAllowed; } void SignerResolvePage::Private::updateModeSelectionWidgets() { const bool bothMutable = signingMutable && encryptionMutable; const bool noSigningPossible = !signingSelected && !signingMutable; const bool noEncryptionPossible = !encryptionSelected && !encryptionMutable; signAndEncryptRB->setChecked(signingSelected && encryptionSelected); signOnlyRB->setChecked(signingSelected && !encryptionSelected); encryptOnlyRB->setChecked(encryptionSelected && !signingSelected); const bool canSignAndEncrypt = !noSigningPossible && !noEncryptionPossible && bothMutable && presetProtocols != std::vector(1, CMS); const bool canSignOnly = !encryptionSelected || encryptionMutable; const bool canEncryptOnly = !signingSelected || signingMutable; signAndEncryptRB->setEnabled(canSignAndEncrypt); signOnlyRB->setEnabled(canSignOnly); encryptOnlyRB->setEnabled(canEncryptOnly); const bool buttonsVisible = signingMutable || encryptionMutable; signOnlyRB->setVisible(buttonsVisible); encryptOnlyRB->setVisible(buttonsVisible); signAndEncryptRB->setVisible(buttonsVisible); signingProtocolSelectionWidget->setExclusive(!multipleProtocolsAllowed); signingCertificateBox->setVisible(!noSigningPossible); encryptBox->setVisible(!noEncryptionPossible); updateUi(); } void SignerResolvePage::Private::selectCertificates() { QPointer dlg = new SigningCertificateSelectionDialog(q); dlg->setAllowedProtocols(QVector::fromStdVector(signingProtocolSelectionWidget->checkedProtocols())); if (dlg->exec() == QDialog::Accepted && dlg) { const QMap certs = dlg->selectedCertificates(); setCertificates(certs); if (signingPreferences && dlg->rememberAsDefault()) { signingPreferences->setPreferredCertificate(OpenPGP, certs.value(OpenPGP)); signingPreferences->setPreferredCertificate(CMS, certs.value(CMS)); } } delete dlg; updateUi(); } void SignerResolvePage::Private::operationButtonClicked(int mode_) { const Operation op = static_cast(mode_); signingCertificateBox->setEnabled(op != EncryptOnly); encryptBox->setEnabled(op != SignOnly); if (op == SignAndEncrypt) { signingProtocolSelectionWidget->setProtocolChecked(CMS, false); readOnlyProtocolSelectionWidget->setProtocolChecked(CMS, false); signingProtocolSelectionWidget->setProtocolChecked(OpenPGP, true); readOnlyProtocolSelectionWidget->setProtocolChecked(OpenPGP, true); } updateUi(); } void SignerResolvePage::Private::setOperation(Operation op) { switch (op) { case SignOnly: signOnlyRB->click(); break; case EncryptOnly: encryptOnlyRB->click(); break; case SignAndEncrypt: signAndEncryptRB->click(); break; } } SignerResolvePage::Operation SignerResolvePage::operation() const { return operationFromFlags(signingSelected(), encryptionSelected()); } SignerResolvePage::SignerResolvePage(QWidget *parent, Qt::WindowFlags f) : WizardPage(parent, f), d(new Private(this)) { setTitle(i18n("Choose Operation to be Performed")); // setSubTitle( i18n( "TODO" ) ); setPresetProtocol(UnknownProtocol); d->setCertificates(QMap()); d->updateModeSelectionWidgets(); d->operationButtonClicked(EncryptOnly); } SignerResolvePage::~SignerResolvePage() {} void SignerResolvePage::setSignersAndCandidates(const std::vector &signers, const std::vector< std::vector > &keys) { kleo_assert(signers.empty() || signers.size() == keys.size()); switch (signers.size()) { case 0: d->signerLabelLabel->setVisible(false); d->signerLabel->setVisible(false); // TODO: use default identity? break; case 1: d->signerLabelLabel->setVisible(true); d->signerLabel->setVisible(true); // TODO: use default identity? d->signerLabel->setText(signers.front().prettyAddress()); break; default: // > 1 kleo_assert(!"Resolving multiple signers not implemented"); } d->updateUi(); } void SignerResolvePage::setPresetProtocol(Protocol protocol) { std::vector protocols; if (protocol != CMS) { protocols.push_back(OpenPGP); } if (protocol != OpenPGP) { protocols.push_back(CMS); } setPresetProtocols(protocols); d->updateUi(); } void SignerResolvePage::setPresetProtocols(const std::vector &protocols) { d->presetProtocols = protocols; Q_FOREACH (const Protocol i, supportedProtocols()) { //krazy:exclude=foreach const bool checked = std::find(protocols.begin(), protocols.end(), i) != protocols.end(); d->signingProtocolSelectionWidget->setProtocolChecked(i, checked); d->readOnlyProtocolSelectionWidget->setProtocolChecked(i, checked); } d->updateModeSelectionWidgets(); } std::vector SignerResolvePage::selectedProtocols() const { return d->signingProtocolSelectionWidget->checkedProtocols(); } std::vector SignerResolvePage::signingCertificates(Protocol protocol) const { std::vector result; if (protocol != CMS && d->signingProtocolSelectionWidget->isProtocolChecked(OpenPGP) && !d->certificates[OpenPGP].isNull()) { result.push_back(d->certificates[OpenPGP]); } if (protocol != OpenPGP && d->signingProtocolSelectionWidget->isProtocolChecked(CMS) && !d->certificates[CMS].isNull()) { result.push_back(d->certificates[CMS]); } return result; } std::vector SignerResolvePage::resolvedSigners() const { std::vector result = signingCertificates(CMS); const std::vector pgp = signingCertificates(OpenPGP); result.insert(result.end(), pgp.begin(), pgp.end()); return result; } bool SignerResolvePage::isComplete() const { Q_ASSERT(d->validator); return d->validator->isComplete(); } bool SignerResolvePage::encryptionSelected() const { return !d->signOnlyRB->isChecked(); } void SignerResolvePage::setEncryptionSelected(bool selected) { d->encryptionSelected = selected; d->updateModeSelectionWidgets(); d->setOperation(operationFromFlags(d->signingSelected, d->encryptionSelected)); } bool SignerResolvePage::signingSelected() const { return !d->encryptOnlyRB->isChecked(); } void SignerResolvePage::setSigningSelected(bool selected) { d->signingSelected = selected; d->updateModeSelectionWidgets(); d->setOperation(operationFromFlags(d->signingSelected, d->encryptionSelected)); } bool SignerResolvePage::isEncryptionUserMutable() const { return d->encryptionMutable; } bool SignerResolvePage::isSigningUserMutable() const { return d->signingMutable; } void SignerResolvePage::setEncryptionUserMutable(bool ismutable) { d->encryptionMutable = ismutable; d->updateModeSelectionWidgets(); } void SignerResolvePage::setSigningUserMutable(bool ismutable) { d->signingMutable = ismutable; d->updateModeSelectionWidgets(); } std::vector SignerResolvePage::selectedProtocolsWithoutSigningCertificate() const { std::vector res; Q_FOREACH (const Protocol i, selectedProtocols()) //krazy:exclude=foreach if (signingCertificates(i).empty()) { res.push_back(i); } return res; } bool SignerResolvePage::isAsciiArmorEnabled() const { return d->textArmorCO->isChecked(); } void SignerResolvePage::setAsciiArmorEnabled(bool enabled) { d->textArmorCO->setChecked(enabled); } void SignerResolvePage::setSigningPreferences(const std::shared_ptr &prefs) { d->signingPreferences = prefs; QMap map; map[OpenPGP] = prefs ? prefs->preferredCertificate(OpenPGP) : Key(); map[CMS] = prefs ? prefs->preferredCertificate(CMS) : Key(); d->setCertificates(map); } std::shared_ptr SignerResolvePage::signingPreferences() const { return d->signingPreferences; } void SignerResolvePage::onNext() { } #include "moc_signerresolvepage.cpp" #include "moc_signerresolvepage_p.cpp" diff --git a/src/crypto/gui/signingcertificateselectiondialog.h b/src/crypto/gui/signingcertificateselectiondialog.h index 9c7523f44..fdc2e1e41 100644 --- a/src/crypto/gui/signingcertificateselectiondialog.h +++ b/src/crypto/gui/signingcertificateselectiondialog.h @@ -1,51 +1,51 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/gui/signingcertificateselectiondialog.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef __KLEOPATRA_CRYPTO_GUI_SIGNINGCERTIFICATESELECTIONDIALOG_H__ #define __KLEOPATRA_CRYPTO_GUI_SIGNINGCERTIFICATESELECTIONDIALOG_H__ #include #include #include template class QMap; namespace Kleo { namespace Crypto { namespace Gui { class SigningCertificateSelectionWidget; class SigningCertificateSelectionDialog : public QDialog { Q_OBJECT public: explicit SigningCertificateSelectionDialog(QWidget *parent = nullptr); ~SigningCertificateSelectionDialog(); void setAllowedProtocols(const QVector &allowedProtocols); void setSelectedCertificates(const QMap &certificates); - QMap selectedCertificates() const; + Q_REQUIRED_RESULT QMap selectedCertificates() const; - bool rememberAsDefault() const; + Q_REQUIRED_RESULT bool rememberAsDefault() const; private: - SigningCertificateSelectionWidget *widget; + SigningCertificateSelectionWidget *const widget; }; } } } #endif // __KLEOPATRA_CRYPTO_GUI_SIGNINGCERTIFICATESELECTIONDIALOG_H__ diff --git a/src/crypto/gui/wizard.cpp b/src/crypto/gui/wizard.cpp index 341e6e876..10273b258 100644 --- a/src/crypto/gui/wizard.cpp +++ b/src/crypto/gui/wizard.cpp @@ -1,347 +1,347 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/gui/wizard.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 "wizard.h" #include "wizardpage.h" #include #include #include #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" #include #include using namespace Kleo::Crypto::Gui; class Wizard::Private { friend class ::Wizard; Wizard *const q; public: explicit Private(Wizard *qq); ~Private(); void updateButtonStates(); bool isLastPage(int id) const; int previousPage() const; void updateHeader(); private: std::vector pageOrder; std::set hiddenPages; std::map idToPage; - int currentId; - QStackedWidget *stack; - QPushButton *nextButton; - QPushButton *backButton; - QPushButton *cancelButton; + int currentId = -1; + QStackedWidget *const stack; + QPushButton *nextButton = nullptr; + QPushButton *backButton = nullptr; + QPushButton *cancelButton = nullptr; KGuiItem finishItem; KGuiItem nextItem; - QFrame *titleFrame; - QLabel *titleLabel; - QLabel *subTitleLabel; - QFrame *explanationFrame; - QLabel *explanationLabel; - QTimer *nextPageTimer; + QFrame *titleFrame = nullptr; + QLabel *titleLabel = nullptr; + QLabel *subTitleLabel = nullptr; + QFrame *explanationFrame = nullptr; + QLabel *explanationLabel = nullptr; + QTimer *nextPageTimer = nullptr; }; Wizard::Private::Private(Wizard *qq) - : q(qq), currentId(-1), stack(new QStackedWidget) + : q(qq), stack(new QStackedWidget) { nextPageTimer = new QTimer(q); nextPageTimer->setInterval(0); connect(nextPageTimer, &QTimer::timeout, q, &Wizard::next); nextItem = KGuiItem(i18n("&Next")); finishItem = KStandardGuiItem::ok(); QVBoxLayout *const top = new QVBoxLayout(q); top->setContentsMargins(0, 0, 0, 0); titleFrame = new QFrame; titleFrame->setFrameShape(QFrame::StyledPanel); titleFrame->setAutoFillBackground(true); titleFrame->setBackgroundRole(QPalette::Base); QVBoxLayout *const titleLayout = new QVBoxLayout(titleFrame); titleLabel = new QLabel; titleLayout->addWidget(titleLabel); subTitleLabel = new QLabel; subTitleLabel->setWordWrap(true); titleLayout->addWidget(subTitleLabel); top->addWidget(titleFrame); titleFrame->setVisible(false); top->addWidget(stack); explanationFrame = new QFrame; explanationFrame->setFrameShape(QFrame::StyledPanel); explanationFrame->setAutoFillBackground(true); explanationFrame->setBackgroundRole(QPalette::Base); QVBoxLayout *const explanationLayout = new QVBoxLayout(explanationFrame); explanationLabel = new QLabel; explanationLabel->setWordWrap(true); explanationLayout->addWidget(explanationLabel); top->addWidget(explanationFrame); explanationFrame->setVisible(false); QWidget *buttonWidget = new QWidget; QHBoxLayout *buttonLayout = new QHBoxLayout(buttonWidget); QDialogButtonBox *const box = new QDialogButtonBox; cancelButton = box->addButton(QDialogButtonBox::Cancel); q->connect(cancelButton, &QPushButton::clicked, q, &Wizard::reject); backButton = new QPushButton; backButton->setText(i18n("Back")); q->connect(backButton, &QPushButton::clicked, q, &Wizard::back); box->addButton(backButton, QDialogButtonBox::ActionRole); nextButton = new QPushButton; KGuiItem::assign(nextButton, nextItem); q->connect(nextButton, &QPushButton::clicked, q, &Wizard::next); box->addButton(nextButton, QDialogButtonBox::ActionRole); buttonLayout->addWidget(box); top->addWidget(buttonWidget); q->connect(q, &Wizard::rejected, q, &Wizard::canceled); } Wizard::Private::~Private() { qCDebug(KLEOPATRA_LOG); } bool Wizard::Private::isLastPage(int id) const { return !pageOrder.empty() ? pageOrder.back() == id : false; } void Wizard::Private::updateButtonStates() { const bool isLast = isLastPage(currentId); const bool canGoToNext = q->canGoToNextPage(); WizardPage *const page = q->page(currentId); const KGuiItem customNext = page ? page->customNextButton() : KGuiItem(); if (customNext.text().isEmpty() && customNext.icon().isNull()) { KGuiItem::assign(nextButton, isLast ? finishItem : nextItem); } else { KGuiItem::assign(nextButton, customNext); } nextButton->setEnabled(canGoToNext); cancelButton->setEnabled(!isLast || !canGoToNext); backButton->setEnabled(q->canGoToPreviousPage()); if (page && page->autoAdvance() && page->isComplete()) { nextPageTimer->start(); } } void Wizard::Private::updateHeader() { WizardPage *const widget = q->page(currentId); Q_ASSERT(!widget || stack->indexOf(widget) != -1); if (widget) { stack->setCurrentWidget(widget); } const QString title = widget ? widget->title() : QString(); const QString subTitle = widget ? widget->subTitle() : QString(); const QString explanation = widget ? widget->explanation() : QString(); titleFrame->setVisible(!title.isEmpty() || !subTitle.isEmpty() || !explanation.isEmpty()); titleLabel->setVisible(!title.isEmpty()); titleLabel->setText(title); subTitleLabel->setText(subTitle); subTitleLabel->setVisible(!subTitle.isEmpty()); explanationFrame->setVisible(!explanation.isEmpty()); explanationLabel->setVisible(!explanation.isEmpty()); explanationLabel->setText(explanation); q->resize(q->sizeHint().expandedTo(q->size())); } Wizard::Wizard(QWidget *parent, Qt::WindowFlags f) : QDialog(parent, f), d(new Private(this)) { } Wizard::~Wizard() { qCDebug(KLEOPATRA_LOG); } void Wizard::setPage(int id, WizardPage *widget) { kleo_assert(id != InvalidPage); kleo_assert(d->idToPage.find(id) == d->idToPage.end()); d->idToPage[id] = widget; d->stack->addWidget(widget); connect(widget, SIGNAL(completeChanged()), this, SLOT(updateButtonStates())); connect(widget, SIGNAL(titleChanged()), this, SLOT(updateHeader())); connect(widget, SIGNAL(subTitleChanged()), this, SLOT(updateHeader())); connect(widget, SIGNAL(explanationChanged()), this, SLOT(updateHeader())); connect(widget, SIGNAL(autoAdvanceChanged()), this, SLOT(updateButtonStates())); connect(widget, SIGNAL(windowTitleChanged(QString)), this, SLOT(setWindowTitle(QString))); } void Wizard::setPageOrder(const std::vector &pageOrder) { d->pageOrder = pageOrder; d->hiddenPages.clear(); if (pageOrder.empty()) { return; } setCurrentPage(pageOrder.front()); } void Wizard::setCurrentPage(int id) { d->currentId = id; if (id == InvalidPage) { return; } d->updateHeader(); d->updateButtonStates(); } void Wizard::setPageVisible(int id, bool visible) { if (visible) { d->hiddenPages.erase(id); } else { d->hiddenPages.insert(id); } if (currentPage() == id && !visible) { next(); } } int Wizard::currentPage() const { return d->currentId; } bool Wizard::canGoToNextPage() const { const WizardPage *const current = currentPageWidget(); return current ? current->isComplete() : false; } bool Wizard::canGoToPreviousPage() const { const int prev = d->previousPage(); if (prev == InvalidPage) { return false; } const WizardPage *const prevPage = page(prev); Q_ASSERT(prevPage); return !prevPage->isCommitPage(); } void Wizard::next() { WizardPage *const current = currentPageWidget(); if (current) { current->onNext(); } onNext(d->currentId); std::vector::const_iterator it = qBinaryFind(d->pageOrder.begin(), d->pageOrder.end(), d->currentId); Q_ASSERT(it != d->pageOrder.end()); do { ++it; } while (d->hiddenPages.find(*it) != d->hiddenPages.end()); if (it == d->pageOrder.end()) { // "Finish" d->currentId = InvalidPage; close(); } else { // "next" setCurrentPage(*it); } } int Wizard::Private::previousPage() const { if (pageOrder.empty()) { return InvalidPage; } std::vector::const_iterator it = qBinaryFind(pageOrder.begin(), pageOrder.end(), currentId); if (it == pageOrder.begin() || it == pageOrder.end()) { return InvalidPage; } do { --it; } while (it != pageOrder.begin() && hiddenPages.find(*it) != hiddenPages.end()); return *it; } void Wizard::back() { onBack(d->currentId); const int prev = d->previousPage(); if (prev == InvalidPage) { return; } setCurrentPage(prev); } const WizardPage *Wizard::page(int id) const { if (id == InvalidPage) { return nullptr; } const std::map::const_iterator it = d->idToPage.find(id); kleo_assert(it != d->idToPage.end()); return (*it).second; } const WizardPage *Wizard::currentPageWidget() const { return page(d->currentId); } WizardPage *Wizard::currentPageWidget() { return page(d->currentId); } void Wizard::onNext(int currentId) { Q_UNUSED(currentId) } void Wizard::onBack(int currentId) { Q_UNUSED(currentId) } WizardPage *Wizard::page(int id) { if (id == InvalidPage) { return nullptr; } const std::map::const_iterator it = d->idToPage.find(id); kleo_assert(it != d->idToPage.end()); return (*it).second; } #include "moc_wizard.cpp" diff --git a/src/crypto/gui/wizardpage.cpp b/src/crypto/gui/wizardpage.cpp index 2616b47e4..a10972115 100644 --- a/src/crypto/gui/wizardpage.cpp +++ b/src/crypto/gui/wizardpage.cpp @@ -1,127 +1,127 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/gui/wizardpage.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 "wizardpage.h" #include using namespace Kleo::Crypto::Gui; class WizardPage::Private { friend class ::WizardPage; WizardPage *const q; public: explicit Private(WizardPage *qq); ~Private(); private: - bool commitPage; - bool autoAdvance; + bool commitPage = false; + bool autoAdvance = false; QString title; QString subTitle; QString explanation; KGuiItem customNextButton; }; WizardPage::Private::Private(WizardPage *qq) - : q(qq), commitPage(false), autoAdvance(false) + : q(qq) { } WizardPage::Private::~Private() {} WizardPage::WizardPage(QWidget *parent, Qt::WindowFlags f) : QWidget(parent, f), d(new Private(this)) { } bool WizardPage::isCommitPage() const { return d->commitPage; } void WizardPage::setCommitPage(bool commitPage) { d->commitPage = commitPage; } bool WizardPage::autoAdvance() const { return d->autoAdvance; } void WizardPage::setAutoAdvance(bool enabled) { if (d->autoAdvance == enabled) { return; } d->autoAdvance = enabled; Q_EMIT autoAdvanceChanged(); } QString WizardPage::title() const { return d->title; } void WizardPage::setTitle(const QString &title) { if (d->title == title) { return; } d->title = title; Q_EMIT titleChanged(); } QString WizardPage::subTitle() const { return d->subTitle; } void WizardPage::setSubTitle(const QString &subTitle) { if (d->subTitle == subTitle) { return; } d->subTitle = subTitle; Q_EMIT subTitleChanged(); } QString WizardPage::explanation() const { return d->explanation; } void WizardPage::setExplanation(const QString &explanation) { if (d->explanation == explanation) { return; } d->explanation = explanation; Q_EMIT explanationChanged(); } KGuiItem WizardPage::customNextButton() const { return d->customNextButton; } void WizardPage::setCustomNextButton(const KGuiItem &item) { d->customNextButton = item; } WizardPage::~WizardPage() {} void WizardPage::onNext() {} diff --git a/src/crypto/task.cpp b/src/crypto/task.cpp index 412cef804..2fff31d15 100644 --- a/src/crypto/task.cpp +++ b/src/crypto/task.cpp @@ -1,236 +1,236 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/task.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 "task.h" #include "task_p.h" #include "kleopatra_debug.h" #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Crypto; using namespace GpgME; namespace { class ErrorResult : public Task::Result { public: ErrorResult(int code, const QString &details) : Task::Result(), m_code(code), m_details(details) {} QString overview() const override { return makeOverview(m_details); } QString details() const override { return QString(); } int errorCode() const override { return m_code; } QString errorString() const override { return m_details; } VisualCode code() const override { return NeutralError; } AuditLog auditLog() const override { return AuditLog(); } private: - int m_code; - QString m_details; + const int m_code; + const QString m_details; }; } class Task::Private { friend class ::Kleo::Crypto::Task; Task *const q; public: explicit Private(Task *qq); private: QString m_progressLabel; int m_progress; int m_totalProgress; bool m_asciiArmor; int m_id; }; namespace { static int nextTaskId = 0; } Task::Private::Private(Task *qq) : q(qq), m_progressLabel(), m_progress(0), m_totalProgress(0), m_asciiArmor(false), m_id(nextTaskId++) { } Task::Task(QObject *p) : QObject(p), d(new Private(this)) { } Task::~Task() {} void Task::setAsciiArmor(bool armor) { d->m_asciiArmor = armor; } bool Task::asciiArmor() const { return d->m_asciiArmor; } std::shared_ptr Task::makeErrorTask(int code, const QString &details, const QString &label) { const std::shared_ptr t(new SimpleTask(label)); t->setResult(t->makeErrorResult(code, details)); return t; } int Task::id() const { return d->m_id; } int Task::currentProgress() const { return d->m_progress; } int Task::totalProgress() const { return d->m_totalProgress; } QString Task::tag() const { return QString(); } QString Task::progressLabel() const { return d->m_progressLabel; } void Task::setProgress(const QString &label, int processed, int total) { d->m_progress = processed; d->m_totalProgress = total; d->m_progressLabel = label; Q_EMIT progress(label, processed, total, QPrivateSignal()); } void Task::start() { try { doStart(); } catch (const Kleo::Exception &e) { QMetaObject::invokeMethod(this, "emitError", Qt::QueuedConnection, Q_ARG(int, e.error().encodedError()), Q_ARG(QString, e.message())); } catch (const GpgME::Exception &e) { QMetaObject::invokeMethod(this, "emitError", Qt::QueuedConnection, Q_ARG(int, e.error().encodedError()), Q_ARG(QString, QString::fromLocal8Bit(e.what()))); } catch (const std::exception &e) { QMetaObject::invokeMethod(this, "emitError", Qt::QueuedConnection, Q_ARG(int, makeGnuPGError(GPG_ERR_UNEXPECTED)), Q_ARG(QString, QString::fromLocal8Bit(e.what()))); } catch (...) { QMetaObject::invokeMethod(this, "emitError", Qt::QueuedConnection, Q_ARG(int, makeGnuPGError(GPG_ERR_UNEXPECTED)), Q_ARG(QString, i18n("Unknown exception in Task::start()"))); } Q_EMIT started(QPrivateSignal()); } void Task::emitError(int errCode, const QString &details) { emitResult(makeErrorResult(errCode, details)); } void Task::emitResult(const std::shared_ptr &r) { d->m_progress = d->m_totalProgress; Q_EMIT progress(progressLabel(), currentProgress(), totalProgress(), QPrivateSignal()); Q_EMIT result(r, QPrivateSignal()); } std::shared_ptr Task::makeErrorResult(int errCode, const QString &details) { return std::shared_ptr(new ErrorResult(errCode, details)); } class Task::Result::Private { public: Private() {} }; Task::Result::Result() : d(new Private()) {} Task::Result::~Result() {} bool Task::Result::hasError() const { return errorCode() != 0; } static QString image(const char *img) { // ### escape? return KIconLoader::global()->iconPath(QLatin1String(img), KIconLoader::Small); } QString Task::Result::makeOverview(const QString &msg) { return QLatin1String("") + msg + QLatin1String(""); } QString Task::Result::iconPath(VisualCode code) { switch (code) { case Danger: return image("dialog-error"); case AllGood: return image("dialog-ok"); case Warning: return image("dialog-warning"); case NeutralError: case NeutralSuccess: default: return QString(); } } QString Task::Result::icon() const { return iconPath(code()); } #include "moc_task_p.cpp" diff --git a/src/dialogs/addemaildialog.cpp b/src/dialogs/addemaildialog.cpp index fb6a16c74..f1d1c2106 100644 --- a/src/dialogs/addemaildialog.cpp +++ b/src/dialogs/addemaildialog.cpp @@ -1,104 +1,103 @@ /* -*- mode: c++; c-basic-offset:4 -*- dialogs/addemaildialog.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2019 g 10 Code GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "addemaildialog.h" #include #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" using namespace Kleo; using namespace Kleo::Dialogs; class AddEmailDialog::Private { public: Private(AddEmailDialog *qq): - q(qq), - mAdvancedSelected(false) + q(qq) { auto mainLay = new QVBoxLayout(q); auto btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); mOkButton = btnBox->button(QDialogButtonBox::Ok); QObject::connect (btnBox, &QDialogButtonBox::accepted, q, [this] () { q->accept(); }); QObject::connect (btnBox, &QDialogButtonBox::rejected, q, &QDialog::reject); btnBox->addButton(i18n("Advanced"), QDialogButtonBox::HelpRole); QObject::connect (btnBox, &QDialogButtonBox::helpRequested, q, [this] () { mAdvancedSelected = true; q->accept(); }); mainLay->addStretch(-1); auto emailLay = new QHBoxLayout; auto emailLbl = new QLabel(i18n("EMail") + QLatin1Char(':')); mEmailEdit = new QLineEdit(q); mEmailEdit->setValidator(Validation::email(mEmailEdit)); connect(mEmailEdit, &QLineEdit::textChanged, q, [this] () { mOkButton->setEnabled(!mEmailEdit->text().isEmpty() && mEmailEdit->hasAcceptableInput()); }); emailLbl->setBuddy(mEmailEdit); emailLay->addWidget(emailLbl); emailLay->addWidget(mEmailEdit); mainLay->addLayout(emailLay); mainLay->addWidget(btnBox); mOkButton->setEnabled(!mEmailEdit->text().isEmpty() && mEmailEdit->hasAcceptableInput()); } AddEmailDialog *const q; - QPushButton *mOkButton; - QLineEdit *mEmailEdit; - bool mAdvancedSelected; + QPushButton *mOkButton = nullptr; + QLineEdit *mEmailEdit = nullptr; + bool mAdvancedSelected = false; }; AddEmailDialog::AddEmailDialog(QWidget *parent): QDialog(parent), d(new Private(this)) { setWindowTitle(i18nc("@title:window", "Add New EMail")); } AddEmailDialog::~AddEmailDialog() { } void AddEmailDialog::setEmail(const QString &email) { return d->mEmailEdit->setText(email); } QString AddEmailDialog::email() const { return d->mEmailEdit->text().trimmed(); } bool AddEmailDialog::advancedSelected() { return d->mAdvancedSelected; } diff --git a/src/dialogs/adduseriddialog.cpp b/src/dialogs/adduseriddialog.cpp index 0550281e5..3dbc16c1a 100644 --- a/src/dialogs/adduseriddialog.cpp +++ b/src/dialogs/adduseriddialog.cpp @@ -1,336 +1,336 @@ /* -*- mode: c++; c-basic-offset:4 -*- dialogs/adduseriddialog.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 "adduseriddialog.h" #include "ui_adduseriddialog.h" #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" #include using namespace Kleo; using namespace Kleo::Dialogs; namespace { struct Line { QString attr; QString label; QString regex; - QLineEdit *edit; + QLineEdit *edit = nullptr; }; } static QString pgpLabel(const QString &attr) { if (attr == QLatin1String("NAME")) { return i18n("Name"); } else if (attr == QLatin1String("COMMENT")) { return i18n("Comment"); } else if (attr == QLatin1String("EMAIL")) { return i18n("EMail"); } return QString(); } static QString attributeLabel(const QString &attr, bool pgp) { if (attr.isEmpty()) { return QString(); } const QString label = /*pgp ?*/ pgpLabel(attr) /*: Kleo::DNAttributeMapper::instance()->name2label( attr )*/; if (!label.isEmpty()) if (pgp) { return label; } else return i18nc("Format string for the labels in the \"Your Personal Data\" page", "%1 (%2)", label, attr); else { return attr; } } static QString attributeFromKey(QString key) { return key.remove(QLatin1Char('!')); } static int row_index_of(QWidget *w, QGridLayout *l) { const int idx = l->indexOf(w); int r, c, rs, cs; l->getItemPosition(idx, &r, &c, &rs, &cs); return r; } static QLineEdit *adjust_row(QGridLayout *l, int row, const QString &label, const QString &preset, QValidator *validator, bool readonly, bool required) { Q_ASSERT(l); Q_ASSERT(row >= 0); Q_ASSERT(row < l->rowCount()); QLabel *lb = qobject_cast(l->itemAtPosition(row, 0)->widget()); Q_ASSERT(lb); QLineEdit *le = qobject_cast(l->itemAtPosition(row, 1)->widget()); Q_ASSERT(le); QLabel *reqLB = qobject_cast(l->itemAtPosition(row, 2)->widget()); Q_ASSERT(reqLB); lb->setText(i18nc("interpunctation for labels", "%1:", label)); le->setText(preset); reqLB->setText(required ? i18n("(required)") : i18n("(optional)")); delete le->validator(); if (validator) { if (!validator->parent()) { validator->setParent(le); } le->setValidator(validator); } le->setReadOnly(readonly && le->hasAcceptableInput()); lb->show(); le->show(); reqLB->show(); return le; } class AddUserIDDialog::Private { friend class ::Kleo::Dialogs::AddUserIDDialog; AddUserIDDialog *const q; public: explicit Private(AddUserIDDialog *qq) : q(qq), ui(q) { } private: void slotUserIDChanged(); private: bool isComplete() const; private: struct UI : public Ui_AddUserIDDialog { QVector lineList; explicit UI(AddUserIDDialog *qq) : Ui_AddUserIDDialog() { setupUi(qq); // ### this code is mostly the same as the one in // ### newcertificatewizard. Find some time to factor them // ### into a single copy. // hide the stuff nameLB->hide(); nameLE->hide(); nameRequiredLB->hide(); emailLB->hide(); emailLE->hide(); emailRequiredLB->hide(); commentLB->hide(); commentLE->hide(); commentRequiredLB->hide(); // set errorLB to have a fixed height of two lines: errorLB->setText(QStringLiteral("2
1")); errorLB->setFixedHeight(errorLB->minimumSizeHint().height()); errorLB->clear(); const KConfigGroup config(KSharedConfig::openConfig(), "CertificateCreationWizard"); const QStringList attrOrder = config.readEntry("OpenPGPAttributeOrder", QStringList() << QStringLiteral("NAME") << QStringLiteral("EMAIL") << QStringLiteral("COMMENT")); QMap lines; for (const QString &rawKey : attrOrder) { const QString key = rawKey.trimmed().toUpper(); const QString attr = attributeFromKey(key); if (attr.isEmpty()) { continue; } const QString preset = config.readEntry(attr); const bool required = key.endsWith(QLatin1Char('!')); const bool readonly = config.isEntryImmutable(attr); const QString label = config.readEntry(attr + QLatin1String("_label"), attributeLabel(attr, true)); const QString regex = config.readEntry(attr + QLatin1String("_regex")); int row; QValidator *validator = nullptr; if (attr == QLatin1String("EMAIL")) { validator = regex.isEmpty() ? Validation::email() : Validation::email(QRegExp(regex)); row = row_index_of(emailLE, gridLayout); } else if (attr == QLatin1String("NAME")) { validator = regex.isEmpty() ? Validation::pgpName() : Validation::pgpName(QRegExp(regex)); row = row_index_of(nameLE, gridLayout); } else if (attr == QLatin1String("COMMENT")) { validator = regex.isEmpty() ? Validation::pgpComment() : Validation::pgpComment(QRegExp(regex)); row = row_index_of(commentLE, gridLayout); } else { continue; } QLineEdit *le = adjust_row(gridLayout, row, label, preset, validator, readonly, required); const Line line = { key, label, regex, le }; lines[row] = line; } std::copy(lines.begin(), lines.end(), std::back_inserter(lineList)); } QPushButton *okPB() const { return buttonBox->button(QDialogButtonBox::Ok); } } ui; }; AddUserIDDialog::AddUserIDDialog(QWidget *p) : QDialog(p), d(new Private(this)) { } AddUserIDDialog::~AddUserIDDialog() {} void AddUserIDDialog::setName(const QString &name) { d->ui.nameLE->setText(name); } QString AddUserIDDialog::name() const { return d->ui.nameLE->text().trimmed(); } void AddUserIDDialog::setEmail(const QString &email) { d->ui.emailLE->setText(email); } QString AddUserIDDialog::email() const { return d->ui.emailLE->text().trimmed(); } void AddUserIDDialog::setComment(const QString &comment) { d->ui.commentLE->setText(comment); } QString AddUserIDDialog::comment() const { return d->ui.commentLE->text().trimmed(); } static bool has_intermediate_input(const QLineEdit *le) { QString text = le->text(); int pos = le->cursorPosition(); const QValidator *const v = le->validator(); return !v || v->validate(text, pos) == QValidator::Intermediate; } static bool requirementsAreMet(const QVector &list, QString &error) { bool allEmpty = true; for (const Line &line : list) { const QLineEdit *le = line.edit; if (!le) { continue; } const QString key = line.attr; qCDebug(KLEOPATRA_LOG) << "requirementsAreMet(): checking \"" << key << "\" against \"" << le->text() << "\":"; if (le->text().trimmed().isEmpty()) { if (key.endsWith(QLatin1Char('!'))) { if (line.regex.isEmpty()) { error = xi18nc("@info", "%1 is required, but empty.", line.label); } else error = xi18nc("@info", "%1 is required, but empty." "Local Admin rule: %2", line.label, line.regex); return false; } } else if (has_intermediate_input(le)) { if (line.regex.isEmpty()) { error = xi18nc("@info", "%1 is incomplete.", line.label); } else error = xi18nc("@info", "%1 is incomplete." "Local Admin rule: %2", line.label, line.regex); return false; } else if (!le->hasAcceptableInput()) { if (line.regex.isEmpty()) { error = xi18nc("@info", "%1 is invalid.", line.label); } else error = xi18nc("@info", "%1 is invalid." "Local Admin rule: %2", line.label, line.regex); return false; } else { allEmpty = false; } } return !allEmpty; } bool AddUserIDDialog::Private::isComplete() const { QString error; const bool ok = requirementsAreMet(ui.lineList, error); ui.errorLB->setText(error); return ok; } void AddUserIDDialog::Private::slotUserIDChanged() { ui.okPB()->setEnabled(isComplete()); const QString name = q->name(); const QString email = q->email(); const QString comment = q->comment(); QStringList parts; if (!name.isEmpty()) { parts.push_back(name); } if (!comment.isEmpty()) { parts.push_back(QLatin1Char('(') + comment + QLatin1Char(')')); } if (!email.isEmpty()) { parts.push_back(QLatin1Char('<') + email + QLatin1Char('>')); } ui.resultLB->setText(parts.join(QLatin1Char(' '))); } #include "moc_adduseriddialog.cpp" diff --git a/src/uiserver/echocommand.cpp b/src/uiserver/echocommand.cpp index bc4bb678c..df6deb3e7 100644 --- a/src/uiserver/echocommand.cpp +++ b/src/uiserver/echocommand.cpp @@ -1,193 +1,191 @@ /* -*- mode: c++; c-basic-offset:4 -*- uiserver/echocommand.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 "echocommand.h" #include #include #include #include #include #include #include #include #include #include using namespace Kleo; static const char option_prefix[] = "prefix"; class EchoCommand::Private { public: - Private() : operationsInFlight(0), buffer() {} - - int operationsInFlight; + int operationsInFlight = 0; QByteArray buffer; }; EchoCommand::EchoCommand() : QObject(), AssuanCommandMixin(), d(new Private) {} EchoCommand::~EchoCommand() {} int EchoCommand::doStart() { const std::vector< std::shared_ptr > in = inputs(), msg = messages(); const std::vector< std::shared_ptr > out = outputs(); if (!in.empty() && out.empty()) { return makeError(GPG_ERR_NOT_SUPPORTED); } if (!msg.empty()) { return makeError(GPG_ERR_NOT_SUPPORTED); } if (hasOption(option_prefix) && !option(option_prefix).toByteArray().isEmpty()) { return makeError(GPG_ERR_NOT_IMPLEMENTED); } std::string keyword; if (hasOption("inquire")) { keyword = option("inquire").toString().toStdString(); if (keyword.empty()) { return makeError(GPG_ERR_INV_ARG); } } const std::string output = option("text").toString().toStdString(); // aaand ACTION: // 1. echo the command line though the status channel sendStatus("ECHO", output.empty() ? QString() : QLatin1String(output.c_str())); // 2. if --inquire was given, inquire more data from the client: if (!keyword.empty()) { if (const int err = inquire(keyword.c_str(), this, SLOT(slotInquireData(int,QByteArray)))) { return err; } else { ++d->operationsInFlight; } } // 3. if INPUT was given, start the data pump for input->output if (const std::shared_ptr i = in.at(0)->ioDevice()) { const std::shared_ptr o = out.at(0)->ioDevice(); ++d->operationsInFlight; connect(i.get(), &QIODevice::readyRead, this, &EchoCommand::slotInputReadyRead); connect(o.get(), &QIODevice::bytesWritten, this, &EchoCommand::slotOutputBytesWritten); if (i->bytesAvailable()) { slotInputReadyRead(); } } if (!d->operationsInFlight) { done(); } return 0; } void EchoCommand::doCanceled() { } void EchoCommand::slotInquireData(int rc, const QByteArray &data) { --d->operationsInFlight; if (rc) { done(rc); return; } try { sendStatus("ECHOINQ", QLatin1String(data)); if (!d->operationsInFlight) { done(); } } catch (const Exception &e) { done(e.error(), e.message()); } catch (const std::exception &e) { done(makeError(GPG_ERR_UNEXPECTED), i18n("Caught unexpected exception in SignCommand::Private::slotMicAlgDetermined: %1", QString::fromLocal8Bit(e.what()))); } catch (...) { done(makeError(GPG_ERR_UNEXPECTED), i18n("Caught unknown exception in SignCommand::Private::slotMicAlgDetermined")); } } void EchoCommand::slotInputReadyRead() { const std::shared_ptr in = inputs().at(0)->ioDevice(); Q_ASSERT(in); QByteArray buffer; buffer.resize(in->bytesAvailable()); const qint64 read = in->read(buffer.data(), buffer.size()); if (read == - 1) { done(makeError(GPG_ERR_EIO)); return; } if (read == 0 || (!in->isSequential() && read == in->size())) { in->close(); } buffer.resize(read); d->buffer += buffer; slotOutputBytesWritten(); } void EchoCommand::slotOutputBytesWritten() { const std::shared_ptr out = outputs().at(0)->ioDevice(); Q_ASSERT(out); if (!d->buffer.isEmpty()) { if (out->bytesToWrite()) { return; } const qint64 written = out->write(d->buffer); if (written == -1) { done(makeError(GPG_ERR_EIO)); return; } d->buffer.remove(0, written); } if (out->isOpen() && d->buffer.isEmpty() && !inputs().at(0)->ioDevice()->isOpen()) { out->close(); if (!--d->operationsInFlight) { done(); } } }