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 @@ -1,432 +1,434 @@ # Makefile.am for GPGMEPP. # Copyright (C) 2016 Bundesamt für Sicherheit in der Informationstechnik # Software engineering by Intevation GmbH # # This file is part of GPGMEPP. # # GPGME-CL 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. # # GPGME-CL 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 Lesser 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., 59 Temple Place - Suite 330, Boston, MA # 02111-1307, USA if WANT_QT5 lib_LTLIBRARIES = libqgpgme.la endif if WANT_QT6 lib_LTLIBRARIES = libqgpgmeqt6.la endif EXTRA_DIST = QGpgmeConfig.cmake.in.in QGpgmeConfigVersion.cmake.in \ qgpgme_debug.h qgpgme_version.h.in \ QGpgmeConfig-w32.cmake.in.in QGpgmeQt6Config.cmake.in.in \ QGpgmeQt6Config-w32.cmake.in.in QGpgmeQt6ConfigVersion.cmake.in qgpgme_sources = \ cleaner.cpp \ dataprovider.cpp \ debug.cpp \ decryptverifyarchivejob.cpp \ encryptarchivejob.cpp \ filelistdataprovider.cpp \ job.cpp multideletejob.cpp qgpgmeadduseridjob.cpp \ qgpgmeaddexistingsubkeyjob.cpp \ qgpgmebackend.cpp qgpgmechangeexpiryjob.cpp qgpgmechangeownertrustjob.cpp \ qgpgmechangepasswdjob.cpp qgpgmedecryptjob.cpp \ qgpgmedecryptverifyarchivejob.cpp \ qgpgmedecryptverifyjob.cpp qgpgmedeletejob.cpp qgpgmedownloadjob.cpp \ qgpgmeencryptarchivejob.cpp \ qgpgmeencryptjob.cpp qgpgmeexportjob.cpp qgpgmeimportfromkeyserverjob.cpp \ qgpgmeimportjob.cpp qgpgmekeygenerationjob.cpp qgpgmekeylistjob.cpp \ listallkeysjob.cpp qgpgmelistallkeysjob.cpp qgpgmenewcryptoconfig.cpp \ qgpgmereceivekeysjob.cpp \ qgpgmerefreshsmimekeysjob.cpp \ qgpgmerevokekeyjob.cpp \ qgpgmesetprimaryuseridjob.cpp \ qgpgmesignarchivejob.cpp \ qgpgmesignencryptjob.cpp \ qgpgmesignencryptarchivejob.cpp \ qgpgmesignjob.cpp qgpgmesignkeyjob.cpp qgpgmeverifydetachedjob.cpp \ qgpgmeverifyopaquejob.cpp qgpgmewkdlookupjob.cpp threadedjobmixin.cpp \ qgpgmewkdrefreshjob.cpp \ qgpgmekeyformailboxjob.cpp qgpgme_debug.cpp \ qgpgmetofupolicyjob.cpp qgpgmequickjob.cpp \ defaultkeygenerationjob.cpp qgpgmewkspublishjob.cpp \ qgpgmegpgcardjob.cpp changeexpiryjob.cpp encryptjob.cpp importjob.cpp \ signarchivejob.cpp \ signencryptjob.cpp \ signencryptarchivejob.cpp \ + signjob.cpp \ dn.cpp cryptoconfig.cpp wkdlookupresult.cpp \ util.cpp \ wkdrefreshjob.cpp # If you add one here make sure that you also add one in camelcase qgpgme_headers= \ abstractimportjob.h \ addexistingsubkeyjob.h \ adduseridjob.h \ changeexpiryjob.h \ changeownertrustjob.h \ changepasswdjob.h \ dataprovider.h \ debug.h \ decryptjob.h \ decryptverifyarchivejob.h \ decryptverifyjob.h \ downloadjob.h \ encryptarchivejob.h \ encryptjob.h \ exportjob.h \ filelistdataprovider.h \ hierarchicalkeylistjob.h \ job.h \ keyformailboxjob.h \ multideletejob.h \ protocol.h \ qgpgme_export.h \ qgpgmenewcryptoconfig.h \ quickjob.h \ receivekeysjob.h \ revokekeyjob.h \ setprimaryuseridjob.h \ specialjob.h \ signarchivejob.h \ signjob.h \ signkeyjob.h \ signencryptjob.h \ signencryptarchivejob.h \ verifyopaquejob.h \ refreshkeysjob.h \ cryptoconfig.h \ deletejob.h \ importfromkeyserverjob.h \ importjob.h \ keygenerationjob.h \ keylistjob.h \ listallkeysjob.h \ verifydetachedjob.h \ defaultkeygenerationjob.h \ tofupolicyjob.h \ wkdlookupjob.h \ wkdlookupresult.h \ wkdrefreshjob.h \ wkspublishjob.h \ gpgcardjob.h \ dn.h camelcase_headers= \ AddExistingSubkeyJob \ AddUserIDJob \ AbstractImportJob \ ChangeExpiryJob \ ChangeOwnerTrustJob \ ChangePasswdJob \ DataProvider \ Debug \ DecryptJob \ DecryptVerifyArchiveJob \ DecryptVerifyJob \ DN \ DownloadJob \ EncryptArchiveJob \ EncryptJob \ ExportJob \ FileListDataProvider \ HierarchicalKeyKistJob \ Job \ MultiDeleteJob \ Protocol \ QGpgMENewCryptoConfig \ QuickJob \ ReceiveKeysJob \ RevokeKeyJob \ SetPrimaryUserIDJob \ SpecialJob \ SignArchiveJob \ SignJob \ SignKeyJob \ SignEncryptArchiveJob \ SignEncryptJob \ VerifyOpaqueJob \ RefreshKeysJob \ CryptoConfig \ DeleteJob \ ImportFromKeyserverJob \ ImportJob \ KeyGenerationJob \ KeyListJob \ ListAllKeysJob \ VerifyDetachedJob \ KeyForMailboxJob \ DefaultKeyGenerationJob \ WKDLookupJob \ WKDLookupResult \ WKDRefreshJob \ WKSPublishJob \ TofuPolicyJob \ GpgCardJob private_qgpgme_headers = \ changeexpiryjob_p.h \ cleaner.h \ decryptverifyarchivejob_p.h \ encryptarchivejob_p.h \ encryptjob_p.h \ importjob_p.h \ listallkeysjob_p.h \ qgpgme_export.h \ protocol_p.h \ job_p.h \ qgpgmeaddexistingsubkeyjob.h \ qgpgmeadduseridjob.h \ qgpgmebackend.h \ qgpgmechangeexpiryjob.h \ qgpgmechangeownertrustjob.h \ qgpgmechangepasswdjob.h \ qgpgmedecryptjob.h \ qgpgmedecryptverifyarchivejob.h \ qgpgmedecryptverifyjob.h \ qgpgmedeletejob.h \ qgpgmedownloadjob.h \ qgpgmeencryptarchivejob.h \ qgpgmeencryptjob.h \ qgpgmeexportjob.h \ qgpgmeimportfromkeyserverjob.h \ qgpgmeimportjob.h \ qgpgmekeygenerationjob.h \ qgpgmekeylistjob.h \ qgpgmelistallkeysjob.h \ qgpgmereceivekeysjob.h \ qgpgmerefreshsmimekeysjob.h \ qgpgmerevokekeyjob.h \ qgpgmesetprimaryuseridjob.h \ qgpgmesignarchivejob.h \ qgpgmesignencryptjob.h \ qgpgmesignencryptarchivejob.h \ qgpgmesignjob.h \ qgpgmesignkeyjob.h \ qgpgmeverifydetachedjob.h \ qgpgmeverifyopaquejob.h \ qgpgmewkdlookupjob.h \ qgpgmewkdrefreshjob.h \ qgpgmekeyformailboxjob.h \ qgpgmewkspublishjob.h \ qgpgmetofupolicyjob.h \ qgpgmegpgcardjob.h \ qgpgmequickjob.h \ signarchivejob_p.h \ signencryptjob_p.h \ signencryptarchivejob_p.h \ + signjob_p.h \ threadedjobmixin.h \ util.h \ wkdrefreshjob_p.h qgpgme_moc_sources = \ abstractimportjob.moc \ addexistingsubkeyjob.moc \ adduseridjob.moc \ changeexpiryjob.moc \ changeownertrustjob.moc \ changepasswdjob.moc \ cleaner.moc \ decryptjob.moc \ decryptverifyarchivejob.moc \ decryptverifyjob.moc \ deletejob.moc \ downloadjob.moc \ encryptarchivejob.moc \ encryptjob.moc \ exportjob.moc \ hierarchicalkeylistjob.moc \ importfromkeyserverjob.moc \ importjob.moc \ job.moc \ keygenerationjob.moc \ keylistjob.moc \ listallkeysjob.moc \ multideletejob.moc \ qgpgmeaddexistingsubkeyjob.moc \ qgpgmeadduseridjob.moc \ qgpgmechangeexpiryjob.moc \ qgpgmechangeownertrustjob.moc \ qgpgmechangepasswdjob.moc \ qgpgmedecryptjob.moc \ qgpgmedecryptverifyarchivejob.moc \ qgpgmedecryptverifyjob.moc \ qgpgmedeletejob.moc \ qgpgmedownloadjob.moc \ qgpgmeencryptarchivejob.moc \ qgpgmeencryptjob.moc \ qgpgmeexportjob.moc \ qgpgmeimportfromkeyserverjob.moc \ qgpgmeimportjob.moc \ qgpgmekeygenerationjob.moc \ qgpgmekeylistjob.moc \ qgpgmelistallkeysjob.moc \ qgpgmereceivekeysjob.moc \ qgpgmerefreshsmimekeysjob.moc \ qgpgmerevokekeyjob.moc \ qgpgmesetprimaryuseridjob.moc \ qgpgmesignarchivejob.moc \ qgpgmesignencryptjob.moc \ qgpgmesignencryptarchivejob.moc \ qgpgmesignjob.moc \ qgpgmesignkeyjob.moc \ qgpgmeverifydetachedjob.moc \ qgpgmeverifyopaquejob.moc \ qgpgmewkdlookupjob.moc \ qgpgmewkdrefreshjob.moc \ qgpgmewkspublishjob.moc \ tofupolicyjob.moc \ qgpgmetofupolicyjob.moc \ receivekeysjob.moc \ refreshkeysjob.moc \ revokekeyjob.moc \ setprimaryuseridjob.moc \ signarchivejob.moc \ signencryptjob.moc \ signencryptarchivejob.moc \ signjob.moc \ signkeyjob.moc \ specialjob.moc \ verifydetachedjob.moc \ verifyopaquejob.moc \ wkdlookupjob.moc \ wkdrefreshjob.moc \ keyformailboxjob.moc \ wkspublishjob.moc \ qgpgmekeyformailboxjob.moc \ defaultkeygenerationjob.moc \ quickjob.moc \ qgpgmequickjob.moc \ gpgcardjob.moc \ qgpgmegpgcardjob.moc qgpgmeincludedir = $(includedir)/qgpgme qgpgmeinclude_HEADERS = $(qgpgme_headers) camelcaseincludedir = $(includedir)/QGpgME camelcaseinclude_HEADERS = $(camelcase_headers) nodist_qgpgmeinclude_HEADERS = qgpgme_version.h if WANT_QT5 AM_CPPFLAGS = -I$(top_srcdir)/lang/cpp/src -I$(top_builddir)/src \ @GPGME_QT5_CFLAGS@ @GPG_ERROR_CFLAGS@ @LIBASSUAN_CFLAGS@ \ -DBUILDING_QGPGME -Wsuggest-override \ -Wzero-as-null-pointer-constant libqgpgme_la_SOURCES = $(qgpgme_sources) $(qgpgme_headers) $(private_qgpgme_headers) libqgpgme_la_LIBADD = ../../cpp/src/libgpgmepp.la ../../../src/libgpgme.la \ @LIBASSUAN_LIBS@ @GPG_ERROR_LIBS@ @GPGME_QT5_LIBS@ libqgpgme_la_LDFLAGS = -no-undefined -version-info \ @LIBQGPGME_LT_CURRENT@:@LIBQGPGME_LT_REVISION@:@LIBQGPGME_LT_AGE@ endif if WANT_QT6 AM_CPPFLAGS = -I$(top_srcdir)/lang/cpp/src -I$(top_builddir)/src \ @GPGME_QT6_CFLAGS@ @GPG_ERROR_CFLAGS@ @LIBASSUAN_CFLAGS@ \ -DBUILDING_QGPGME -Wsuggest-override \ -Wzero-as-null-pointer-constant libqgpgmeqt6_la_SOURCES = $(qgpgme_sources) $(qgpgme_headers) $(private_qgpgme_headers) libqgpgmeqt6_la_LIBADD = ../../cpp/src/libgpgmepp.la ../../../src/libgpgme.la \ @LIBASSUAN_LIBS@ @GPG_ERROR_LIBS@ @GPGME_QT6_LIBS@ libqgpgmeqt6_la_LDFLAGS = -no-undefined -version-info \ @LIBQGPGME_LT_CURRENT@:@LIBQGPGME_LT_REVISION@:@LIBQGPGME_LT_AGE@ endif if HAVE_MACOS_SYSTEM libsuffix=.dylib else libsuffix=.so endif if WANT_QT5 if HAVE_W32_SYSTEM QGpgmeConfig.cmake: QGpgmeConfig-w32.cmake.in sed -e 's|[@]resolved_bindir@|$(bindir)|g' < "$<" | \ sed -e 's|[@]resolved_libdir@|$(libdir)|g' | \ sed -e 's|[@]resolved_includedir@|$(includedir)|g' > $@ else QGpgmeConfig.cmake: QGpgmeConfig.cmake.in sed -e 's|[@]resolved_libdir@|$(libdir)|g' < "$<" | \ sed -e 's|[@]libsuffix@|$(libsuffix)|g' | \ sed -e 's|[@]resolved_includedir@|$(includedir)|g' > $@ endif endif if WANT_QT6 if HAVE_W32_SYSTEM QGpgmeQt6Config.cmake: QGpgmeQt6Config-w32.cmake.in sed -e 's|[@]resolved_bindir@|$(bindir)|g' < "$<" | \ sed -e 's|[@]resolved_libdir@|$(libdir)|g' | \ sed -e 's|[@]resolved_includedir@|$(includedir)|g' > $@ else QGpgmeQt6Config.cmake: QGpgmeQt6Config.cmake.in sed -e 's|[@]resolved_libdir@|$(libdir)|g' < "$<" | \ sed -e 's|[@]libsuffix@|$(libsuffix)|g' | \ sed -e 's|[@]resolved_includedir@|$(includedir)|g' > $@ endif endif $(camelcase_headers): Makefile.am echo -n "#include \"qgpgme/" > "$@" echo -n "$@" | tr '[:upper:]' '[:lower:]' >> "$@" echo ".h\"" >> "$@" if WANT_QT5 install-cmake-files: QGpgmeConfig.cmake QGpgmeConfigVersion.cmake -$(INSTALL) -d $(DESTDIR)$(libdir)/cmake/QGpgme $(INSTALL) -m 644 QGpgmeConfig.cmake \ $(DESTDIR)$(libdir)/cmake/QGpgme/QGpgmeConfig.cmake $(INSTALL) -m 644 QGpgmeConfigVersion.cmake \ $(DESTDIR)$(libdir)/cmake/QGpgme/QGpgmeConfigVersion.cmake uninstall-cmake-files: -rm $(DESTDIR)$(libdir)/cmake/QGpgme/QGpgmeConfigVersion.cmake -rm $(DESTDIR)$(libdir)/cmake/QGpgme/QGpgmeConfig.cmake -rmdir $(DESTDIR)$(libdir)/cmake/QGpgme/ endif if WANT_QT6 install-cmake-files: QGpgmeQt6Config.cmake QGpgmeQt6ConfigVersion.cmake -$(INSTALL) -d $(DESTDIR)$(libdir)/cmake/QGpgmeQt6 $(INSTALL) -m 644 QGpgmeQt6Config.cmake \ $(DESTDIR)$(libdir)/cmake/QGpgmeQt6/QGpgmeQt6Config.cmake $(INSTALL) -m 644 QGpgmeQt6ConfigVersion.cmake \ $(DESTDIR)$(libdir)/cmake/QGpgmeQt6/QGpgmeQt6ConfigVersion.cmake uninstall-cmake-files: -rm $(DESTDIR)$(libdir)/cmake/QGpgmeQt6/QGpgmeQt6ConfigVersion.cmake -rm $(DESTDIR)$(libdir)/cmake/QGpgmeQt6/QGpgmeQt6Config.cmake -rmdir $(DESTDIR)$(libdir)/cmake/QGpgmeQt6/ endif install-data-local: install-cmake-files uninstall-local: uninstall-cmake-files BUILT_SOURCES = $(qgpgme_moc_sources) $(camelcase_headers) CLEANFILES = $(qgpgme_moc_sources) $(camelcase_headers) QGpgmeConfig.cmake \ qgpgme_version.h QGpgmeConfig.cmake.in \ QGpgmeConfig-w32.cmake.in QGpgmeConfigVersion.cmake \ QGpgmeQt6Config.cmake.in QGpgmeQt6Config-w32.cmake.in \ QGpgmeQt6Config.cmake QGpgmeQt6ConfigVersion.cmake if WANT_QT5 nodist_libqgpgme_la_SOURCES = $(qgpgme_moc_sources) endif if WANT_QT6 nodist_libqgpgmeqt6_la_SOURCES = $(qgpgme_moc_sources) endif .h.moc: $(MOC) `test -f '$<' || echo '$(srcdir)/'`$< -o $@ 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 @@ -1,65 +1,122 @@ /* encryptjob.cpp This file is part of qgpgme, the Qt API binding for gpgme Copyright (c) 2022 g10 Code GmbH Software engineering by Ingo Klöcker 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 "encryptjob.h" #include "encryptjob_p.h" using namespace QGpgME; +EncryptJob::EncryptJob(QObject *parent) + : Job{parent} +{ +} + +EncryptJob::~EncryptJob() = default; + void EncryptJob::setFileName(const QString &fileName) { auto d = jobPrivate(this); d->m_fileName = fileName; } QString EncryptJob::fileName() const { auto d = jobPrivate(this); return d->m_fileName; } void EncryptJob::setInputEncoding(GpgME::Data::Encoding inputEncoding) { auto d = jobPrivate(this); d->m_inputEncoding = inputEncoding; } GpgME::Data::Encoding EncryptJob::inputEncoding() const { auto d = jobPrivate(this); return d->m_inputEncoding; } + +void EncryptJob::setRecipients(const std::vector &recipients) +{ + auto d = jobPrivate(this); + d->m_recipients = recipients; +} + +std::vector EncryptJob::recipients() const +{ + auto d = jobPrivate(this); + return d->m_recipients; +} + +void EncryptJob::setInputFile(const QString &path) +{ + auto d = jobPrivate(this); + d->m_inputFilePath = path; +} + +QString EncryptJob::inputFile() const +{ + auto d = jobPrivate(this); + return d->m_inputFilePath; +} + +void EncryptJob::setOutputFile(const QString &path) +{ + auto d = jobPrivate(this); + d->m_outputFilePath = path; +} + +QString EncryptJob::outputFile() const +{ + auto d = jobPrivate(this); + return d->m_outputFilePath; +} + +void EncryptJob::setEncryptionFlags(GpgME::Context::EncryptionFlags flags) +{ + auto d = jobPrivate(this); + d->m_encryptionFlags = static_cast(flags | GpgME::Context::EncryptFile); +} + +GpgME::Context::EncryptionFlags EncryptJob::encryptionFlags() const +{ + auto d = jobPrivate(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 @@ -1,144 +1,202 @@ /* encryptjob.h This file is part of qgpgme, the Qt API binding for gpgme Copyright (c) 2004, 2007 Klarälvdalens Datakonsult AB Copyright (c) 2016 by Bundesamt für Sicherheit in der Informationstechnik Software engineering by Intevation GmbH Copyright (c) 2022 g10 Code GmbH Software engineering by Ingo Klöcker 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 __KLEO_ENCRYPTJOB_H__ #define __KLEO_ENCRYPTJOB_H__ #include "job.h" #include "data.h" #include #include #ifdef BUILDING_QGPGME # include "context.h" #else # include #endif class QByteArray; class QIODevice; namespace GpgME { class Error; class Key; class EncryptionResult; } namespace QGpgME { /** @short An abstract base class for asynchronous encrypters To use a EncryptJob, first obtain an instance from the CryptoBackend implementation, connect the progress() and result() signals to suitable slots and then start the encryption with a call to start(). This call might fail, in which case the 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(). */ class QGPGME_EXPORT EncryptJob : public Job { Q_OBJECT 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 &recipients); + std::vector 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 (passphrase) encryption. If \a alwaysTrust is true, validity checking for the keys will not be performed, but full validity assumed for all keys without further checks. */ virtual GpgME::Error start(const std::vector &recipients, const QByteArray &plainText, bool alwaysTrust = false) = 0; /*! \overload If \a cipherText is non-null, the ciphertext is written there. Otherwise, it will be delivered in the second argument of result(). */ virtual void start(const std::vector &recipients, const std::shared_ptr &plainText, const std::shared_ptr &cipherText = std::shared_ptr(), bool alwaysTrust = false) = 0; virtual GpgME::EncryptionResult exec(const std::vector &recipients, const QByteArray &plainText, bool alwaysTrust, QByteArray &cipherText) = 0; /*! This is a hack to request BASE64 output (instead of whatever comes out normally). */ virtual void setOutputIsBase64Encoded(bool) = 0; /** Like start but with an additional argument for EncryptionFlags for * more flexibility. */ virtual void start(const std::vector &recipients, const std::shared_ptr &plainText, const std::shared_ptr &cipherText = std::shared_ptr(), const GpgME::Context::EncryptionFlags flags = GpgME::Context::None) = 0; /** Like exec but with an additional argument for EncryptionFlags for * more flexibility. */ virtual GpgME::EncryptionResult exec(const std::vector &recipients, const QByteArray &plainText, const GpgME::Context::EncryptionFlags flags, QByteArray &cipherText) = 0; Q_SIGNALS: void result(const GpgME::EncryptionResult &result, const QByteArray &cipherText, const QString &auditLogAsHtml = QString(), const GpgME::Error &auditLogError = GpgME::Error()); }; } #endif // __KLEO_ENCRYPTJOB_H__ 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 @@ -1,51 +1,60 @@ /* encryptjob_p.h This file is part of qgpgme, the Qt API binding for gpgme Copyright (c) 2022,2023 g10 Code GmbH Software engineering by Ingo Klöcker 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_ENCRYPTJOB_P_H__ #define __QGPGME_ENCRYPTJOB_P_H__ #include "job_p.h" -#include "data.h" + +#include +#include namespace QGpgME { struct EncryptJobPrivate : public JobPrivate { + // used by start() functions QString m_fileName; GpgME::Data::Encoding m_inputEncoding; + + // used by startIt() + std::vector m_recipients; + QString m_inputFilePath; + QString m_outputFilePath; + GpgME::Context::EncryptionFlags m_encryptionFlags = GpgME::Context::EncryptFile; }; } #endif // __QGPGME_ENCRYPTJOB_P_H__ 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 @@ -1,235 +1,226 @@ /* job.cpp This file is part of qgpgme, the Qt API binding for gpgme Copyright (c) 2004,2005 Klarälvdalens Datakonsult AB Copyright (c) 2016 by Bundesamt für Sicherheit in der Informationstechnik Software engineering by Intevation GmbH Copyright (c) 2021 g10 Code GmbH Software engineering by Ingo Klöcker 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 "job.h" #include "job_p.h" #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" #include "importjob.h" #include "importfromkeyserverjob.h" #include "exportjob.h" #include "changeexpiryjob.h" #include "changeownertrustjob.h" #include "changepasswdjob.h" #include "downloadjob.h" #include "deletejob.h" #include "refreshkeysjob.h" #include "addexistingsubkeyjob.h" #include "adduseridjob.h" #include "specialjob.h" #include "keyformailboxjob.h" #include "wkdlookupjob.h" #include "wkspublishjob.h" #include "tofupolicyjob.h" #include "threadedjobmixin.h" #include "quickjob.h" #include "gpgcardjob.h" #include "receivekeysjob.h" #include "revokekeyjob.h" #include "setprimaryuseridjob.h" #include #include #include #include namespace { typedef std::unordered_map> JobPrivateHash; Q_GLOBAL_STATIC(JobPrivateHash, d_func) } void QGpgME::setJobPrivate(const Job *job, std::unique_ptr d) { auto &ref = d_func()->operator[](job); ref = std::move(d); } const QGpgME::JobPrivate *QGpgME::getJobPrivate(const Job *job) { return d_func()->operator[](job).get(); } QGpgME::JobPrivate *QGpgME::getJobPrivate(Job *job) { return d_func()->operator[](job).get(); } QGpgME::Job::Job(QObject *parent) : QObject(parent) { if (QCoreApplication *app = QCoreApplication::instance()) { connect(app, &QCoreApplication::aboutToQuit, this, &Job::slotCancel); } } QGpgME::Job::~Job() { ::d_func()->erase(this); } QString QGpgME::Job::auditLogAsHtml() const { qDebug() << "QGpgME::Job::auditLogAsHtml() should be reimplemented in Kleo::Job subclasses!"; return QString(); } GpgME::Error QGpgME::Job::auditLogError() const { qDebug() << "QGpgME::Job::auditLogError() should be reimplemented in Kleo::Job subclasses!"; return GpgME::Error::fromCode(GPG_ERR_NOT_IMPLEMENTED); } bool QGpgME::Job::isAuditLogSupported() const { return auditLogError().code() != GPG_ERR_NOT_IMPLEMENTED; } QMap QGpgME::g_context_map; /* static */ GpgME::Context *QGpgME::Job::context(QGpgME::Job *job) { return QGpgME::g_context_map.value (job, nullptr); } GpgME::Error QGpgME::Job::startIt() { auto d = getJobPrivate(this); Q_ASSERT(d && "This Job class has no JobPrivate class"); return d->startIt(); } void QGpgME::Job::startNow() { auto d = getJobPrivate(this); Q_ASSERT(d && "This Job class has no JobPrivate class"); d->startNow(); } #define make_job_subclass_ext(x,y) \ QGpgME::x::x( QObject * parent ) : y( parent ) {} \ QGpgME::x::~x() {} #define make_job_subclass(x) make_job_subclass_ext(x,Job) 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) make_job_subclass(KeyGenerationJob) make_job_subclass(AbstractImportJob) make_job_subclass_ext(ImportJob, AbstractImportJob) make_job_subclass_ext(ImportFromKeyserverJob, AbstractImportJob) make_job_subclass_ext(ReceiveKeysJob, AbstractImportJob) make_job_subclass(ExportJob) make_job_subclass(ChangeExpiryJob) make_job_subclass(ChangeOwnerTrustJob) make_job_subclass(ChangePasswdJob) make_job_subclass(DownloadJob) make_job_subclass(DeleteJob) make_job_subclass(RefreshKeysJob) make_job_subclass(AddExistingSubkeyJob) make_job_subclass(AddUserIDJob) make_job_subclass(SpecialJob) make_job_subclass(KeyForMailboxJob) make_job_subclass(WKDLookupJob) make_job_subclass(WKSPublishJob) make_job_subclass(TofuPolicyJob) make_job_subclass(QuickJob) make_job_subclass(GpgCardJob) make_job_subclass(RevokeKeyJob) make_job_subclass(SetPrimaryUserIDJob) #undef make_job_subclass #include "job.moc" #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" #include "keygenerationjob.moc" #include "abstractimportjob.moc" #include "importjob.moc" #include "importfromkeyserverjob.moc" #include "exportjob.moc" #include "changeexpiryjob.moc" #include "changeownertrustjob.moc" #include "changepasswdjob.moc" #include "downloadjob.moc" #include "deletejob.moc" #include "refreshkeysjob.moc" #include "addexistingsubkeyjob.moc" #include "adduseridjob.moc" #include "specialjob.moc" #include "keyformailboxjob.moc" #include "wkdlookupjob.moc" #include "wkspublishjob.moc" #include "tofupolicyjob.moc" #include "quickjob.moc" #include "gpgcardjob.moc" #include "receivekeysjob.moc" #include "revokekeyjob.moc" #include "setprimaryuseridjob.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 @@ -1,216 +1,263 @@ /* qgpgmeencryptjob.cpp This file is part of qgpgme, the Qt API binding for gpgme Copyright (c) 2004,2007,2008 Klarälvdalens Datakonsult AB Copyright (c) 2016 by Bundesamt für Sicherheit in der Informationstechnik Software engineering by Intevation GmbH Copyright (c) 2022,2023 g10 Code GmbH Software engineering by Ingo Klöcker 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 "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 +#include +#include #include #include #include using namespace QGpgME; using namespace GpgME; namespace { class QGpgMEEncryptJobPrivate : public EncryptJobPrivate { QGpgMEEncryptJob *q = nullptr; public: QGpgMEEncryptJobPrivate(QGpgMEEncryptJob *qq) : q{qq} { } ~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 { q->run(); } }; } QGpgMEEncryptJob::QGpgMEEncryptJob(Context *context) : mixin_type(context), mOutputIsBase64Encoded(false) { setJobPrivate(this, std::unique_ptr{new QGpgMEEncryptJobPrivate{this}}); lateInitialization(); } QGpgMEEncryptJob::~QGpgMEEncryptJob() {} void QGpgMEEncryptJob::setOutputIsBase64Encoded(bool on) { mOutputIsBase64Encoded = on; } static QGpgMEEncryptJob::result_type encrypt(Context *ctx, QThread *thread, const std::vector &recipients, const std::weak_ptr &plainText_, const std::weak_ptr &cipherText_, const Context::EncryptionFlags eflags, bool outputIsBsse64Encoded, Data::Encoding inputEncoding, const QString &fileName) { const std::shared_ptr plainText = plainText_.lock(); const std::shared_ptr cipherText = cipherText_.lock(); const _detail::ToThreadMover ctMover(cipherText, thread); const _detail::ToThreadMover ptMover(plainText, thread); QGpgME::QIODeviceDataProvider in(plainText); Data indata(&in); indata.setEncoding(inputEncoding); if (!plainText->isSequential()) { indata.setSizeHint(plainText->size()); } const auto pureFileName = QFileInfo{fileName}.fileName().toStdString(); if (!pureFileName.empty()) { indata.setFileName(pureFileName.c_str()); } if (!cipherText) { QGpgME::QByteArrayDataProvider out; Data outdata(&out); if (outputIsBsse64Encoded) { outdata.setEncoding(Data::Base64Encoding); } const EncryptionResult res = ctx->encrypt(recipients, indata, outdata, eflags); Error ae; const QString log = _detail::audit_log_as_html(ctx, ae); return std::make_tuple(res, out.data(), log, ae); } else { QGpgME::QIODeviceDataProvider out(cipherText); Data outdata(&out); if (outputIsBsse64Encoded) { outdata.setEncoding(Data::Base64Encoding); } const EncryptionResult res = ctx->encrypt(recipients, indata, outdata, eflags); Error ae; const QString log = _detail::audit_log_as_html(ctx, ae); return std::make_tuple(res, QByteArray(), log, ae); } } static QGpgMEEncryptJob::result_type encrypt_qba(Context *ctx, const std::vector &recipients, const QByteArray &plainText, const Context::EncryptionFlags eflags, bool outputIsBsse64Encoded, Data::Encoding inputEncoding, const QString &fileName) { const std::shared_ptr buffer(new QBuffer); buffer->setData(plainText); if (!buffer->open(QIODevice::ReadOnly)) { assert(!"This should never happen: QBuffer::open() failed"); } return encrypt(ctx, nullptr, recipients, buffer, std::shared_ptr(), eflags, outputIsBsse64Encoded, inputEncoding, fileName); } +static QGpgMEEncryptJob::result_type encrypt_to_filename(Context *ctx, + const std::vector &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(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 &recipients, const QByteArray &plainText, bool alwaysTrust) { run(std::bind(&encrypt_qba, std::placeholders::_1, recipients, plainText, alwaysTrust ? Context::AlwaysTrust : Context::None, mOutputIsBase64Encoded, inputEncoding(), fileName())); return Error(); } void QGpgMEEncryptJob::start(const std::vector &recipients, const std::shared_ptr &plainText, const std::shared_ptr &cipherText, const Context::EncryptionFlags eflags) { run(std::bind(&encrypt, std::placeholders::_1, std::placeholders::_2, recipients, std::placeholders::_3, std::placeholders::_4, eflags, mOutputIsBase64Encoded, inputEncoding(), fileName()), plainText, cipherText); } EncryptionResult QGpgMEEncryptJob::exec(const std::vector &recipients, const QByteArray &plainText, const Context::EncryptionFlags eflags, QByteArray &cipherText) { const result_type r = encrypt_qba(context(), recipients, plainText, eflags, mOutputIsBase64Encoded, inputEncoding(), fileName()); cipherText = std::get<1>(r); resultHook(r); return mResult; } void QGpgMEEncryptJob::start(const std::vector &recipients, const std::shared_ptr &plainText, const std::shared_ptr &cipherText, bool alwaysTrust) { return start(recipients, plainText, cipherText, alwaysTrust ? Context::AlwaysTrust : Context::None); } EncryptionResult QGpgMEEncryptJob::exec(const std::vector &recipients, const QByteArray &plainText, bool alwaysTrust, QByteArray &cipherText) { return exec(recipients, plainText, alwaysTrust ? Context::AlwaysTrust : Context::None, cipherText); } 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 @@ -1,208 +1,272 @@ /* qgpgmesignencryptjob.cpp This file is part of qgpgme, the Qt API binding for gpgme Copyright (c) 2004, 2007 Klarälvdalens Datakonsult AB Copyright (c) 2016 by Bundesamt für Sicherheit in der Informationstechnik Software engineering by Intevation GmbH Copyright (c) 2022,2023 g10 Code GmbH Software engineering by Ingo Klöcker 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 "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 +#include +#include +#include #include #include #include using namespace QGpgME; using namespace GpgME; namespace { class QGpgMESignEncryptJobPrivate : public SignEncryptJobPrivate { QGpgMESignEncryptJob *q = nullptr; public: QGpgMESignEncryptJobPrivate(QGpgMESignEncryptJob *qq) : q{qq} { } ~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 { q->run(); } }; } QGpgMESignEncryptJob::QGpgMESignEncryptJob(Context *context) : mixin_type(context), mOutputIsBase64Encoded(false) { setJobPrivate(this, std::unique_ptr{new QGpgMESignEncryptJobPrivate{this}}); lateInitialization(); } QGpgMESignEncryptJob::~QGpgMESignEncryptJob() {} void QGpgMESignEncryptJob::setOutputIsBase64Encoded(bool on) { mOutputIsBase64Encoded = on; } static QGpgMESignEncryptJob::result_type sign_encrypt(Context *ctx, QThread *thread, const std::vector &signers, const std::vector &recipients, const std::weak_ptr &plainText_, const std::weak_ptr &cipherText_, const Context::EncryptionFlags eflags, bool outputIsBsse64Encoded, const QString &fileName) { const std::shared_ptr &plainText = plainText_.lock(); const std::shared_ptr &cipherText = cipherText_.lock(); const _detail::ToThreadMover ctMover(cipherText, thread); const _detail::ToThreadMover ptMover(plainText, thread); QGpgME::QIODeviceDataProvider in(plainText); Data indata(&in); if (!plainText->isSequential()) { indata.setSizeHint(plainText->size()); } const auto pureFileName = QFileInfo{fileName}.fileName().toStdString(); if (!pureFileName.empty()) { indata.setFileName(pureFileName.c_str()); } 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()); } } } if (!cipherText) { QGpgME::QByteArrayDataProvider out; Data outdata(&out); if (outputIsBsse64Encoded) { outdata.setEncoding(Data::Base64Encoding); } const std::pair res = ctx->signAndEncrypt(recipients, indata, outdata, eflags); Error ae; const QString log = _detail::audit_log_as_html(ctx, ae); return std::make_tuple(res.first, res.second, out.data(), log, ae); } else { QGpgME::QIODeviceDataProvider out(cipherText); Data outdata(&out); if (outputIsBsse64Encoded) { outdata.setEncoding(Data::Base64Encoding); } const std::pair res = ctx->signAndEncrypt(recipients, indata, outdata, eflags); Error ae; const QString log = _detail::audit_log_as_html(ctx, ae); return std::make_tuple(res.first, res.second, QByteArray(), log, ae); } } static QGpgMESignEncryptJob::result_type sign_encrypt_qba(Context *ctx, const std::vector &signers, const std::vector &recipients, const QByteArray &plainText, const Context::EncryptionFlags eflags, bool outputIsBsse64Encoded, const QString &fileName) { const std::shared_ptr buffer(new QBuffer); buffer->setData(plainText); if (!buffer->open(QIODevice::ReadOnly)) { assert(!"This should never happen: QBuffer::open() failed"); } return sign_encrypt(ctx, nullptr, signers, recipients, buffer, std::shared_ptr(), eflags, outputIsBsse64Encoded, fileName); } +static QGpgMESignEncryptJob::result_type sign_encrypt_to_filename(Context *ctx, + const std::vector &signers, + const std::vector &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(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 &signers, const std::vector &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())); return Error(); } void QGpgMESignEncryptJob::start(const std::vector &signers, const std::vector &recipients, const std::shared_ptr &plainText, const std::shared_ptr &cipherText, const Context::EncryptionFlags eflags) { run(std::bind(&sign_encrypt, std::placeholders::_1, std::placeholders::_2, signers, recipients, std::placeholders::_3, std::placeholders::_4, eflags, mOutputIsBase64Encoded, fileName()), plainText, cipherText); } void QGpgMESignEncryptJob::start(const std::vector &signers, const std::vector &recipients, const std::shared_ptr &plainText, const std::shared_ptr &cipherText, bool alwaysTrust) { return start(signers, recipients, plainText, cipherText, alwaysTrust ? Context::AlwaysTrust : Context::None); } std::pair QGpgMESignEncryptJob::exec(const std::vector &signers, const std::vector &recipients, const QByteArray &plainText, const Context::EncryptionFlags eflags, QByteArray &cipherText) { const result_type r = sign_encrypt_qba(context(), signers, recipients, plainText, eflags, mOutputIsBase64Encoded, fileName()); cipherText = std::get<2>(r); resultHook(r); return mResult; } std::pair QGpgMESignEncryptJob::exec(const std::vector &signers, const std::vector &recipients, const QByteArray &plainText, bool alwaysTrust, QByteArray &cipherText) { return exec(signers, recipients, plainText, alwaysTrust ? Context::AlwaysTrust : Context::None, cipherText); } 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 @@ -1,164 +1,256 @@ /* qgpgmesignjob.cpp This file is part of qgpgme, the Qt API binding for gpgme Copyright (c) 2004,2007,2008 Klarälvdalens Datakonsult AB Copyright (c) 2016 by Bundesamt für Sicherheit in der Informationstechnik Software engineering by Intevation GmbH 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 "qgpgmesignjob.h" #include "dataprovider.h" +#include "signjob_p.h" +#include "util.h" -#include "context.h" -#include "signingresult.h" -#include "data.h" +#include +#include +#include #include - +#include #include 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{new QGpgMESignJobPrivate{this}}); lateInitialization(); } QGpgMESignJob::~QGpgMESignJob() {} void QGpgMESignJob::setOutputIsBase64Encoded(bool on) { mOutputIsBase64Encoded = on; } static QGpgMESignJob::result_type sign(Context *ctx, QThread *thread, const std::vector &signers, const std::weak_ptr &plainText_, const std::weak_ptr &signature_, SignatureMode mode, bool outputIsBsse64Encoded) { const std::shared_ptr plainText = plainText_.lock(); const std::shared_ptr signature = signature_.lock(); const _detail::ToThreadMover ptMover(plainText, thread); const _detail::ToThreadMover sgMover(signature, thread); QGpgME::QIODeviceDataProvider in(plainText); Data indata(&in); if (!plainText->isSequential()) { indata.setSizeHint(plainText->size()); } 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()); } } } if (!signature) { QGpgME::QByteArrayDataProvider out; Data outdata(&out); if (outputIsBsse64Encoded) { outdata.setEncoding(Data::Base64Encoding); } const SigningResult res = ctx->sign(indata, outdata, mode); Error ae; const QString log = _detail::audit_log_as_html(ctx, ae); return std::make_tuple(res, out.data(), log, ae); } else { QGpgME::QIODeviceDataProvider out(signature); Data outdata(&out); if (outputIsBsse64Encoded) { outdata.setEncoding(Data::Base64Encoding); } const SigningResult res = ctx->sign(indata, outdata, mode); Error ae; const QString log = _detail::audit_log_as_html(ctx, ae); return std::make_tuple(res, QByteArray(), log, ae); } } static QGpgMESignJob::result_type sign_qba(Context *ctx, const std::vector &signers, const QByteArray &plainText, SignatureMode mode, bool outputIsBsse64Encoded) { const std::shared_ptr buffer(new QBuffer); buffer->setData(plainText); if (!buffer->open(QIODevice::ReadOnly)) { assert(!"This should never happen: QBuffer::open() failed"); } return sign(ctx, nullptr, signers, buffer, std::shared_ptr(), mode, outputIsBsse64Encoded); } +static QGpgMESignJob::result_type sign_to_filename(Context *ctx, + const std::vector &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(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 &signers, const QByteArray &plainText, SignatureMode mode) { run(std::bind(&sign_qba, std::placeholders::_1, signers, plainText, mode, mOutputIsBase64Encoded)); return Error(); } void QGpgMESignJob::start(const std::vector &signers, const std::shared_ptr &plainText, const std::shared_ptr &signature, SignatureMode mode) { run(std::bind(&sign, std::placeholders::_1, std::placeholders::_2, signers, std::placeholders::_3, std::placeholders::_4, mode, mOutputIsBase64Encoded), plainText, signature); } SigningResult QGpgMESignJob::exec(const std::vector &signers, const QByteArray &plainText, SignatureMode mode, QByteArray &signature) { const result_type r = sign_qba(context(), signers, plainText, mode, mOutputIsBase64Encoded); signature = std::get<1>(r); resultHook(r); return mResult; } 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 @@ -1,53 +1,122 @@ /* signencryptjob.cpp This file is part of qgpgme, the Qt API binding for gpgme Copyright (c) 2022 g10 Code GmbH Software engineering by Ingo Klöcker 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 "signencryptjob.h" #include "signencryptjob_p.h" using namespace QGpgME; +SignEncryptJob::SignEncryptJob(QObject *parent) + : Job{parent} +{ +} + +SignEncryptJob::~SignEncryptJob() = default; + void SignEncryptJob::setFileName(const QString &fileName) { auto d = jobPrivate(this); d->m_fileName = fileName; } QString SignEncryptJob::fileName() const { auto d = jobPrivate(this); return d->m_fileName; } + +void SignEncryptJob::setSigners(const std::vector &signers) +{ + auto d = jobPrivate(this); + d->m_signers = signers; +} + +std::vector SignEncryptJob::signers() const +{ + auto d = jobPrivate(this); + return d->m_signers; +} + +void SignEncryptJob::setRecipients(const std::vector &recipients) +{ + auto d = jobPrivate(this); + d->m_recipients = recipients; +} + +std::vector SignEncryptJob::recipients() const +{ + auto d = jobPrivate(this); + return d->m_recipients; +} + +void SignEncryptJob::setInputFile(const QString &path) +{ + auto d = jobPrivate(this); + d->m_inputFilePath = path; +} + +QString SignEncryptJob::inputFile() const +{ + auto d = jobPrivate(this); + return d->m_inputFilePath; +} + +void SignEncryptJob::setOutputFile(const QString &path) +{ + auto d = jobPrivate(this); + d->m_outputFilePath = path; +} + +QString SignEncryptJob::outputFile() const +{ + auto d = jobPrivate(this); + return d->m_outputFilePath; +} + +void SignEncryptJob::setEncryptionFlags(GpgME::Context::EncryptionFlags flags) +{ + auto d = jobPrivate(this); + d->m_encryptionFlags = static_cast(flags | GpgME::Context::EncryptFile); +} + +GpgME::Context::EncryptionFlags SignEncryptJob::encryptionFlags() const +{ + auto d = jobPrivate(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 @@ -1,157 +1,218 @@ /* signencryptjob.h This file is part of qgpgme, the Qt API binding for gpgme Copyright (c) 2004, 2007 Klarälvdalens Datakonsult AB Copyright (c) 2016 by Bundesamt für Sicherheit in der Informationstechnik Software engineering by Intevation GmbH Copyright (c) 2022 g10 Code GmbH Software engineering by Ingo Klöcker 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 __KLEO_SIGNENCRYPTJOB_H__ #define __KLEO_SIGNENCRYPTJOB_H__ #include "job.h" #include "qgpgme_export.h" #ifdef BUILDING_QGPGME # include "global.h" # include "context.h" #else # include # include #endif #include #include #include class QByteArray; class QIODevice; namespace GpgME { class Error; class Key; class SigningResult; class EncryptionResult; } namespace QGpgME { /** @short An abstract base class for asynchronous combined signing and encrypting To use a SignEncryptJob, first obtain an instance from the CryptoBackend implementation, connect the progress() and result() signals to suitable slots and then start the operation with a call to start(). This call might fail, in which case the 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(). */ class QGPGME_EXPORT SignEncryptJob : public Job { Q_OBJECT 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 &signers); + std::vector signers() const; + + /** + * Sets the keys to use for encryption. + * + * Used if the job is started with startIt(). + */ + void setRecipients(const std::vector &recipients); + std::vector 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 lists, empty (null) keys are ignored. If \a alwaysTrust is true, validity checking for the \em recipient keys will not be performed, but full validity assumed for all \em recipient keys without further checks. */ virtual GpgME::Error start(const std::vector &signers, const std::vector &recipients, const QByteArray &plainText, bool alwaysTrust = false) = 0; /*! \overload If \a cipherText is non-null, the ciphertext is written there. Otherwise, it will be delivered in the third argument of result(). */ virtual void start(const std::vector &signers, const std::vector &recipients, const std::shared_ptr &plainText, const std::shared_ptr &cipherText = std::shared_ptr(), bool alwaysTrust = false) = 0; virtual std::pair exec(const std::vector &signers, const std::vector &recipients, const QByteArray &plainText, bool alwaysTrust, QByteArray &cipherText) = 0; /*! This is a hack to request BASE64 output (instead of whatever comes out normally). */ virtual void setOutputIsBase64Encoded(bool) = 0; /** Like start but with an additional argument for EncryptionFlags for * more flexibility. */ virtual void start(const std::vector &signers, const std::vector &recipients, const std::shared_ptr &plainText, const std::shared_ptr &cipherText = std::shared_ptr(), const GpgME::Context::EncryptionFlags flags = GpgME::Context::None) = 0; /** Like exec but with an additional argument for EncryptionFlags for * more flexibility. */ virtual std::pair exec(const std::vector &signers, const std::vector &recipients, const QByteArray &plainText, const GpgME::Context::EncryptionFlags flags, QByteArray &cipherText) = 0; Q_SIGNALS: void result(const GpgME::SigningResult &signingresult, const GpgME::EncryptionResult &encryptionresult, const QByteArray &cipherText, const QString &auditLogAsHtml = QString(), const GpgME::Error &auditLogError = GpgME::Error()); }; } #endif // __KLEO_SIGNENCRYPTJOB_H__ 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 @@ -1,49 +1,59 @@ /* signencryptjob_p.h This file is part of qgpgme, the Qt API binding for gpgme Copyright (c) 2022,2023 g10 Code GmbH Software engineering by Ingo Klöcker 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_SIGNENCRYPTJOB_P_H__ #define __QGPGME_SIGNENCRYPTJOB_P_H__ #include "job_p.h" +#include + namespace QGpgME { struct SignEncryptJobPrivate : public JobPrivate { + // used by start() functions QString m_fileName; + + // used by startIt() + std::vector m_signers; + std::vector m_recipients; + QString m_inputFilePath; + QString m_outputFilePath; + GpgME::Context::EncryptionFlags m_encryptionFlags = GpgME::Context::EncryptFile; }; } #endif // __QGPGME_SIGNENCRYPTJOB_P_H__ diff --git a/lang/qt/src/encryptjob.cpp b/lang/qt/src/signjob.cpp similarity index 55% copy from lang/qt/src/encryptjob.cpp copy to lang/qt/src/signjob.cpp index 48a0e24e..0a9c8651 100644 --- a/lang/qt/src/encryptjob.cpp +++ b/lang/qt/src/signjob.cpp @@ -1,65 +1,98 @@ /* - encryptjob.cpp + signjob.cpp This file is part of qgpgme, the Qt API binding for gpgme - Copyright (c) 2022 g10 Code GmbH + Copyright (c) 2023 g10 Code GmbH Software engineering by Ingo Klöcker 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 "encryptjob.h" -#include "encryptjob_p.h" +#include "signjob.h" +#include "signjob_p.h" using namespace QGpgME; -void EncryptJob::setFileName(const QString &fileName) +SignJob::SignJob(QObject *parent) + : Job{parent} { - auto d = jobPrivate(this); - d->m_fileName = fileName; } -QString EncryptJob::fileName() const +SignJob::~SignJob() = default; + +void SignJob::setSigners(const std::vector &signers) { - auto d = jobPrivate(this); - return d->m_fileName; + auto d = jobPrivate(this); + d->m_signers = signers; } -void EncryptJob::setInputEncoding(GpgME::Data::Encoding inputEncoding) +std::vector SignJob::signers() const { - auto d = jobPrivate(this); - d->m_inputEncoding = inputEncoding; + auto d = jobPrivate(this); + return d->m_signers; } -GpgME::Data::Encoding EncryptJob::inputEncoding() const +void SignJob::setInputFile(const QString &path) { - auto d = jobPrivate(this); - return d->m_inputEncoding; + auto d = jobPrivate(this); + d->m_inputFilePath = path; } + +QString SignJob::inputFile() const +{ + auto d = jobPrivate(this); + return d->m_inputFilePath; +} + +void SignJob::setOutputFile(const QString &path) +{ + auto d = jobPrivate(this); + d->m_outputFilePath = path; +} + +QString SignJob::outputFile() const +{ + auto d = jobPrivate(this); + return d->m_outputFilePath; +} + +void SignJob::setSigningFlags(GpgME::SignatureMode flags) +{ + auto d = jobPrivate(this); + d->m_signingFlags = static_cast(flags | GpgME::SignFile); +} + +GpgME::SignatureMode SignJob::signingFlags() const +{ + auto d = jobPrivate(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 @@ -1,121 +1,169 @@ /* signjob.h This file is part of qgpgme, the Qt API binding for gpgme Copyright (c) 2004, 2007 Klarälvdalens Datakonsult AB Copyright (c) 2016 by Bundesamt für Sicherheit in der Informationstechnik Software engineering by Intevation GmbH 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 __KLEO_SIGNJOB_H__ #define __KLEO_SIGNJOB_H__ #include "job.h" #ifdef BUILDING_QGPGME # include "global.h" #else # include #endif #include #include class QByteArray; class QIODevice; namespace GpgME { class Error; class Key; class SigningResult; } namespace QGpgME { /** @short An abstract base class for asynchronous signing To use a SignJob, first obtain an instance from the CryptoBackend implementation, connect the progress() and result() signals to suitable slots and then start the signing with a call to start(). This call might fail, in which case the SignJob 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 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(). */ class QGPGME_EXPORT SignJob : public Job { Q_OBJECT 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 &signers); + std::vector 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 sign \a plainText with. Empty (null) keys are ignored. */ virtual GpgME::Error start(const std::vector &signers, const QByteArray &plainText, GpgME::SignatureMode mode) = 0; /*! \overload If \a signature is non-null the signature is written there. Otherwise, it will be delivered in the second argument of result(). */ virtual void start(const std::vector &signers, const std::shared_ptr &plainText, const std::shared_ptr &signature, GpgME::SignatureMode mode) = 0; virtual GpgME::SigningResult exec(const std::vector &signers, const QByteArray &plainText, GpgME::SignatureMode mode, QByteArray &signature) = 0; /*! This is a hack to request BASE64 output (instead of whatever comes out normally). */ virtual void setOutputIsBase64Encoded(bool) = 0; Q_SIGNALS: void result(const GpgME::SigningResult &result, const QByteArray &signature, const QString &auditLogAsHtml = QString(), const GpgME::Error &auditLogError = GpgME::Error()); }; } #endif // __KLEO_SIGNJOB_H__ diff --git a/lang/qt/src/signencryptjob_p.h b/lang/qt/src/signjob_p.h similarity index 79% copy from lang/qt/src/signencryptjob_p.h copy to lang/qt/src/signjob_p.h index 85afae26..75309782 100644 --- a/lang/qt/src/signencryptjob_p.h +++ b/lang/qt/src/signjob_p.h @@ -1,49 +1,55 @@ /* - signencryptjob_p.h + signjob_p.h This file is part of qgpgme, the Qt API binding for gpgme - Copyright (c) 2022,2023 g10 Code GmbH + Copyright (c) 2023 g10 Code GmbH Software engineering by Ingo Klöcker 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_SIGNENCRYPTJOB_P_H__ -#define __QGPGME_SIGNENCRYPTJOB_P_H__ +#ifndef __QGPGME_SIGNJOB_P_H__ +#define __QGPGME_SIGNJOB_P_H__ #include "job_p.h" +#include + namespace QGpgME { -struct SignEncryptJobPrivate : public JobPrivate +struct SignJobPrivate : public JobPrivate { - QString m_fileName; + // used by startIt() + std::vector m_signers; + QString m_inputFilePath; + QString m_outputFilePath; + GpgME::SignatureMode m_signingFlags = GpgME::SignFile; }; } -#endif // __QGPGME_SIGNENCRYPTJOB_P_H__ +#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 @@ -1,148 +1,152 @@ # Makefile.am - Makefile for GPGME Qt tests. # Copyright (C) 2016 Bundesamt für Sicherheit in der Informationstechnik # Software engineering by Intevation GmbH # # This file is part of GPGME. # # GPGME is free software; you can redistribute it and/or modify it # under the terms of the GNU Lesser General Public License as # published by the Free Software Foundation; either version 2.1 of the # License, or (at your option) any later version. # # GPGME 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 Lesser General # Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this program; if not, see . ## Process this file with automake to produce Makefile.in GPG = gpg GNUPGHOME=$(abs_builddir) TESTS_ENVIRONMENT = GNUPGHOME=$(GNUPGHOME) EXTRA_DIST = initial.test final.test the_tests = \ t-addexistingsubkey \ t-decryptverify \ t-keylist t-keylocate t-ownertrust t-tofuinfo \ t-encrypt t-verify t-various t-config t-remarks t-trustsignatures \ t-changeexpiryjob t-wkdlookup t-import t-revokekey t-setprimaryuserid TESTS = initial.test $(the_tests) final.test moc_files = \ t-addexistingsubkey.moc \ t-decryptverify.moc \ t-keylist.moc t-keylocate.moc t-ownertrust.moc t-tofuinfo.moc \ t-encrypt.moc t-support.hmoc t-wkspublish.moc t-verify.moc \ t-various.moc t-config.moc t-remarks.moc t-trustsignatures.moc \ t-changeexpiryjob.moc t-wkdlookup.moc t-import.moc t-revokekey.moc \ t-setprimaryuserid.moc AM_LDFLAGS = -no-install if WANT_QT5 LDADD = ../../cpp/src/libgpgmepp.la ../src/libqgpgme.la \ ../../../src/libgpgme.la @GPGME_QT5_LIBS@ @GPG_ERROR_LIBS@ \ @GPGME_QT5TEST_LIBS@ @LDADD_FOR_TESTS_KLUDGE@ -lstdc++ AM_CPPFLAGS = -I$(top_srcdir)/lang/qt/src -I$(top_srcdir)/lang/cpp/src \ -I$(top_builddir)/src \ @GPG_ERROR_CFLAGS@ @GPGME_QT5_CFLAGS@ @GPG_ERROR_CFLAGS@ \ @LIBASSUAN_CFLAGS@ @GPGME_QT5TEST_CFLAGS@ -DBUILDING_QGPGME \ -DTOP_SRCDIR="$(top_srcdir)" endif if WANT_QT6 LDADD = ../../cpp/src/libgpgmepp.la ../src/libqgpgmeqt6.la \ ../../../src/libgpgme.la @GPGME_QT6_LIBS@ @GPG_ERROR_LIBS@ \ @GPGME_QT6TEST_LIBS@ @LDADD_FOR_TESTS_KLUDGE@ -lstdc++ AM_CPPFLAGS = -I$(top_srcdir)/lang/qt/src -I$(top_srcdir)/lang/cpp/src \ -I$(top_builddir)/src \ @GPG_ERROR_CFLAGS@ @GPGME_QT6_CFLAGS@ @GPG_ERROR_CFLAGS@ \ @LIBASSUAN_CFLAGS@ @GPGME_QT6TEST_CFLAGS@ -DBUILDING_QGPGME \ -DTOP_SRCDIR="$(top_srcdir)" endif support_src = t-support.h t-support.cpp t_addexistingsubkey_SOURCES = t-addexistingsubkey.cpp $(support_src) t_decryptverify_SOURCES = t-decryptverify.cpp $(support_src) t_keylist_SOURCES = t-keylist.cpp $(support_src) t_keylocate_SOURCES = t-keylocate.cpp $(support_src) t_ownertrust_SOURCES = t-ownertrust.cpp $(support_src) t_tofuinfo_SOURCES = t-tofuinfo.cpp $(support_src) t_encrypt_SOURCES = t-encrypt.cpp $(support_src) t_wkspublish_SOURCES = t-wkspublish.cpp $(support_src) t_verify_SOURCES = t-verify.cpp $(support_src) t_various_SOURCES = t-various.cpp $(support_src) t_config_SOURCES = t-config.cpp $(support_src) t_remarks_SOURCES = t-remarks.cpp $(support_src) t_trustsignatures_SOURCES = t-trustsignatures.cpp $(support_src) t_changeexpiryjob_SOURCES = t-changeexpiryjob.cpp $(support_src) 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_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) BUILT_SOURCES = $(moc_files) pubring-stamp noinst_PROGRAMS = \ t-addexistingsubkey \ t-decryptverify \ t-keylist t-keylocate t-ownertrust t-tofuinfo t-encrypt \ run-keyformailboxjob t-wkspublish t-verify t-various t-config t-remarks \ t-trustsignatures t-changeexpiryjob t-wkdlookup t-import t-revokekey \ t-setprimaryuserid \ run-decryptverifyarchivejob \ run-encryptarchivejob \ + run-encryptjob \ run-importjob run-exportjob run-receivekeysjob run-refreshkeysjob \ run-signarchivejob \ + run-signjob \ run-wkdrefreshjob CLEANFILES = secring.gpg pubring.gpg pubring.kbx trustdb.gpg dirmngr.conf \ gpg-agent.conf pubring.kbx~ S.gpg-agent gpg.conf pubring.gpg~ \ random_seed S.gpg-agent .gpg-v21-migrated pubring-stamp $(moc_files) \ gpg.conf tofu.db reader_0.status reader_1.status clean-local: -$(TESTS_ENVIRONMENT) $(top_srcdir)/tests/start-stop-agent --stop -rm -fR private-keys-v1.d crls.d pubring-stamp: $(top_srcdir)/tests/gpg/pubdemo.asc \ $(top_srcdir)/tests/gpg/secdemo.asc -$(TESTS_ENVIRONMENT) gpgconf --kill all echo "ignore-invalid-option allow-loopback-pinentry" > $(abs_builddir)/gpg-agent.conf echo "allow-loopback-pinentry" >> gpg-agent.conf echo "ignore-invalid-option pinentry-mode" > gpg.conf echo "pinentry-mode loopback" >> gpg.conf $(TESTS_ENVIRONMENT) $(GPG) --no-permission-warning \ --import $(top_srcdir)/tests/gpg/pubdemo.asc $(TESTS_ENVIRONMENT) $(GPG) --no-permission-warning \ --passphrase "abc" \ --import $(top_srcdir)/tests/gpg/secdemo.asc -$(TESTS_ENVIRONMENT) gpgconf --kill all touch pubring-stamp .cpp.moc: $(MOC) `test -f '$<' || echo '$(srcdir)/'`$< -o $@ .h.hmoc: $(MOC) `test -f '$<' || echo '$(srcdir)/'`$< -o $@ 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 + + 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 +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +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 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 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 + + 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 +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include + +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 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 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(); +}