diff --git a/src/crypto/signencrypttask.cpp b/src/crypto/signencrypttask.cpp index dd7a3ba3..449d1c34 100644 --- a/src/crypto/signencrypttask.cpp +++ b/src/crypto/signencrypttask.cpp @@ -1,698 +1,708 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/signencrypttask.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2007 Klarälvdalens Datakonsult AB Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include #include "signencrypttask.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" #include #include // for Qt::escape using namespace Kleo; using namespace Kleo::Crypto; using namespace GpgME; namespace { QString formatInputOutputLabel(const QString &input, const QString &output, bool outputDeleted) { return i18nc("Input file --> Output file (rarr is arrow", "%1 → %2", input.toHtmlEscaped(), outputDeleted ? QStringLiteral("%1").arg(output.toHtmlEscaped()) : output.toHtmlEscaped()); } class ErrorResult : public Task::Result { public: ErrorResult(bool sign, bool encrypt, const Error &err, const QString &errStr, const QString &input, const QString &output, const AuditLog &auditLog) : Task::Result(), m_sign(sign), m_encrypt(encrypt), m_error(err), m_errString(errStr), m_inputLabel(input), m_outputLabel(output), m_auditLog(auditLog) {} QString overview() const override; QString details() const override; int errorCode() const override { return m_error.encodedError(); } QString errorString() const override { return m_errString; } VisualCode code() const override { return NeutralError; } AuditLog auditLog() const override { return m_auditLog; } private: const bool m_sign; const bool m_encrypt; const Error m_error; const QString m_errString; const QString m_inputLabel; const QString m_outputLabel; const AuditLog m_auditLog; }; class SignEncryptFilesResult : public Task::Result { public: SignEncryptFilesResult(const SigningResult &sr, const std::shared_ptr &input, const std::shared_ptr &output, bool outputCreated, const AuditLog &auditLog) : Task::Result(), m_sresult(sr), m_inputLabel(input ? input->label() : QString()), m_inputErrorString(input ? input->errorString() : QString()), m_outputLabel(output ? output->label() : QString()), m_outputErrorString(output ? output->errorString() : QString()), m_outputCreated(outputCreated), m_auditLog(auditLog) { qCDebug(KLEOPATRA_LOG) << endl << "inputError :" << m_inputErrorString << endl << "outputError:" << m_outputErrorString; Q_ASSERT(!m_sresult.isNull()); } SignEncryptFilesResult(const EncryptionResult &er, const std::shared_ptr &input, const std::shared_ptr &output, bool outputCreated, const AuditLog &auditLog) : Task::Result(), m_eresult(er), m_inputLabel(input ? input->label() : QString()), m_inputErrorString(input ? input->errorString() : QString()), m_outputLabel(output ? output->label() : QString()), m_outputErrorString(output ? output->errorString() : QString()), m_outputCreated(outputCreated), m_auditLog(auditLog) { qCDebug(KLEOPATRA_LOG) << endl << "inputError :" << m_inputErrorString << endl << "outputError:" << m_outputErrorString; Q_ASSERT(!m_eresult.isNull()); } SignEncryptFilesResult(const SigningResult &sr, const EncryptionResult &er, const std::shared_ptr &input, const std::shared_ptr &output, bool outputCreated, const AuditLog &auditLog) : Task::Result(), m_sresult(sr), m_eresult(er), m_inputLabel(input ? input->label() : QString()), m_inputErrorString(input ? input->errorString() : QString()), m_outputLabel(output ? output->label() : QString()), m_outputErrorString(output ? output->errorString() : QString()), m_outputCreated(outputCreated), m_auditLog(auditLog) { qCDebug(KLEOPATRA_LOG) << endl << "inputError :" << m_inputErrorString << endl << "outputError:" << m_outputErrorString; Q_ASSERT(!m_sresult.isNull() || !m_eresult.isNull()); } QString overview() const override; QString details() const override; int errorCode() const override; QString errorString() const override; VisualCode code() const override; AuditLog auditLog() const override; private: const SigningResult m_sresult; const EncryptionResult m_eresult; const QString m_inputLabel; const QString m_inputErrorString; const QString m_outputLabel; const QString m_outputErrorString; const bool m_outputCreated; const AuditLog m_auditLog; }; static QString makeSigningOverview(const Error &err) { if (err.isCanceled()) { return i18n("Signing canceled."); } if (err) { return i18n("Signing failed."); } return i18n("Signing succeeded."); } static QString makeResultOverview(const SigningResult &result) { return makeSigningOverview(result.error()); } static QString makeEncryptionOverview(const Error &err) { if (err.isCanceled()) { return i18n("Encryption canceled."); } if (err) { return i18n("Encryption failed."); } return i18n("Encryption succeeded."); } static QString makeResultOverview(const EncryptionResult &result) { return makeEncryptionOverview(result.error()); } static QString makeResultOverview(const SigningResult &sr, const EncryptionResult &er) { if (er.isNull() && sr.isNull()) { return QString(); } if (er.isNull()) { return makeResultOverview(sr); } if (sr.isNull()) { return makeResultOverview(er); } if (sr.error().isCanceled() || sr.error()) { return makeResultOverview(sr); } if (er.error().isCanceled() || er.error()) { return makeResultOverview(er); } return i18n("Signing and encryption succeeded."); } static QString escape(QString s) { s = s.toHtmlEscaped(); s.replace(QLatin1Char('\n'), QStringLiteral("
")); return s; } static QString makeResultDetails(const SigningResult &result, const QString &inputError, const QString &outputError) { const Error err = result.error(); if (err.code() == GPG_ERR_EIO) { if (!inputError.isEmpty()) { return i18n("Input error: %1", escape(inputError)); } else if (!outputError.isEmpty()) { return i18n("Output error: %1", escape(outputError)); } } if (err) { return QString::fromLocal8Bit(err.asString()).toHtmlEscaped(); } return QString(); } static QString makeResultDetails(const EncryptionResult &result, const QString &inputError, const QString &outputError) { const Error err = result.error(); if (err.code() == GPG_ERR_EIO) { if (!inputError.isEmpty()) { return i18n("Input error: %1", escape(inputError)); } else if (!outputError.isEmpty()) { return i18n("Output error: %1", escape(outputError)); } } if (err) { return QString::fromLocal8Bit(err.asString()).toHtmlEscaped(); } return i18n(" Encryption succeeded."); } } QString ErrorResult::overview() const { Q_ASSERT(m_error || m_error.isCanceled()); Q_ASSERT(m_sign || m_encrypt); const QString label = formatInputOutputLabel(m_inputLabel, m_outputLabel, true); const bool canceled = m_error.isCanceled(); if (m_sign && m_encrypt) { return canceled ? i18n("%1: Sign/encrypt canceled.", label) : i18n(" %1: Sign/encrypt failed.", label); } return i18nc("label: result. Example: foo -> foo.gpg: Encryption failed.", "%1: %2", label, m_sign ? makeSigningOverview(m_error) : makeEncryptionOverview(m_error)); } QString ErrorResult::details() const { return m_errString; } class SignEncryptTask::Private { friend class ::Kleo::Crypto::SignEncryptTask; SignEncryptTask *const q; public: explicit Private(SignEncryptTask *qq); private: std::unique_ptr createSignJob(GpgME::Protocol proto); std::unique_ptr createSignEncryptJob(GpgME::Protocol proto); std::unique_ptr createEncryptJob(GpgME::Protocol proto); std::shared_ptr makeErrorResult(const Error &err, const QString &errStr, const AuditLog &auditLog); private: void slotResult(const SigningResult &); void slotResult(const SigningResult &, const EncryptionResult &); void slotResult(const EncryptionResult &); private: std::shared_ptr input; std::shared_ptr output; QStringList inputFileNames; QString outputFileName; std::vector signers; std::vector recipients; bool sign : 1; bool encrypt : 1; bool detached : 1; bool symmetric: 1; + bool clearsign: 1; QPointer job; std::shared_ptr m_overwritePolicy; }; SignEncryptTask::Private::Private(SignEncryptTask *qq) : q(qq), input(), output(), inputFileNames(), outputFileName(), signers(), recipients(), sign(true), encrypt(true), detached(false), + clearsign(false), job(nullptr), m_overwritePolicy(new OverwritePolicy(nullptr)) { q->setAsciiArmor(true); } std::shared_ptr SignEncryptTask::Private::makeErrorResult(const Error &err, const QString &errStr, const AuditLog &auditLog) { return std::shared_ptr(new ErrorResult(sign, encrypt, err, errStr, input->label(), output->label(), auditLog)); } SignEncryptTask::SignEncryptTask(QObject *p) : Task(p), d(new Private(this)) { } SignEncryptTask::~SignEncryptTask() {} void SignEncryptTask::setInputFileName(const QString &fileName) { kleo_assert(!d->job); kleo_assert(!fileName.isEmpty()); d->inputFileNames = QStringList(fileName); } void SignEncryptTask::setInputFileNames(const QStringList &fileNames) { kleo_assert(!d->job); kleo_assert(!fileNames.empty()); d->inputFileNames = fileNames; } void SignEncryptTask::setInput(const std::shared_ptr &input) { kleo_assert(!d->job); kleo_assert(input); d->input = input; } void SignEncryptTask::setOutput(const std::shared_ptr &output) { kleo_assert(!d->job); kleo_assert(output); d->output = output; } void SignEncryptTask::setOutputFileName(const QString &fileName) { kleo_assert(!d->job); kleo_assert(!fileName.isEmpty()); d->outputFileName = fileName; } void SignEncryptTask::setSigners(const std::vector &signers) { kleo_assert(!d->job); d->signers = signers; } void SignEncryptTask::setRecipients(const std::vector &recipients) { kleo_assert(!d->job); d->recipients = recipients; } void SignEncryptTask::setOverwritePolicy(const std::shared_ptr &policy) { kleo_assert(!d->job); d->m_overwritePolicy = policy; } void SignEncryptTask::setSign(bool sign) { kleo_assert(!d->job); d->sign = sign; } void SignEncryptTask::setEncrypt(bool encrypt) { kleo_assert(!d->job); d->encrypt = encrypt; } void SignEncryptTask::setDetachedSignature(bool detached) { kleo_assert(!d->job); d->detached = detached; } void SignEncryptTask::setEncryptSymmetric(bool symmetric) { kleo_assert(!d->job); d->symmetric = symmetric; } +void SignEncryptTask::setClearsign(bool clearsign) +{ + kleo_assert(!d->job); + d->clearsign = clearsign; +} + Protocol SignEncryptTask::protocol() const { if (d->sign && !d->signers.empty()) { return d->signers.front().protocol(); } if (d->encrypt || d->symmetric) { if (!d->recipients.empty()) { return d->recipients.front().protocol(); } else { return GpgME::OpenPGP; // symmetric OpenPGP encryption } } throw Kleo::Exception(gpg_error(GPG_ERR_INTERNAL), i18n("Cannot determine protocol for task")); } QString SignEncryptTask::label() const { return d->input ? d->input->label() : QString(); } QString SignEncryptTask::tag() const { return Formatting::displayName(protocol()); } unsigned long long SignEncryptTask::inputSize() const { return d->input ? d->input->size() : 0U; } void SignEncryptTask::doStart() { kleo_assert(!d->job); if (d->sign) { kleo_assert(!d->signers.empty()); } kleo_assert(d->input); if (!d->output) { d->output = Output::createFromFile(d->outputFileName, d->m_overwritePolicy); } if (d->encrypt || d->symmetric) { Context::EncryptionFlags flags = Context::AlwaysTrust; if (d->symmetric) { flags = static_cast(flags | Context::Symmetric); qCDebug(KLEOPATRA_LOG) << "Adding symmetric flag"; } if (d->sign) { std::unique_ptr job = d->createSignEncryptJob(protocol()); kleo_assert(job.get()); job->start(d->signers, d->recipients, d->input->ioDevice(), d->output->ioDevice(), flags); d->job = job.release(); } else { std::unique_ptr job = d->createEncryptJob(protocol()); kleo_assert(job.get()); job->start(d->recipients, d->input->ioDevice(), d->output->ioDevice(), flags); d->job = job.release(); } } else if (d->sign) { std::unique_ptr job = d->createSignJob(protocol()); kleo_assert(job.get()); + kleo_assert(! (d->detached && d->clearsign)); job->start(d->signers, d->input->ioDevice(), d->output->ioDevice(), - d->detached ? GpgME::Detached : GpgME::NormalSignatureMode); + d->detached ? GpgME::Detached : d->clearsign ? + GpgME::Clearsigned : GpgME::NormalSignatureMode); d->job = job.release(); } else { kleo_assert(!"Either 'sign' or 'encrypt' or 'symmetric' must be set!"); } } void SignEncryptTask::cancel() { if (d->job) { d->job->slotCancel(); } } std::unique_ptr SignEncryptTask::Private::createSignJob(GpgME::Protocol proto) { const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime(); kleo_assert(backend); std::unique_ptr signJob(backend->signJob(q->asciiArmor(), /*textmode=*/false)); kleo_assert(signJob.get()); connect(signJob.get(), SIGNAL(progress(QString,int,int)), q, SLOT(setProgress(QString,int,int))); connect(signJob.get(), SIGNAL(result(GpgME::SigningResult,QByteArray)), q, SLOT(slotResult(GpgME::SigningResult))); return signJob; } std::unique_ptr SignEncryptTask::Private::createSignEncryptJob(GpgME::Protocol proto) { const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime(); kleo_assert(backend); std::unique_ptr signEncryptJob(backend->signEncryptJob(q->asciiArmor(), /*textmode=*/false)); kleo_assert(signEncryptJob.get()); connect(signEncryptJob.get(), SIGNAL(progress(QString,int,int)), q, SLOT(setProgress(QString,int,int))); connect(signEncryptJob.get(), SIGNAL(result(GpgME::SigningResult,GpgME::EncryptionResult,QByteArray)), q, SLOT(slotResult(GpgME::SigningResult,GpgME::EncryptionResult))); return signEncryptJob; } std::unique_ptr SignEncryptTask::Private::createEncryptJob(GpgME::Protocol proto) { const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime(); kleo_assert(backend); std::unique_ptr encryptJob(backend->encryptJob(q->asciiArmor(), /*textmode=*/false)); kleo_assert(encryptJob.get()); connect(encryptJob.get(), SIGNAL(progress(QString,int,int)), q, SLOT(setProgress(QString,int,int))); connect(encryptJob.get(), SIGNAL(result(GpgME::EncryptionResult,QByteArray)), q, SLOT(slotResult(GpgME::EncryptionResult))); return encryptJob; } void SignEncryptTask::Private::slotResult(const SigningResult &result) { const QGpgME::Job *const job = qobject_cast(q->sender()); const AuditLog auditLog = AuditLog::fromJob(job); bool outputCreated = false; if (result.error().code()) { output->cancel(); } else if (input->failed()) { q->emitResult(makeErrorResult(Error::fromCode(GPG_ERR_EIO), i18n("Input error: %1", escape( input->errorString())), auditLog)); return; } else { try { kleo_assert(!result.isNull()); output->finalize(); outputCreated = true; input->finalize(); } catch (const GpgME::Exception &e) { q->emitResult(makeErrorResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog)); return; } } q->emitResult(std::shared_ptr(new SignEncryptFilesResult(result, input, output, outputCreated, auditLog))); } void SignEncryptTask::Private::slotResult(const SigningResult &sresult, const EncryptionResult &eresult) { const QGpgME::Job *const job = qobject_cast(q->sender()); const AuditLog auditLog = AuditLog::fromJob(job); bool outputCreated = false; if (sresult.error().code() || eresult.error().code()) { output->cancel(); } else if (input->failed()) { output->cancel(); q->emitResult(makeErrorResult(Error::fromCode(GPG_ERR_EIO), i18n("Input error: %1", escape( input->errorString())), auditLog)); return; } else { try { kleo_assert(!sresult.isNull() || !eresult.isNull()); output->finalize(); outputCreated = true; input->finalize(); } catch (const GpgME::Exception &e) { q->emitResult(makeErrorResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog)); return; } } q->emitResult(std::shared_ptr(new SignEncryptFilesResult(sresult, eresult, input, output, outputCreated, auditLog))); } void SignEncryptTask::Private::slotResult(const EncryptionResult &result) { const QGpgME::Job *const job = qobject_cast(q->sender()); const AuditLog auditLog = AuditLog::fromJob(job); bool outputCreated = false; if (result.error().code()) { output->cancel(); } else if (input->failed()) { output->cancel(); q->emitResult(makeErrorResult(Error::fromCode(GPG_ERR_EIO), i18n("Input error: %1", escape(input->errorString())), auditLog)); return; } else { try { kleo_assert(!result.isNull()); output->finalize(); outputCreated = true; input->finalize(); } catch (const GpgME::Exception &e) { q->emitResult(makeErrorResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog)); return; } } q->emitResult(std::shared_ptr(new SignEncryptFilesResult(result, input, output, outputCreated, auditLog))); } QString SignEncryptFilesResult::overview() const { const QString files = formatInputOutputLabel(m_inputLabel, m_outputLabel, !m_outputCreated); return files + QLatin1String(": ") + makeOverview(makeResultOverview(m_sresult, m_eresult)); } QString SignEncryptFilesResult::details() const { return errorString(); } int SignEncryptFilesResult::errorCode() const { if (m_sresult.error().code()) { return m_sresult.error().encodedError(); } if (m_eresult.error().code()) { return m_eresult.error().encodedError(); } return 0; } QString SignEncryptFilesResult::errorString() const { const bool sign = !m_sresult.isNull(); const bool encrypt = !m_eresult.isNull(); kleo_assert(sign || encrypt); if (sign && encrypt) { return m_sresult.error().code() ? makeResultDetails(m_sresult, m_inputErrorString, m_outputErrorString) : m_eresult.error().code() ? makeResultDetails(m_eresult, m_inputErrorString, m_outputErrorString) : QString(); } return sign ? makeResultDetails(m_sresult, m_inputErrorString, m_outputErrorString) : /*else*/ makeResultDetails(m_eresult, m_inputErrorString, m_outputErrorString); } Task::Result::VisualCode SignEncryptFilesResult::code() const { if (m_sresult.error().isCanceled() || m_eresult.error().isCanceled()) { return Warning; } return (m_sresult.error().code() || m_eresult.error().code()) ? NeutralError : NeutralSuccess; } AuditLog SignEncryptFilesResult::auditLog() const { return m_auditLog; } #include "moc_signencrypttask.cpp" diff --git a/src/crypto/signencrypttask.h b/src/crypto/signencrypttask.h index e366572a..e407eea0 100644 --- a/src/crypto/signencrypttask.h +++ b/src/crypto/signencrypttask.h @@ -1,107 +1,108 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/signencrypttask.h This file is part of Kleopatra, the KDE keymanager Copyright (c) 2007 Klarälvdalens Datakonsult AB Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #ifndef __KLEOPATRA_CRYPTO_SIGNENCRYPTFILESTASK_H__ #define __KLEOPATRA_CRYPTO_SIGNENCRYPTFILESTASK_H__ #include #include #include #include #include class QString; namespace GpgME { class Key; } namespace Kleo { class OverwritePolicy; class Input; class Output; } namespace Kleo { namespace Crypto { class SignEncryptTask : public Task { Q_OBJECT public: explicit SignEncryptTask(QObject *parent = nullptr); ~SignEncryptTask(); void setInputFileName(const QString &fileName); void setInputFileNames(const QStringList &fileNames); void setInput(const std::shared_ptr &input); void setOutput(const std::shared_ptr &output); void setOutputFileName(const QString &fileName); void setSigners(const std::vector &singners); void setRecipients(const std::vector &recipients); void setSign(bool sign); void setEncrypt(bool encrypt); void setDetachedSignature(bool detached); void setEncryptSymmetric(bool symmetric); + void setClearsign(bool clearsign); void setOverwritePolicy(const std::shared_ptr &policy); GpgME::Protocol protocol() const override; void cancel() override; QString label() const override; QString tag() const override; private: void doStart() override; unsigned long long inputSize() const override; private: class Private; kdtools::pimpl_ptr d; Q_PRIVATE_SLOT(d, void slotResult(const GpgME::SigningResult &)) Q_PRIVATE_SLOT(d, void slotResult(const GpgME::SigningResult &, const GpgME::EncryptionResult &)) Q_PRIVATE_SLOT(d, void slotResult(const GpgME::EncryptionResult &)) }; } } #endif /* __KLEOPATRA_CRYPTO_SIGNENCRYPTFILESTASK_H__ */ diff --git a/src/view/padwidget.cpp b/src/view/padwidget.cpp index 8e579a78..b354cbf7 100644 --- a/src/view/padwidget.cpp +++ b/src/view/padwidget.cpp @@ -1,522 +1,530 @@ /* -*- mode: c++; c-basic-offset:4 -*- padwidget.cpp This file is part of Kleopatra, the KDE keymanager Copyright (c) 2018 Intevation GmbH Kleopatra is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Kleopatra is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA In addition, as a special exception, the copyright holders give permission to link the code of this program with any edition of the Qt library by Trolltech AS, Norway (or with modified versions of Qt that use the same license as Qt), and distribute linked combinations including the two. You must obey the GNU General Public License in all respects for all of the code used other than Qt. If you modify this file, you may extend this exception to your version of the file, but you are not obligated to do so. If you do not wish to do so, delete this exception statement from your version. */ #include "padwidget.h" #include "kleopatra_debug.h" #include #include #include #include "crypto/gui/signencryptwidget.h" #include "crypto/gui/resultitemwidget.h" #include "crypto/signencrypttask.h" #include "crypto/decryptverifytask.h" #include "utils/gnupg-helper.h" #include "utils/input.h" #include "utils/output.h" #include "commands/importcertificatefromdatacommand.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Crypto; using namespace Kleo::Crypto::Gui; static GpgME::Protocol getProtocol(const std::shared_ptr &result) { const auto dvResult = dynamic_cast(result.get()); if (dvResult) { for (const auto &key: KeyCache::instance()->findRecipients(dvResult->decryptionResult())) { return key.protocol(); } for (const auto &key: KeyCache::instance()->findSigners(dvResult->verificationResult())) { return key.protocol(); } } return GpgME::UnknownProtocol; } class PadWidget::Private { public: Private(PadWidget *qq): q(qq), mEdit(new QTextEdit), mCryptBtn(new QPushButton(QIcon::fromTheme("document-edit-sign-encrypt"), i18n("Sign / Encrypt Notepad"))), mDecryptBtn(new QPushButton(QIcon::fromTheme("document-edit-decrypt-verify"), i18n("Decrypt / Verify Notepad"))), mRevertBtn(new QPushButton(QIcon::fromTheme("edit-undo"), i18n("Revert"))), mAdditionalInfoLabel(new QLabel), mSigEncWidget(new SignEncryptWidget(nullptr, true)), mProgressBar(new QProgressBar), mProgressLabel(new QLabel), mLastResultWidget(nullptr), mPGPRB(nullptr), mCMSRB(nullptr), mImportProto(GpgME::UnknownProtocol) { auto vLay = new QVBoxLayout(q); auto btnLay = new QHBoxLayout; vLay->addLayout(btnLay); btnLay->addWidget(mCryptBtn); btnLay->addWidget(mDecryptBtn); btnLay->addWidget(mRevertBtn); mRevertBtn->setVisible(false); btnLay->addWidget(mAdditionalInfoLabel); btnLay->addStretch(-1); mProgressBar->setRange(0, 0); mProgressBar->setVisible(false); mProgressLabel->setVisible(false); auto progLay = new QHBoxLayout; progLay->addWidget(mProgressLabel); progLay->addWidget(mProgressBar); mStatusLay = new QVBoxLayout; mStatusLay->addLayout(progLay); vLay->addLayout(mStatusLay, 0.1); auto tabWidget = new QTabWidget; vLay->addWidget(tabWidget, 1); tabWidget->addTab(mEdit, QIcon::fromTheme("edittext"), i18n("Notepad")); // The recipients area auto recipientsWidget = new QWidget; auto recipientsVLay = new QVBoxLayout(recipientsWidget); auto protocolSelectionLay = new QHBoxLayout; bool pgpOnly = KeyCache::instance()->pgpOnly(); if (!pgpOnly) { recipientsVLay->addLayout(protocolSelectionLay); } protocolSelectionLay->addWidget(new QLabel(i18n("

Protocol:

"))); protocolSelectionLay->addStretch(-1); // Once S/MIME is supported add radio for S/MIME here. recipientsVLay->addWidget(mSigEncWidget); tabWidget->addTab(recipientsWidget, QIcon::fromTheme("contact-new-symbolic"), i18n("Recipients")); mEdit->setPlaceholderText("Enter a message to encrypt or decrypt..."); auto fixedFont = QFont("Monospace", 10); fixedFont.setStyleHint(QFont::TypeWriter); // This does not work well // QFontDatabase::systemFont(QFontDatabase::FixedFont); mEdit->setCurrentFont(fixedFont); mEdit->setMinimumWidth(QFontMetrics(fixedFont).averageCharWidth() * 70); if (KeyCache::instance()->pgpOnly()) { mSigEncWidget->setProtocol(GpgME::OpenPGP); } else { auto grp = new QButtonGroup(q); auto mPGPRB = new QRadioButton(i18n("OpenPGP")); auto mCMSRB = new QRadioButton(i18n("S/MIME")); grp->addButton(mPGPRB); grp->addButton(mCMSRB); KConfigGroup config(KSharedConfig::openConfig(), "Notepad"); if (config.readEntry("wasCMS", false)) { mCMSRB->setChecked(true); mSigEncWidget->setProtocol(GpgME::CMS); } else { mPGPRB->setChecked(true); mSigEncWidget->setProtocol(GpgME::OpenPGP); } protocolSelectionLay->addWidget(mPGPRB); protocolSelectionLay->addWidget(mCMSRB); connect(mPGPRB, &QRadioButton::toggled, q, [this] (bool value) { if (value) { mSigEncWidget->setProtocol(GpgME::OpenPGP); } }); connect(mCMSRB, &QRadioButton::toggled, q, [this] (bool value) { if (value) { mSigEncWidget->setProtocol(GpgME::CMS); } }); } updateCommitButton(); connect(mEdit, &QTextEdit::textChanged, q, [this] () { updateCommitButton(); }); connect(mCryptBtn, &QPushButton::clicked, q, [this] () { if (mImportProto != GpgME::UnknownProtocol) { doImport(); } else { doEncryptSign(); } }); connect(mSigEncWidget, &SignEncryptWidget::operationChanged, q, [this] (const QString &) { updateCommitButton(); }); connect(mDecryptBtn, &QPushButton::clicked, q, [this] () { doDecryptVerify(); }); connect(mRevertBtn, &QPushButton::clicked, q, [this] () { revert(); }); } void revert() { mEdit->setPlainText(QString::fromUtf8(mInputData)); mRevertBtn->setVisible(false); } void updateRecipientsFromResult(const Kleo::Crypto::DecryptVerifyResult &result) { const auto decResult = result.decryptionResult(); for (const auto &recipient: decResult.recipients()) { if (!recipient.keyID()) { continue; } GpgME::Key key; if (strlen(recipient.keyID()) < 16) { key = KeyCache::instance()->findByShortKeyID(recipient.keyID()); } else { key = KeyCache::instance()->findByKeyIDOrFingerprint(recipient.keyID()); } if (key.isNull()) { std::vector subids; subids.push_back(std::string(recipient.keyID())); for (const auto &subkey: KeyCache::instance()->findSubkeysByKeyID(subids)) { key = subkey.parent(); break; } } if (key.isNull()) { qCDebug(KLEOPATRA_LOG) << "Unknown key" << recipient.keyID(); mSigEncWidget->addUnknownRecipient(recipient.keyID()); continue; } bool keyFound = false; for (const auto &existingKey: mSigEncWidget->recipients()) { if (existingKey.primaryFingerprint() && key.primaryFingerprint() && !strcmp (existingKey.primaryFingerprint(), key.primaryFingerprint())) { keyFound = true; break; } } if (!keyFound) { mSigEncWidget->addRecipient(key); } } } void cryptDone(const std::shared_ptr &result) { updateCommitButton(); mDecryptBtn->setEnabled(true); mProgressBar->setVisible(false); mProgressLabel->setVisible(false); mLastResultWidget = new ResultItemWidget(result); mLastResultWidget->showCloseButton(true); mStatusLay->addWidget(mLastResultWidget); connect(mLastResultWidget, &ResultItemWidget::closeButtonClicked, q, [this] () { removeLastResultItem(); }); // Check result protocol if (mPGPRB) { auto proto = getProtocol(result); if (proto == GpgME::UnknownProtocol) { proto = mPGPRB->isChecked() ? GpgME::OpenPGP : GpgME::CMS; } else if (proto == GpgME::OpenPGP) { mPGPRB->setChecked(true); } else if (proto == GpgME::CMS) { mCMSRB->setChecked(true); } KConfigGroup config(KSharedConfig::openConfig(), "Notepad"); config.writeEntry("wasCMS", proto == GpgME::CMS); } if (result->errorCode()) { if (!result->errorString().isEmpty()) { KMessageBox::error(q, result->errorString(), i18nc("@title", "Error in crypto action")); } return; } mEdit->setPlainText(QString::fromUtf8(mOutputData)); mOutputData.clear(); mRevertBtn->setVisible(true); const auto decryptVerifyResult = dynamic_cast(result.get()); if (decryptVerifyResult) { updateRecipientsFromResult(*decryptVerifyResult); } } void doDecryptVerify() { doCryptoCommon(); mSigEncWidget->clearAddedRecipients(); mProgressLabel->setText(i18n("Decrypt / Verify") + QStringLiteral("...")); auto input = Input::createFromByteArray(&mInputData, i18n("Notepad")); auto output = Output::createFromByteArray(&mOutputData, i18n("Notepad")); AbstractDecryptVerifyTask *task; auto classification = input->classification(); if (classification & Class::OpaqueSignature || classification & Class::ClearsignedMessage) { auto verifyTask = new VerifyOpaqueTask(); verifyTask->setInput(input); verifyTask->setOutput(output); task = verifyTask; } else { auto decTask = new DecryptVerifyTask(); decTask->setInput(input); decTask->setOutput(output); task = decTask; } try { task->autodetectProtocolFromInput(); } catch (const Kleo::Exception &e) { KMessageBox::error(q, e.message(), i18nc("@title", "Error in crypto action")); mCryptBtn->setEnabled(true); mDecryptBtn->setEnabled(true); mProgressBar->setVisible(false); mProgressLabel->setVisible(false); return; } connect (task, &Task::result, q, [this, task] (const std::shared_ptr &result) { qCDebug(KLEOPATRA_LOG) << "Decrypt / Verify done. Err:" << result->errorCode(); task->deleteLater(); cryptDone(result); }); task->start(); } void removeLastResultItem() { if (mLastResultWidget) { mStatusLay->removeWidget(mLastResultWidget); delete mLastResultWidget; mLastResultWidget = nullptr; } } void doCryptoCommon() { mCryptBtn->setEnabled(false); mDecryptBtn->setEnabled(false); mProgressBar->setVisible(true); mProgressLabel->setVisible(true); mInputData = mEdit->toPlainText().toUtf8(); removeLastResultItem(); } void doEncryptSign() { doCryptoCommon(); mProgressLabel->setText(mSigEncWidget->currentOp() + QStringLiteral("...")); auto input = Input::createFromByteArray(&mInputData, i18n("Notepad")); auto output = Output::createFromByteArray(&mOutputData, i18n("Notepad")); auto task = new SignEncryptTask(); task->setInput(input); task->setOutput(output); const auto sigKey = mSigEncWidget->signKey(); - if (!sigKey.isNull()) { + + bool encrypt = mSigEncWidget->encryptSymmetric() || mSigEncWidget->recipients().size(); + bool sign = !sigKey.isNull(); + + if (sign) { task->setSign(true); std::vector signVector; signVector.push_back(sigKey); task->setSigners(signVector); } else { task->setSign(false); } - task->setEncrypt(mSigEncWidget->encryptSymmetric() || mSigEncWidget->recipients().size()); + task->setEncrypt(encrypt); task->setRecipients(mSigEncWidget->recipients().toStdVector()); task->setEncryptSymmetric(mSigEncWidget->encryptSymmetric()); task->setAsciiArmor(true); + if (sign && !encrypt && sigKey.protocol() == GpgME::OpenPGP) { + task->setClearsign(true); + } + connect (task, &Task::result, q, [this, task] (const std::shared_ptr &result) { qCDebug(KLEOPATRA_LOG) << "Encrypt / Sign done. Err:" << result->errorCode(); task->deleteLater(); cryptDone(result); }); task->start(); } void doImport() { doCryptoCommon(); mProgressLabel->setText(i18n("Importing...")); auto cmd = new Kleo::ImportCertificateFromDataCommand(mInputData, mImportProto); connect(cmd, &Kleo::ImportCertificatesCommand::finished, q, [this] () { mCryptBtn->setEnabled(true); mDecryptBtn->setEnabled(true); mProgressBar->setVisible(false); mProgressLabel->setVisible(false); updateCommitButton(); mRevertBtn->setVisible(true); mEdit->setPlainText(QString()); }); cmd->start(); } void checkImportProtocol() { QGpgME::QByteArrayDataProvider dp(mEdit->toPlainText().toUtf8()); GpgME::Data data(&dp); auto type = data.type(); if (type == GpgME::Data::PGPKey) { mImportProto = GpgME::OpenPGP; } else if (type == GpgME::Data::X509Cert || type == GpgME::Data::PKCS12) { mImportProto = GpgME::CMS; } else { mImportProto = GpgME::UnknownProtocol; } } void updateCommitButton() { mAdditionalInfoLabel->setVisible(false); checkImportProtocol(); if (mImportProto != GpgME::UnknownProtocol) { mCryptBtn->setText(i18nc("1 is an operation to apply to the notepad. " "Like Sign/Encrypt or just Encrypt.", "%1 Notepad", i18n("Import"))); mCryptBtn->setDisabled(false); return; } if (!mSigEncWidget->currentOp().isEmpty()) { mCryptBtn->setDisabled(false); mCryptBtn->setText(i18nc("1 is an operation to apply to the notepad. " "Like Sign/Encrypt or just Encrypt.", "%1 Notepad", mSigEncWidget->currentOp())); } else { mCryptBtn->setText(i18n("Sign / Encrypt Notepad")); mCryptBtn->setDisabled(true); } if (Kleo::gpgComplianceP("de-vs")) { bool de_vs = mSigEncWidget->isDeVsAndValid(); mCryptBtn->setIcon(QIcon::fromTheme(de_vs ? "security-high" : "security-medium")); mCryptBtn->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())); mAdditionalInfoLabel->setText(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.")); mAdditionalInfoLabel->setVisible(true); } } private: PadWidget *q; QTextEdit *mEdit; QPushButton *mCryptBtn; QPushButton *mDecryptBtn; QPushButton *mRevertBtn; QLabel *mAdditionalInfoLabel; QByteArray mInputData; QByteArray mOutputData; SignEncryptWidget *mSigEncWidget; QProgressBar *mProgressBar; QLabel *mProgressLabel; QVBoxLayout *mStatusLay; ResultItemWidget *mLastResultWidget; QList mAutoAddedKeys; QRadioButton *mPGPRB; QRadioButton *mCMSRB; GpgME::Protocol mImportProto; }; PadWidget::PadWidget(QWidget *parent): QWidget(parent), d(new Private(this)) { }