aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIngo Klöcker <[email protected]>2023-12-21 08:38:52 +0000
committerIngo Klöcker <[email protected]>2023-12-21 10:49:23 +0000
commita44d84772d6197dfb977b411f6eb7cb29b08471b (patch)
treed3b679026236ab83faabc36b94a466b6787f593d
parentqt: Remove dead code (diff)
downloadgpgme-a44d84772d6197dfb977b411f6eb7cb29b08471b.tar.gz
gpgme-a44d84772d6197dfb977b411f6eb7cb29b08471b.zip
qt: Support writing/reading signed/encrypted files directly to/from file
* lang/qt/src/Makefile.am: Add new files. * lang/qt/src/job.cpp (EncryptJob, SignJob, SignEncryptJob): Move definition of constructor and destructor and inclusion of the moc file to the corresponding .cpp files. * lang/qt/src/encryptjob.cpp (EncryptJob): Define constructor. Define destructor as default. Include moc file. * lang/qt/src/encryptjob.cpp, lang/qt/src/encryptjob.h (EncryptJob): Add member functions setRecipients, recipients, setInputFile, inputFile, setOutputFile, outputFile, setEncryptionFlags, encryptionFlags. * lang/qt/src/encryptjob_p.h (EncryptJobPrivate): Add members m_recipients, m_inputFilePath, m_outputFilePath, m_encryptionFlags. * lang/qt/src/qgpgmeencryptjob.cpp (encrypt_to_filename): New. (QGpgMEEncryptJobPrivate::startIt): Start the job with the values from the member variables. * lang/qt/src/qgpgmesignencryptjob.cpp (sign_encrypt_to_filename): New. (QGpgMESignEncryptJobPrivate::startIt): Start the job with the values from the member variables. * lang/qt/src/qgpgmesignjob.cpp (class QGpgMESignJobPrivate): New. (QGpgMESignJob::QGpgMESignJob): Instantiate private job class. (sign_to_filename): New. * lang/qt/src/signencryptjob.cpp (SignEncryptJob): Define constructor. Define destructor as default. Include moc file. * lang/qt/src/signencryptjob.cpp, lang/qt/src/signencryptjob.h (SignEncryptJob): Add member functions setSigners, signers, setRecipients, recipients, setInputFile, inputFile, setOutputFile, outputFile, setEncryptionFlags, encryptionFlags. * lang/qt/src/signencryptjob_p.h (SignEncryptJobPrivate): Add members m_signers, m_recipients, m_inputFilePath, m_outputFilePath, m_encryptionFlags. * lang/qt/src/signjob.cpp: New. * lang/qt/src/signjob.h (SignJob): Add member functions setSigners, signers, setInputFile, inputFile, setOutputFile, outputFile, setSigningFlags, signingFlags. * lang/qt/src/signjob_p.h: New. * lang/qt/tests/Makefile.am: Add new test programs. * lang/qt/tests/run-encryptjob.cpp: New. * lang/qt/tests/run-signjob.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 signing and/or encrypting a file. GnuPG-bug-id: 6550
-rw-r--r--lang/qt/src/Makefile.am2
-rw-r--r--lang/qt/src/encryptjob.cpp57
-rw-r--r--lang/qt/src/encryptjob.h60
-rw-r--r--lang/qt/src/encryptjob_p.h11
-rw-r--r--lang/qt/src/job.cpp9
-rw-r--r--lang/qt/src/qgpgmeencryptjob.cpp67
-rw-r--r--lang/qt/src/qgpgmesignencryptjob.cpp86
-rw-r--r--lang/qt/src/qgpgmesignjob.cpp100
-rw-r--r--lang/qt/src/signencryptjob.cpp69
-rw-r--r--lang/qt/src/signencryptjob.h63
-rw-r--r--lang/qt/src/signencryptjob_p.h10
-rw-r--r--lang/qt/src/signjob.cpp98
-rw-r--r--lang/qt/src/signjob.h50
-rw-r--r--lang/qt/src/signjob_p.h55
-rw-r--r--lang/qt/tests/Makefile.am4
-rw-r--r--lang/qt/tests/run-encryptjob.cpp198
-rw-r--r--lang/qt/tests/run-signjob.cpp157
17 files changed, 1058 insertions, 38 deletions
diff --git a/lang/qt/src/Makefile.am b/lang/qt/src/Makefile.am
index 326db516..5e16373e 100644
--- a/lang/qt/src/Makefile.am
+++ b/lang/qt/src/Makefile.am
@@ -65,6 +65,7 @@ qgpgme_sources = \
signarchivejob.cpp \
signencryptjob.cpp \
signencryptarchivejob.cpp \
+ signjob.cpp \
dn.cpp cryptoconfig.cpp wkdlookupresult.cpp \
util.cpp \
wkdrefreshjob.cpp
@@ -226,6 +227,7 @@ private_qgpgme_headers = \
signarchivejob_p.h \
signencryptjob_p.h \
signencryptarchivejob_p.h \
+ signjob_p.h \
threadedjobmixin.h \
util.h \
wkdrefreshjob_p.h
diff --git a/lang/qt/src/encryptjob.cpp b/lang/qt/src/encryptjob.cpp
index 48a0e24e..d4337bad 100644
--- a/lang/qt/src/encryptjob.cpp
+++ b/lang/qt/src/encryptjob.cpp
@@ -40,6 +40,13 @@
using namespace QGpgME;
+EncryptJob::EncryptJob(QObject *parent)
+ : Job{parent}
+{
+}
+
+EncryptJob::~EncryptJob() = default;
+
void EncryptJob::setFileName(const QString &fileName)
{
auto d = jobPrivate<EncryptJobPrivate>(this);
@@ -63,3 +70,53 @@ GpgME::Data::Encoding EncryptJob::inputEncoding() const
auto d = jobPrivate<EncryptJobPrivate>(this);
return d->m_inputEncoding;
}
+
+void EncryptJob::setRecipients(const std::vector<GpgME::Key> &recipients)
+{
+ auto d = jobPrivate<EncryptJobPrivate>(this);
+ d->m_recipients = recipients;
+}
+
+std::vector<GpgME::Key> EncryptJob::recipients() const
+{
+ auto d = jobPrivate<EncryptJobPrivate>(this);
+ return d->m_recipients;
+}
+
+void EncryptJob::setInputFile(const QString &path)
+{
+ auto d = jobPrivate<EncryptJobPrivate>(this);
+ d->m_inputFilePath = path;
+}
+
+QString EncryptJob::inputFile() const
+{
+ auto d = jobPrivate<EncryptJobPrivate>(this);
+ return d->m_inputFilePath;
+}
+
+void EncryptJob::setOutputFile(const QString &path)
+{
+ auto d = jobPrivate<EncryptJobPrivate>(this);
+ d->m_outputFilePath = path;
+}
+
+QString EncryptJob::outputFile() const
+{
+ auto d = jobPrivate<EncryptJobPrivate>(this);
+ return d->m_outputFilePath;
+}
+
+void EncryptJob::setEncryptionFlags(GpgME::Context::EncryptionFlags flags)
+{
+ auto d = jobPrivate<EncryptJobPrivate>(this);
+ d->m_encryptionFlags = static_cast<GpgME::Context::EncryptionFlags>(flags | GpgME::Context::EncryptFile);
+}
+
+GpgME::Context::EncryptionFlags EncryptJob::encryptionFlags() const
+{
+ auto d = jobPrivate<EncryptJobPrivate>(this);
+ return d->m_encryptionFlags;
+}
+
+#include "encryptjob.moc"
diff --git a/lang/qt/src/encryptjob.h b/lang/qt/src/encryptjob.h
index ac3664fa..48b36598 100644
--- a/lang/qt/src/encryptjob.h
+++ b/lang/qt/src/encryptjob.h
@@ -72,6 +72,15 @@ namespace QGpgME
EncryptJob 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 and, optionally, recipients or flags.
+ 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 cipherText 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 EncryptJob will schedule it's own
destruction by calling QObject::deleteLater().
*/
@@ -81,15 +90,64 @@ class QGPGME_EXPORT EncryptJob : public Job
protected:
explicit EncryptJob(QObject *parent);
public:
- ~EncryptJob();
+ ~EncryptJob() override;
+ /**
+ * Sets the file name to embed in the encryption result.
+ *
+ * This is only used if one of the start() functions is used.
+ */
void setFileName(const QString &fileName);
QString fileName() const;
+ /**
+ * Sets the encoding of the plaintext.
+ *
+ * This is only used if one of the start() functions is used.
+ */
void setInputEncoding(GpgME::Data::Encoding);
GpgME::Data::Encoding inputEncoding() const;
/**
+ * Sets the keys to use for encryption.
+ *
+ * Used if the job is started with startIt().
+ */
+ void setRecipients(const std::vector<GpgME::Key> &recipients);
+ std::vector<GpgME::Key> recipients() const;
+
+ /**
+ * Sets the path of the file to encrypt.
+ *
+ * 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 encryption 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;
+
+ /**
+ * Sets the flags to use for encryption.
+ *
+ * Defaults to \c EncryptFile.
+ *
+ * Used if the job is started with startIt(). The \c EncryptFile flag is
+ * always assumed set.
+ */
+ void setEncryptionFlags(GpgME::Context::EncryptionFlags flags);
+ GpgME::Context::EncryptionFlags encryptionFlags() const;
+
+ /**
Starts the encryption operation. \a recipients is the a list of
keys to encrypt \a plainText to. Empty (null) keys are
ignored. If \a recipients is empty, performs symmetric
diff --git a/lang/qt/src/encryptjob_p.h b/lang/qt/src/encryptjob_p.h
index 9bb9e952..b92d784e 100644
--- a/lang/qt/src/encryptjob_p.h
+++ b/lang/qt/src/encryptjob_p.h
@@ -35,15 +35,24 @@
#define __QGPGME_ENCRYPTJOB_P_H__
#include "job_p.h"
-#include "data.h"
+
+#include <data.h>
+#include <key.h>
namespace QGpgME
{
struct EncryptJobPrivate : public JobPrivate
{
+ // used by start() functions
QString m_fileName;
GpgME::Data::Encoding m_inputEncoding;
+
+ // used by startIt()
+ std::vector<GpgME::Key> m_recipients;
+ QString m_inputFilePath;
+ QString m_outputFilePath;
+ GpgME::Context::EncryptionFlags m_encryptionFlags = GpgME::Context::EncryptFile;
};
}
diff --git a/lang/qt/src/job.cpp b/lang/qt/src/job.cpp
index 3e19e64a..3c9422bb 100644
--- a/lang/qt/src/job.cpp
+++ b/lang/qt/src/job.cpp
@@ -43,12 +43,9 @@
#include "keylistjob.h"
#include "listallkeysjob.h"
-#include "encryptjob.h"
#include "decryptjob.h"
#include "decryptverifyjob.h"
-#include "signjob.h"
#include "signkeyjob.h"
-#include "signencryptjob.h"
#include "verifydetachedjob.h"
#include "verifyopaquejob.h"
#include "keygenerationjob.h"
@@ -164,11 +161,8 @@ void QGpgME::Job::startNow()
make_job_subclass(KeyListJob)
make_job_subclass(ListAllKeysJob)
-make_job_subclass(EncryptJob)
make_job_subclass(DecryptJob)
make_job_subclass(DecryptVerifyJob)
-make_job_subclass(SignJob)
-make_job_subclass(SignEncryptJob)
make_job_subclass(SignKeyJob)
make_job_subclass(VerifyDetachedJob)
make_job_subclass(VerifyOpaqueJob)
@@ -202,11 +196,8 @@ make_job_subclass(SetPrimaryUserIDJob)
#include "keylistjob.moc"
#include "listallkeysjob.moc"
-#include "encryptjob.moc"
#include "decryptjob.moc"
#include "decryptverifyjob.moc"
-#include "signjob.moc"
-#include "signencryptjob.moc"
#include "signkeyjob.moc"
#include "verifydetachedjob.moc"
#include "verifyopaquejob.moc"
diff --git a/lang/qt/src/qgpgmeencryptjob.cpp b/lang/qt/src/qgpgmeencryptjob.cpp
index c11089ff..d254d8a4 100644
--- a/lang/qt/src/qgpgmeencryptjob.cpp
+++ b/lang/qt/src/qgpgmeencryptjob.cpp
@@ -40,13 +40,13 @@
#include "qgpgmeencryptjob.h"
-#include "encryptjob_p.h"
-
#include "dataprovider.h"
+#include "encryptjob_p.h"
+#include "util.h"
-#include "context.h"
-#include "encryptionresult.h"
-#include "data.h"
+#include <context.h>
+#include <data.h>
+#include <encryptionresult.h>
#include <QBuffer>
#include <QFileInfo>
@@ -72,11 +72,7 @@ public:
~QGpgMEEncryptJobPrivate() override = default;
private:
- GpgME::Error startIt() override
- {
- Q_ASSERT(!"Not supported by this Job class.");
- return Error::fromCode(GPG_ERR_NOT_SUPPORTED);
- }
+ GpgME::Error startIt() override;
void startNow() override
{
@@ -168,6 +164,44 @@ static QGpgMEEncryptJob::result_type encrypt_qba(Context *ctx, const std::vector
return encrypt(ctx, nullptr, recipients, buffer, std::shared_ptr<QIODevice>(), eflags, outputIsBsse64Encoded, inputEncoding, fileName);
}
+static QGpgMEEncryptJob::result_type encrypt_to_filename(Context *ctx,
+ const std::vector<Key> &recipients,
+ const QString &inputFilePath,
+ const QString &outputFilePath,
+ Context::EncryptionFlags flags)
+{
+ 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(EncryptionResult{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
+
+ flags = static_cast<Context::EncryptionFlags>(flags | Context::EncryptFile);
+ const auto encryptionResult = ctx->encrypt(recipients, indata, outdata, flags);
+
+ if (!encryptionResult.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(encryptionResult, QByteArray{}, log, ae);
+}
+
Error QGpgMEEncryptJob::start(const std::vector<Key> &recipients, const QByteArray &plainText, bool alwaysTrust)
{
run(std::bind(&encrypt_qba, std::placeholders::_1, recipients, plainText,
@@ -213,4 +247,17 @@ void QGpgMEEncryptJob::resultHook(const result_type &tuple)
mResult = std::get<0>(tuple);
}
+GpgME::Error QGpgMEEncryptJobPrivate::startIt()
+{
+ if (m_inputFilePath.isEmpty() || m_outputFilePath.isEmpty()) {
+ return Error::fromCode(GPG_ERR_INV_VALUE);
+ }
+
+ q->run([=](Context *ctx) {
+ return encrypt_to_filename(ctx, m_recipients, m_inputFilePath, m_outputFilePath, m_encryptionFlags);
+ });
+
+ return {};
+}
+
#include "qgpgmeencryptjob.moc"
diff --git a/lang/qt/src/qgpgmesignencryptjob.cpp b/lang/qt/src/qgpgmesignencryptjob.cpp
index 0f5642a9..27af7ae9 100644
--- a/lang/qt/src/qgpgmesignencryptjob.cpp
+++ b/lang/qt/src/qgpgmesignencryptjob.cpp
@@ -40,14 +40,14 @@
#include "qgpgmesignencryptjob.h"
-#include "signencryptjob_p.h"
-
#include "dataprovider.h"
+#include "signencryptjob_p.h"
+#include "util.h"
-#include "context.h"
-#include "data.h"
-#include "key.h"
-#include "exception.h"
+#include <context.h>
+#include <data.h>
+#include <exception.h>
+#include <key.h>
#include <QBuffer>
#include <QFileInfo>
@@ -73,11 +73,7 @@ public:
~QGpgMESignEncryptJobPrivate() override = default;
private:
- GpgME::Error startIt() override
- {
- Q_ASSERT(!"Not supported by this Job class.");
- return Error::fromCode(GPG_ERR_NOT_SUPPORTED);
- }
+ GpgME::Error startIt() override;
void startNow() override
{
@@ -171,6 +167,60 @@ static QGpgMESignEncryptJob::result_type sign_encrypt_qba(Context *ctx, const st
return sign_encrypt(ctx, nullptr, signers, recipients, buffer, std::shared_ptr<QIODevice>(), eflags, outputIsBsse64Encoded, fileName);
}
+static QGpgMESignEncryptJob::result_type sign_encrypt_to_filename(Context *ctx,
+ const std::vector<Key> &signers,
+ const std::vector<Key> &recipients,
+ const QString &inputFilePath,
+ const QString &outputFilePath,
+ Context::EncryptionFlags flags)
+{
+ 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(SigningResult{Error::fromCode(GPG_ERR_EEXIST)},
+ EncryptionResult{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
+
+ ctx->clearSigningKeys();
+ for (const Key &signer : signers) {
+ if (!signer.isNull()) {
+ if (const Error err = ctx->addSigningKey(signer)) {
+ return std::make_tuple(SigningResult{err}, EncryptionResult{}, QByteArray{}, QString{}, Error{});
+ }
+ }
+ }
+
+ flags = static_cast<Context::EncryptionFlags>(flags | Context::EncryptFile);
+ const auto results = ctx->signAndEncrypt(recipients, indata, outdata, flags);
+ const auto &signingResult = results.first;
+ const auto &encryptionResult = results.second;
+
+ if (!signingResult.error().code() && !encryptionResult.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(signingResult, encryptionResult, QByteArray{}, log, ae);
+}
+
Error QGpgMESignEncryptJob::start(const std::vector<Key> &signers, const std::vector<Key> &recipients, const QByteArray &plainText, bool alwaysTrust)
{
run(std::bind(&sign_encrypt_qba, std::placeholders::_1, signers, recipients, plainText, alwaysTrust ? Context::AlwaysTrust : Context::None, mOutputIsBase64Encoded, fileName()));
@@ -205,4 +255,18 @@ void QGpgMESignEncryptJob::resultHook(const result_type &tuple)
{
mResult = std::make_pair(std::get<0>(tuple), std::get<1>(tuple));
}
+
+GpgME::Error QGpgMESignEncryptJobPrivate::startIt()
+{
+ if (m_inputFilePath.isEmpty() || m_outputFilePath.isEmpty()) {
+ return Error::fromCode(GPG_ERR_INV_VALUE);
+ }
+
+ q->run([=](Context *ctx) {
+ return sign_encrypt_to_filename(ctx, m_signers, m_recipients, m_inputFilePath, m_outputFilePath, m_encryptionFlags);
+ });
+
+ return {};
+}
+
#include "qgpgmesignencryptjob.moc"
diff --git a/lang/qt/src/qgpgmesignjob.cpp b/lang/qt/src/qgpgmesignjob.cpp
index 1913157b..5c1d9622 100644
--- a/lang/qt/src/qgpgmesignjob.cpp
+++ b/lang/qt/src/qgpgmesignjob.cpp
@@ -39,23 +39,52 @@
#include "qgpgmesignjob.h"
#include "dataprovider.h"
+#include "signjob_p.h"
+#include "util.h"
-#include "context.h"
-#include "signingresult.h"
-#include "data.h"
+#include <context.h>
+#include <data.h>
+#include <signingresult.h>
#include <QBuffer>
-
+#include <QFile>
#include <cassert>
using namespace QGpgME;
using namespace GpgME;
+namespace
+{
+
+class QGpgMESignJobPrivate : public SignJobPrivate
+{
+ QGpgMESignJob *q = nullptr;
+
+public:
+ QGpgMESignJobPrivate(QGpgMESignJob *qq)
+ : q{qq}
+ {
+ }
+
+ ~QGpgMESignJobPrivate() override = default;
+
+private:
+ GpgME::Error startIt() override;
+
+ void startNow() override
+ {
+ q->run();
+ }
+};
+
+}
+
QGpgMESignJob::QGpgMESignJob(Context *context)
: mixin_type(context),
mOutputIsBase64Encoded(false)
{
+ setJobPrivate(this, std::unique_ptr<QGpgMESignJobPrivate>{new QGpgMESignJobPrivate{this}});
lateInitialization();
}
@@ -137,6 +166,56 @@ static QGpgMESignJob::result_type sign_qba(Context *ctx,
return sign(ctx, nullptr, signers, buffer, std::shared_ptr<QIODevice>(), mode, outputIsBsse64Encoded);
}
+static QGpgMESignJob::result_type sign_to_filename(Context *ctx,
+ const std::vector<Key> &signers,
+ const QString &inputFilePath,
+ const QString &outputFilePath,
+ SignatureMode flags)
+{
+ 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(SigningResult{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
+
+ ctx->clearSigningKeys();
+ for (const Key &signer : signers) {
+ if (!signer.isNull()) {
+ if (const Error err = ctx->addSigningKey(signer)) {
+ return std::make_tuple(SigningResult{err}, QByteArray{}, QString{}, Error{});
+ }
+ }
+ }
+
+ flags = static_cast<SignatureMode>(flags | SignFile);
+ const auto signingResult = ctx->sign(indata, outdata, flags);
+
+ if (!signingResult.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(signingResult, QByteArray{}, log, ae);
+}
+
Error QGpgMESignJob::start(const std::vector<Key> &signers, const QByteArray &plainText, SignatureMode mode)
{
run(std::bind(&sign_qba, std::placeholders::_1, signers, plainText, mode, mOutputIsBase64Encoded));
@@ -161,4 +240,17 @@ void QGpgMESignJob::resultHook(const result_type &tuple)
mResult = std::get<0>(tuple);
}
+GpgME::Error QGpgMESignJobPrivate::startIt()
+{
+ if (m_inputFilePath.isEmpty() || m_outputFilePath.isEmpty()) {
+ return Error::fromCode(GPG_ERR_INV_VALUE);
+ }
+
+ q->run([=](Context *ctx) {
+ return sign_to_filename(ctx, m_signers, m_inputFilePath, m_outputFilePath, m_signingFlags);
+ });
+
+ return {};
+}
+
#include "qgpgmesignjob.moc"
diff --git a/lang/qt/src/signencryptjob.cpp b/lang/qt/src/signencryptjob.cpp
index 2f4c5fd6..e82730e6 100644
--- a/lang/qt/src/signencryptjob.cpp
+++ b/lang/qt/src/signencryptjob.cpp
@@ -40,6 +40,13 @@
using namespace QGpgME;
+SignEncryptJob::SignEncryptJob(QObject *parent)
+ : Job{parent}
+{
+}
+
+SignEncryptJob::~SignEncryptJob() = default;
+
void SignEncryptJob::setFileName(const QString &fileName)
{
auto d = jobPrivate<SignEncryptJobPrivate>(this);
@@ -51,3 +58,65 @@ QString SignEncryptJob::fileName() const
auto d = jobPrivate<SignEncryptJobPrivate>(this);
return d->m_fileName;
}
+
+void SignEncryptJob::setSigners(const std::vector<GpgME::Key> &signers)
+{
+ auto d = jobPrivate<SignEncryptJobPrivate>(this);
+ d->m_signers = signers;
+}
+
+std::vector<GpgME::Key> SignEncryptJob::signers() const
+{
+ auto d = jobPrivate<SignEncryptJobPrivate>(this);
+ return d->m_signers;
+}
+
+void SignEncryptJob::setRecipients(const std::vector<GpgME::Key> &recipients)
+{
+ auto d = jobPrivate<SignEncryptJobPrivate>(this);
+ d->m_recipients = recipients;
+}
+
+std::vector<GpgME::Key> SignEncryptJob::recipients() const
+{
+ auto d = jobPrivate<SignEncryptJobPrivate>(this);
+ return d->m_recipients;
+}
+
+void SignEncryptJob::setInputFile(const QString &path)
+{
+ auto d = jobPrivate<SignEncryptJobPrivate>(this);
+ d->m_inputFilePath = path;
+}
+
+QString SignEncryptJob::inputFile() const
+{
+ auto d = jobPrivate<SignEncryptJobPrivate>(this);
+ return d->m_inputFilePath;
+}
+
+void SignEncryptJob::setOutputFile(const QString &path)
+{
+ auto d = jobPrivate<SignEncryptJobPrivate>(this);
+ d->m_outputFilePath = path;
+}
+
+QString SignEncryptJob::outputFile() const
+{
+ auto d = jobPrivate<SignEncryptJobPrivate>(this);
+ return d->m_outputFilePath;
+}
+
+void SignEncryptJob::setEncryptionFlags(GpgME::Context::EncryptionFlags flags)
+{
+ auto d = jobPrivate<SignEncryptJobPrivate>(this);
+ d->m_encryptionFlags = static_cast<GpgME::Context::EncryptionFlags>(flags | GpgME::Context::EncryptFile);
+}
+
+GpgME::Context::EncryptionFlags SignEncryptJob::encryptionFlags() const
+{
+ auto d = jobPrivate<SignEncryptJobPrivate>(this);
+ return d->m_encryptionFlags;
+}
+
+#include "signencryptjob.moc"
diff --git a/lang/qt/src/signencryptjob.h b/lang/qt/src/signencryptjob.h
index ebb866d1..54f4aa53 100644
--- a/lang/qt/src/signencryptjob.h
+++ b/lang/qt/src/signencryptjob.h
@@ -76,6 +76,15 @@ namespace QGpgME
SignEncryptJob 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 and, optionally, signers, recipients or flags.
+ 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 cipherText 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 SignEncryptJob will schedule it's
own destruction by calling QObject::deleteLater().
*/
@@ -85,12 +94,64 @@ class QGPGME_EXPORT SignEncryptJob : public Job
protected:
explicit SignEncryptJob(QObject *parent);
public:
- ~SignEncryptJob();
+ ~SignEncryptJob() override;
+ /**
+ * Sets the file name to embed in the encryption result.
+ *
+ * This is only used if one of the start() functions is used.
+ */
void setFileName(const QString &fileName);
QString fileName() const;
/**
+ * Sets the keys to use for signing.
+ *
+ * Used if the job is started with startIt().
+ */
+ void setSigners(const std::vector<GpgME::Key> &signers);
+ std::vector<GpgME::Key> signers() const;
+
+ /**
+ * Sets the keys to use for encryption.
+ *
+ * Used if the job is started with startIt().
+ */
+ void setRecipients(const std::vector<GpgME::Key> &recipients);
+ std::vector<GpgME::Key> recipients() const;
+
+ /**
+ * Sets the path of the file to encrypt.
+ *
+ * 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 encryption 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;
+
+ /**
+ * Sets the flags to use for encryption.
+ *
+ * Defaults to \c EncryptFile.
+ *
+ * Used if the job is started with startIt(). The \c EncryptFile flag is
+ * always assumed set.
+ */
+ void setEncryptionFlags(GpgME::Context::EncryptionFlags flags);
+ GpgME::Context::EncryptionFlags encryptionFlags() const;
+
+ /**
Starts the combined signing and encrypting operation. \a signers
is the list of keys to sign \a plainText with. \a recipients is
a list of keys to encrypt the signed \a plainText to. In both
diff --git a/lang/qt/src/signencryptjob_p.h b/lang/qt/src/signencryptjob_p.h
index 85afae26..600d752b 100644
--- a/lang/qt/src/signencryptjob_p.h
+++ b/lang/qt/src/signencryptjob_p.h
@@ -36,12 +36,22 @@
#include "job_p.h"
+#include <key.h>
+
namespace QGpgME
{
struct SignEncryptJobPrivate : public JobPrivate
{
+ // used by start() functions
QString m_fileName;
+
+ // used by startIt()
+ std::vector<GpgME::Key> m_signers;
+ std::vector<GpgME::Key> m_recipients;
+ QString m_inputFilePath;
+ QString m_outputFilePath;
+ GpgME::Context::EncryptionFlags m_encryptionFlags = GpgME::Context::EncryptFile;
};
}
diff --git a/lang/qt/src/signjob.cpp b/lang/qt/src/signjob.cpp
new file mode 100644
index 00000000..0a9c8651
--- /dev/null
+++ b/lang/qt/src/signjob.cpp
@@ -0,0 +1,98 @@
+/*
+ signjob.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 "signjob.h"
+#include "signjob_p.h"
+
+using namespace QGpgME;
+
+SignJob::SignJob(QObject *parent)
+ : Job{parent}
+{
+}
+
+SignJob::~SignJob() = default;
+
+void SignJob::setSigners(const std::vector<GpgME::Key> &signers)
+{
+ auto d = jobPrivate<SignJobPrivate>(this);
+ d->m_signers = signers;
+}
+
+std::vector<GpgME::Key> SignJob::signers() const
+{
+ auto d = jobPrivate<SignJobPrivate>(this);
+ return d->m_signers;
+}
+
+void SignJob::setInputFile(const QString &path)
+{
+ auto d = jobPrivate<SignJobPrivate>(this);
+ d->m_inputFilePath = path;
+}
+
+QString SignJob::inputFile() const
+{
+ auto d = jobPrivate<SignJobPrivate>(this);
+ return d->m_inputFilePath;
+}
+
+void SignJob::setOutputFile(const QString &path)
+{
+ auto d = jobPrivate<SignJobPrivate>(this);
+ d->m_outputFilePath = path;
+}
+
+QString SignJob::outputFile() const
+{
+ auto d = jobPrivate<SignJobPrivate>(this);
+ return d->m_outputFilePath;
+}
+
+void SignJob::setSigningFlags(GpgME::SignatureMode flags)
+{
+ auto d = jobPrivate<SignJobPrivate>(this);
+ d->m_signingFlags = static_cast<GpgME::SignatureMode>(flags | GpgME::SignFile);
+}
+
+GpgME::SignatureMode SignJob::signingFlags() const
+{
+ auto d = jobPrivate<SignJobPrivate>(this);
+ return d->m_signingFlags;
+}
+
+#include "signjob.moc"
diff --git a/lang/qt/src/signjob.h b/lang/qt/src/signjob.h
index c05231cc..273277b5 100644
--- a/lang/qt/src/signjob.h
+++ b/lang/qt/src/signjob.h
@@ -70,6 +70,15 @@ namespace QGpgME
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 and, optionally, signers or flags.
+ 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 signature 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 SignJob will schedule it's own
destruction by calling QObject::deleteLater().
*/
@@ -79,7 +88,46 @@ class QGPGME_EXPORT SignJob : public Job
protected:
explicit SignJob(QObject *parent);
public:
- ~SignJob();
+ ~SignJob() override;
+
+ /**
+ * Sets the keys to use for signing.
+ *
+ * Used if the job is started with startIt().
+ */
+ void setSigners(const std::vector<GpgME::Key> &signers);
+ std::vector<GpgME::Key> signers() const;
+
+ /**
+ * Sets the path of the file to sign.
+ *
+ * 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 signing 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;
+
+ /**
+ * Sets the flags to use for signing.
+ *
+ * Defaults to \c SignFile.
+ *
+ * Used if the job is started with startIt(). The \c SignFile flag is
+ * always assumed set.
+ */
+ void setSigningFlags(GpgME::SignatureMode flags);
+ GpgME::SignatureMode signingFlags() const;
/**
Starts the signing operation. \a signers is the list of keys to
diff --git a/lang/qt/src/signjob_p.h b/lang/qt/src/signjob_p.h
new file mode 100644
index 00000000..75309782
--- /dev/null
+++ b/lang/qt/src/signjob_p.h
@@ -0,0 +1,55 @@
+/*
+ signjob_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_SIGNJOB_P_H__
+#define __QGPGME_SIGNJOB_P_H__
+
+#include "job_p.h"
+
+#include <key.h>
+
+namespace QGpgME
+{
+
+struct SignJobPrivate : public JobPrivate
+{
+ // used by startIt()
+ std::vector<GpgME::Key> m_signers;
+ QString m_inputFilePath;
+ QString m_outputFilePath;
+ GpgME::SignatureMode m_signingFlags = GpgME::SignFile;
+};
+
+}
+
+#endif // __QGPGME_SIGNJOB_P_H__
diff --git a/lang/qt/tests/Makefile.am b/lang/qt/tests/Makefile.am
index 615daf6d..0007401c 100644
--- a/lang/qt/tests/Makefile.am
+++ b/lang/qt/tests/Makefile.am
@@ -91,12 +91,14 @@ t_revokekey_SOURCES = t-revokekey.cpp $(support_src)
t_setprimaryuserid_SOURCES = t-setprimaryuserid.cpp $(support_src)
run_decryptverifyarchivejob_SOURCES = run-decryptverifyarchivejob.cpp
run_encryptarchivejob_SOURCES = run-encryptarchivejob.cpp
+run_encryptjob_SOURCES = run-encryptjob.cpp
run_exportjob_SOURCES = run-exportjob.cpp
run_importjob_SOURCES = run-importjob.cpp
run_keyformailboxjob_SOURCES = run-keyformailboxjob.cpp
run_receivekeysjob_SOURCES = run-receivekeysjob.cpp
run_refreshkeysjob_SOURCES = run-refreshkeysjob.cpp
run_signarchivejob_SOURCES = run-signarchivejob.cpp
+run_signjob_SOURCES = run-signjob.cpp
run_wkdrefreshjob_SOURCES = run-wkdrefreshjob.cpp
nodist_t_keylist_SOURCES = $(moc_files)
@@ -112,8 +114,10 @@ noinst_PROGRAMS = \
t-setprimaryuserid \
run-decryptverifyarchivejob \
run-encryptarchivejob \
+ run-encryptjob \
run-importjob run-exportjob run-receivekeysjob run-refreshkeysjob \
run-signarchivejob \
+ run-signjob \
run-wkdrefreshjob
diff --git a/lang/qt/tests/run-encryptjob.cpp b/lang/qt/tests/run-encryptjob.cpp
new file mode 100644
index 00000000..c3d6c9ee
--- /dev/null
+++ b/lang/qt/tests/run-encryptjob.cpp
@@ -0,0 +1,198 @@
+/*
+ run-encryptjob.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 <encryptjob.h>
+#include <signencryptjob.h>
+
+#include <QCommandLineParser>
+#include <QCoreApplication>
+#include <QDebug>
+#include <QFile>
+#include <QTimer>
+
+#include <context.h>
+#include <encryptionresult.h>
+#include <signingresult.h>
+
+#include <iostream>
+
+using namespace GpgME;
+
+std::ostream &operator<<(std::ostream &os, const QString &s)
+{
+ return os << s.toLocal8Bit().constData();
+}
+
+struct CommandLineOptions {
+ bool armor = false;
+ bool sign = false;
+ QString inputFile;
+ QString outputFile;
+ std::chrono::seconds cancelTimeout{0};
+};
+
+CommandLineOptions parseCommandLine(const QStringList &arguments)
+{
+ CommandLineOptions options;
+
+ QCommandLineParser parser;
+ parser.setApplicationDescription("Test program for EncryptJob and SignEncryptJob");
+ parser.addHelpOption();
+ parser.addOptions({
+ {{"s", "sign"}, "Sign file before encryption."},
+ {{"o", "output"}, "Write output to FILE.", "FILE"},
+ {{"a", "armor"}, "Create ASCII armored output."},
+ {"cancel-after", "Cancel the running job after SECONDS seconds.", "SECONDS"},
+ });
+ parser.addPositionalArgument("file", "File to encrypt", "FILE");
+
+ parser.process(arguments);
+
+ const auto args = parser.positionalArguments();
+ if (args.size() != 1) {
+ parser.showHelp(1);
+ }
+
+ options.armor = parser.isSet("armor");
+ options.sign = parser.isSet("sign");
+ 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-encryptjob");
+
+ 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;
+ }
+ }
+
+ std::shared_ptr<QFile> input;
+
+ if (options.sign) {
+ auto job = QGpgME::openpgp()->signEncryptJob(options.armor);
+ if (!job) {
+ std::cerr << "Error: Could not create job" << std::endl;
+ return 1;
+ }
+ QObject::connect(job, &QGpgME::SignEncryptJob::result, &app, [](const GpgME::SigningResult &signingResult, const GpgME::EncryptionResult &encryptionResult, const QByteArray &, const QString &auditLog, const GpgME::Error &) {
+ std::cerr << "Diagnostics: " << auditLog << std::endl;
+ std::cerr << "Signing Result: " << signingResult << std::endl;
+ std::cerr << "Encryption Result: " << encryptionResult << std::endl;
+ qApp->quit();
+ });
+ if (options.cancelTimeout.count() > 0) {
+ QTimer::singleShot(options.cancelTimeout, job, [job]() {
+ std::cerr << "Canceling job" << std::endl;
+ job->slotCancel();
+ });
+ }
+
+ GpgME::Error err;
+ if (output) {
+ input.reset(new QFile{options.inputFile});
+ input->open(QIODevice::ReadOnly);
+ job->start({}, {}, input, output, GpgME::Context::None);
+ } 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;
+ }
+ } else {
+ auto job = QGpgME::openpgp()->encryptJob(options.armor);
+ if (!job) {
+ std::cerr << "Error: Could not create job" << std::endl;
+ return 1;
+ }
+ QObject::connect(job, &QGpgME::EncryptJob::result, &app, [](const GpgME::EncryptionResult &result, const QByteArray &, const QString &auditLog, const GpgME::Error &) {
+ std::cerr << "Diagnostics: " << auditLog << std::endl;
+ std::cerr << "Result: " << result << std::endl;
+ qApp->quit();
+ });
+ if (options.cancelTimeout.count() > 0) {
+ QTimer::singleShot(options.cancelTimeout, job, [job]() {
+ std::cerr << "Canceling job" << std::endl;
+ job->slotCancel();
+ });
+ }
+
+ GpgME::Error err;
+ if (output) {
+ input.reset(new QFile{options.inputFile});
+ input->open(QIODevice::ReadOnly);
+ job->start({}, input, output, GpgME::Context::None);
+ } 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();
+}
diff --git a/lang/qt/tests/run-signjob.cpp b/lang/qt/tests/run-signjob.cpp
new file mode 100644
index 00000000..14b0a406
--- /dev/null
+++ b/lang/qt/tests/run-signjob.cpp
@@ -0,0 +1,157 @@
+/*
+ run-signjob.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 <signjob.h>
+
+#include <QCommandLineParser>
+#include <QCoreApplication>
+#include <QDebug>
+#include <QFile>
+#include <QTimer>
+
+#include <context.h>
+#include <signingresult.h>
+
+#include <iostream>
+
+using namespace GpgME;
+
+std::ostream &operator<<(std::ostream &os, const QString &s)
+{
+ return os << s.toLocal8Bit().constData();
+}
+
+struct CommandLineOptions {
+ bool armor;
+ QString inputFile;
+ QString outputFile;
+ std::chrono::seconds cancelTimeout{0};
+};
+
+CommandLineOptions parseCommandLine(const QStringList &arguments)
+{
+ CommandLineOptions options;
+
+ QCommandLineParser parser;
+ parser.setApplicationDescription("Test program for SignJob");
+ parser.addHelpOption();
+ parser.addOptions({
+ {{"o", "output"}, "Write output to FILE.", "FILE"},
+ {{"a", "armor"}, "Create ASCII armored output."},
+ {"cancel-after", "Cancel the running job after SECONDS seconds.", "SECONDS"},
+ });
+ parser.addPositionalArgument("file", "File to sign", "FILE");
+
+ parser.process(arguments);
+
+ const auto args = parser.positionalArguments();
+ if (args.size() != 1) {
+ parser.showHelp(1);
+ }
+
+ options.armor = parser.isSet("armor");
+ 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-signjob");
+
+ 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()->signJob(options.armor);
+ if (!job) {
+ std::cerr << "Error: Could not create job" << std::endl;
+ return 1;
+ }
+ QObject::connect(job, &QGpgME::SignJob::result, &app, [](const GpgME::SigningResult &result, const QByteArray &, const QString &auditLog, const GpgME::Error &) {
+ std::cerr << "Diagnostics: " << auditLog << std::endl;
+ std::cerr << "Result: " << result << 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, GpgME::NormalSignatureMode);
+ } 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();
+}