diff --git a/lang/qt/src/Makefile.am b/lang/qt/src/Makefile.am index 2c8976ba..261eff60 100644 --- a/lang/qt/src/Makefile.am +++ b/lang/qt/src/Makefile.am @@ -1,289 +1,296 @@ # 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 lib_LTLIBRARIES = libqgpgme.la EXTRA_DIST = QGpgmeConfig.cmake.in.in QGpgmeConfigVersion.cmake.in \ qgpgme_debug.h qgpgme_version.h.in \ QGpgmeConfig.cmake.in.in qgpgme_sources = \ dataprovider.cpp \ debug.cpp \ job.cpp multideletejob.cpp qgpgmeadduseridjob.cpp \ qgpgmebackend.cpp qgpgmechangeexpiryjob.cpp qgpgmechangeownertrustjob.cpp \ qgpgmechangepasswdjob.cpp qgpgmedecryptjob.cpp \ qgpgmedecryptverifyjob.cpp qgpgmedeletejob.cpp qgpgmedownloadjob.cpp \ qgpgmeencryptjob.cpp qgpgmeexportjob.cpp qgpgmeimportfromkeyserverjob.cpp \ qgpgmeimportjob.cpp qgpgmekeygenerationjob.cpp qgpgmekeylistjob.cpp \ qgpgmelistallkeysjob.cpp qgpgmenewcryptoconfig.cpp \ qgpgmerefreshkeysjob.cpp \ qgpgmesecretkeyexportjob.cpp qgpgmesignencryptjob.cpp \ qgpgmesignjob.cpp qgpgmesignkeyjob.cpp qgpgmeverifydetachedjob.cpp \ - qgpgmeverifyopaquejob.cpp threadedjobmixin.cpp \ + qgpgmeverifyopaquejob.cpp qgpgmewkdlookupjob.cpp threadedjobmixin.cpp \ qgpgmekeyformailboxjob.cpp qgpgme_debug.cpp \ qgpgmetofupolicyjob.cpp qgpgmequickjob.cpp \ defaultkeygenerationjob.cpp qgpgmewkspublishjob.cpp \ qgpgmegpgcardjob.cpp changeexpiryjob.cpp \ - dn.cpp cryptoconfig.cpp + dn.cpp cryptoconfig.cpp wkdlookupresult.cpp # If you add one here make sure that you also add one in camelcase qgpgme_headers= \ abstractimportjob.h \ adduseridjob.h \ changeexpiryjob.h \ changeownertrustjob.h \ changepasswdjob.h \ dataprovider.h \ debug.h \ decryptjob.h \ decryptverifyjob.h \ downloadjob.h \ encryptjob.h \ exportjob.h \ hierarchicalkeylistjob.h \ job.h \ keyformailboxjob.h \ multideletejob.h \ protocol.h \ qgpgme_export.h \ qgpgmenewcryptoconfig.h \ quickjob.h \ specialjob.h \ signjob.h \ signkeyjob.h \ signencryptjob.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 \ wkspublishjob.h \ gpgcardjob.h \ dn.h camelcase_headers= \ AddUserIDJob \ AbstractImportJob \ ChangeExpiryJob \ ChangeOwnerTrustJob \ ChangePasswdJob \ DataProvider \ Debug \ DecryptJob \ DecryptVerifyJob \ DN \ DownloadJob \ EncryptJob \ ExportJob \ HierarchicalKeyKistJob \ Job \ MultiDeleteJob \ Protocol \ QGpgMENewCryptoConfig \ QuickJob \ SpecialJob \ SignJob \ SignKeyJob \ SignEncryptJob \ VerifyOpaqueJob \ RefreshKeysJob \ CryptoConfig \ DeleteJob \ ImportFromKeyserverJob \ ImportJob \ KeyGenerationJob \ KeyListJob \ ListAllKeysJob \ VerifyDetachedJob \ KeyForMailboxJob \ DefaultKeyGenerationJob \ + WKDLookupJob \ + WKDLookupResult \ WKSPublishJob \ TofuPolicyJob \ GpgCardJob private_qgpgme_headers = \ qgpgme_export.h \ protocol_p.h \ job_p.h \ qgpgmeadduseridjob.h \ qgpgmebackend.h \ qgpgmechangeexpiryjob.h \ qgpgmechangeownertrustjob.h \ qgpgmechangepasswdjob.h \ qgpgmedecryptjob.h \ qgpgmedecryptverifyjob.h \ qgpgmedeletejob.h \ qgpgmedownloadjob.h \ qgpgmeencryptjob.h \ qgpgmeexportjob.h \ qgpgmeimportfromkeyserverjob.h \ qgpgmeimportjob.h \ qgpgmekeygenerationjob.h \ qgpgmekeylistjob.h \ qgpgmelistallkeysjob.h \ qgpgmerefreshkeysjob.h \ qgpgmesecretkeyexportjob.h \ qgpgmesignencryptjob.h \ qgpgmesignjob.h \ qgpgmesignkeyjob.h \ qgpgmeverifydetachedjob.h \ qgpgmeverifyopaquejob.h \ + qgpgmewkdlookupjob.h \ qgpgmekeyformailboxjob.h \ qgpgmewkspublishjob.h \ qgpgmetofupolicyjob.h \ qgpgmegpgcardjob.h \ qgpgmequickjob.h \ threadedjobmixin.h qgpgme_moc_sources = \ abstractimportjob.moc \ adduseridjob.moc \ changeexpiryjob.moc \ changeownertrustjob.moc \ changepasswdjob.moc \ decryptjob.moc \ decryptverifyjob.moc \ deletejob.moc \ downloadjob.moc \ encryptjob.moc \ exportjob.moc \ hierarchicalkeylistjob.moc \ importfromkeyserverjob.moc \ importjob.moc \ job.moc \ keygenerationjob.moc \ keylistjob.moc \ listallkeysjob.moc \ multideletejob.moc \ qgpgmeadduseridjob.moc \ qgpgmechangeexpiryjob.moc \ qgpgmechangeownertrustjob.moc \ qgpgmechangepasswdjob.moc \ qgpgmedecryptjob.moc \ qgpgmedecryptverifyjob.moc \ qgpgmedeletejob.moc \ qgpgmedownloadjob.moc \ qgpgmeencryptjob.moc \ qgpgmeexportjob.moc \ qgpgmeimportfromkeyserverjob.moc \ qgpgmeimportjob.moc \ qgpgmekeygenerationjob.moc \ qgpgmekeylistjob.moc \ qgpgmelistallkeysjob.moc \ qgpgmerefreshkeysjob.moc \ qgpgmesecretkeyexportjob.moc \ qgpgmesignencryptjob.moc \ qgpgmesignjob.moc \ qgpgmesignkeyjob.moc \ qgpgmeverifydetachedjob.moc \ qgpgmeverifyopaquejob.moc \ + qgpgmewkdlookupjob.moc \ qgpgmewkspublishjob.moc \ tofupolicyjob.moc \ qgpgmetofupolicyjob.moc \ refreshkeysjob.moc \ signencryptjob.moc \ signjob.moc \ signkeyjob.moc \ specialjob.moc \ verifydetachedjob.moc \ verifyopaquejob.moc \ + wkdlookupjob.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 libqgpgme_la_SOURCES = $(qgpgme_sources) $(qgpgme_headers) $(private_qgpgme_headers) AM_CPPFLAGS = -I$(top_srcdir)/lang/cpp/src -I$(top_builddir)/src \ @GPGME_QT_CFLAGS@ @GPG_ERROR_CFLAGS@ @LIBASSUAN_CFLAGS@ \ -DBUILDING_QGPGME -Wsuggest-override \ -Wzero-as-null-pointer-constant libqgpgme_la_LIBADD = ../../cpp/src/libgpgmepp.la ../../../src/libgpgme.la \ @LIBASSUAN_LIBS@ @GPG_ERROR_LIBS@ @GPGME_QT_LIBS@ libqgpgme_la_LDFLAGS = -no-undefined -version-info \ @LIBQGPGME_LT_CURRENT@:@LIBQGPGME_LT_REVISION@:@LIBQGPGME_LT_AGE@ if HAVE_MACOS_SYSTEM libsuffix=.dylib else libsuffix=.so endif 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 $(camelcase_headers): Makefile.am echo -n "#include \"qgpgme/" > "$@" echo -n "$@" | tr '[:upper:]' '[:lower:]' >> "$@" echo ".h\"" >> "$@" 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/ 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 \ QGpgmeConfigVersion.cmake nodist_libqgpgme_la_SOURCES = $(qgpgme_moc_sources) .h.moc: $(MOC) `test -f '$<' || echo '$(srcdir)/'`$< -o $@ diff --git a/lang/qt/src/job.cpp b/lang/qt/src/job.cpp index c346a355..be637a6d 100644 --- a/lang/qt/src/job.cpp +++ b/lang/qt/src/job.cpp @@ -1,200 +1,203 @@ /* 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 "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 #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); } QGpgME::JobPrivate *QGpgME::getJobPrivate(const 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() { } 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); } #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(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(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) #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 "adduseridjob.moc" #include "specialjob.moc" #include "keyformailboxjob.moc" +#include "wkdlookupjob.moc" #include "wkspublishjob.moc" #include "tofupolicyjob.moc" #include "quickjob.moc" #include "gpgcardjob.moc" diff --git a/lang/qt/src/protocol.h b/lang/qt/src/protocol.h index dcc7ade4..cffd53b2 100644 --- a/lang/qt/src/protocol.h +++ b/lang/qt/src/protocol.h @@ -1,197 +1,201 @@ /* protocol.h 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 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_PROTOCOL_H__ #define __QGPGME_PROTOCOL_H__ #include #include #include "qgpgme_export.h" namespace QGpgME { class CryptoConfig; class KeyListJob; class ListAllKeysJob; class KeyGenerationJob; class ImportJob; class ImportFromKeyserverJob; class ExportJob; class DownloadJob; class DeleteJob; class EncryptJob; class DecryptJob; class SignJob; class SignKeyJob; class VerifyDetachedJob; class VerifyOpaqueJob; class SignEncryptJob; class DecryptVerifyJob; class RefreshKeysJob; class ChangeExpiryJob; class ChangeOwnerTrustJob; class ChangePasswdJob; class AddUserIDJob; class SpecialJob; class KeyForMailboxJob; +class WKDLookupJob; class WKSPublishJob; class TofuPolicyJob; class QuickJob; class GpgCardJob; /** The main entry point for QGpgME Comes in OpenPGP and SMIME(CMS) flavors. * * Use the proctocol class to obtain an instance of a job. Jobs * provide async API for GnuPG that can be connected to signals / slots. * * A job is usually started with start() and emits a result signal. * The parameters of the result signal depend on the job but the last * two are always a QString for the auditlog and an GpgME::Error for * an eventual error. * * In case async API is used and the result signal is emitted a * job schedules its own deletion. * * Most jobs also provide a synchronous call exec in which case * you have to explicitly delete the job if you don't need it anymore. * * \code * // Async example: * KeyListJob *job = openpgp()->keyListJob(); * connect(job, &KeyListJob::result, job, [this, job](KeyListResult, std::vector keys, QString, Error) * { * // keys and resuls can now be used. * }); * job->start({QStringLiteral("alfa@example.net")}, false); * \endcode * * \code * // Sync eaxmple: * KeyListJob *job = openpgp()->keyListJob(false, false, false); * std::vector keys; * GpgME::KeyListResult result = job->exec(QStringList() << * QStringLiteral("alfa@example.net"), * false, keys); * delete job; * \endcode */ class QGPGME_EXPORT Protocol { public: virtual ~Protocol() {} virtual QString name() const = 0; virtual QString displayName() const = 0; virtual KeyListJob *keyListJob(bool remote = false, bool includeSigs = false, bool validate = false) const = 0; virtual ListAllKeysJob *listAllKeysJob(bool includeSigs = false, bool validate = false) const = 0; virtual EncryptJob *encryptJob(bool armor = false, bool textmode = false) const = 0; virtual DecryptJob *decryptJob() const = 0; virtual SignJob *signJob(bool armor = false, bool textMode = false) const = 0; virtual VerifyDetachedJob *verifyDetachedJob(bool textmode = false) const = 0; virtual VerifyOpaqueJob *verifyOpaqueJob(bool textmode = false) const = 0; virtual KeyGenerationJob *keyGenerationJob() const = 0; virtual ImportJob *importJob() const = 0; virtual ImportFromKeyserverJob *importFromKeyserverJob() const = 0; virtual ExportJob *publicKeyExportJob(bool armor = false) const = 0; // @param charset the encoding of the passphrase in the exported file virtual ExportJob *secretKeyExportJob(bool armor = false, const QString &charset = QString()) const = 0; virtual DownloadJob *downloadJob(bool armor = false) const = 0; virtual DeleteJob *deleteJob() const = 0; virtual SignEncryptJob *signEncryptJob(bool armor = false, bool textMode = false) const = 0; virtual DecryptVerifyJob *decryptVerifyJob(bool textmode = false) const = 0; virtual RefreshKeysJob *refreshKeysJob() const = 0; virtual ChangeExpiryJob *changeExpiryJob() const = 0; virtual SignKeyJob *signKeyJob() const = 0; virtual ChangePasswdJob *changePasswdJob() const = 0; virtual ChangeOwnerTrustJob *changeOwnerTrustJob() const = 0; virtual AddUserIDJob *addUserIDJob() const = 0; virtual SpecialJob *specialJob(const char *type, const QMap &args) const = 0; /** A key locate job. * * This tries to find a key in local * and remote sources, if the key was remote it is imported * by GnuPG. Same as KeyListJob but intended to be used * to locate keys automatically. This ends up calling --locate-keys. * * Only available for OpenPGP * * Results are validated. As if keyListJob was called * with both includeSigs and validate options. */ virtual KeyListJob *locateKeysJob() const = 0; /** Find the best key to use for a mailbox. */ virtual KeyForMailboxJob *keyForMailboxJob() const = 0; + /** This job looks up a key via WKD without importing it. */ + virtual WKDLookupJob *wkdLookupJob() const = 0; + /** A Job for interacting with gnupg's wks tools. */ virtual WKSPublishJob *wksPublishJob() const = 0; /** A Job to set tofu policy */ virtual TofuPolicyJob *tofuPolicyJob() const = 0; /** A Job for the quick commands */ virtual QuickJob *quickJob() const = 0; }; /** Obtain a reference to the OpenPGP Protocol. * * The reference is to a static object. * @returns Reference to the OpenPGP Protocol. */ QGPGME_EXPORT Protocol *openpgp(); /** Obtain a reference to the smime Protocol. * * The reference is to a static object. * @returns Reference to the smime Protocol. */ QGPGME_EXPORT Protocol *smime(); /** Obtain a reference to a cryptoConfig object. * * The reference is to a static object. * @returns reference to cryptoConfig object. */ QGPGME_EXPORT CryptoConfig *cryptoConfig(); /** Obtain a reference to a protocol agnostic GpgCardJob. * * The reference is to a static object. * @returns reference to a GpgCardJob following the job pattern. */ QGPGME_EXPORT GpgCardJob *gpgCardJob(); } #endif diff --git a/lang/qt/src/protocol_p.h b/lang/qt/src/protocol_p.h index 57c1ed81..da5ce011 100644 --- a/lang/qt/src/protocol_p.h +++ b/lang/qt/src/protocol_p.h @@ -1,433 +1,446 @@ /* protocol_p.h 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 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_PROTOCOL_P_H__ #define __QGPGME_PROTOCOL_P_H__ #include "qgpgmenewcryptoconfig.h" #include "qgpgmekeygenerationjob.h" #include "qgpgmekeylistjob.h" #include "qgpgmelistallkeysjob.h" #include "qgpgmedecryptjob.h" #include "qgpgmedecryptverifyjob.h" #include "qgpgmerefreshkeysjob.h" #include "qgpgmedeletejob.h" #include "qgpgmesecretkeyexportjob.h" #include "qgpgmedownloadjob.h" #include "qgpgmesignencryptjob.h" #include "qgpgmeencryptjob.h" #include "qgpgmesignjob.h" #include "qgpgmesignkeyjob.h" #include "qgpgmeexportjob.h" #include "qgpgmeverifydetachedjob.h" #include "qgpgmeimportjob.h" #include "qgpgmeimportfromkeyserverjob.h" #include "qgpgmeverifyopaquejob.h" #include "qgpgmechangeexpiryjob.h" #include "qgpgmechangeownertrustjob.h" #include "qgpgmechangepasswdjob.h" #include "qgpgmeadduseridjob.h" #include "qgpgmekeyformailboxjob.h" +#include "qgpgmewkdlookupjob.h" #include "qgpgmewkspublishjob.h" #include "qgpgmetofupolicyjob.h" #include "qgpgmequickjob.h" namespace { class Protocol : public QGpgME::Protocol { GpgME::Protocol mProtocol; public: explicit Protocol(GpgME::Protocol proto) : mProtocol(proto) {} QString name() const Q_DECL_OVERRIDE { switch (mProtocol) { case GpgME::OpenPGP: return QStringLiteral("OpenPGP"); case GpgME::CMS: return QStringLiteral("SMIME"); default: return QString(); } } QString displayName() const Q_DECL_OVERRIDE { // ah (2.4.16): Where is this used and isn't this inverted // with name switch (mProtocol) { case GpgME::OpenPGP: return QStringLiteral("gpg"); case GpgME::CMS: return QStringLiteral("gpgsm"); default: return QStringLiteral("unknown"); } } QGpgME::SpecialJob *specialJob(const char *, const QMap &) const Q_DECL_OVERRIDE { return nullptr; } QGpgME::KeyListJob *keyListJob(bool remote, bool includeSigs, bool validate) const Q_DECL_OVERRIDE { GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol); if (!context) { return nullptr; } unsigned int mode = context->keyListMode(); if (remote) { mode |= GpgME::Extern; mode &= ~GpgME::Local; } else { mode |= GpgME::Local; mode &= ~GpgME::Extern; } if (includeSigs) { mode |= GpgME::Signatures; } if (validate) { mode |= GpgME::Validate; } context->setKeyListMode(mode); return new QGpgME::QGpgMEKeyListJob(context); } QGpgME::ListAllKeysJob *listAllKeysJob(bool includeSigs, bool validate) const Q_DECL_OVERRIDE { GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol); if (!context) { return nullptr; } unsigned int mode = context->keyListMode(); mode |= GpgME::Local; mode &= ~GpgME::Extern; if (includeSigs) { mode |= GpgME::Signatures; } if (validate) { mode |= GpgME::Validate; /* Setting the context to offline mode disables CRL / OCSP checks in this Job. Otherwise we would try to fetch the CRL's for all CMS keys in the users keyring because GpgME::Validate includes remote resources by default in the validity check. This setting only has any effect if gpgsm >= 2.1.6 is used. */ context->setOffline(true); } context->setKeyListMode(mode); return new QGpgME::QGpgMEListAllKeysJob(context); } QGpgME::EncryptJob *encryptJob(bool armor, bool textmode) const Q_DECL_OVERRIDE { GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol); if (!context) { return nullptr; } context->setArmor(armor); context->setTextMode(textmode); return new QGpgME::QGpgMEEncryptJob(context); } QGpgME::DecryptJob *decryptJob() const Q_DECL_OVERRIDE { GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol); if (!context) { return nullptr; } return new QGpgME::QGpgMEDecryptJob(context); } QGpgME::SignJob *signJob(bool armor, bool textMode) const Q_DECL_OVERRIDE { GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol); if (!context) { return nullptr; } context->setArmor(armor); context->setTextMode(textMode); return new QGpgME::QGpgMESignJob(context); } QGpgME::VerifyDetachedJob *verifyDetachedJob(bool textMode) const Q_DECL_OVERRIDE { GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol); if (!context) { return nullptr; } context->setTextMode(textMode); return new QGpgME::QGpgMEVerifyDetachedJob(context); } QGpgME::VerifyOpaqueJob *verifyOpaqueJob(bool textMode) const Q_DECL_OVERRIDE { GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol); if (!context) { return nullptr; } context->setTextMode(textMode); return new QGpgME::QGpgMEVerifyOpaqueJob(context); } QGpgME::KeyGenerationJob *keyGenerationJob() const Q_DECL_OVERRIDE { GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol); if (!context) { return nullptr; } return new QGpgME::QGpgMEKeyGenerationJob(context); } QGpgME::ImportJob *importJob() const Q_DECL_OVERRIDE { GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol); if (!context) { return nullptr; } return new QGpgME::QGpgMEImportJob(context); } QGpgME::ImportFromKeyserverJob *importFromKeyserverJob() const Q_DECL_OVERRIDE { GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol); if (!context) { return nullptr; } return new QGpgME::QGpgMEImportFromKeyserverJob(context); } QGpgME::ExportJob *publicKeyExportJob(bool armor) const Q_DECL_OVERRIDE { GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol); if (!context) { return nullptr; } context->setArmor(armor); return new QGpgME::QGpgMEExportJob(context); } QGpgME::ExportJob *secretKeyExportJob(bool armor, const QString &charset) const Q_DECL_OVERRIDE { if (mProtocol != GpgME::CMS) { // fixme: add support for gpg, too return nullptr; } // this operation is not supported by gpgme, so we have to call gpgsm ourselves: return new QGpgME::QGpgMESecretKeyExportJob(armor, charset); } QGpgME::RefreshKeysJob *refreshKeysJob() const Q_DECL_OVERRIDE { if (mProtocol != GpgME::CMS) { // fixme: add support for gpg, too return nullptr; } // this operation is not supported by gpgme, so we have to call gpgsm ourselves: return new QGpgME::QGpgMERefreshKeysJob(); } QGpgME::DownloadJob *downloadJob(bool armor) const Q_DECL_OVERRIDE { GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol); if (!context) { return nullptr; } context->setArmor(armor); // this is the hackish interface for downloading from keyserers currently: context->setKeyListMode(GpgME::Extern); return new QGpgME::QGpgMEDownloadJob(context); } QGpgME::DeleteJob *deleteJob() const Q_DECL_OVERRIDE { GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol); if (!context) { return nullptr; } return new QGpgME::QGpgMEDeleteJob(context); } QGpgME::SignEncryptJob *signEncryptJob(bool armor, bool textMode) const Q_DECL_OVERRIDE { GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol); if (!context) { return nullptr; } context->setArmor(armor); context->setTextMode(textMode); return new QGpgME::QGpgMESignEncryptJob(context); } QGpgME::DecryptVerifyJob *decryptVerifyJob(bool textMode) const Q_DECL_OVERRIDE { GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol); if (!context) { return nullptr; } context->setTextMode(textMode); return new QGpgME::QGpgMEDecryptVerifyJob(context); } QGpgME::ChangeExpiryJob *changeExpiryJob() const Q_DECL_OVERRIDE { if (mProtocol != GpgME::OpenPGP) { return nullptr; // only supported by gpg } GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol); if (!context) { return nullptr; } return new QGpgME::QGpgMEChangeExpiryJob(context); } QGpgME::ChangePasswdJob *changePasswdJob() const Q_DECL_OVERRIDE { if (!GpgME::hasFeature(GpgME::PasswdFeature, 0)) { return nullptr; } GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol); if (!context) { return nullptr; } return new QGpgME::QGpgMEChangePasswdJob(context); } QGpgME::SignKeyJob *signKeyJob() const Q_DECL_OVERRIDE { if (mProtocol != GpgME::OpenPGP) { return nullptr; // only supported by gpg } GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol); if (!context) { return nullptr; } return new QGpgME::QGpgMESignKeyJob(context); } QGpgME::ChangeOwnerTrustJob *changeOwnerTrustJob() const Q_DECL_OVERRIDE { if (mProtocol != GpgME::OpenPGP) { return nullptr; // only supported by gpg } GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol); if (!context) { return nullptr; } return new QGpgME::QGpgMEChangeOwnerTrustJob(context); } QGpgME::AddUserIDJob *addUserIDJob() const Q_DECL_OVERRIDE { if (mProtocol != GpgME::OpenPGP) { return nullptr; // only supported by gpg } GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol); if (!context) { return nullptr; } return new QGpgME::QGpgMEAddUserIDJob(context); } QGpgME::KeyListJob *locateKeysJob() const Q_DECL_OVERRIDE { if (mProtocol != GpgME::OpenPGP) { return nullptr; } GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol); if (!context) { return nullptr; } context->setKeyListMode(GpgME::Extern | GpgME::Local | GpgME::Signatures | GpgME::Validate); return new QGpgME::QGpgMEKeyListJob(context); } QGpgME::KeyForMailboxJob *keyForMailboxJob() const Q_DECL_OVERRIDE { GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol); if (!context) { return nullptr; } return new QGpgME::QGpgMEKeyForMailboxJob(context); } + QGpgME::WKDLookupJob *wkdLookupJob() const Q_DECL_OVERRIDE + { + if (mProtocol != GpgME::OpenPGP) { + return nullptr; + } + auto context = GpgME::Context::createForEngine(GpgME::AssuanEngine); + if (!context) { + return nullptr; + } + return new QGpgME::QGpgMEWKDLookupJob(context.release()); + } + QGpgME::WKSPublishJob *wksPublishJob() const Q_DECL_OVERRIDE { if (mProtocol != GpgME::OpenPGP) { return nullptr; } auto context = GpgME::Context::createForEngine(GpgME::SpawnEngine); if (!context) { return nullptr; } return new QGpgME::QGpgMEWKSPublishJob(context.release()); } QGpgME::TofuPolicyJob *tofuPolicyJob() const Q_DECL_OVERRIDE { if (mProtocol != GpgME::OpenPGP) { return nullptr; } GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol); if (!context) { return nullptr; } return new QGpgME::QGpgMETofuPolicyJob(context); } QGpgME::QuickJob *quickJob() const Q_DECL_OVERRIDE { if (mProtocol != GpgME::OpenPGP) { return nullptr; } GpgME::Context *context = GpgME::Context::createForProtocol(mProtocol); if (!context) { return nullptr; } return new QGpgME::QGpgMEQuickJob(context); } }; } #endif diff --git a/lang/qt/src/qgpgmewkdlookupjob.cpp b/lang/qt/src/qgpgmewkdlookupjob.cpp new file mode 100644 index 00000000..fcb757e1 --- /dev/null +++ b/lang/qt/src/qgpgmewkdlookupjob.cpp @@ -0,0 +1,182 @@ +/* + qgpgmewkdlookupjob.cpp + + This file is part of qgpgme, the Qt API binding for gpgme + 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 "qgpgmewkdlookupjob.h" + +#include "qgpgme_debug.h" + +#include +#include +#include + +#include + +using namespace QGpgME; +using namespace GpgME; + +QGpgMEWKDLookupJob::QGpgMEWKDLookupJob(Context *context) + : mixin_type{context} +{ + lateInitialization(); +} + +QGpgMEWKDLookupJob::~QGpgMEWKDLookupJob() = default; + +static GpgME::Error startDirmngr(Context *assuanCtx) +{ + Error err; + + auto spawnCtx = std::unique_ptr{Context::createForEngine(SpawnEngine, &err)}; + if (err) { + qCDebug(QGPGME_LOG) << "Error: Failed to get context for spawn engine (" << err.asString() << ")"; + } + + const auto dirmngrProgram = GpgME::dirInfo("dirmngr-name"); + const auto homedir = GpgME::dirInfo("homedir"); + const char *argv[] = { + dirmngrProgram, + "--homedir", + homedir, + "--daemon", + NULL + }; + auto ignoreIO = Data{Data::null}; + if (!err) { + qCDebug(QGPGME_LOG) << "Starting dirmngr ..."; + err = spawnCtx->spawnAsync(dirmngrProgram, argv, + ignoreIO, ignoreIO, ignoreIO, + Context::SpawnDetached); + } + + if (!err) { + // wait for socket to become available + int cnt = 0; + do { + ++cnt; + qCDebug(QGPGME_LOG) << "Waiting for dirmngr to start ..."; + QThread::msleep(250 * cnt); + err = assuanCtx->assuanTransact("GETINFO version"); + } while (err.code() == GPG_ERR_ASS_CONNECT_FAILED && cnt < 5); + } + + return err; +} + +static GpgME::Error setUpDirmngrAssuanConnection(Context *ctx) +{ + Error err; + + const std::string dirmngrSocket = GpgME::dirInfo("dirmngr-socket"); + err = ctx->setEngineFileName(dirmngrSocket.c_str()); + + if (!err) { + err = ctx->setEngineHomeDirectory(""); + } + + if (!err) { + // try do connect to dirmngr + err = ctx->assuanTransact("GETINFO version"); + if (err.code() == GPG_ERR_ASS_CONNECT_FAILED) { + err = startDirmngr(ctx); + } + } + + return err; +} + +static GpgME::Error run_wkd_get(Context *ctx, const QString &email) +{ + Error err; + + const auto cmd = std::string{"WKD_GET "} + email.toUtf8().toStdString(); + err = ctx->assuanTransact(cmd.c_str()); + if (err.code() == GPG_ERR_NO_NAME || err.code() == GPG_ERR_NO_DATA) { + // ignore those benign errors; GPG_ERR_NO_NAME indicates that the domain + // doesn't exist (on first request); GPG_ERR_NO_DATA indicates that + // no key for email is available via WKD or that the domain doesn't + // support WKD or that the domain doesn't exist (on subsequent requests + // using dirmngr's internal cache) + qCDebug(QGPGME_LOG) << "WKD_GET returned" << err.asString() << "; ignoring..."; + err = {}; + } + if (err) { + qCDebug(QGPGME_LOG) << "WKD_GET failed with" << err.asString(); + } + + return err; +} + +static QGpgMEWKDLookupJob::result_type lookup_keys(Context *ctx, const QString &email) +{ + WKDLookupResult result; + + Error err = setUpDirmngrAssuanConnection(ctx); + + if (!err) { + err = run_wkd_get(ctx, email); + } + + if (!err) { + const auto transaction = std::unique_ptr(dynamic_cast(ctx->takeLastAssuanTransaction().release())); + const auto source = transaction->firstStatusLine("SOURCE"); + const auto rawData = transaction->data(); + if (rawData.size() == 0) { + qCDebug(QGPGME_LOG) << "No key found for" << email; + result = WKDLookupResult{GpgME::Data::null, {}, {}}; + } else { + qCDebug(QGPGME_LOG) << "Found key for" << email << "at" << source.c_str(); + result = WKDLookupResult{GpgME::Data{rawData.c_str(), rawData.size()}, source, {}}; + } + } + + return std::make_tuple(err ? WKDLookupResult{err} : result, QString{}, Error{}); +} + +Error QGpgMEWKDLookupJob::start(const QString &email) +{ + run(std::bind(&lookup_keys, std::placeholders::_1, email)); + return Error(); +} + +WKDLookupResult QGpgMEWKDLookupJob::exec(const QString &email) +{ + const result_type r = lookup_keys(context(), email); + resultHook(r); + return std::get<0>(r); +} + +#include "qgpgmewkdlookupjob.moc" diff --git a/lang/qt/tests/t-support.h b/lang/qt/src/qgpgmewkdlookupjob.h similarity index 58% copy from lang/qt/tests/t-support.h copy to lang/qt/src/qgpgmewkdlookupjob.h index 77bef56d..61f9465c 100644 --- a/lang/qt/tests/t-support.h +++ b/lang/qt/src/qgpgmewkdlookupjob.h @@ -1,74 +1,70 @@ -/* t-support.h +/* + qgpgmewkdlookupjob.h This file is part of qgpgme, the Qt API binding for gpgme - 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. */ -#ifndef T_SUPPORT_H -#define T_SUPPORT_H -#include "interfaces/passphraseprovider.h" -#include +#ifndef __QGPGME_QGPGMEWKDLOOKUPJOB_H__ +#define __QGPGME_QGPGMEWKDLOOKUPJOB_H__ -#include +#include "threadedjobmixin.h" +#include "wkdlookupjob.h" +#include "wkdlookupresult.h" -namespace GpgME +namespace QGpgME { -class TestPassphraseProvider : public PassphraseProvider -{ -public: - char *getPassphrase(const char * /*useridHint*/, const char * /*description*/, - bool /*previousWasBad*/, bool &/*canceled*/) Q_DECL_OVERRIDE - { - char *ret; - gpgrt_asprintf(&ret, "abc"); - return ret; - } -}; -} // namespace GpgME - -void killAgent(const QString &dir = qgetenv("GNUPGHOME")); -/* Is the passphrase Provider / loopback Supported */ -bool loopbackSupported(); +class WKDLookupResult; -class QGpgMETest : public QObject +class QGpgMEWKDLookupJob +#ifdef Q_MOC_RUN + : public WKDLookupJob +#else + : public _detail::ThreadedJobMixin > +#endif { Q_OBJECT -protected: - bool copyKeyrings(const QString &from, const QString& to); - +#ifdef Q_MOC_RUN public Q_SLOTS: - void initTestCase(); - void cleanupTestCase(); + void slotFinished(); +#endif +public: + explicit QGpgMEWKDLookupJob(GpgME::Context *context); + ~QGpgMEWKDLookupJob(); + + /* from WKDLookupJob */ + GpgME::Error start(const QString &email) Q_DECL_OVERRIDE; + + /* from WKDLookupJob */ + WKDLookupResult exec(const QString &email) Q_DECL_OVERRIDE; }; -/* Timeout, in milliseconds, for use with QSignalSpy to wait on - signals. */ -#define QSIGNALSPY_TIMEOUT 60000 +} -#endif // T_SUPPORT_H +#endif // __QGPGME_QGPGMEWKDLOOKUPJOB_H__ diff --git a/lang/qt/tests/t-support.h b/lang/qt/src/wkdlookupjob.h similarity index 58% copy from lang/qt/tests/t-support.h copy to lang/qt/src/wkdlookupjob.h index 77bef56d..ae228744 100644 --- a/lang/qt/tests/t-support.h +++ b/lang/qt/src/wkdlookupjob.h @@ -1,74 +1,78 @@ -/* t-support.h +/* + wkdlookupjob.h This file is part of qgpgme, the Qt API binding for gpgme - 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. */ -#ifndef T_SUPPORT_H -#define T_SUPPORT_H -#include "interfaces/passphraseprovider.h" -#include +#ifndef __QGPGME_WKDLOOKUPJOB_H__ +#define __QGPGME_WKDLOOKUPJOB_H__ -#include +#include "job.h" +#include "qgpgme_export.h" + +class QString; namespace GpgME { -class TestPassphraseProvider : public PassphraseProvider +class Data; +class Error; +} + +namespace QGpgME { -public: - char *getPassphrase(const char * /*useridHint*/, const char * /*description*/, - bool /*previousWasBad*/, bool &/*canceled*/) Q_DECL_OVERRIDE - { - char *ret; - gpgrt_asprintf(&ret, "abc"); - return ret; - } -}; -} // namespace GpgME -void killAgent(const QString &dir = qgetenv("GNUPGHOME")); -/* Is the passphrase Provider / loopback Supported */ -bool loopbackSupported(); +class WKDLookupResult; -class QGpgMETest : public QObject +class QGPGME_EXPORT WKDLookupJob : public Job { Q_OBJECT protected: - bool copyKeyrings(const QString &from, const QString& to); + explicit WKDLookupJob(QObject *parent); + +public: + ~WKDLookupJob(); + + /** + Starts a key lookup operation for the email address \a email via WKD. + */ + virtual GpgME::Error start(const QString &email) = 0; + + /** + Runs a key lookup operation for the email address \a email via WKD. + */ + virtual WKDLookupResult exec(const QString &email) = 0; -public Q_SLOTS: - void initTestCase(); - void cleanupTestCase(); +Q_SIGNALS: + void result(const WKDLookupResult &result, const QString &auditLogAsHtml = {}, const GpgME::Error &auditLogError = {}); }; -/* Timeout, in milliseconds, for use with QSignalSpy to wait on - signals. */ -#define QSIGNALSPY_TIMEOUT 60000 +} -#endif // T_SUPPORT_H +#endif // __QGPGME_WKDLOOKUPJOB_H__ diff --git a/lang/qt/src/wkdlookupresult.cpp b/lang/qt/src/wkdlookupresult.cpp new file mode 100644 index 00000000..71aa75cf --- /dev/null +++ b/lang/qt/src/wkdlookupresult.cpp @@ -0,0 +1,111 @@ +/* + wkdlookupresult.cpp - wraps the result of a WKDLookupJob + + This file is part of qgpgme, the Qt API binding for gpgme + 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 "wkdlookupresult.h" + +#include + +using namespace QGpgME; +using namespace GpgME; + +class WKDLookupResult::Private +{ +public: + GpgME::Data keyData; + std::string source; +}; + +WKDLookupResult::WKDLookupResult() = default; + +WKDLookupResult::~WKDLookupResult() = default; + +WKDLookupResult::WKDLookupResult(const Error &error) + : Result{error} + , d{} +{ +} + +WKDLookupResult::WKDLookupResult(const Data &keyData, const std::string &source, const Error &error) + : Result{error} + , d{new Private{keyData, source}} +{ +} + +WKDLookupResult::WKDLookupResult(const WKDLookupResult &other) + : Result{other} +{ + if (other.d) { + d.reset(new Private{*other.d}); + } +} + +WKDLookupResult &WKDLookupResult::operator=(const WKDLookupResult &other) +{ + auto tmp = other; + swap(tmp); + return *this; +} + +WKDLookupResult::WKDLookupResult(WKDLookupResult &&other) = default; + +WKDLookupResult &WKDLookupResult::operator=(WKDLookupResult &&other) = default; + +void WKDLookupResult::swap(WKDLookupResult &other) noexcept +{ + Result::swap(other); + std::swap(this->d, other.d); +} + +bool WKDLookupResult::isNull() const +{ + return !d && !bool(error()); +} + +Data WKDLookupResult::keyData() const +{ + return d ? d->keyData : Data{}; +} + +std::string WKDLookupResult::source() const +{ + return d ? d->source : std::string{}; +} + +void QGpgME::swap(WKDLookupResult &a, WKDLookupResult &b) +{ + a.swap(b); +} diff --git a/lang/qt/tests/t-support.h b/lang/qt/src/wkdlookupresult.h similarity index 55% copy from lang/qt/tests/t-support.h copy to lang/qt/src/wkdlookupresult.h index 77bef56d..c40220a6 100644 --- a/lang/qt/tests/t-support.h +++ b/lang/qt/src/wkdlookupresult.h @@ -1,74 +1,83 @@ -/* t-support.h +/* + wkdlookupresult.h - wraps the result of a WKDLookupJob This file is part of qgpgme, the Qt API binding for gpgme - 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. */ -#ifndef T_SUPPORT_H -#define T_SUPPORT_H -#include "interfaces/passphraseprovider.h" -#include +#ifndef __QGPGME_WKDLOOKUPRESULT_H__ +#define __QGPGME_WKDLOOKUPRESULT_H__ -#include +#include "qgpgme_export.h" + +#include + +#include namespace GpgME { -class TestPassphraseProvider : public PassphraseProvider +class Data; +class Error; +} + +namespace QGpgME +{ + +class QGPGME_EXPORT WKDLookupResult : public GpgME::Result { public: - char *getPassphrase(const char * /*useridHint*/, const char * /*description*/, - bool /*previousWasBad*/, bool &/*canceled*/) Q_DECL_OVERRIDE - { - char *ret; - gpgrt_asprintf(&ret, "abc"); - return ret; - } -}; -} // namespace GpgME + WKDLookupResult(); + ~WKDLookupResult(); -void killAgent(const QString &dir = qgetenv("GNUPGHOME")); -/* Is the passphrase Provider / loopback Supported */ -bool loopbackSupported(); + explicit WKDLookupResult(const GpgME::Error &err); + explicit WKDLookupResult(const GpgME::Data &keyData, const std::string &source, const GpgME::Error &err); -class QGpgMETest : public QObject -{ - Q_OBJECT -protected: - bool copyKeyrings(const QString &from, const QString& to); + WKDLookupResult(const WKDLookupResult &other); + WKDLookupResult &operator=(const WKDLookupResult &other); + + WKDLookupResult(WKDLookupResult &&other); + WKDLookupResult &operator=(WKDLookupResult &&other); + + void swap(WKDLookupResult &other) noexcept; -public Q_SLOTS: - void initTestCase(); - void cleanupTestCase(); + bool isNull() const; + + GpgME::Data keyData() const; + std::string source() const; + +private: + class Private; + std::unique_ptr d; }; -/* Timeout, in milliseconds, for use with QSignalSpy to wait on - signals. */ -#define QSIGNALSPY_TIMEOUT 60000 +QGPGME_EXPORT void swap(WKDLookupResult &a, WKDLookupResult &b); + +} -#endif // T_SUPPORT_H +#endif // __QGPGME_WKDLOOKUPRESULT_H__ diff --git a/lang/qt/tests/Makefile.am b/lang/qt/tests/Makefile.am index 8c44681b..b7ec546e 100644 --- a/lang/qt/tests/Makefile.am +++ b/lang/qt/tests/Makefile.am @@ -1,101 +1,105 @@ # 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 +EXTRA_DIST = initial.test final.test -TESTS = initial.test t-keylist t-keylocate t-ownertrust t-tofuinfo \ - t-encrypt t-verify t-various t-config t-remarks t-trustsignatures \ - t-changeexpiryjob +the_tests = \ + t-keylist t-keylocate t-ownertrust t-tofuinfo \ + t-encrypt t-verify t-various t-config t-remarks t-trustsignatures \ + t-changeexpiryjob t-wkdlookup + +TESTS = initial.test $(the_tests) final.test moc_files = 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-changeexpiryjob.moc t-wkdlookup.moc AM_LDFLAGS = -no-install LDADD = ../../cpp/src/libgpgmepp.la ../src/libqgpgme.la \ ../../../src/libgpgme.la @GPGME_QT_LIBS@ @GPG_ERROR_LIBS@ \ @GPGME_QTTEST_LIBS@ @LDADD_FOR_TESTS_KLUDGE@ -lstdc++ AM_CPPFLAGS = -I$(top_srcdir)/lang/cpp/src -I$(top_builddir)/src \ @GPG_ERROR_CFLAGS@ @GPGME_QT_CFLAGS@ @GPG_ERROR_CFLAGS@ \ @LIBASSUAN_CFLAGS@ @GPGME_QTTEST_CFLAGS@ -DBUILDING_QGPGME \ -I$(top_srcdir)/lang/qt/src \ -DTOP_SRCDIR="$(top_srcdir)" support_src = t-support.h t-support.cpp 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) run_keyformailboxjob_SOURCES = run-keyformailboxjob.cpp nodist_t_keylist_SOURCES = $(moc_files) BUILT_SOURCES = $(moc_files) pubring-stamp noinst_PROGRAMS = 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-trustsignatures t-changeexpiryjob t-wkdlookup 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 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/final.test b/lang/qt/tests/final.test new file mode 100755 index 00000000..f28aaa4c --- /dev/null +++ b/lang/qt/tests/final.test @@ -0,0 +1,6 @@ +#!/bin/sh + +# stop the dirmngr that may have been started +gpgconf --kill dirmngr + +exit 0 diff --git a/lang/qt/tests/t-support.h b/lang/qt/tests/t-support.h index 77bef56d..22ba473c 100644 --- a/lang/qt/tests/t-support.h +++ b/lang/qt/tests/t-support.h @@ -1,74 +1,84 @@ /* t-support.h This file is part of qgpgme, the Qt API binding for gpgme 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 T_SUPPORT_H #define T_SUPPORT_H #include "interfaces/passphraseprovider.h" #include +#include #include +namespace QTest +{ +template <> +inline char *toString(const std::string &s) +{ + return QTest::toString(s.c_str()); +} +} + namespace GpgME { class TestPassphraseProvider : public PassphraseProvider { public: char *getPassphrase(const char * /*useridHint*/, const char * /*description*/, bool /*previousWasBad*/, bool &/*canceled*/) Q_DECL_OVERRIDE { char *ret; gpgrt_asprintf(&ret, "abc"); return ret; } }; } // namespace GpgME void killAgent(const QString &dir = qgetenv("GNUPGHOME")); /* Is the passphrase Provider / loopback Supported */ bool loopbackSupported(); class QGpgMETest : public QObject { Q_OBJECT protected: bool copyKeyrings(const QString &from, const QString& to); public Q_SLOTS: void initTestCase(); void cleanupTestCase(); }; /* Timeout, in milliseconds, for use with QSignalSpy to wait on signals. */ #define QSIGNALSPY_TIMEOUT 60000 #endif // T_SUPPORT_H diff --git a/lang/qt/tests/t-wkdlookup.cpp b/lang/qt/tests/t-wkdlookup.cpp new file mode 100644 index 00000000..13c70269 --- /dev/null +++ b/lang/qt/tests/t-wkdlookup.cpp @@ -0,0 +1,148 @@ +/* t-wkdlookup.cpp + + This file is part of qgpgme, the Qt API binding for gpgme + 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 "t-support.h" + +#include "data.h" +#include "engineinfo.h" +#include "protocol.h" +#include "wkdlookupjob.h" +#include "wkdlookupresult.h" + +#include +#include +#include + +#include + +using namespace QGpgME; +using namespace GpgME; + +static const char *requiredVersion = "2.1.12"; + +namespace +{ +bool keyHasUserIDWithMatchingEmailAddress(const Key &key, const QString &expectedEmailAddress) +{ + const auto email = expectedEmailAddress.toLower(); + const auto userIds = key.userIDs(); + return std::any_of( + std::begin(userIds), std::end(userIds), + [email](const UserID &uid) { + return email == QString::fromUtf8(uid.email()).toLower(); + }); +} +} + +class WKDLookupTest : public QGpgMETest +{ + Q_OBJECT + +Q_SIGNALS: + void asyncDone(); + +private Q_SLOTS: + +#ifndef DO_ONLINE_TESTS + void testWKDLookupAsync() + { + if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < requiredVersion) { + QSKIP("dirmngr does not yet support WKD lookup"); + } + const QString email = QLatin1String{"wk@gnupg.org"}; + + WKDLookupResult result; + auto *job = openpgp()->wkdLookupJob(); + connect(job, &WKDLookupJob::result, job, [this, &result](const WKDLookupResult &result_, const QString &, const Error &) + { + result = result_; + Q_EMIT asyncDone(); + }); + job->start(email); + QSignalSpy spy (this, SIGNAL(asyncDone())); + QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); + + QVERIFY(result.error().code() == GPG_ERR_NO_ERROR); + QCOMPARE(result.source(), "https://openpgpkey.gnupg.org"); + const auto keys = result.keyData().toKeys(GpgME::OpenPGP); + QVERIFY(keys.size() == 1); + QVERIFY(keyHasUserIDWithMatchingEmailAddress(keys.front(), email)); + } + + void testWKDLookupSync() + { + if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < requiredVersion) { + QSKIP("dirmngr does not yet support WKD lookup"); + } + const QString email = QLatin1String{"wk@gnupg.org"}; + + auto *job = openpgp()->wkdLookupJob(); + const auto result = job->exec(email); + + QVERIFY(result.error().code() == GPG_ERR_NO_ERROR); + QCOMPARE(result.source(), "https://openpgpkey.gnupg.org"); + const auto keys = result.keyData().toKeys(GpgME::OpenPGP); + QVERIFY(keys.size() == 1); + QVERIFY(keyHasUserIDWithMatchingEmailAddress(keys.front(), email)); + } + + void testLookupWithNoResultAsync() + { + if (GpgME::engineInfo(GpgME::GpgEngine).engineVersion() < requiredVersion) { + QSKIP("dirmngr does not yet support WKD lookup"); + } + const QString email = QLatin1String{"alfa@example.net"}; + + WKDLookupResult result; + auto *job = openpgp()->wkdLookupJob(); + connect(job, &WKDLookupJob::result, job, [this, &result](const WKDLookupResult &result_, const QString &, const Error &) + { + result = result_; + Q_EMIT asyncDone(); + }); + job->start(email); + QSignalSpy spy (this, SIGNAL(asyncDone())); + QVERIFY(spy.wait(QSIGNALSPY_TIMEOUT)); + + QVERIFY(result.error().code() == GPG_ERR_NO_ERROR); + QCOMPARE(result.source(), ""); + QVERIFY(result.keyData().isNull()); + } +#endif +}; + +QTEST_MAIN(WKDLookupTest) + +#include "t-wkdlookup.moc"