diff --git a/lang/qt/src/Makefile.am b/lang/qt/src/Makefile.am index ae316bac..59d8abc8 100644 --- a/lang/qt/src/Makefile.am +++ b/lang/qt/src/Makefile.am @@ -33,7 +33,7 @@ qgpgme_sources = \ qgpgmesecretkeyexportjob.cpp qgpgmesignencryptjob.cpp \ qgpgmesignjob.cpp qgpgmesignkeyjob.cpp qgpgmeverifydetachedjob.cpp \ qgpgmeverifyopaquejob.cpp threadedjobmixin.cpp \ - gpgme_backend_debug.cpp + qgpgmekeyformailboxjob.cpp gpgme_backend_debug.cpp # If you add one here make sure that you also add one in camelcase qgpgme_headers= \ @@ -49,6 +49,7 @@ qgpgme_headers= \ exportjob.h \ hierarchicalkeylistjob.h \ job.h \ + keyformailboxjob.h \ multideletejob.h \ protocol.h \ qgpgme_export.h \ @@ -95,7 +96,8 @@ camelcase_headers= \ KeyGenerationJob \ KeyListJob \ ListAllKeysJob \ - VerifyDetachedJob + VerifyDetachedJob \ + KeyForMailboxJob private_qgpgme_headers = \ qgpgme_export.h \ @@ -124,6 +126,7 @@ private_qgpgme_headers = \ qgpgmesignkeyjob.h \ qgpgmeverifydetachedjob.h \ qgpgmeverifyopaquejob.h \ + qgpgmekeyformailboxjob.h \ specialjob.h \ threadedjobmixin.h @@ -175,7 +178,9 @@ qgpgme_moc_sources = \ signkeyjob.moc \ specialjob.moc \ verifydetachedjob.moc \ - verifyopaquejob.moc + verifyopaquejob.moc \ + keyformailboxjob.moc \ + qgpgmekeyformailboxjob.moc qgpgmeincludedir = $(includedir)/qgpgme qgpgmeinclude_HEADERS = $(qgpgme_headers) diff --git a/lang/qt/src/job.cpp b/lang/qt/src/job.cpp index 7ca1df9b..8e506474 100644 --- a/lang/qt/src/job.cpp +++ b/lang/qt/src/job.cpp @@ -55,6 +55,7 @@ #include "refreshkeysjob.h" #include "adduseridjob.h" #include "specialjob.h" +#include "keyformailboxjob.h" #include #include @@ -120,6 +121,7 @@ make_job_subclass(DeleteJob) make_job_subclass(RefreshKeysJob) make_job_subclass(AddUserIDJob) make_job_subclass(SpecialJob) +make_job_subclass(KeyForMailboxJob) #undef make_job_subclass @@ -148,3 +150,4 @@ make_job_subclass(SpecialJob) #include "refreshkeysjob.moc" #include "adduseridjob.moc" #include "specialjob.moc" +#include "keyformailboxjob.moc" diff --git a/lang/qt/src/keyformailboxjob.h b/lang/qt/src/keyformailboxjob.h new file mode 100644 index 00000000..9e76df51 --- /dev/null +++ b/lang/qt/src/keyformailboxjob.h @@ -0,0 +1,101 @@ +/* + keyformailboxjob.h + + This file is part of qgpgme, the Qt API binding for gpgme + Copyright (c) 2016 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_KEYFORMAILBOX_H__ +#define __KLEO_KEYFORMAILBOX_H__ + +#include + +#include "job.h" + +#include +namespace GpgME +{ +class Error; +class KeyListResult; +} + +namespace QGpgME +{ + +/** + @short Get the best key to use for a Mailbox + + To use the keyformailboxjob, first obtain an instance from the + CryptoBackend and either exec it or start and + conncet the result() signals to a suitable slot. + The job will be automatically deleted in which + case the KeylistJob instance will have schedules it's own + destruction with a call to QObject::deleteLater(). + + The best key is defined as the key with a UID that has an + E-Mail that matches the mailbox provided. If multiple + keys are found the one with the highest validity is returned. + + After result() is emitted, the + KeyListJob will schedule it's own destruction by calling + QObject::deleteLater(). +*/ +class QGPGME_EXPORT KeyForMailboxJob: public Job +{ + Q_OBJECT +protected: + explicit KeyForMailboxJob(QObject *parent); + +public: + ~KeyForMailboxJob(); + + /** + Starts the operation. \a mailbox is the mailbox to + look for. + + The result is the same as for the LocateKeysJob. + + If \a canEncrypt is true, only keys that have a subkey for encryption + usage are returned. Use this if you need to select a + key for signing. + */ + virtual GpgME::Error start(const QString &mailbox, bool canEncrypt = true) = 0; + + virtual GpgME::KeyListResult exec(const QString &mailbox, bool canEncrypt, GpgME::Key &key, GpgME::UserID &uid) = 0; + +Q_SIGNALS: + /** The result. \a Key is the key found or a Null key. + * + * The userid is the uid where the mailbox matches. + * + * The auditlog params are always null / empty. + */ + void result(const GpgME::KeyListResult &result, const GpgME::Key &key, const GpgME::UserID &uid, const QString &auditLogAsHtml = QString(), const GpgME::Error &auditLogError = GpgME::Error()); +}; + +} +#endif diff --git a/lang/qt/src/protocol.h b/lang/qt/src/protocol.h index 64146b82..23b9d937 100644 --- a/lang/qt/src/protocol.h +++ b/lang/qt/src/protocol.h @@ -62,6 +62,7 @@ class ChangeOwnerTrustJob; class ChangePasswdJob; class AddUserIDJob; class SpecialJob; +class KeyForMailboxJob; /** The main entry point for QGpgME Comes in OpenPGP and SMIME(CMS) flavors. * @@ -145,6 +146,8 @@ public: * 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; }; /** Obtain a reference to the OpenPGP Protocol. diff --git a/lang/qt/src/protocol_p.h b/lang/qt/src/protocol_p.h index 9fcbc8b7..afb4f9cb 100644 --- a/lang/qt/src/protocol_p.h +++ b/lang/qt/src/protocol_p.h @@ -56,6 +56,7 @@ #include "qgpgmechangeownertrustjob.h" #include "qgpgmechangepasswdjob.h" #include "qgpgmeadduseridjob.h" +#include "qgpgmekeyformailboxjob.h" namespace { @@ -377,6 +378,15 @@ public: 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 Q_NULLPTR; + } + return new QGpgME::QGpgMEKeyForMailboxJob(context); + } }; } diff --git a/lang/qt/src/qgpgmekeyformailboxjob.cpp b/lang/qt/src/qgpgmekeyformailboxjob.cpp new file mode 100644 index 00000000..0702a365 --- /dev/null +++ b/lang/qt/src/qgpgmekeyformailboxjob.cpp @@ -0,0 +1,136 @@ +/* + qgpgmekeyformailboxjob.cpp + + This file is part of qgpgme, the Qt API binding for gpgme + Copyright (c) 2016 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. +*/ + +#include "qgpgmekeyformailboxjob.h" +#include "qgpgmekeylistjob.h" + +#include + +using namespace GpgME; +using namespace QGpgME; + +QGpgMEKeyForMailboxJob::QGpgMEKeyForMailboxJob(Context *context) + : mixin_type(context) +{ + lateInitialization(); +} + +QGpgMEKeyForMailboxJob::~QGpgMEKeyForMailboxJob() {} + +static bool keyIsOk(const Key k) +{ + return !k.isExpired() && !k.isRevoked() && !k.isInvalid() && !k.isDisabled(); +} + +static bool uidIsOk(const UserID uid) +{ + return keyIsOk(uid.parent()) && !uid.isRevoked() && !uid.isInvalid(); +} + +static bool subkeyIsOk(const Subkey s) +{ + return !s.isRevoked() && !s.isInvalid() && !s.isDisabled(); +} + +static QGpgMEKeyForMailboxJob::result_type do_work(Context *ctx, const QString &mailbox, bool canEncrypt) +{ + /* Do a Keylisting. */ + ctx->setKeyListMode(GpgME::Extern | GpgME::Local | GpgME::Signatures | GpgME::Validate); + std::vector keys; + QGpgMEKeyListJob *keylist = new QGpgMEKeyListJob(ctx); + + KeyListResult result = keylist->exec(QStringList() << mailbox, false, keys); + + if (result.error()) { + return std::make_tuple(result, Key(), UserID(), QString(), Error()); + } + + // This should ideally be decided by GnuPG and this Job changed + // to just call the according API in GpgME + // See: https://bugs.gnupg.org/gnupg/issue2359 + Key keyC; + UserID uidC; + Q_FOREACH (const Key k, keys) { + if (canEncrypt && !k.canEncrypt()) { + continue; + } + /* First get the uid that matches the mailbox */ + Q_FOREACH (const UserID u, k.userIDs()) { + if (QString::fromUtf8(u.email()).toLower() == mailbox.toLower()) { + if (uidC.isNull()) { + keyC = k; + uidC = u; + } else if ((!uidIsOk(uidC) && uidIsOk(u)) || uidC.validity() < u.validity()) { + /* Validity of the new key is better. */ + uidC = u; + keyC = k; + } else if (uidC.validity() == u.validity() && uidIsOk(u)) { + /* Both are the same check which one is newer. */ + time_t oldTime = 0; + Q_FOREACH (const Subkey s, keyC.subkeys()) { + if ((canEncrypt && s.canEncrypt()) && subkeyIsOk(s)) { + oldTime = s.creationTime(); + } + } + time_t newTime = 0; + Q_FOREACH (const Subkey s, k.subkeys()) { + if ((canEncrypt && s.canEncrypt()) && subkeyIsOk(s)) { + newTime = s.creationTime(); + } + } + if (newTime > oldTime) { + uidC = u; + keyC = k; + } + } + } + } + } + return std::make_tuple(result, keyC, uidC, QString(), Error()); +} + +Error QGpgMEKeyForMailboxJob::start(const QString &mailbox, bool canEncrypt) +{ + run(std::bind(&do_work, std::placeholders::_1, mailbox, canEncrypt)); + return Error(); +} + +KeyListResult QGpgMEKeyForMailboxJob::exec(const QString &mailbox, bool canEncrypt, Key &key, UserID &uid) +{ + const result_type r = do_work(context(), mailbox, canEncrypt); + resultHook(r); + key = std::get<1>(r); + uid = std::get<2>(r); + return std::get<0>(r); +} + +#include "qgpgmekeyformailboxjob.moc" diff --git a/lang/qt/src/qgpgmekeyformailboxjob.h b/lang/qt/src/qgpgmekeyformailboxjob.h new file mode 100644 index 00000000..02a16d32 --- /dev/null +++ b/lang/qt/src/qgpgmekeyformailboxjob.h @@ -0,0 +1,79 @@ +/* + qgpgmekeyformailboxjob.h + + This file is part of libkleopatra, the KDE keymanagement library + Copyright (c) 2004,2008 Klarälvdalens Datakonsult AB + This file is part of qgpgme, the Qt API binding for gpgme + Copyright (c) 2016 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_QGPGMEKEYFORMAILBOXJOB_H__ +#define __QGPGME_QGPGMEKEYFORMAILBOXJOB_H__ +#include "keyformailboxjob.h" + +#include "threadedjobmixin.h" + +#include +#include + +namespace QGpgME +{ + +class QGpgMEKeyForMailboxJob +#ifdef Q_MOC_RUN + : public KeyForMailboxJob +#else + : public _detail::ThreadedJobMixin > +#endif +{ + Q_OBJECT +#ifdef Q_MOC_RUN +public Q_SLOTS: + void slotFinished(); +#endif +public: + explicit QGpgMEKeyForMailboxJob(GpgME::Context *context); + ~QGpgMEKeyForMailboxJob(); + + /** + Starts the operation. \a mailbox is the mailbox to + look for. + + The result is the same as for the LocateKeysJob. + + If \a canEncrypt is true, only keys that have a subkey for encryption + usage are returned. Use this if you need to select a + key for signing. + */ + GpgME::Error start(const QString &mailbox, bool canEncrypt = true) Q_DECL_OVERRIDE; + + GpgME::KeyListResult exec(const QString &mailbox, bool canEncrypt, GpgME::Key &key, GpgME::UserID &uid) Q_DECL_OVERRIDE; +}; + +} +#endif diff --git a/lang/qt/tests/Makefile.am b/lang/qt/tests/Makefile.am index 13495a81..85f6fa6b 100644 --- a/lang/qt/tests/Makefile.am +++ b/lang/qt/tests/Makefile.am @@ -55,12 +55,14 @@ 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) +run_keyformailboxjob_SOURCES = run-keyformailboxjob.cpp nodist_t_keylist_SOURCES = $(moc_files) BUILT_SOURCES = $(moc_files) -noinst_PROGRAMS = t-keylist t-keylocate t-ownertrust t-tofuinfo t-encrypt +noinst_PROGRAMS = t-keylist t-keylocate t-ownertrust t-tofuinfo t-encrypt \ + run-keyformailboxjob CLEANFILES = secring.gpg pubring.gpg pubring.kbx trustdb.gpg dirmngr.conf \ gpg-agent.conf pubring.kbx~ S.gpg-agent gpg.conf pubring.gpg~ \ diff --git a/lang/qt/tests/run-keyformailboxjob.cpp b/lang/qt/tests/run-keyformailboxjob.cpp new file mode 100644 index 00000000..9ac76682 --- /dev/null +++ b/lang/qt/tests/run-keyformailboxjob.cpp @@ -0,0 +1,56 @@ +/* + run-keyformailbox.cpp + + This file is part of QGpgME's test suite. + Copyright (c) 2016 Intevation GmbH + + 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. +*/ + +#include "keyformailboxjob.h" +#include "keylistjob.h" +#include "protocol.h" + +#include "key.h" +#include "keylistresult.h" + +#include + + +int main(int argc, char **argv) +{ + QString mailbox; + if (argc == 2) { + mailbox = QString::fromLocal8Bit(argv[1]); + } + + auto job = QGpgME::openpgp()->keyForMailboxJob(); + GpgME::Key k; + GpgME::UserID uid; + job->exec(mailbox, true, k, uid); + qDebug() << "UID Name: " << uid.name() << " Mail: " << uid.email() << " id: " << uid.id(); + qDebug() << "Key fpr: " << k.primaryFingerprint(); + return 0; +}