diff options
Diffstat (limited to 'lang/cpp/src/context.cpp')
-rw-r--r-- | lang/cpp/src/context.cpp | 1707 |
1 files changed, 1707 insertions, 0 deletions
diff --git a/lang/cpp/src/context.cpp b/lang/cpp/src/context.cpp new file mode 100644 index 00000000..ab633d7a --- /dev/null +++ b/lang/cpp/src/context.cpp @@ -0,0 +1,1707 @@ +/* + context.cpp - wraps a gpgme key context + Copyright (C) 2003, 2007 Klarälvdalens Datakonsult AB + + This file is part of GPGME++. + + GPGME++ is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 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 Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with GPGME++; see the file COPYING.LIB. If not, write to the + Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "config-gpgme++.h" + +#include <context.h> +#include <eventloopinteractor.h> +#include <trustitem.h> +#include <assuanresult.h> +#include <keylistresult.h> +#include <keygenerationresult.h> +#include <importresult.h> +#include <decryptionresult.h> +#include <verificationresult.h> +#include <signingresult.h> +#include <encryptionresult.h> +#include <engineinfo.h> +#include <editinteractor.h> +#include <vfsmountresult.h> + +#include <interfaces/assuantransaction.h> +#include <defaultassuantransaction.h> + +#include "callbacks.h" +#include "data_p.h" +#include "context_p.h" +#include "util.h" + +#include <gpgme.h> + +#include <boost/scoped_array.hpp> + +#include <istream> +#ifndef NDEBUG +#include <iostream> +using std::cerr; +using std::endl; +#endif + +#include <cassert> + +#include <qglobal.h> + +namespace GpgME +{ + +static inline unsigned int xtoi_1(const char *str) +{ + const unsigned int ch = *str; + const unsigned int result = + ch <= '9' ? ch - '0' : + ch <= 'F' ? ch - 'A' + 10 : + /* else */ ch - 'a' + 10 ; + return result < 16 ? result : 0 ; +} +static inline int xtoi_2(const char *str) +{ + return xtoi_1(str) * 16U + xtoi_1(str + 1); +} + +#ifdef HAVE_GPGME_ASSUAN_ENGINE +static void percent_unescape(std::string &s, bool plus2space) +{ + std::string::iterator src = s.begin(), dest = s.begin(), end = s.end(); + while (src != end) { + if (*src == '%' && end - src > 2) { + *dest++ = xtoi_2(&*++src); + src += 2; + } else if (*src == '+' && plus2space) { + *dest++ = ' '; + ++src; + } else { + *dest++ = *src++; + } + } + s.erase(dest, end); +} +#endif + +void initializeLibrary() +{ + gpgme_check_version(0); +} + +Error initializeLibrary(int) +{ + if (gpgme_check_version(GPGME_VERSION)) { + return Error(); + } else { + return Error::fromCode(GPG_ERR_USER_1); + } +} + +static void format_error(gpgme_error_t err, std::string &str) +{ + char buffer[ 1024 ]; + gpgme_strerror_r(err, buffer, sizeof buffer); + buffer[ sizeof buffer - 1 ] = '\0'; + str = buffer; +} + +const char *Error::source() const +{ + return gpgme_strsource((gpgme_error_t)mErr); +} + +const char *Error::asString() const +{ + if (mMessage.empty()) { + format_error(static_cast<gpgme_error_t>(mErr), mMessage); + } + return mMessage.c_str(); +} + +int Error::code() const +{ + return gpgme_err_code(mErr); +} + +int Error::sourceID() const +{ + return gpgme_err_source(mErr); +} + +bool Error::isCanceled() const +{ + return code() == GPG_ERR_CANCELED; +} + +int Error::toErrno() const +{ +//#ifdef HAVE_GPGME_GPG_ERROR_WRAPPERS + return gpgme_err_code_to_errno(static_cast<gpgme_err_code_t>(code())); +//#else +// return gpg_err_code_to_errno( static_cast<gpg_err_code_t>( code() ) ); +//#endif +} + +// static +bool Error::hasSystemError() +{ +#ifdef HAVE_GPGME_GPG_ERROR_WRAPPERS + return gpgme_err_code_from_syserror() == GPG_ERR_MISSING_ERRNO ; +#else + return gpg_err_code_from_syserror() == GPG_ERR_MISSING_ERRNO ; +#endif +} + +// static +void Error::setSystemError(gpg_err_code_t err) +{ + setErrno(gpgme_err_code_to_errno(err)); +} + +// static +void Error::setErrno(int err) +{ +#ifdef HAVE_GPGME_GPG_ERROR_WRAPPERS + gpgme_err_set_errno(err); +#else + gpg_err_set_errno(err); +#endif +} + +// static +Error Error::fromSystemError(unsigned int src) +{ +#ifdef HAVE_GPGME_GPG_ERROR_WRAPPERS + return Error(gpgme_err_make(static_cast<gpgme_err_source_t>(src), gpgme_err_code_from_syserror())); +#else + return Error(gpg_err_make(static_cast<gpg_err_source_t>(src), gpg_err_code_from_syserror())); +#endif +} + +// static +Error Error::fromErrno(int err, unsigned int src) +{ +//#ifdef HAVE_GPGME_GPG_ERROR_WRAPPERS + return Error(gpgme_err_make(static_cast<gpgme_err_source_t>(src), gpgme_err_code_from_errno(err))); +//#else +// return Error( gpg_err_make( static_cast<gpg_err_source_t>( src ), gpg_err_from_from_errno( err ) ) ); +//#endif +} + +// static +Error Error::fromCode(unsigned int err, unsigned int src) +{ +//#ifdef HAVE_GPGME_GPG_ERROR_WRAPPERS + return Error(gpgme_err_make(static_cast<gpgme_err_source_t>(src), static_cast<gpgme_err_code_t>(err))); +//#else +// return Error( gpg_err_make( static_cast<gpg_err_source_t>( src ), static_cast<gpgme_err_code_t>( err ) ) ); +//#endif +} + +std::ostream &operator<<(std::ostream &os, const Error &err) +{ + return os << "GpgME::Error(" << err.encodedError() << " (" << err.asString() << "))"; +} + +Context::Context(gpgme_ctx_t ctx) : d(new Private(ctx)) +{ +} + +Context::~Context() +{ + delete d; +} + +Context *Context::createForProtocol(Protocol proto) +{ + gpgme_ctx_t ctx = 0; + if (gpgme_new(&ctx) != 0) { + return 0; + } + + switch (proto) { + case OpenPGP: + if (gpgme_set_protocol(ctx, GPGME_PROTOCOL_OpenPGP) != 0) { + gpgme_release(ctx); + return 0; + } + break; + case CMS: + if (gpgme_set_protocol(ctx, GPGME_PROTOCOL_CMS) != 0) { + gpgme_release(ctx); + return 0; + } + break; + default: + return 0; + } + + return new Context(ctx); +} + +std::auto_ptr<Context> Context::createForEngine(Engine eng, Error *error) +{ + gpgme_ctx_t ctx = 0; + if (const gpgme_error_t err = gpgme_new(&ctx)) { + if (error) { + *error = Error(err); + } + return std::auto_ptr<Context>(); + } + + switch (eng) { + case AssuanEngine: +#ifdef HAVE_GPGME_ASSUAN_ENGINE + if (const gpgme_error_t err = gpgme_set_protocol(ctx, GPGME_PROTOCOL_ASSUAN)) { + gpgme_release(ctx); + if (error) { + *error = Error(err); + } + return std::auto_ptr<Context>(); + } + break; +#else + if (error) { + *error = Error::fromCode(GPG_ERR_NOT_SUPPORTED); + } + return std::auto_ptr<Context>(); +#endif + case G13Engine: +#ifdef HAVE_GPGME_G13_VFS + if (const gpgme_error_t err = gpgme_set_protocol(ctx, GPGME_PROTOCOL_G13)) { + gpgme_release(ctx); + if (error) { + *error = Error(err); + } + return std::auto_ptr<Context>(); + } + break; +#else + if (error) { + *error = Error::fromCode(GPG_ERR_NOT_SUPPORTED); + } + return std::auto_ptr<Context>(); +#endif + default: + if (error) { + *error = Error::fromCode(GPG_ERR_INV_ARG); + } + return std::auto_ptr<Context>(); + } + + if (error) { + *error = Error(); + } + + return std::auto_ptr<Context>(new Context(ctx)); +} + +// +// +// Context::Private +// +// + +Context::Private::Private(gpgme_ctx_t c) + : ctx(c), + iocbs(0), + lastop(None), + lasterr(GPG_ERR_NO_ERROR), + lastAssuanInquireData(Data::null), + lastAssuanTransaction(), + lastEditInteractor(), + lastCardEditInteractor() +{ + +} + +Context::Private::~Private() +{ + if (ctx) { + gpgme_release(ctx); + } + ctx = 0; + delete iocbs; +} + +// +// +// Context attributes: +// +// + +Protocol Context::protocol() const +{ + gpgme_protocol_t p = gpgme_get_protocol(d->ctx); + switch (p) { + case GPGME_PROTOCOL_OpenPGP: return OpenPGP; + case GPGME_PROTOCOL_CMS: return CMS; + default: return UnknownProtocol; + } +} + +void Context::setArmor(bool useArmor) +{ + gpgme_set_armor(d->ctx, int(useArmor)); +} +bool Context::armor() const +{ + return gpgme_get_armor(d->ctx); +} + +void Context::setTextMode(bool useTextMode) +{ + gpgme_set_textmode(d->ctx, int(useTextMode)); +} +bool Context::textMode() const +{ + return gpgme_get_textmode(d->ctx); +} + +void Context::setOffline(bool useOfflineMode) +{ +#ifdef HAVE_GPGME_CTX_OFFLINE + gpgme_set_offline(d->ctx, int(useOfflineMode)); +#else + Q_UNUSED(useOfflineMode); +#endif +} +bool Context::offline() const +{ +#ifdef HAVE_GPGME_CTX_OFFLINE + return gpgme_get_offline(d->ctx); +#else + return false; +#endif +} + +void Context::setIncludeCertificates(int which) +{ + if (which == DefaultCertificates) { +#ifdef HAVE_GPGME_INCLUDE_CERTS_DEFAULT + which = GPGME_INCLUDE_CERTS_DEFAULT; +#else + which = 1; +#endif + } + gpgme_set_include_certs(d->ctx, which); +} + +int Context::includeCertificates() const +{ + return gpgme_get_include_certs(d->ctx); +} + +void Context::setKeyListMode(unsigned int mode) +{ + gpgme_set_keylist_mode(d->ctx, add_to_gpgme_keylist_mode_t(0, mode)); +} + +void Context::addKeyListMode(unsigned int mode) +{ + const unsigned int cur = gpgme_get_keylist_mode(d->ctx); + gpgme_set_keylist_mode(d->ctx, add_to_gpgme_keylist_mode_t(cur, mode)); +} + +unsigned int Context::keyListMode() const +{ + return convert_from_gpgme_keylist_mode_t(gpgme_get_keylist_mode(d->ctx)); +} + +void Context::setProgressProvider(ProgressProvider *provider) +{ + gpgme_set_progress_cb(d->ctx, provider ? &progress_callback : 0, provider); +} +ProgressProvider *Context::progressProvider() const +{ + void *pp = 0; + gpgme_progress_cb_t pcb = &progress_callback; + gpgme_get_progress_cb(d->ctx, &pcb, &pp); + return static_cast<ProgressProvider *>(pp); +} + +void Context::setPassphraseProvider(PassphraseProvider *provider) +{ + gpgme_set_passphrase_cb(d->ctx, provider ? &passphrase_callback : 0, provider); +} + +PassphraseProvider *Context::passphraseProvider() const +{ + void *pp = 0; + gpgme_passphrase_cb_t pcb = &passphrase_callback; + gpgme_get_passphrase_cb(d->ctx, &pcb, &pp); + return static_cast<PassphraseProvider *>(pp); +} + +void Context::setManagedByEventLoopInteractor(bool manage) +{ + if (!EventLoopInteractor::instance()) { +#ifndef NDEBUG + cerr << "Context::setManagedByEventLoopInteractor(): " + "You must create an instance of EventLoopInteractor " + "before using anything that needs one." << endl; +#endif + return; + } + if (manage) { + EventLoopInteractor::instance()->manage(this); + } else { + EventLoopInteractor::instance()->unmanage(this); + } +} +bool Context::managedByEventLoopInteractor() const +{ + return d->iocbs != 0; +} + +void Context::installIOCallbacks(gpgme_io_cbs *iocbs) +{ + if (!iocbs) { + uninstallIOCallbacks(); + return; + } + gpgme_set_io_cbs(d->ctx, iocbs); + delete d->iocbs; d->iocbs = iocbs; +} + +void Context::uninstallIOCallbacks() +{ + static gpgme_io_cbs noiocbs = { 0, 0, 0, 0, 0 }; + // io.add == 0 means disable io callbacks: + gpgme_set_io_cbs(d->ctx, &noiocbs); + delete d->iocbs; d->iocbs = 0; +} + +Error Context::setLocale(int cat, const char *val) +{ + return Error(d->lasterr = gpgme_set_locale(d->ctx, cat, val)); +} + +EngineInfo Context::engineInfo() const +{ +#ifdef HAVE_GPGME_CTX_GETSET_ENGINE_INFO + return EngineInfo(gpgme_ctx_get_engine_info(d->ctx)); +#else + return EngineInfo(); +#endif +} + +Error Context::setEngineFileName(const char *filename) +{ +#ifdef HAVE_GPGME_CTX_GETSET_ENGINE_INFO + const char *const home_dir = engineInfo().homeDirectory(); + return Error(gpgme_ctx_set_engine_info(d->ctx, gpgme_get_protocol(d->ctx), filename, home_dir)); +#else + return Error::fromCode(GPG_ERR_NOT_IMPLEMENTED); +#endif +} + +Error Context::setEngineHomeDirectory(const char *home_dir) +{ +#ifdef HAVE_GPGME_CTX_GETSET_ENGINE_INFO + const char *const filename = engineInfo().fileName(); + return Error(gpgme_ctx_set_engine_info(d->ctx, gpgme_get_protocol(d->ctx), filename, home_dir)); +#else + return Error::fromCode(GPG_ERR_NOT_IMPLEMENTED); +#endif +} + +// +// +// Key Management +// +// + +Error Context::startKeyListing(const char *pattern, bool secretOnly) +{ + d->lastop = Private::KeyList; + return Error(d->lasterr = gpgme_op_keylist_start(d->ctx, pattern, int(secretOnly))); +} + +Error Context::startKeyListing(const char *patterns[], bool secretOnly) +{ + d->lastop = Private::KeyList; +#ifndef HAVE_GPGME_EXT_KEYLIST_MODE_EXTERNAL_NONBROKEN + if (!patterns || !patterns[0] || !patterns[1]) { + // max. one pattern -> use the non-ext version + return startKeyListing(patterns ? patterns[0] : 0, secretOnly); + } +#endif + return Error(d->lasterr = gpgme_op_keylist_ext_start(d->ctx, patterns, int(secretOnly), 0)); +} + +Key Context::nextKey(GpgME::Error &e) +{ + d->lastop = Private::KeyList; + gpgme_key_t key; + e = Error(d->lasterr = gpgme_op_keylist_next(d->ctx, &key)); + return Key(key, false); +} + +KeyListResult Context::endKeyListing() +{ + d->lasterr = gpgme_op_keylist_end(d->ctx); + return keyListResult(); +} + +KeyListResult Context::keyListResult() const +{ + return KeyListResult(d->ctx, Error(d->lasterr)); +} + +Key Context::key(const char *fingerprint, GpgME::Error &e , bool secret /*, bool forceUpdate*/) +{ + d->lastop = Private::KeyList; + gpgme_key_t key; + e = Error(d->lasterr = gpgme_get_key(d->ctx, fingerprint, &key, int(secret)/*, int( forceUpdate )*/)); + return Key(key, false); +} + +KeyGenerationResult Context::generateKey(const char *parameters, Data &pubKey) +{ + d->lastop = Private::KeyGen; + Data::Private *const dp = pubKey.impl(); + d->lasterr = gpgme_op_genkey(d->ctx, parameters, dp ? dp->data : 0, 0); + return KeyGenerationResult(d->ctx, Error(d->lasterr)); +} + +Error Context::startKeyGeneration(const char *parameters, Data &pubKey) +{ + d->lastop = Private::KeyGen; + Data::Private *const dp = pubKey.impl(); + return Error(d->lasterr = gpgme_op_genkey_start(d->ctx, parameters, dp ? dp->data : 0, 0)); +} + +KeyGenerationResult Context::keyGenerationResult() const +{ + if (d->lastop & Private::KeyGen) { + return KeyGenerationResult(d->ctx, Error(d->lasterr)); + } else { + return KeyGenerationResult(); + } +} + +Error Context::exportPublicKeys(const char *pattern, Data &keyData) +{ + d->lastop = Private::Export; + Data::Private *const dp = keyData.impl(); + return Error(d->lasterr = gpgme_op_export(d->ctx, pattern, 0, dp ? dp->data : 0)); +} + +Error Context::exportPublicKeys(const char *patterns[], Data &keyData) +{ + d->lastop = Private::Export; +#ifndef HAVE_GPGME_EXT_KEYLIST_MODE_EXTERNAL_NONBROKEN + if (!patterns || !patterns[0] || !patterns[1]) { + // max. one pattern -> use the non-ext version + return exportPublicKeys(patterns ? patterns[0] : 0, keyData); + } +#endif + Data::Private *const dp = keyData.impl(); + return Error(d->lasterr = gpgme_op_export_ext(d->ctx, patterns, 0, dp ? dp->data : 0)); +} + +Error Context::startPublicKeyExport(const char *pattern, Data &keyData) +{ + d->lastop = Private::Export; + Data::Private *const dp = keyData.impl(); + return Error(d->lasterr = gpgme_op_export_start(d->ctx, pattern, 0, dp ? dp->data : 0)); +} + +Error Context::startPublicKeyExport(const char *patterns[], Data &keyData) +{ + d->lastop = Private::Export; +#ifndef HAVE_GPGME_EXT_KEYLIST_MODE_EXTERNAL_NONBROKEN + if (!patterns || !patterns[0] || !patterns[1]) { + // max. one pattern -> use the non-ext version + return startPublicKeyExport(patterns ? patterns[0] : 0, keyData); + } +#endif + Data::Private *const dp = keyData.impl(); + return Error(d->lasterr = gpgme_op_export_ext_start(d->ctx, patterns, 0, dp ? dp->data : 0)); +} + +ImportResult Context::importKeys(const Data &data) +{ + d->lastop = Private::Import; + const Data::Private *const dp = data.impl(); + d->lasterr = gpgme_op_import(d->ctx, dp ? dp->data : 0); + return ImportResult(d->ctx, Error(d->lasterr)); +} + +ImportResult Context::importKeys(const std::vector<Key> &kk) +{ + d->lastop = Private::Import; + d->lasterr = make_error(GPG_ERR_NOT_IMPLEMENTED); + + bool shouldHaveResult = false; +#ifdef HAVE_GPGME_OP_IMPORT_KEYS + const boost::scoped_array<gpgme_key_t> keys(new gpgme_key_t[ kk.size() + 1 ]); + gpgme_key_t *keys_it = &keys[0]; + for (std::vector<Key>::const_iterator it = kk.begin(), end = kk.end() ; it != end ; ++it) { + if (it->impl()) { + *keys_it++ = it->impl(); + } + } + *keys_it++ = 0; + d->lasterr = gpgme_op_import_keys(d->ctx, keys.get()); + shouldHaveResult = true; +#endif + if ((gpgme_err_code(d->lasterr) == GPG_ERR_NOT_IMPLEMENTED || + gpgme_err_code(d->lasterr) == GPG_ERR_NOT_SUPPORTED) && + protocol() == CMS) { + // ok, try the workaround (export+import): + std::vector<const char *> fprs; + for (std::vector<Key>::const_iterator it = kk.begin(), end = kk.end() ; it != end ; ++it) { + if (const char *fpr = it->primaryFingerprint()) { + if (*fpr) { + fprs.push_back(fpr); + } + } else if (const char *keyid = it->keyID()) { + if (*keyid) { + fprs.push_back(keyid); + } + } + } + fprs.push_back(0); + Data data; + Data::Private *const dp = data.impl(); + const gpgme_keylist_mode_t oldMode = gpgme_get_keylist_mode(d->ctx); + gpgme_set_keylist_mode(d->ctx, GPGME_KEYLIST_MODE_EXTERN); + d->lasterr = gpgme_op_export_ext(d->ctx, &fprs[0], 0, dp ? dp->data : 0); + gpgme_set_keylist_mode(d->ctx, oldMode); + if (!d->lasterr) { + data.seek(0, SEEK_SET); + d->lasterr = gpgme_op_import(d->ctx, dp ? dp->data : 0); + shouldHaveResult = true; + } + } + if (shouldHaveResult) { + return ImportResult(d->ctx, Error(d->lasterr)); + } else { + return ImportResult(Error(d->lasterr)); + } +} + +Error Context::startKeyImport(const Data &data) +{ + d->lastop = Private::Import; + const Data::Private *const dp = data.impl(); + return Error(d->lasterr = gpgme_op_import_start(d->ctx, dp ? dp->data : 0)); +} + +Error Context::startKeyImport(const std::vector<Key> &kk) +{ + d->lastop = Private::Import; +#ifdef HAVE_GPGME_OP_IMPORT_KEYS + const boost::scoped_array<gpgme_key_t> keys(new gpgme_key_t[ kk.size() + 1 ]); + gpgme_key_t *keys_it = &keys[0]; + for (std::vector<Key>::const_iterator it = kk.begin(), end = kk.end() ; it != end ; ++it) { + if (it->impl()) { + *keys_it++ = it->impl(); + } + } + *keys_it++ = 0; + return Error(d->lasterr = gpgme_op_import_keys_start(d->ctx, keys.get())); +#else + (void)kk; + return Error(d->lasterr = make_error(GPG_ERR_NOT_IMPLEMENTED)); +#endif +} + +ImportResult Context::importResult() const +{ + if (d->lastop & Private::Import) { + return ImportResult(d->ctx, Error(d->lasterr)); + } else { + return ImportResult(); + } +} + +Error Context::deleteKey(const Key &key, bool allowSecretKeyDeletion) +{ + d->lastop = Private::Delete; + return Error(d->lasterr = gpgme_op_delete(d->ctx, key.impl(), int(allowSecretKeyDeletion))); +} + +Error Context::startKeyDeletion(const Key &key, bool allowSecretKeyDeletion) +{ + d->lastop = Private::Delete; + return Error(d->lasterr = gpgme_op_delete_start(d->ctx, key.impl(), int(allowSecretKeyDeletion))); +} + +Error Context::passwd(const Key &key) +{ + d->lastop = Private::Passwd; +#ifdef HAVE_GPGME_OP_PASSWD + return Error(d->lasterr = gpgme_op_passwd(d->ctx, key.impl(), 0U)); +#else + (void)key; + return Error(d->lasterr = make_error(GPG_ERR_NOT_IMPLEMENTED)); +#endif +} + +Error Context::startPasswd(const Key &key) +{ + d->lastop = Private::Passwd; +#ifdef HAVE_GPGME_OP_PASSWD + return Error(d->lasterr = gpgme_op_passwd_start(d->ctx, key.impl(), 0U)); +#else + (void)key; + return Error(d->lasterr = make_error(GPG_ERR_NOT_IMPLEMENTED)); +#endif +} + +Error Context::edit(const Key &key, std::auto_ptr<EditInteractor> func, Data &data) +{ + d->lastop = Private::Edit; + d->lastEditInteractor = func; + Data::Private *const dp = data.impl(); + return Error(d->lasterr = gpgme_op_edit(d->ctx, key.impl(), + d->lastEditInteractor.get() ? edit_interactor_callback : 0, + d->lastEditInteractor.get() ? d->lastEditInteractor->d : 0, + dp ? dp->data : 0)); +} + +Error Context::startEditing(const Key &key, std::auto_ptr<EditInteractor> func, Data &data) +{ + d->lastop = Private::Edit; + d->lastEditInteractor = func; + Data::Private *const dp = data.impl(); + return Error(d->lasterr = gpgme_op_edit_start(d->ctx, key.impl(), + d->lastEditInteractor.get() ? edit_interactor_callback : 0, + d->lastEditInteractor.get() ? d->lastEditInteractor->d : 0, + dp ? dp->data : 0)); +} + +EditInteractor *Context::lastEditInteractor() const +{ + return d->lastEditInteractor.get(); +} + +std::auto_ptr<EditInteractor> Context::takeLastEditInteractor() +{ + return d->lastEditInteractor; +} + +Error Context::cardEdit(const Key &key, std::auto_ptr<EditInteractor> func, Data &data) +{ + d->lastop = Private::CardEdit; + d->lastCardEditInteractor = func; + Data::Private *const dp = data.impl(); + return Error(d->lasterr = gpgme_op_card_edit(d->ctx, key.impl(), + d->lastCardEditInteractor.get() ? edit_interactor_callback : 0, + d->lastCardEditInteractor.get() ? d->lastCardEditInteractor->d : 0, + dp ? dp->data : 0)); +} + +Error Context::startCardEditing(const Key &key, std::auto_ptr<EditInteractor> func, Data &data) +{ + d->lastop = Private::CardEdit; + d->lastCardEditInteractor = func; + Data::Private *const dp = data.impl(); + return Error(d->lasterr = gpgme_op_card_edit_start(d->ctx, key.impl(), + d->lastCardEditInteractor.get() ? edit_interactor_callback : 0, + d->lastCardEditInteractor.get() ? d->lastCardEditInteractor->d : 0, + dp ? dp->data : 0)); +} + +EditInteractor *Context::lastCardEditInteractor() const +{ + return d->lastCardEditInteractor.get(); +} + +std::auto_ptr<EditInteractor> Context::takeLastCardEditInteractor() +{ + return d->lastCardEditInteractor; +} + +Error Context::startTrustItemListing(const char *pattern, int maxLevel) +{ + d->lastop = Private::TrustList; + return Error(d->lasterr = gpgme_op_trustlist_start(d->ctx, pattern, maxLevel)); +} + +TrustItem Context::nextTrustItem(Error &e) +{ + gpgme_trust_item_t ti = 0; + e = Error(d->lasterr = gpgme_op_trustlist_next(d->ctx, &ti)); + return TrustItem(ti); +} + +Error Context::endTrustItemListing() +{ + return Error(d->lasterr = gpgme_op_trustlist_end(d->ctx)); +} + +#ifdef HAVE_GPGME_ASSUAN_ENGINE +static gpgme_error_t assuan_transaction_data_callback(void *opaque, const void *data, size_t datalen) +{ + assert(opaque); + AssuanTransaction *t = static_cast<AssuanTransaction *>(opaque); + return t->data(static_cast<const char *>(data), datalen).encodedError(); +} + +static gpgme_error_t assuan_transaction_inquire_callback(void *opaque, const char *name, const char *args, gpgme_data_t *r_data) +{ + assert(opaque); + Context::Private *p = static_cast<Context::Private *>(opaque); + AssuanTransaction *t = p->lastAssuanTransaction.get(); + assert(t); + Error err; + if (name) { + p->lastAssuanInquireData = t->inquire(name, args, err); + } else { + p->lastAssuanInquireData = Data::null; + } + if (!p->lastAssuanInquireData.isNull()) { + *r_data = p->lastAssuanInquireData.impl()->data; + } + return err.encodedError(); +} + +static gpgme_error_t assuan_transaction_status_callback(void *opaque, const char *status, const char *args) +{ + assert(opaque); + AssuanTransaction *t = static_cast<AssuanTransaction *>(opaque); + std::string a = args; + percent_unescape(a, true); // ### why doesn't gpgme do this?? + return t->status(status, a.c_str()).encodedError(); +} +#endif + +AssuanResult Context::assuanTransact(const char *command) +{ + return assuanTransact(command, std::auto_ptr<AssuanTransaction>(new DefaultAssuanTransaction)); +} + +AssuanResult Context::assuanTransact(const char *command, std::auto_ptr<AssuanTransaction> transaction) +{ + d->lastop = Private::AssuanTransact; + d->lastAssuanTransaction = transaction; + if (!d->lastAssuanTransaction.get()) { + return AssuanResult(Error(d->lasterr = make_error(GPG_ERR_INV_ARG))); + } +#ifdef HAVE_GPGME_ASSUAN_ENGINE + d->lasterr = gpgme_op_assuan_transact(d->ctx, command, + assuan_transaction_data_callback, + d->lastAssuanTransaction.get(), + assuan_transaction_inquire_callback, + d, // sic! + assuan_transaction_status_callback, + d->lastAssuanTransaction.get()); +#else + (void)command; + d->lasterr = make_error(GPG_ERR_NOT_SUPPORTED); +#endif + return AssuanResult(d->ctx, d->lasterr); +} + +Error Context::startAssuanTransaction(const char *command) +{ + return startAssuanTransaction(command, std::auto_ptr<AssuanTransaction>(new DefaultAssuanTransaction)); +} + +Error Context::startAssuanTransaction(const char *command, std::auto_ptr<AssuanTransaction> transaction) +{ + d->lastop = Private::AssuanTransact; + d->lastAssuanTransaction = transaction; + if (!d->lastAssuanTransaction.get()) { + return Error(d->lasterr = make_error(GPG_ERR_INV_ARG)); + } +#ifdef HAVE_GPGME_ASSUAN_ENGINE + return Error(d->lasterr = gpgme_op_assuan_transact_start(d->ctx, command, + assuan_transaction_data_callback, + d->lastAssuanTransaction.get(), + assuan_transaction_inquire_callback, + d, // sic! + assuan_transaction_status_callback, + d->lastAssuanTransaction.get())); +#else + (void)command; + return Error(d->lasterr = make_error(GPG_ERR_NOT_SUPPORTED)); +#endif +} + +AssuanResult Context::assuanResult() const +{ + if (d->lastop & Private::AssuanTransact) { + return AssuanResult(d->ctx, d->lasterr); + } else { + return AssuanResult(); + } +} + +AssuanTransaction *Context::lastAssuanTransaction() const +{ + return d->lastAssuanTransaction.get(); +} + +std::auto_ptr<AssuanTransaction> Context::takeLastAssuanTransaction() +{ + return d->lastAssuanTransaction; +} + +DecryptionResult Context::decrypt(const Data &cipherText, Data &plainText) +{ + d->lastop = Private::Decrypt; + const Data::Private *const cdp = cipherText.impl(); + Data::Private *const pdp = plainText.impl(); + d->lasterr = gpgme_op_decrypt(d->ctx, cdp ? cdp->data : 0, pdp ? pdp->data : 0); + return DecryptionResult(d->ctx, Error(d->lasterr)); +} + +Error Context::startDecryption(const Data &cipherText, Data &plainText) +{ + d->lastop = Private::Decrypt; + const Data::Private *const cdp = cipherText.impl(); + Data::Private *const pdp = plainText.impl(); + return Error(d->lasterr = gpgme_op_decrypt_start(d->ctx, cdp ? cdp->data : 0, pdp ? pdp->data : 0)); +} + +DecryptionResult Context::decryptionResult() const +{ + if (d->lastop & Private::Decrypt) { + return DecryptionResult(d->ctx, Error(d->lasterr)); + } else { + return DecryptionResult(); + } +} + +VerificationResult Context::verifyDetachedSignature(const Data &signature, const Data &signedText) +{ + d->lastop = Private::Verify; + const Data::Private *const sdp = signature.impl(); + const Data::Private *const tdp = signedText.impl(); + d->lasterr = gpgme_op_verify(d->ctx, sdp ? sdp->data : 0, tdp ? tdp->data : 0, 0); + return VerificationResult(d->ctx, Error(d->lasterr)); +} + +VerificationResult Context::verifyOpaqueSignature(const Data &signedData, Data &plainText) +{ + d->lastop = Private::Verify; + const Data::Private *const sdp = signedData.impl(); + Data::Private *const pdp = plainText.impl(); + d->lasterr = gpgme_op_verify(d->ctx, sdp ? sdp->data : 0, 0, pdp ? pdp->data : 0); + return VerificationResult(d->ctx, Error(d->lasterr)); +} + +Error Context::startDetachedSignatureVerification(const Data &signature, const Data &signedText) +{ + d->lastop = Private::Verify; + const Data::Private *const sdp = signature.impl(); + const Data::Private *const tdp = signedText.impl(); + return Error(d->lasterr = gpgme_op_verify_start(d->ctx, sdp ? sdp->data : 0, tdp ? tdp->data : 0, 0)); +} + +Error Context::startOpaqueSignatureVerification(const Data &signedData, Data &plainText) +{ + d->lastop = Private::Verify; + const Data::Private *const sdp = signedData.impl(); + Data::Private *const pdp = plainText.impl(); + return Error(d->lasterr = gpgme_op_verify_start(d->ctx, sdp ? sdp->data : 0, 0, pdp ? pdp->data : 0)); +} + +VerificationResult Context::verificationResult() const +{ + if (d->lastop & Private::Verify) { + return VerificationResult(d->ctx, Error(d->lasterr)); + } else { + return VerificationResult(); + } +} + +std::pair<DecryptionResult, VerificationResult> Context::decryptAndVerify(const Data &cipherText, Data &plainText) +{ + d->lastop = Private::DecryptAndVerify; + const Data::Private *const cdp = cipherText.impl(); + Data::Private *const pdp = plainText.impl(); + d->lasterr = gpgme_op_decrypt_verify(d->ctx, cdp ? cdp->data : 0, pdp ? pdp->data : 0); + return std::make_pair(DecryptionResult(d->ctx, Error(d->lasterr)), + VerificationResult(d->ctx, Error(d->lasterr))); +} + +Error Context::startCombinedDecryptionAndVerification(const Data &cipherText, Data &plainText) +{ + d->lastop = Private::DecryptAndVerify; + const Data::Private *const cdp = cipherText.impl(); + Data::Private *const pdp = plainText.impl(); + return Error(d->lasterr = gpgme_op_decrypt_verify_start(d->ctx, cdp ? cdp->data : 0, pdp ? pdp->data : 0)); +} + +#ifdef HAVE_GPGME_OP_GETAUDITLOG +unsigned int to_auditlog_flags(unsigned int flags) +{ + unsigned int result = 0; + if (flags & Context::HtmlAuditLog) { + result |= GPGME_AUDITLOG_HTML; + } + if (flags & Context::AuditLogWithHelp) { + result |= GPGME_AUDITLOG_WITH_HELP; + } + return result; +} +#endif // HAVE_GPGME_OP_GETAUDITLOG + +Error Context::startGetAuditLog(Data &output, unsigned int flags) +{ + d->lastop = Private::GetAuditLog; +#ifdef HAVE_GPGME_OP_GETAUDITLOG + Data::Private *const odp = output.impl(); + return Error(d->lasterr = gpgme_op_getauditlog_start(d->ctx, odp ? odp->data : 0, to_auditlog_flags(flags))); +#else + (void)output; (void)flags; + return Error(d->lasterr = make_error(GPG_ERR_NOT_IMPLEMENTED)); +#endif +} + +Error Context::getAuditLog(Data &output, unsigned int flags) +{ + d->lastop = Private::GetAuditLog; +#ifdef HAVE_GPGME_OP_GETAUDITLOG + Data::Private *const odp = output.impl(); + return Error(d->lasterr = gpgme_op_getauditlog(d->ctx, odp ? odp->data : 0, to_auditlog_flags(flags))); +#else + (void)output; (void)flags; + return Error(d->lasterr = make_error(GPG_ERR_NOT_IMPLEMENTED)); +#endif +} + +void Context::clearSigningKeys() +{ + gpgme_signers_clear(d->ctx); +} + +Error Context::addSigningKey(const Key &key) +{ + return Error(d->lasterr = gpgme_signers_add(d->ctx, key.impl())); +} + +Key Context::signingKey(unsigned int idx) const +{ + gpgme_key_t key = gpgme_signers_enum(d->ctx, idx); + return Key(key, false); +} + +std::vector<Key> Context::signingKeys() const +{ + std::vector<Key> result; + gpgme_key_t key; + for (unsigned int i = 0 ; (key = gpgme_signers_enum(d->ctx, i)) ; ++i) { + result.push_back(Key(key, false)); + } + return result; +} + +void Context::clearSignatureNotations() +{ +#ifdef HAVE_GPGME_SIG_NOTATION_CLEARADDGET + gpgme_sig_notation_clear(d->ctx); +#endif +} + +GpgME::Error Context::addSignatureNotation(const char *name, const char *value, unsigned int flags) +{ +#ifdef HAVE_GPGME_SIG_NOTATION_CLEARADDGET + return Error(gpgme_sig_notation_add(d->ctx, name, value, add_to_gpgme_sig_notation_flags_t(0, flags))); +#else + (void)name; (void)value; (void)flags; + return Error(make_error(GPG_ERR_NOT_IMPLEMENTED)); +#endif +} + +GpgME::Error Context::addSignaturePolicyURL(const char *url, bool critical) +{ +#ifdef HAVE_GPGME_SIG_NOTATION_CLEARADDGET + return Error(gpgme_sig_notation_add(d->ctx, 0, url, critical ? GPGME_SIG_NOTATION_CRITICAL : 0)); +#else + (void)url; (void)critical; + return Error(make_error(GPG_ERR_NOT_IMPLEMENTED)); +#endif +} + +const char *Context::signaturePolicyURL() const +{ +#ifdef HAVE_GPGME_SIG_NOTATION_CLEARADDGET + for (gpgme_sig_notation_t n = gpgme_sig_notation_get(d->ctx) ; n ; n = n->next) { + if (!n->name) { + return n->value; + } + } +#endif + return 0; +} + +Notation Context::signatureNotation(unsigned int idx) const +{ +#ifdef HAVE_GPGME_SIG_NOTATION_CLEARADDGET + for (gpgme_sig_notation_t n = gpgme_sig_notation_get(d->ctx) ; n ; n = n->next) { + if (n->name) { + if (idx-- == 0) { + return Notation(n); + } + } + } +#endif + return Notation(); +} + +std::vector<Notation> Context::signatureNotations() const +{ + std::vector<Notation> result; +#ifdef HAVE_GPGME_SIG_NOTATION_CLEARADDGET + for (gpgme_sig_notation_t n = gpgme_sig_notation_get(d->ctx) ; n ; n = n->next) { + if (n->name) { + result.push_back(Notation(n)); + } + } +#endif + return result; +} + +static gpgme_sig_mode_t sigmode2sigmode(SignatureMode mode) +{ + switch (mode) { + default: + case NormalSignatureMode: return GPGME_SIG_MODE_NORMAL; + case Detached: return GPGME_SIG_MODE_DETACH; + case Clearsigned: return GPGME_SIG_MODE_CLEAR; + } +} + +SigningResult Context::sign(const Data &plainText, Data &signature, SignatureMode mode) +{ + d->lastop = Private::Sign; + const Data::Private *const pdp = plainText.impl(); + Data::Private *const sdp = signature.impl(); + d->lasterr = gpgme_op_sign(d->ctx, pdp ? pdp->data : 0, sdp ? sdp->data : 0, sigmode2sigmode(mode)); + return SigningResult(d->ctx, Error(d->lasterr)); +} + +Error Context::startSigning(const Data &plainText, Data &signature, SignatureMode mode) +{ + d->lastop = Private::Sign; + const Data::Private *const pdp = plainText.impl(); + Data::Private *const sdp = signature.impl(); + return Error(d->lasterr = gpgme_op_sign_start(d->ctx, pdp ? pdp->data : 0, sdp ? sdp->data : 0, sigmode2sigmode(mode))); +} + +SigningResult Context::signingResult() const +{ + if (d->lastop & Private::Sign) { + return SigningResult(d->ctx, Error(d->lasterr)); + } else { + return SigningResult(); + } +} + +static gpgme_encrypt_flags_t encryptflags2encryptflags(Context::EncryptionFlags flags) +{ + unsigned int result = 0; + if (flags & Context::AlwaysTrust) { + result |= GPGME_ENCRYPT_ALWAYS_TRUST; + } +#ifdef HAVE_GPGME_ENCRYPT_NO_ENCRYPT_TO + if (flags & Context::NoEncryptTo) { + result |= GPGME_ENCRYPT_NO_ENCRYPT_TO; + } +#endif + return static_cast<gpgme_encrypt_flags_t>(result); +} + +EncryptionResult Context::encrypt(const std::vector<Key> &recipients, const Data &plainText, Data &cipherText, EncryptionFlags flags) +{ + d->lastop = Private::Encrypt; +#ifndef HAVE_GPGME_ENCRYPT_NO_ENCRYPT_TO + if (flags & NoEncryptTo) { + return EncryptionResult(Error(d->lasterr = make_error(GPG_ERR_NOT_IMPLEMENTED))); + } +#endif + const Data::Private *const pdp = plainText.impl(); + Data::Private *const cdp = cipherText.impl(); + gpgme_key_t *const keys = new gpgme_key_t[ recipients.size() + 1 ]; + gpgme_key_t *keys_it = keys; + for (std::vector<Key>::const_iterator it = recipients.begin() ; it != recipients.end() ; ++it) { + if (it->impl()) { + *keys_it++ = it->impl(); + } + } + *keys_it++ = 0; + d->lasterr = gpgme_op_encrypt(d->ctx, keys, encryptflags2encryptflags(flags), + pdp ? pdp->data : 0, cdp ? cdp->data : 0); + delete[] keys; + return EncryptionResult(d->ctx, Error(d->lasterr)); +} + +Error Context::encryptSymmetrically(const Data &plainText, Data &cipherText) +{ + d->lastop = Private::Encrypt; + const Data::Private *const pdp = plainText.impl(); + Data::Private *const cdp = cipherText.impl(); + return Error(d->lasterr = gpgme_op_encrypt(d->ctx, 0, (gpgme_encrypt_flags_t)0, + pdp ? pdp->data : 0, cdp ? cdp->data : 0)); +} + +Error Context::startEncryption(const std::vector<Key> &recipients, const Data &plainText, Data &cipherText, EncryptionFlags flags) +{ + d->lastop = Private::Encrypt; +#ifndef HAVE_GPGME_ENCRYPT_NO_ENCRYPT_TO + if (flags & NoEncryptTo) { + return Error(d->lasterr = make_error(GPG_ERR_NOT_IMPLEMENTED)); + } +#endif + const Data::Private *const pdp = plainText.impl(); + Data::Private *const cdp = cipherText.impl(); + gpgme_key_t *const keys = new gpgme_key_t[ recipients.size() + 1 ]; + gpgme_key_t *keys_it = keys; + for (std::vector<Key>::const_iterator it = recipients.begin() ; it != recipients.end() ; ++it) { + if (it->impl()) { + *keys_it++ = it->impl(); + } + } + *keys_it++ = 0; + d->lasterr = gpgme_op_encrypt_start(d->ctx, keys, encryptflags2encryptflags(flags), + pdp ? pdp->data : 0, cdp ? cdp->data : 0); + delete[] keys; + return Error(d->lasterr); +} + +EncryptionResult Context::encryptionResult() const +{ + if (d->lastop & Private::Encrypt) { + return EncryptionResult(d->ctx, Error(d->lasterr)); + } else { + return EncryptionResult(); + } +} + +std::pair<SigningResult, EncryptionResult> Context::signAndEncrypt(const std::vector<Key> &recipients, const Data &plainText, Data &cipherText, EncryptionFlags flags) +{ + d->lastop = Private::SignAndEncrypt; + const Data::Private *const pdp = plainText.impl(); + Data::Private *const cdp = cipherText.impl(); + gpgme_key_t *const keys = new gpgme_key_t[ recipients.size() + 1 ]; + gpgme_key_t *keys_it = keys; + for (std::vector<Key>::const_iterator it = recipients.begin() ; it != recipients.end() ; ++it) { + if (it->impl()) { + *keys_it++ = it->impl(); + } + } + *keys_it++ = 0; + d->lasterr = gpgme_op_encrypt_sign(d->ctx, keys, encryptflags2encryptflags(flags), + pdp ? pdp->data : 0, cdp ? cdp->data : 0); + delete[] keys; + return std::make_pair(SigningResult(d->ctx, Error(d->lasterr)), + EncryptionResult(d->ctx, Error(d->lasterr))); +} + +Error Context::startCombinedSigningAndEncryption(const std::vector<Key> &recipients, const Data &plainText, Data &cipherText, EncryptionFlags flags) +{ + d->lastop = Private::SignAndEncrypt; + const Data::Private *const pdp = plainText.impl(); + Data::Private *const cdp = cipherText.impl(); + gpgme_key_t *const keys = new gpgme_key_t[ recipients.size() + 1 ]; + gpgme_key_t *keys_it = keys; + for (std::vector<Key>::const_iterator it = recipients.begin() ; it != recipients.end() ; ++it) { + if (it->impl()) { + *keys_it++ = it->impl(); + } + } + *keys_it++ = 0; + d->lasterr = gpgme_op_encrypt_sign_start(d->ctx, keys, encryptflags2encryptflags(flags), + pdp ? pdp->data : 0, cdp ? cdp->data : 0); + delete[] keys; + return Error(d->lasterr); +} + +Error Context::createVFS(const char *containerFile, const std::vector< Key > &recipients) +{ + d->lastop = Private::CreateVFS; +#ifdef HAVE_GPGME_G13_VFS + gpgme_key_t *const keys = new gpgme_key_t[ recipients.size() + 1 ]; + gpgme_key_t *keys_it = keys; + for (std::vector<Key>::const_iterator it = recipients.begin() ; it != recipients.end() ; ++it) { + if (it->impl()) { + *keys_it++ = it->impl(); + } + } + *keys_it++ = 0; + + gpgme_error_t op_err; + d->lasterr = gpgme_op_vfs_create(d->ctx, keys, containerFile, 0, &op_err); + delete[] keys; + Error error(d->lasterr); + if (error) { + return error; + } + return Error(d->lasterr = op_err); +#else + Q_UNUSED(containerFile); + Q_UNUSED(recipients); + return Error(d->lasterr = make_error(GPG_ERR_NOT_SUPPORTED)); +#endif +} + +VfsMountResult Context::mountVFS(const char *containerFile, const char *mountDir) +{ + d->lastop = Private::MountVFS; +#ifdef HAVE_GPGME_G13_VFS + gpgme_error_t op_err; + d->lasterr = gpgme_op_vfs_mount(d->ctx, containerFile, mountDir, 0, &op_err); + return VfsMountResult(d->ctx, Error(d->lasterr), Error(op_err)); +#else + Q_UNUSED(containerFile); + Q_UNUSED(mountDir); + return VfsMountResult(d->ctx, Error(d->lasterr = make_error(GPG_ERR_NOT_SUPPORTED)), Error()); +#endif +} + +Error Context::cancelPendingOperation() +{ +#ifdef HAVE_GPGME_CANCEL_ASYNC + return Error(gpgme_cancel_async(d->ctx)); +#else + return Error(gpgme_cancel(d->ctx)); +#endif +} + +bool Context::poll() +{ + gpgme_error_t e = GPG_ERR_NO_ERROR; + const bool finished = gpgme_wait(d->ctx, &e, 0); + if (finished) { + d->lasterr = e; + } + return finished; +} + +Error Context::wait() +{ + gpgme_error_t e = GPG_ERR_NO_ERROR; + gpgme_wait(d->ctx, &e, 1); + return Error(d->lasterr = e); +} + +Error Context::lastError() const +{ + return Error(d->lasterr); +} + +std::ostream &operator<<(std::ostream &os, Protocol proto) +{ + os << "GpgME::Protocol("; + switch (proto) { + case OpenPGP: + os << "OpenPGP"; + break; + case CMS: + os << "CMS"; + break; + default: + case UnknownProtocol: + os << "UnknownProtocol"; + break; + } + return os << ')'; +} + +std::ostream &operator<<(std::ostream &os, Engine eng) +{ + os << "GpgME::Engine("; + switch (eng) { + case GpgEngine: + os << "GpgEngine"; + break; + case GpgSMEngine: + os << "GpgSMEngine"; + break; + case GpgConfEngine: + os << "GpgConfEngine"; + break; + case AssuanEngine: + os << "AssuanEngine"; + break; + default: + case UnknownEngine: + os << "UnknownEngine"; + break; + } + return os << ')'; +} + +std::ostream &operator<<(std::ostream &os, Context::CertificateInclusion incl) +{ + os << "GpgME::Context::CertificateInclusion(" << static_cast<int>(incl); + switch (incl) { + case Context::DefaultCertificates: + os << "(DefaultCertificates)"; + break; + case Context::AllCertificatesExceptRoot: + os << "(AllCertificatesExceptRoot)"; + break; + case Context::AllCertificates: + os << "(AllCertificates)"; + break; + case Context::NoCertificates: + os << "(NoCertificates)"; + break; + case Context::OnlySenderCertificate: + os << "(OnlySenderCertificate)"; + break; + } + return os << ')'; +} + +std::ostream &operator<<(std::ostream &os, KeyListMode mode) +{ + os << "GpgME::KeyListMode("; +#define CHECK( x ) if ( !(mode & (x)) ) {} else do { os << #x " "; } while (0) + CHECK(Local); + CHECK(Extern); + CHECK(Signatures); + CHECK(Validate); + CHECK(Ephemeral); +#undef CHECK + return os << ')'; +} + +std::ostream &operator<<(std::ostream &os, SignatureMode mode) +{ + os << "GpgME::SignatureMode("; + switch (mode) { +#define CHECK( x ) case x: os << #x; break + CHECK(NormalSignatureMode); + CHECK(Detached); + CHECK(Clearsigned); +#undef CHECK + default: + os << "???" "(" << static_cast<int>(mode) << ')'; + break; + } + return os << ')'; +} + +std::ostream &operator<<(std::ostream &os, Context::EncryptionFlags flags) +{ + os << "GpgME::Context::EncryptionFlags("; +#define CHECK( x ) if ( !(flags & (Context::x)) ) {} else do { os << #x " "; } while (0) + CHECK(AlwaysTrust); +#undef CHECK + return os << ')'; +} + +std::ostream &operator<<(std::ostream &os, Context::AuditLogFlags flags) +{ + os << "GpgME::Context::AuditLogFlags("; +#define CHECK( x ) if ( !(flags & (Context::x)) ) {} else do { os << #x " "; } while (0) + CHECK(HtmlAuditLog); + CHECK(AuditLogWithHelp); +#undef CHECK + return os << ')'; +} + +} // namespace GpgME + +GpgME::Error GpgME::setDefaultLocale(int cat, const char *val) +{ + return Error(gpgme_set_locale(0, cat, val)); +} + +GpgME::EngineInfo GpgME::engineInfo(GpgME::Protocol proto) +{ + gpgme_engine_info_t ei = 0; + if (gpgme_get_engine_info(&ei)) { + return EngineInfo(); + } + + const gpgme_protocol_t p = proto == CMS ? GPGME_PROTOCOL_CMS : GPGME_PROTOCOL_OpenPGP ; + + for (gpgme_engine_info_t i = ei ; i ; i = i->next) { + if (i->protocol == p) { + return EngineInfo(i); + } + } + + return EngineInfo(); +} + +GpgME::Error GpgME::checkEngine(GpgME::Protocol proto) +{ + const gpgme_protocol_t p = proto == CMS ? GPGME_PROTOCOL_CMS : GPGME_PROTOCOL_OpenPGP ; + + return Error(gpgme_engine_check_version(p)); +} + +static const gpgme_protocol_t UNKNOWN_PROTOCOL = static_cast<gpgme_protocol_t>(255); + +static gpgme_protocol_t engine2protocol(const GpgME::Engine engine) +{ + switch (engine) { + case GpgME::GpgEngine: return GPGME_PROTOCOL_OpenPGP; + case GpgME::GpgSMEngine: return GPGME_PROTOCOL_CMS; + case GpgME::GpgConfEngine: +#ifdef HAVE_GPGME_PROTOCOL_GPGCONF + return GPGME_PROTOCOL_GPGCONF; +#else + break; +#endif + case GpgME::AssuanEngine: +#ifdef HAVE_GPGME_ASSUAN_ENGINE + return GPGME_PROTOCOL_ASSUAN; +#else + break; +#endif + case GpgME::G13Engine: +#ifdef HAVE_GPGME_G13_VFS + return GPGME_PROTOCOL_G13; +#else + break; +#endif + case GpgME::UnknownEngine: + ; + } + return UNKNOWN_PROTOCOL; +} + +GpgME::EngineInfo GpgME::engineInfo(GpgME::Engine engine) +{ + gpgme_engine_info_t ei = 0; + if (gpgme_get_engine_info(&ei)) { + return EngineInfo(); + } + + const gpgme_protocol_t p = engine2protocol(engine); + + for (gpgme_engine_info_t i = ei ; i ; i = i->next) { + if (i->protocol == p) { + return EngineInfo(i); + } + } + + return EngineInfo(); +} + +GpgME::Error GpgME::checkEngine(GpgME::Engine engine) +{ + const gpgme_protocol_t p = engine2protocol(engine); + + return Error(gpgme_engine_check_version(p)); +} + +static const unsigned long supported_features = 0 + | GpgME::ValidatingKeylistModeFeature + | GpgME::CancelOperationFeature + | GpgME::WrongKeyUsageFeature +#ifdef HAVE_GPGME_INCLUDE_CERTS_DEFAULT + | GpgME::DefaultCertificateInclusionFeature +#endif +#ifdef HAVE_GPGME_CTX_GETSET_ENGINE_INFO + | GpgME::GetSetEngineInfoFeature +#endif +#ifdef HAVE_GPGME_SIG_NOTATION_CLEARADDGET + | GpgME::ClearAddGetSignatureNotationsFeature +#endif +#ifdef HAVE_GPGME_DATA_SET_FILE_NAME + | GpgME::SetDataFileNameFeeature +#endif +#ifdef HAVE_GPGME_KEYLIST_MODE_SIG_NOTATIONS + | GpgME::SignatureNotationsKeylistModeFeature +#endif +#ifdef HAVE_GPGME_KEY_SIG_NOTATIONS + | GpgME::KeySignatureNotationsFeature +#endif +#ifdef HAVE_GPGME_KEY_T_IS_QUALIFIED + | GpgME::KeyIsQualifiedFeature +#endif +#ifdef HAVE_GPGME_SIG_NOTATION_CRITICAL + | GpgME::SignatureNotationsCriticalFlagFeature +#endif +#ifdef HAVE_GPGME_SIG_NOTATION_FLAGS_T + | GpgME::SignatureNotationsFlagsFeature +#endif +#ifdef HAVE_GPGME_SIG_NOTATION_HUMAN_READABLE + | GpgME::SignatureNotationsHumanReadableFlagFeature +#endif +#ifdef HAVE_GPGME_SUBKEY_T_IS_QUALIFIED + | GpgME::SubkeyIsQualifiedFeature +#endif +#ifdef HAVE_GPGME_ENGINE_INFO_T_HOME_DIR + | GpgME::EngineInfoHomeDirFeature +#endif +#ifdef HAVE_GPGME_DECRYPT_RESULT_T_FILE_NAME + | GpgME::DecryptionResultFileNameFeature +#endif +#ifdef HAVE_GPGME_DECRYPT_RESULT_T_RECIPIENTS + | GpgME::DecryptionResultRecipientsFeature +#endif +#ifdef HAVE_GPGME_VERIFY_RESULT_T_FILE_NAME + | GpgME::VerificationResultFileNameFeature +#endif +#ifdef HAVE_GPGME_SIGNATURE_T_PKA_FIELDS + | GpgME::SignaturePkaFieldsFeature +#endif +#ifdef HAVE_GPGME_SIGNATURE_T_ALGORITHM_FIELDS + | GpgME::SignatureAlgorithmFieldsFeature +#endif +#ifdef HAVE_GPGME_GET_FDPTR + | GpgME::FdPointerFeature +#endif +#ifdef HAVE_GPGME_OP_GETAUDITLOG + | GpgME::AuditLogFeature +#endif +#ifdef HAVE_GPGME_PROTOCOL_GPGCONF + | GpgME::GpgConfEngineFeature +#endif +#ifdef HAVE_GPGME_CANCEL_ASYNC + | GpgME::CancelOperationAsyncFeature +#endif +#ifdef HAVE_GPGME_ENCRYPT_NO_ENCRYPT_TO + | GpgME::NoEncryptToEncryptionFlagFeature +#endif +#ifdef HAVE_GPGME_SUBKEY_T_IS_CARDKEY + | GpgME::CardKeyFeature +#endif +#ifdef HAVE_GPGME_ASSUAN_ENGINE + | GpgME::AssuanEngineFeature +#endif +#ifdef HAVE_GPGME_KEYLIST_MODE_EPHEMERAL + | GpgME::EphemeralKeylistModeFeature +#endif +#ifdef HAVE_GPGME_OP_IMPORT_KEYS + | GpgME::ImportFromKeyserverFeature +#endif +#ifdef HAVE_GPGME_G13_VFS + | GpgME::G13VFSFeature +#endif +#ifdef HAVE_GPGME_OP_PASSWD + | GpgME::PasswdFeature +#endif + ; + +static const unsigned long supported_features2 = 0 + ; + +bool GpgME::hasFeature(unsigned long features) +{ + return features == (features & supported_features); +} + +bool GpgME::hasFeature(unsigned long features, unsigned long features2) +{ + return features == (features & supported_features) + && features2 == (features2 & supported_features2) + ; +} |