aboutsummaryrefslogtreecommitdiffstats
path: root/lang
diff options
context:
space:
mode:
authorIngo Klöcker <[email protected]>2023-12-21 10:53:23 +0000
committerIngo Klöcker <[email protected]>2023-12-22 09:01:17 +0000
commit20b32e03503f8c3187508d9cd5ba07db2b03f014 (patch)
tree16eae3b81c8c584ae98a958549e5920bd982cd7c /lang
parentqt: Support writing/reading signed/encrypted files directly to/from file (diff)
downloadgpgme-20b32e03503f8c3187508d9cd5ba07db2b03f014.tar.gz
gpgme-20b32e03503f8c3187508d9cd5ba07db2b03f014.zip
qt: Support decryption (with verification) directly to/from files
* lang/qt/src/Makefile.am: Add new files. * lang/qt/src/job.cpp (DecryptVerifyJob): Move definition of constructor and destructor and inclusion of the moc file to the corresponding .cpp file. * lang/qt/src/decryptverifyjob.cpp: New. * lang/qt/src/decryptverifyjob.h (DecryptVerifyJob): Add member functions setInputFile, inputFile, setOutputFile, outputFile. * lang/qt/src/decryptverifyjob_p.h: New. * lang/qt/src/qgpgmedecryptverifyjob.cpp (class QGpgMEDecryptVerifyJobPrivate): New. (QGpgMEDecryptVerifyJob::QGpgMEDecryptVerifyJob): Instantiate private job class. (decrypt_verify_from_filename): New. * lang/qt/tests/Makefile.am: Add new test program. * lang/qt/tests/run-decryptverifyjob.cpp: New. -- This makes it possible to tell gpg to read the input and write the output directly to a specified file bypassing GpgME's Data IO when decrypting (and verifying) a file. GnuPG-bug-id: 6550
Diffstat (limited to 'lang')
-rw-r--r--lang/qt/src/Makefile.am2
-rw-r--r--lang/qt/src/decryptverifyjob.cpp74
-rw-r--r--lang/qt/src/decryptverifyjob.h30
-rw-r--r--lang/qt/src/decryptverifyjob_p.h50
-rw-r--r--lang/qt/src/job.cpp3
-rw-r--r--lang/qt/src/qgpgmedecryptverifyjob.cpp91
-rw-r--r--lang/qt/tests/Makefile.am2
-rw-r--r--lang/qt/tests/run-decryptverifyjob.cpp163
8 files changed, 405 insertions, 10 deletions
diff --git a/lang/qt/src/Makefile.am b/lang/qt/src/Makefile.am
index 5e16373e..ef625d89 100644
--- a/lang/qt/src/Makefile.am
+++ b/lang/qt/src/Makefile.am
@@ -36,6 +36,7 @@ qgpgme_sources = \
dataprovider.cpp \
debug.cpp \
decryptverifyarchivejob.cpp \
+ decryptverifyjob.cpp \
encryptarchivejob.cpp \
filelistdataprovider.cpp \
job.cpp multideletejob.cpp qgpgmeadduseridjob.cpp \
@@ -180,6 +181,7 @@ private_qgpgme_headers = \
changeexpiryjob_p.h \
cleaner.h \
decryptverifyarchivejob_p.h \
+ decryptverifyjob_p.h \
encryptarchivejob_p.h \
encryptjob_p.h \
importjob_p.h \
diff --git a/lang/qt/src/decryptverifyjob.cpp b/lang/qt/src/decryptverifyjob.cpp
new file mode 100644
index 00000000..f369f1d5
--- /dev/null
+++ b/lang/qt/src/decryptverifyjob.cpp
@@ -0,0 +1,74 @@
+/*
+ decryptverifyjob.cpp
+
+ This file is part of qgpgme, the Qt API binding for gpgme
+ Copyright (c) 2023 g10 Code GmbH
+ Software engineering by Ingo Klöcker <[email protected]>
+
+ QGpgME 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.
+
+ QGpgME 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.
+*/
+
+#ifdef HAVE_CONFIG_H
+ #include "config.h"
+#endif
+
+#include "decryptverifyjob.h"
+#include "decryptverifyjob_p.h"
+
+using namespace QGpgME;
+
+DecryptVerifyJob::DecryptVerifyJob(QObject *parent)
+ : Job{parent}
+{
+}
+
+DecryptVerifyJob::~DecryptVerifyJob() = default;
+
+void DecryptVerifyJob::setInputFile(const QString &path)
+{
+ auto d = jobPrivate<DecryptVerifyJobPrivate>(this);
+ d->m_inputFilePath = path;
+}
+
+QString DecryptVerifyJob::inputFile() const
+{
+ auto d = jobPrivate<DecryptVerifyJobPrivate>(this);
+ return d->m_inputFilePath;
+}
+
+void DecryptVerifyJob::setOutputFile(const QString &path)
+{
+ auto d = jobPrivate<DecryptVerifyJobPrivate>(this);
+ d->m_outputFilePath = path;
+}
+
+QString DecryptVerifyJob::outputFile() const
+{
+ auto d = jobPrivate<DecryptVerifyJobPrivate>(this);
+ return d->m_outputFilePath;
+}
+
+#include "decryptverifyjob.moc"
diff --git a/lang/qt/src/decryptverifyjob.h b/lang/qt/src/decryptverifyjob.h
index 8444e4d0..710fc80e 100644
--- a/lang/qt/src/decryptverifyjob.h
+++ b/lang/qt/src/decryptverifyjob.h
@@ -62,6 +62,14 @@ namespace QGpgME
DecryptVerifyJob instance will have scheduled it's own destruction with
a call to QObject::deleteLater().
+ Alternatively, the job can be started with startIt() after setting
+ an input file and an output file. If the job is started this way then
+ the backend reads the input and writes the output directly from/to the
+ specified input file and output file. In this case the plainText value of
+ the result signal will always be empty. This direct IO mode is currently
+ only supported for OpenPGP. Note that startIt() does not schedule the job's
+ destruction if starting the job failed.
+
After result() is emitted, the DecryptVerifyJob will schedule it's own
destruction by calling QObject::deleteLater().
*/
@@ -71,7 +79,27 @@ class QGPGME_EXPORT DecryptVerifyJob : public Job
protected:
explicit DecryptVerifyJob(QObject *parent);
public:
- ~DecryptVerifyJob();
+ ~DecryptVerifyJob() override;
+
+ /**
+ * Sets the path of the file to decrypt (and verify).
+ *
+ * Used if the job is started with startIt().
+ */
+ void setInputFile(const QString &path);
+ QString inputFile() const;
+
+ /**
+ * Sets the path of the file to write the result to.
+ *
+ * Used if the job is started with startIt().
+ *
+ * \note If a file with this path exists, then the job will fail, i.e. you
+ * need to delete an existing file that shall be overwritten before you
+ * start the job.
+ */
+ void setOutputFile(const QString &path);
+ QString outputFile() const;
/**
Starts the combined decryption and verification operation.
diff --git a/lang/qt/src/decryptverifyjob_p.h b/lang/qt/src/decryptverifyjob_p.h
new file mode 100644
index 00000000..bbd30b59
--- /dev/null
+++ b/lang/qt/src/decryptverifyjob_p.h
@@ -0,0 +1,50 @@
+/*
+ decryptverifyjob_p.h
+
+ This file is part of qgpgme, the Qt API binding for gpgme
+ Copyright (c) 2023 g10 Code GmbH
+ Software engineering by Ingo Klöcker <[email protected]>
+
+ QGpgME 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.
+
+ QGpgME 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 __QGPGME_DECRYPTVERIFYJOB_P_H__
+#define __QGPGME_DECRYPTVERIFYJOB_P_H__
+
+#include "job_p.h"
+
+namespace QGpgME
+{
+
+struct DecryptVerifyJobPrivate : public JobPrivate
+{
+ QString m_inputFilePath;
+ QString m_outputFilePath;
+};
+
+}
+
+#endif // __QGPGME_DECRYPTVERIFYJOB_P_H__
diff --git a/lang/qt/src/job.cpp b/lang/qt/src/job.cpp
index 3c9422bb..56088ce3 100644
--- a/lang/qt/src/job.cpp
+++ b/lang/qt/src/job.cpp
@@ -44,7 +44,6 @@
#include "keylistjob.h"
#include "listallkeysjob.h"
#include "decryptjob.h"
-#include "decryptverifyjob.h"
#include "signkeyjob.h"
#include "verifydetachedjob.h"
#include "verifyopaquejob.h"
@@ -162,7 +161,6 @@ void QGpgME::Job::startNow()
make_job_subclass(KeyListJob)
make_job_subclass(ListAllKeysJob)
make_job_subclass(DecryptJob)
-make_job_subclass(DecryptVerifyJob)
make_job_subclass(SignKeyJob)
make_job_subclass(VerifyDetachedJob)
make_job_subclass(VerifyOpaqueJob)
@@ -197,7 +195,6 @@ make_job_subclass(SetPrimaryUserIDJob)
#include "keylistjob.moc"
#include "listallkeysjob.moc"
#include "decryptjob.moc"
-#include "decryptverifyjob.moc"
#include "signkeyjob.moc"
#include "verifydetachedjob.moc"
#include "verifyopaquejob.moc"
diff --git a/lang/qt/src/qgpgmedecryptverifyjob.cpp b/lang/qt/src/qgpgmedecryptverifyjob.cpp
index 256160b5..bc135147 100644
--- a/lang/qt/src/qgpgmedecryptverifyjob.cpp
+++ b/lang/qt/src/qgpgmedecryptverifyjob.cpp
@@ -39,25 +39,55 @@
#include "qgpgmedecryptverifyjob.h"
#include "dataprovider.h"
+#include "decryptverifyjob_p.h"
+#include "util.h"
-#include "context.h"
-#include "decryptionresult.h"
-#include "verificationresult.h"
-#include "data.h"
+#include <context.h>
+#include <decryptionresult.h>
+#include <verificationresult.h>
+#include <data.h>
#include <QDebug>
#include "qgpgme_debug.h"
#include <QBuffer>
+#include <QFile>
#include <cassert>
using namespace QGpgME;
using namespace GpgME;
+namespace
+{
+
+class QGpgMEDecryptVerifyJobPrivate : public DecryptVerifyJobPrivate
+{
+ QGpgMEDecryptVerifyJob *q = nullptr;
+
+public:
+ QGpgMEDecryptVerifyJobPrivate(QGpgMEDecryptVerifyJob *qq)
+ : q{qq}
+ {
+ }
+
+ ~QGpgMEDecryptVerifyJobPrivate() override = default;
+
+private:
+ GpgME::Error startIt() override;
+
+ void startNow() override
+ {
+ q->run();
+ }
+};
+
+}
+
QGpgMEDecryptVerifyJob::QGpgMEDecryptVerifyJob(Context *context)
: mixin_type(context)
{
+ setJobPrivate(this, std::unique_ptr<QGpgMEDecryptVerifyJobPrivate>{new QGpgMEDecryptVerifyJobPrivate{this}});
lateInitialization();
}
@@ -112,6 +142,43 @@ static QGpgMEDecryptVerifyJob::result_type decrypt_verify_qba(Context *ctx, cons
return decrypt_verify(ctx, nullptr, buffer, std::shared_ptr<QIODevice>());
}
+static QGpgMEDecryptVerifyJob::result_type decrypt_verify_from_filename(Context *ctx,
+ const QString &inputFilePath,
+ const QString &outputFilePath)
+{
+ Data indata;
+#ifdef Q_OS_WIN
+ indata.setFileName(inputFilePath().toUtf8().constData());
+#else
+ indata.setFileName(QFile::encodeName(inputFilePath).constData());
+#endif
+
+ PartialFileGuard partFileGuard{outputFilePath};
+ if (partFileGuard.tempFileName().isEmpty()) {
+ return std::make_tuple(DecryptionResult{Error::fromCode(GPG_ERR_EEXIST)}, VerificationResult{Error::fromCode(GPG_ERR_EEXIST)}, QByteArray{}, QString{}, Error{});
+ }
+
+ Data outdata;
+#ifdef Q_OS_WIN
+ outdata.setFileName(partFileGuard.tempFileName().toUtf8().constData());
+#else
+ outdata.setFileName(QFile::encodeName(partFileGuard.tempFileName()).constData());
+#endif
+
+ const auto results = ctx->decryptAndVerify(indata, outdata);
+ const auto &decryptionResult = results.first;
+ const auto &verificationResult = results.second;
+
+ if (!decryptionResult.error().code() && !verificationResult.error().code()) {
+ // the operation succeeded -> save the result under the requested file name
+ partFileGuard.commit();
+ }
+
+ Error ae;
+ const QString log = _detail::audit_log_as_html(ctx, ae);
+ return std::make_tuple(decryptionResult, verificationResult, QByteArray{}, log, ae);
+}
+
Error QGpgMEDecryptVerifyJob::start(const QByteArray &cipherText)
{
run(std::bind(&decrypt_verify_qba, std::placeholders::_1, cipherText));
@@ -132,10 +199,22 @@ QGpgME::QGpgMEDecryptVerifyJob::exec(const QByteArray &cipherText, QByteArray &p
return mResult;
}
-//PENDING(marc) implement showErrorDialog()
-
void QGpgMEDecryptVerifyJob::resultHook(const result_type &tuple)
{
mResult = std::make_pair(std::get<0>(tuple), std::get<1>(tuple));
}
+
+GpgME::Error QGpgMEDecryptVerifyJobPrivate::startIt()
+{
+ if (m_inputFilePath.isEmpty() || m_outputFilePath.isEmpty()) {
+ return Error::fromCode(GPG_ERR_INV_VALUE);
+ }
+
+ q->run([=](Context *ctx) {
+ return decrypt_verify_from_filename(ctx, m_inputFilePath, m_outputFilePath);
+ });
+
+ return {};
+}
+
#include "qgpgmedecryptverifyjob.moc"
diff --git a/lang/qt/tests/Makefile.am b/lang/qt/tests/Makefile.am
index 0007401c..df9c905f 100644
--- a/lang/qt/tests/Makefile.am
+++ b/lang/qt/tests/Makefile.am
@@ -89,6 +89,7 @@ t_wkdlookup_SOURCES = t-wkdlookup.cpp $(support_src)
t_import_SOURCES = t-import.cpp $(support_src)
t_revokekey_SOURCES = t-revokekey.cpp $(support_src)
t_setprimaryuserid_SOURCES = t-setprimaryuserid.cpp $(support_src)
+run_decryptverifyjob_SOURCES = run-decryptverifyjob.cpp
run_decryptverifyarchivejob_SOURCES = run-decryptverifyarchivejob.cpp
run_encryptarchivejob_SOURCES = run-encryptarchivejob.cpp
run_encryptjob_SOURCES = run-encryptjob.cpp
@@ -113,6 +114,7 @@ noinst_PROGRAMS = \
t-trustsignatures t-changeexpiryjob t-wkdlookup t-import t-revokekey \
t-setprimaryuserid \
run-decryptverifyarchivejob \
+ run-decryptverifyjob \
run-encryptarchivejob \
run-encryptjob \
run-importjob run-exportjob run-receivekeysjob run-refreshkeysjob \
diff --git a/lang/qt/tests/run-decryptverifyjob.cpp b/lang/qt/tests/run-decryptverifyjob.cpp
new file mode 100644
index 00000000..2ddfb1ae
--- /dev/null
+++ b/lang/qt/tests/run-decryptverifyjob.cpp
@@ -0,0 +1,163 @@
+/*
+ run-decryptverifyjob.cpp
+
+ This file is part of QGpgME's test suite.
+ Copyright (c) 2023 by g10 Code GmbH
+ Software engineering by Ingo Klöcker <[email protected]>
+
+ QGpgME is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ QGpgME 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.
+*/
+
+#ifdef HAVE_CONFIG_H
+ #include "config.h"
+#endif
+
+#include <protocol.h>
+#include <decryptverifyjob.h>
+
+#include <QCommandLineParser>
+#include <QCoreApplication>
+#include <QDebug>
+#include <QFile>
+#include <QTimer>
+
+#include <context.h>
+#include <decryptionresult.h>
+#include <verificationresult.h>
+
+#include <iostream>
+
+using namespace GpgME;
+
+std::ostream &operator<<(std::ostream &os, const QString &s)
+{
+ return os << s.toLocal8Bit().constData();
+}
+
+struct CommandLineOptions {
+ QString inputFile;
+ QString outputFile;
+ std::chrono::seconds cancelTimeout{0};
+};
+
+CommandLineOptions parseCommandLine(const QStringList &arguments)
+{
+ CommandLineOptions options;
+
+ QCommandLineParser parser;
+ parser.setApplicationDescription("Test program for DecryptVerifyJob");
+ parser.addHelpOption();
+ parser.addOptions({
+ {{"o", "output"}, "Write output to FILE.", "FILE"},
+ {"cancel-after", "Cancel the running job after SECONDS seconds.", "SECONDS"},
+ });
+ parser.addPositionalArgument("file", "File to decrypt", "FILE");
+
+ parser.process(arguments);
+
+ const auto args = parser.positionalArguments();
+ if (args.size() != 1) {
+ parser.showHelp(1);
+ }
+
+ options.inputFile = args.front();
+ options.outputFile = parser.value("output");
+ if (parser.isSet("cancel-after")) {
+ bool ok;
+ options.cancelTimeout = std::chrono::seconds{parser.value("cancel-after").toInt(&ok)};
+ if (!ok) {
+ options.cancelTimeout = std::chrono::seconds{-1};
+ }
+ }
+
+ return options;
+}
+
+int main(int argc, char **argv)
+{
+ GpgME::initializeLibrary();
+
+ QCoreApplication app{argc, argv};
+ app.setApplicationName("run-decryptverifyjob");
+
+ const auto options = parseCommandLine(app.arguments());
+ if (options.cancelTimeout.count() < 0) {
+ std::cerr << "Ignoring invalid timeout for cancel." << std::endl;
+ }
+
+ std::shared_ptr<QFile> output;
+ if (options.outputFile.isEmpty() || options.outputFile == QLatin1String{"-"}) {
+ output.reset(new QFile);
+ output->open(stdout, QIODevice::WriteOnly);
+ } else {
+ if (QFile::exists(options.outputFile)) {
+ qCritical() << "File" << options.outputFile << "exists. Bailing out.";
+ return 1;
+ }
+ }
+
+ auto job = QGpgME::openpgp()->decryptVerifyJob();
+ if (!job) {
+ std::cerr << "Error: Could not create job" << std::endl;
+ return 1;
+ }
+ QObject::connect(job,
+ &QGpgME::DecryptVerifyJob::result,
+ &app,
+ [](const GpgME::DecryptionResult &decryptionResult,
+ const GpgME::VerificationResult &verificationResult,
+ const QByteArray &,
+ const QString &auditLog,
+ const GpgME::Error &) {
+ std::cerr << "Diagnostics: " << auditLog << std::endl;
+ std::cerr << "Decryption Result: " << decryptionResult << std::endl;
+ std::cerr << "Verification Result: " << verificationResult << std::endl;
+ qApp->quit();
+ });
+ if (options.cancelTimeout.count() > 0) {
+ QTimer::singleShot(options.cancelTimeout, job, [job]() {
+ std::cerr << "Canceling job" << std::endl;
+ job->slotCancel();
+ });
+ }
+
+ std::shared_ptr<QFile> input;
+ GpgME::Error err;
+ if (output) {
+ input.reset(new QFile{options.inputFile});
+ input->open(QIODevice::ReadOnly);
+ job->start(input, output);
+ } else {
+ job->setInputFile(options.inputFile);
+ job->setOutputFile(options.outputFile);
+ err = job->startIt();
+ }
+ if (err) {
+ std::cerr << "Error: Starting the job failed: " << err.asString() << std::endl;
+ return 1;
+ }
+
+ return app.exec();
+}