[Pkg-gnupg-commit] [gpgme] 01/09: imported cleanup+bugfixes from upstream
Daniel Kahn Gillmor
dkg at fifthhorseman.net
Fri Aug 18 02:29:19 UTC 2017
This is an automated email from the git hooks/post-receive script.
dkg pushed a commit to branch master
in repository gpgme.
commit 05c4c72dda62c02d3c99985267ff4909d4f684ef
Author: Daniel Kahn Gillmor <dkg at fifthhorseman.net>
Date: Thu Aug 17 21:06:02 2017 -0400
imported cleanup+bugfixes from upstream
---
...003-tests-Make-error-message-more-helpful.patch | 27 +
...004-python-build-Reinstate-prepare-target.patch | 106 +
...ython-Generate-files-into-build-directory.patch | 239 +
...006-python-Fix-vpath-builds-fix-distcheck.patch | 334 ++
.../0007-python-simplify-build-some-fixups.patch | 5970 ++++++++++++++++++++
...0008-python-support-.pydistutils.cfg-mode.patch | 101 +
...o-not-use-check-local-magic-as-dependency.patch | 98 +
...10-python-Remove-usage-of-PYTHON_VERSIONS.patch | 111 +
.../0011-python-Remove-unneeded-stats-copy.patch | 30 +
...-Read-gpg-error.h-using-the-pre-processor.patch | 131 +
...thon-Support-alternatate-libdir-for-tests.patch | 63 +
debian/patches/0014-python-Fix-distcheck.patch | 30 +
debian/patches/0015-python-Prune-CLEANFILES.patch | 27 +
...ython-fix-run-tests-missing-python_libdir.patch | 28 +
...autoconf-pre-processor-when-building-via-.patch | 55 +
.../0018-tests-Update-encrypted-sample-files.patch | 80 +
...mprove-doc-on-passphrase_cb-pinentry-mode.patch | 45 +
...core-Don-t-split-gpgconf-strings-on-comma.patch | 40 +
.../0021-qt-tests-Don-t-use-internal-API.patch | 153 +
...022-qt-Undeprecate-API-that-I-find-useful.patch | 92 +
.../0023-qt-Add-a-missing-include-functional.patch | 33 +
debian/patches/0024-qt-Stop-agent-on-clean.patch | 24 +
.../patches/0025-tests-Harmonize-test-suites.patch | 93 +
...sure-to-kill-all-previously-running-daemo.patch | 73 +
...0027-python-Fix-test-environment-creation.patch | 25 +
...0028-tests-Remove-remnants-of-check-local.patch | 35 +
.../0029-python-Fix-build-in-certain-cases.patch | 30 +
.../patches/0030-core-Sort-the-status-table.patch | 85 +
...p-Fix-CMake-config-library-name-for-GPGME.patch | 28 +
debian/patches/0032-Fix-some-shadow-warnings.patch | 38 +
...lify-parsing-of-STATUS_ERROR-in-decrypt.c.patch | 127 +
...-CANCELED-and-BAD_PASSPHRASE-error-code-o.patch | 78 +
...Return-NO_SECKEY-error-code-on-decryption.patch | 86 +
...printf-compiler-warning-for-an-error-case.patch | 25 +
debian/patches/0037-Sync-autogen.sh.patch | 245 +
...038-tests-Make-agent-spawning-more-robust.patch | 54 +
debian/patches/0039-tests-Fix-blunder.patch | 25 +
debian/patches/0040-tests-Fix-distcheck.patch | 25 +
...ore-Fix-status-parsing-for-decrypt-verify.patch | 61 +
.../0042-doc-Add-more-tofu-documentation.patch | 104 +
.../0043-doc-Clarify-import-keys-operation.patch | 92 +
...doc-Clarify-import-keys-operation-further.patch | 42 +
debian/patches/series | 42 +
43 files changed, 9230 insertions(+)
diff --git a/debian/patches/0003-tests-Make-error-message-more-helpful.patch b/debian/patches/0003-tests-Make-error-message-more-helpful.patch
new file mode 100644
index 0000000..27066e5
--- /dev/null
+++ b/debian/patches/0003-tests-Make-error-message-more-helpful.patch
@@ -0,0 +1,27 @@
+From: Justus Winter <justus at g10code.com>
+Date: Tue, 28 Mar 2017 16:30:03 +0200
+Subject: tests: Make error message more helpful.
+
+* tests/gpg/t-keylist.c (main): Print number of returned and expected
+keys.
+
+Signed-off-by: Justus Winter <justus at g10code.com>
+(cherry picked from commit a13e4abe9463579ef23d1acea39a093abfc6528d)
+---
+ tests/gpg/t-keylist.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/tests/gpg/t-keylist.c b/tests/gpg/t-keylist.c
+index 6ee023c..8a32f9b 100644
+--- a/tests/gpg/t-keylist.c
++++ b/tests/gpg/t-keylist.c
+@@ -568,7 +568,8 @@ main (int argc, char **argv)
+
+ if (keys[i].fpr)
+ {
+- fprintf (stderr, "Less keys returned than expected\n");
++ fprintf (stderr, "Less keys (%d) returned than expected (%d)\n",
++ i, DIM (keys) - 1);
+ exit (1);
+ }
+
diff --git a/debian/patches/0004-python-build-Reinstate-prepare-target.patch b/debian/patches/0004-python-build-Reinstate-prepare-target.patch
new file mode 100644
index 0000000..c520190
--- /dev/null
+++ b/debian/patches/0004-python-build-Reinstate-prepare-target.patch
@@ -0,0 +1,106 @@
+From: Alon Bar-Lev <alon.barlev at gmail.com>
+Date: Tue, 28 Mar 2017 21:55:59 +0300
+Subject: python,build: Reinstate prepare target.
+
+* lang/python/Makefile.am: Fix 'prepare' target.
+* lang/python/setup.py.in: Use 'abs_top_builddir' instead of guessing
+the path.
+--
+
+'prepare' will prepare target at PREPAREDIR. The automake integration
+will also make use of prepare target. Downstream distributors may
+also make use of prepare target.
+
+Signed-off-by: Alon Bar-Lev <alon.barlev at gmail.com>
+(cherry picked from commit 9786e3a96e6772166f3523e74a748b9db20fae7c)
+---
+ lang/python/Makefile.am | 25 +++++++++++++++----------
+ lang/python/setup.py.in | 9 +++++----
+ 2 files changed, 20 insertions(+), 14 deletions(-)
+
+diff --git a/lang/python/Makefile.am b/lang/python/Makefile.am
+index d91ead9..a18a014 100644
+--- a/lang/python/Makefile.am
++++ b/lang/python/Makefile.am
+@@ -44,17 +44,22 @@ COPY_FILES_GPG = \
+ $(srcdir)/gpg/results.py \
+ $(srcdir)/gpg/util.py
+
++.PHONY: prepare
++prepare:
++ test -n "$(PREPAREDIR)"
++ $(MKDIR_P) "$(PREPAREDIR)/gpg"
++ cp -R $(COPY_FILES) "$(PREPAREDIR)"
++ cp setup.py "$(PREPAREDIR)"
++ cp gpg/version.py "$(PREPAREDIR)/gpg"
++ ln -sf "$(abs_top_srcdir)/src/data.h" "$(PREPAREDIR)"
++ ln -sf "$(abs_top_builddir)/config.h" "$(PREPAREDIR)"
++ cp -R $(COPY_FILES_GPG) "$(PREPAREDIR)/gpg"
++
+ # For VPATH builds we need to copy some files because Python's
+ # distutils are not VPATH-aware.
+ copystamp: $(COPY_FILES) $(COPY_FILES_GPG)
+ set -e ; for VERSION in $(PYTHON_VERSIONS); do \
+- $(MKDIR_P) python$${VERSION}-gpg/gpg ; \
+- cp -R $(COPY_FILES) python$${VERSION}-gpg ; \
+- cp setup.py python$${VERSION}-gpg ; \
+- cp gpg/version.py python$${VERSION}-gpg/gpg ; \
+- ln -sf "$(abs_top_srcdir)/src/data.h" python$${VERSION}-gpg ; \
+- ln -sf "$(abs_top_builddir)/config.h" python$${VERSION}-gpg ; \
+- cp -R $(COPY_FILES_GPG) python$${VERSION}-gpg/gpg ; \
++ $(MAKE) PREPAREDIR=python$${VERSION}-gpg prepare; \
+ done
+ touch $@
+
+@@ -63,6 +68,7 @@ all-local: copystamp
+ PYTHON="$$1" ; shift ; \
+ cd python$${VERSION}-gpg && \
+ CFLAGS="$(CFLAGS)" \
++ abs_top_builddir="$(abs_top_builddir)" \
+ $$PYTHON setup.py build --verbose ; \
+ cd .. ; \
+ done
+@@ -71,12 +77,10 @@ python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz \
+ python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz.asc: copystamp
+ cd python$(PYTHON_VERSION)-gpg && \
+ CFLAGS="$(CFLAGS)" \
++ abs_top_builddir="$(abs_top_builddir)" \
+ $(PYTHON) setup.py sdist --verbose
+ gpg2 --detach-sign --armor python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz
+
+-.PHONY: prepare
+-prepare: copystamp
+-
+ .PHONY: sdist
+ sdist: python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz \
+ python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz.asc
+@@ -104,6 +108,7 @@ install-exec-local:
+ set -e ; set $(PYTHONS); for VERSION in $(PYTHON_VERSIONS); do \
+ PYTHON="$$1" ; shift ; \
+ cd python$${VERSION}-gpg ; \
++ abs_top_builddir="$(abs_top_builddir)" \
+ $$PYTHON setup.py install \
+ --prefix $(DESTDIR)$(prefix) \
+ --record files.txt \
+diff --git a/lang/python/setup.py.in b/lang/python/setup.py.in
+index bf4efa3..8ddbf27 100755
+--- a/lang/python/setup.py.in
++++ b/lang/python/setup.py.in
+@@ -34,12 +34,13 @@ in_tree = False
+ extra_swig_opts = []
+ extra_macros = dict()
+
+-if os.path.exists("../../../src/gpgme-config"):
++abs_top_builddir = os.environ.get("abs_top_builddir")
++if abs_top_builddir:
+ # In-tree build.
+ in_tree = True
+- gpgme_config = ["../../../src/gpgme-config"] + gpgme_config_flags
+- gpgme_h = "../../../src/gpgme.h"
+- library_dirs = ["../../../src/.libs"] # XXX uses libtool internals
++ gpgme_config = [os.path.join(abs_top_builddir, "src/gpgme-config")] + gpgme_config_flags
++ gpgme_h = os.path.join(abs_top_builddir, "src/gpgme.h")
++ library_dirs = [os.path.join(abs_top_builddir, "src/.libs")] # XXX uses libtool internals
+ extra_macros.update(
+ HAVE_CONFIG_H=1,
+ HAVE_DATA_H=1,
diff --git a/debian/patches/0005-python-Generate-files-into-build-directory.patch b/debian/patches/0005-python-Generate-files-into-build-directory.patch
new file mode 100644
index 0000000..9c81ed1
--- /dev/null
+++ b/debian/patches/0005-python-Generate-files-into-build-directory.patch
@@ -0,0 +1,239 @@
+From: Alon Bar-Lev <alon.barlev at gmail.com>
+Date: Sun, 2 Apr 2017 02:29:52 +0300
+Subject: python: Generate files into build directory
+
+* lang/python/setup.py.in: Generate files within BuildExtFirstHack
+adjust build flags at this point instead of global.
+* lang/python/Makefile.am: Remove logic of separate source directory per
+python version in favor of build directory.
+* lang/python/tests/run-tests.py: Adjust build directory location.
+--
+
+Generate files into build directory, leaving the source directory clean.
+Use the same source directory for multiple python version build. Result
+of 'prepare' target is a standard distutil layout that can be used
+easily by downstream to build all python targets in-place.
+
+Signed-off-by: Alon Bar-Lev <alon.barlev at gmail.com>
+(cherry picked from commit 801d7d8c5dd530d26ad6c4bcc94d986e6e022da4)
+---
+ lang/python/Makefile.am | 79 +++++++++++++++---------------------------
+ lang/python/setup.py.in | 48 +++++++++++++++++++------
+ lang/python/tests/run-tests.py | 1 -
+ 3 files changed, 64 insertions(+), 64 deletions(-)
+
+diff --git a/lang/python/Makefile.am b/lang/python/Makefile.am
+index a18a014..b9145f5 100644
+--- a/lang/python/Makefile.am
++++ b/lang/python/Makefile.am
+@@ -27,70 +27,45 @@ EXTRA_DIST = \
+
+ SUBDIRS = . tests
+
+-COPY_FILES = \
+- $(srcdir)/gpgme.i \
+- $(srcdir)/README \
+- $(srcdir)/MANIFEST.in \
+- $(srcdir)/gpgme-h-clean.py \
+- $(srcdir)/examples \
+- $(srcdir)/helpers.c $(srcdir)/helpers.h $(srcdir)/private.h
+-
+-COPY_FILES_GPG = \
+- $(srcdir)/gpg/callbacks.py \
+- $(srcdir)/gpg/constants \
+- $(srcdir)/gpg/core.py \
+- $(srcdir)/gpg/errors.py \
+- $(srcdir)/gpg/__init__.py \
+- $(srcdir)/gpg/results.py \
+- $(srcdir)/gpg/util.py
+-
+ .PHONY: prepare
+-prepare:
+- test -n "$(PREPAREDIR)"
+- $(MKDIR_P) "$(PREPAREDIR)/gpg"
+- cp -R $(COPY_FILES) "$(PREPAREDIR)"
+- cp setup.py "$(PREPAREDIR)"
+- cp gpg/version.py "$(PREPAREDIR)/gpg"
+- ln -sf "$(abs_top_srcdir)/src/data.h" "$(PREPAREDIR)"
+- ln -sf "$(abs_top_builddir)/config.h" "$(PREPAREDIR)"
+- cp -R $(COPY_FILES_GPG) "$(PREPAREDIR)/gpg"
++prepare: copystamp
+
+ # For VPATH builds we need to copy some files because Python's
+ # distutils are not VPATH-aware.
+-copystamp: $(COPY_FILES) $(COPY_FILES_GPG)
+- set -e ; for VERSION in $(PYTHON_VERSIONS); do \
+- $(MAKE) PREPAREDIR=python$${VERSION}-gpg prepare; \
+- done
++copystamp:
++ ln -sf "$(abs_top_srcdir)/src/data.h" .
++ ln -sf "$(abs_top_builddir)/config.h" .
+ touch $@
+
+ all-local: copystamp
+ set -e ; set $(PYTHONS); for VERSION in $(PYTHON_VERSIONS); do \
+ PYTHON="$$1" ; shift ; \
+- cd python$${VERSION}-gpg && \
+ CFLAGS="$(CFLAGS)" \
+ abs_top_builddir="$(abs_top_builddir)" \
+- $$PYTHON setup.py build --verbose ; \
+- cd .. ; \
++ $$PYTHON setup.py build --verbose --build-base=python$${VERSION}-gpg ; \
+ done
+
+-python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz \
+ python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz.asc: copystamp
+- cd python$(PYTHON_VERSION)-gpg && \
++ $(MKDIR_P) python$(PYTHON_VERSION)-gpg-dist
+ CFLAGS="$(CFLAGS)" \
+ abs_top_builddir="$(abs_top_builddir)" \
+- $(PYTHON) setup.py sdist --verbose
+- gpg2 --detach-sign --armor python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz
++ $(PYTHON) setup.py sdist --verbose --dist-dir=python$(PYTHON_VERSION)-gpg-dist \
++ --manifest=python$(PYTHON_VERSION)-gpg-dist/MANIFEST
++ gpg2 --detach-sign --armor python$(PYTHON_VERSION)-gpg-dist/gpg-$(VERSION).tar.gz
+
+ .PHONY: sdist
+-sdist: python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz \
+- python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz.asc
++sdist: python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz.asc
+
+ .PHONY: upload
+-upload: python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz \
+- python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz.asc
++upload: python$(PYTHON_VERSION)-gpg-dist/gpg-$(VERSION).tar.gz \
++ python$(PYTHON_VERSION)-gpg-dist/gpg-$(VERSION).tar.gz.asc
+ twine upload $^
+
+-CLEANFILES = copystamp
++CLEANFILES = copystamp \
++ config.h \
++ data.h \
++ files.txt \
++ install_files.txt
+
+ # Remove the rest.
+ #
+@@ -104,22 +79,22 @@ clean-local:
+ done
+
+ install-exec-local:
+- rm -f install_files.txt
+ set -e ; set $(PYTHONS); for VERSION in $(PYTHON_VERSIONS); do \
+ PYTHON="$$1" ; shift ; \
+- cd python$${VERSION}-gpg ; \
+ abs_top_builddir="$(abs_top_builddir)" \
+- $$PYTHON setup.py install \
+- --prefix $(DESTDIR)$(prefix) \
++ $$PYTHON setup.py \
++ build \
++ --build-base=python$${VERSION}-gpg \
++ install \
++ --prefix "$(DESTDIR)$(prefix)" \
+ --record files.txt \
+ --verbose ; \
+- cat files.txt >> ../install_files.txt ; \
++ cat files.txt >> install_files.txt ; \
+ rm files.txt ; \
+- cd .. ; \
+ done
+- $(MKDIR_P) $(DESTDIR)$(pythondir)/gpg
+- mv install_files.txt $(DESTDIR)$(pythondir)/gpg
++ $(MKDIR_P) "$(DESTDIR)$(pythondir)/gpg"
++ mv install_files.txt "$(DESTDIR)$(pythondir)/gpg"
+
+ uninstall-local:
+- xargs <$(DESTDIR)$(pythondir)/gpg/install_files.txt -- rm -rf --
+- rm -rf -- $(DESTDIR)$(pythondir)/gpg
++ xargs < "$(DESTDIR)$(pythondir)/gpg/install_files.txt" -- rm -rf --
++ rm -rf -- "$(DESTDIR)$(pythondir)/gpg"
+diff --git a/lang/python/setup.py.in b/lang/python/setup.py.in
+index 8ddbf27..6692de6 100755
+--- a/lang/python/setup.py.in
++++ b/lang/python/setup.py.in
+@@ -21,6 +21,7 @@
+ from distutils.core import setup, Extension
+ import os, os.path, sys
+ import glob
++import shutil
+ import subprocess
+
+ # Out-of-tree build of the gpg bindings.
+@@ -89,14 +90,6 @@ if not os.path.exists(gpg_error_h):
+ glob.glob(os.path.join(gpg_error_prefix, "include",
+ "*", "gpg-error.h"))[0]
+
+-print("Building python gpg module using {} and {}.".format(gpgme_h, gpg_error_h))
+-
+-# Cleanup gpgme.h from deprecated functions and typedefs.
+-subprocess.check_call([sys.executable, "gpgme-h-clean.py", gpgme_h],
+- stdout=open("gpgme.h", "w"))
+-subprocess.check_call([sys.executable, "gpgme-h-clean.py", gpg_error_h],
+- stdout=open("errors.i", "w"))
+-
+ define_macros = []
+ libs = getconfig('libs')
+
+@@ -149,14 +142,47 @@ if uname_s.startswith("MINGW32"):
+ # http://stackoverflow.com/questions/12491328/python-distutils-not-include-the-swig-generated-module
+ from distutils.command.build import build
+ class BuildExtFirstHack(build):
++
++ def _generate(self):
++ print("Building python gpg module using {} and {}.".format(gpgme_h, gpg_error_h))
++
++ # Cleanup gpgme.h from deprecated functions and typedefs.
++ # Keep timestamp to avoid rebuild
++ if not os.path.exists(self.build_base):
++ os.makedirs(self.build_base)
++
++ for src, dst in (
++ (gpgme_h, os.path.join(self.build_base, "gpgme.h")),
++ (gpg_error_h, os.path.join(self.build_base, "errors.i"))
++ ):
++ subprocess.check_call([sys.executable, "gpgme-h-clean.py", src],
++ stdout=open(dst, "w"))
++ shutil.copystat(src, dst)
++
++ # Copy due to http://bugs.python.org/issue2624
++ # Avoid creating in srcdir
++ shutil.copy2("gpgme.i", self.build_base)
++
+ def run(self):
++ self._generate()
++
++ # Append generated files via build_base
++ if not os.path.exists(os.path.join(self.build_lib, "gpg")):
++ os.makedirs(os.path.join(self.build_lib, "gpg"))
++
++ swig_sources.append(os.path.join(self.build_base, 'gpgme.i'))
++ swig_opts.extend(['-I' + self.build_base, '-outdir', os.path.join(self.build_lib, 'gpg')])
++ include_dirs.append(self.build_base)
++
+ self.run_command('build_ext')
+ build.run(self)
+
+ py3 = [] if sys.version_info.major < 3 else ['-py3']
+-swige = Extension("gpg._gpgme", ["gpgme.i", "helpers.c"],
+- swig_opts = ['-threads',
+- '-outdir', 'gpg'] + py3 + extra_swig_opts,
++swig_sources = ['helpers.c']
++swig_opts = ['-threads'] + py3 + extra_swig_opts
++swige = Extension("gpg._gpgme",
++ sources = swig_sources,
++ swig_opts = swig_opts,
+ include_dirs = include_dirs,
+ define_macros = define_macros,
+ library_dirs = library_dirs,
+diff --git a/lang/python/tests/run-tests.py b/lang/python/tests/run-tests.py
+index c4af526..9721997 100644
+--- a/lang/python/tests/run-tests.py
++++ b/lang/python/tests/run-tests.py
+@@ -71,7 +71,6 @@ for interpreter in args.interpreters:
+
+ pattern = os.path.join(args.builddir, "..",
+ "python{0}-gpg".format(version),
+- "build",
+ "lib*"+version)
+ builddirs = glob.glob(pattern)
+ if len(builddirs) == 0:
diff --git a/debian/patches/0006-python-Fix-vpath-builds-fix-distcheck.patch b/debian/patches/0006-python-Fix-vpath-builds-fix-distcheck.patch
new file mode 100644
index 0000000..1648de3
--- /dev/null
+++ b/debian/patches/0006-python-Fix-vpath-builds-fix-distcheck.patch
@@ -0,0 +1,334 @@
+From: Justus Winter <justus at g10code.com>
+Date: Mon, 3 Apr 2017 15:44:14 +0200
+Subject: python: Fix vpath builds, fix distcheck.
+
+* lang/python/gpgme-h-clean.py: Delete file.
+* lang/python/MANIFEST.in: Adapt accordingly.
+* lang/python/Makefile.am (EXTRA_DIST): Likewise.
+(COPY_FILES_GPG): Bring variable back.
+(copystamp): Copy files.
+(clean-local): Delete copied files.
+(install-exec-local): Do not create and install list of installed
+files.
+(uninstall-local): Instead, create some explicit rules to uninstall
+the extension.
+* lang/python/setup.py.in: Parse arguments. Locate files either in
+the source directory, or in the build base directory. Inline the code
+from 'gpgme-h-clean.py'. Copy 'helpers.c', add source directory as
+include directory.
+
+Fixes-commit: 801d7d8c5dd530d26ad6c4bcc94d986e6e022da4
+Signed-off-by: Justus Winter <justus at g10code.com>
+(cherry picked from commit e7d9c0c3d773f826dbd2ed417d04e25c410f3374)
+---
+ lang/python/MANIFEST.in | 2 +-
+ lang/python/Makefile.am | 31 ++++++++++++-----
+ lang/python/gpgme-h-clean.py | 53 -----------------------------
+ lang/python/setup.py.in | 81 +++++++++++++++++++++++++++++++++++++-------
+ 4 files changed, 92 insertions(+), 75 deletions(-)
+ delete mode 100755 lang/python/gpgme-h-clean.py
+
+diff --git a/lang/python/MANIFEST.in b/lang/python/MANIFEST.in
+index 8f63640..ff38172 100644
+--- a/lang/python/MANIFEST.in
++++ b/lang/python/MANIFEST.in
+@@ -1,4 +1,4 @@
+ recursive-include examples *.py
+-include gpgme-h-clean.py gpgme.i
++include gpgme.i
+ include helpers.c helpers.h private.h
+ recursive-include gpg *.py
+diff --git a/lang/python/Makefile.am b/lang/python/Makefile.am
+index b9145f5..42beeee 100644
+--- a/lang/python/Makefile.am
++++ b/lang/python/Makefile.am
+@@ -21,12 +21,20 @@ EXTRA_DIST = \
+ MANIFEST.in \
+ gpgme.i \
+ helpers.c helpers.h private.h \
+- gpgme-h-clean.py \
+ examples \
+ gpg
+
+ SUBDIRS = . tests
+
++COPY_FILES_GPG = \
++ $(srcdir)/gpg/callbacks.py \
++ $(srcdir)/gpg/constants \
++ $(srcdir)/gpg/core.py \
++ $(srcdir)/gpg/errors.py \
++ $(srcdir)/gpg/__init__.py \
++ $(srcdir)/gpg/results.py \
++ $(srcdir)/gpg/util.py
++
+ .PHONY: prepare
+ prepare: copystamp
+
+@@ -35,12 +43,14 @@ prepare: copystamp
+ copystamp:
+ ln -sf "$(abs_top_srcdir)/src/data.h" .
+ ln -sf "$(abs_top_builddir)/config.h" .
++ if test $(srcdir) != . ; then cp -R $(COPY_FILES_GPG) gpg ; fi
+ touch $@
+
+ all-local: copystamp
+ set -e ; set $(PYTHONS); for VERSION in $(PYTHON_VERSIONS); do \
+ PYTHON="$$1" ; shift ; \
+ CFLAGS="$(CFLAGS)" \
++ srcdir="$(srcdir)" \
+ abs_top_builddir="$(abs_top_builddir)" \
+ $$PYTHON setup.py build --verbose --build-base=python$${VERSION}-gpg ; \
+ done
+@@ -48,6 +58,7 @@ all-local: copystamp
+ python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz.asc: copystamp
+ $(MKDIR_P) python$(PYTHON_VERSION)-gpg-dist
+ CFLAGS="$(CFLAGS)" \
++ srcdir="$(srcdir)" \
+ abs_top_builddir="$(abs_top_builddir)" \
+ $(PYTHON) setup.py sdist --verbose --dist-dir=python$(PYTHON_VERSION)-gpg-dist \
+ --manifest=python$(PYTHON_VERSION)-gpg-dist/MANIFEST
+@@ -73,6 +84,12 @@ CLEANFILES = copystamp \
+ # permissions.
+ clean-local:
+ rm -rf -- build
++ if test $(srcdir) != . ; then \
++ find gpg -type d ! -perm -200 -exec chmod u+w {} ';' ; \
++ for FILE in $(COPY_FILES_GPG); do \
++ rm -rf -- gpg/$$(basename $$FILE) ; \
++ done \
++ fi
+ for VERSION in $(PYTHON_VERSIONS); do \
+ find python$${VERSION}-gpg -type d ! -perm -200 -exec chmod u+w {} ';' ; \
+ rm -rf -- python$${VERSION}-gpg ; \
+@@ -81,20 +98,18 @@ clean-local:
+ install-exec-local:
+ set -e ; set $(PYTHONS); for VERSION in $(PYTHON_VERSIONS); do \
+ PYTHON="$$1" ; shift ; \
++ srcdir="$(srcdir)" \
+ abs_top_builddir="$(abs_top_builddir)" \
+ $$PYTHON setup.py \
+ build \
+ --build-base=python$${VERSION}-gpg \
+ install \
+ --prefix "$(DESTDIR)$(prefix)" \
+- --record files.txt \
+ --verbose ; \
+- cat files.txt >> install_files.txt ; \
+- rm files.txt ; \
+ done
+- $(MKDIR_P) "$(DESTDIR)$(pythondir)/gpg"
+- mv install_files.txt "$(DESTDIR)$(pythondir)/gpg"
+
+ uninstall-local:
+- xargs < "$(DESTDIR)$(pythondir)/gpg/install_files.txt" -- rm -rf --
+- rm -rf -- "$(DESTDIR)$(pythondir)/gpg"
++ GV=$$(echo $(VERSION) | tr - _); for PV in $(PYTHON_VERSIONS); do \
++ rm -rf -- "$(DESTDIR)$(prefix)/lib/python$$PV/site-packages/gpg" \
++"$(DESTDIR)$(prefix)/lib/python$$PV/site-packages/gpg-$$GV-py$$PV.egg-info" ; \
++ done
+diff --git a/lang/python/gpgme-h-clean.py b/lang/python/gpgme-h-clean.py
+deleted file mode 100755
+index 52f8676..0000000
+--- a/lang/python/gpgme-h-clean.py
++++ /dev/null
+@@ -1,53 +0,0 @@
+-#!/usr/bin/env python
+-
+-# Copyright (C) 2016 g10 Code GmbH
+-# Copyright (C) 2004,2008 Igor Belyi <belyi at users.sourceforge.net>
+-#
+-# This library 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.
+-#
+-# This library 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 library; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+-
+-from __future__ import absolute_import, print_function, unicode_literals
+-del absolute_import, print_function, unicode_literals
+-
+-import sys, re
+-
+-if len(sys.argv) != 2:
+- sys.stderr.write("Usage: %s path/to/[gpgme|gpg-error].h\n" % sys.argv[0])
+- sys.exit(1)
+-
+-deprec_func = re.compile(r'^(.*typedef.*|.*\(.*\)|[^#]+\s+.+)'
+- + r'\s*_GPGME_DEPRECATED(_OUTSIDE_GPGME)?\(.*\);\s*',
+- re.S)
+-line_break = re.compile(';|\\$|\\x0c|^\s*#|{');
+-
+-if 'gpgme.h' in sys.argv[1]:
+- gpgme = open(sys.argv[1])
+- tmp = gpgme.readline()
+- text = ''
+- while tmp:
+- text += re.sub(' class ', ' _py_obsolete_class ', tmp)
+- if line_break.search(tmp):
+- if not deprec_func.search(text):
+- sys.stdout.write(text)
+- text = ''
+- tmp = gpgme.readline()
+- sys.stdout.write(text)
+- gpgme.close()
+-else:
+- filter_re = re.compile(r'GPG_ERR_[^ ]* =')
+- rewrite_re = re.compile(r' *(.*) = .*')
+- for line in open(sys.argv[1]):
+- if not filter_re.search(line):
+- continue
+- print(rewrite_re.sub(r'%constant long \1 = \1;', line.strip()))
+diff --git a/lang/python/setup.py.in b/lang/python/setup.py.in
+index 6692de6..2114aaf 100755
+--- a/lang/python/setup.py.in
++++ b/lang/python/setup.py.in
+@@ -1,7 +1,7 @@
+ #!/usr/bin/env python
+
+-# Copyright (C) 2016 g10 Code GmbH
+-# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
++# Copyright (C) 2016-2017 g10 Code GmbH
++# Copyright (C) 2004,2008 Igor Belyi <belyi at users.sourceforge.net>
+ # Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
+ #
+ # This library is free software; you can redistribute it and/or
+@@ -19,11 +19,18 @@
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ from distutils.core import setup, Extension
++import argparse
+ import os, os.path, sys
+ import glob
++import re
+ import shutil
+ import subprocess
+
++# We parse a subset of the arguments.
++parser = argparse.ArgumentParser(add_help=False)
++parser.add_argument('--build-base', default='')
++options, _ = parser.parse_known_args()
++
+ # Out-of-tree build of the gpg bindings.
+ gpg_error_config = ["gpg-error-config"]
+ gpgme_config_flags = ["--thread=pthread"]
+@@ -31,6 +38,7 @@ gpgme_config = ["gpgme-config"] + gpgme_config_flags
+ gpgme_h = ""
+ include_dirs = [os.getcwd()]
+ library_dirs = []
++vpath_build = os.environ.get('srcdir', '.') != '.'
+ in_tree = False
+ extra_swig_opts = []
+ extra_macros = dict()
+@@ -133,6 +141,14 @@ if uname_s.startswith("MINGW32"):
+ library_dirs.append(os.path.join(tgt, item))
+ break
+
++def in_srcdir(name):
++ return os.path.join(os.environ.get("srcdir", ""), name)
++def in_build_base(name):
++ return os.path.join(options.build_base, name)
++def up_to_date(source, target):
++ return (os.path.exists(target)
++ and os.path.getmtime(source) <= os.path.getmtime(target))
++
+ # We build an Extension using SWIG, which generates a Python module.
+ # By default, the 'build_py' step is run before 'build_ext', and
+ # therefore the generated Python module is not copied into the build
+@@ -143,25 +159,60 @@ if uname_s.startswith("MINGW32"):
+ from distutils.command.build import build
+ class BuildExtFirstHack(build):
+
++ def _generate_gpgme_h(self, source_name, sink_name):
++ if up_to_date(source_name, sink_name):
++ return
++
++ deprec_func = re.compile(r'^(.*typedef.*|.*\(.*\)|[^#]+\s+.+)'
++ + r'\s*_GPGME_DEPRECATED(_OUTSIDE_GPGME)?\(.*\);\s*',
++ re.S)
++ line_break = re.compile(';|\\$|\\x0c|^\s*#|{')
++
++ with open(sink_name, "w") as sink, open(source_name) as source:
++ text = ''
++ for line in source:
++ text += re.sub(' class ', ' _py_obsolete_class ', line)
++ if line_break.search(line):
++ if not deprec_func.search(text):
++ sink.write(text)
++ text = ''
++ sink.write(text)
++
++ def _generate_errors_i(self, source_name, sink_name):
++ if up_to_date(source_name, sink_name):
++ return
++
++ filter_re = re.compile(r'GPG_ERR_[^ ]* =')
++ rewrite_re = re.compile(r' *(.*) = .*')
++
++ with open(sink_name, "w") as sink, open(source_name) as source:
++ for line in source:
++ if not filter_re.search(line):
++ continue
++ sink.write(rewrite_re.sub(r'%constant long \1 = \1;'+'\n', line.strip()))
++
+ def _generate(self):
+ print("Building python gpg module using {} and {}.".format(gpgme_h, gpg_error_h))
+
+ # Cleanup gpgme.h from deprecated functions and typedefs.
+- # Keep timestamp to avoid rebuild
+ if not os.path.exists(self.build_base):
+ os.makedirs(self.build_base)
+
+- for src, dst in (
+- (gpgme_h, os.path.join(self.build_base, "gpgme.h")),
+- (gpg_error_h, os.path.join(self.build_base, "errors.i"))
+- ):
+- subprocess.check_call([sys.executable, "gpgme-h-clean.py", src],
+- stdout=open(dst, "w"))
+- shutil.copystat(src, dst)
++ self._generate_gpgme_h(gpgme_h, in_build_base("gpgme.h"))
++ self._generate_errors_i(gpg_error_h, in_build_base("errors.i"))
++
++ # Keep timestamp to avoid rebuild
++ for source, target in ((gpgme_h, in_build_base("gpgme.h")),
++ (gpg_error_h, in_build_base("errors.i"))):
++ if not up_to_date(source, target):
++ shutil.copystat(source, target)
+
+ # Copy due to http://bugs.python.org/issue2624
+ # Avoid creating in srcdir
+- shutil.copy2("gpgme.i", self.build_base)
++ for source, target in ((in_srcdir(n), in_build_base(n))
++ for n in ('gpgme.i', 'helpers.c')):
++ if not up_to_date(source, target):
++ shutil.copy2(source, target)
+
+ def run(self):
+ self._generate()
+@@ -171,14 +222,18 @@ class BuildExtFirstHack(build):
+ os.makedirs(os.path.join(self.build_lib, "gpg"))
+
+ swig_sources.append(os.path.join(self.build_base, 'gpgme.i'))
+- swig_opts.extend(['-I' + self.build_base, '-outdir', os.path.join(self.build_lib, 'gpg')])
++ swig_opts.extend(['-I' + self.build_base,
++ '-I' + in_srcdir('.'),
++ '-outdir', os.path.join(self.build_lib, 'gpg')])
++ if vpath_build:
++ include_dirs.append(in_srcdir('.'))
+ include_dirs.append(self.build_base)
+
+ self.run_command('build_ext')
+ build.run(self)
+
+ py3 = [] if sys.version_info.major < 3 else ['-py3']
+-swig_sources = ['helpers.c']
++swig_sources = [in_build_base('helpers.c')]
+ swig_opts = ['-threads'] + py3 + extra_swig_opts
+ swige = Extension("gpg._gpgme",
+ sources = swig_sources,
diff --git a/debian/patches/0007-python-simplify-build-some-fixups.patch b/debian/patches/0007-python-simplify-build-some-fixups.patch
new file mode 100644
index 0000000..9e0ebe6
--- /dev/null
+++ b/debian/patches/0007-python-simplify-build-some-fixups.patch
@@ -0,0 +1,5970 @@
+From: Alon Bar-Lev <alon.barlev at gmail.com>
+Date: Wed, 5 Apr 2017 19:47:08 +0300
+Subject: python: simplify build, some fixups
+
+* lang/python/gpg/version.py.in: Rename to lang/python/version.py.in.
+configure.ac: Generate version.py.in in lang/python.
+* lang/python/MANIFEST.in: Include version.py explicitly.
+* lang/python/gpg: Rename to 'src'.
+* lang/python/Makefile.am: Do not copy source files, do not use absolute
+directories, support lib64 in uninstall, clean also dist directory, use
+symlink for gpg src.
+* lang/python/setup.py.in: Use builddir, copy sources into builddir,
+copy version.py into module.
+--
+
+Simplify build to symlink the gpg sources into builddir instead of
+copying. This requires handling of version.py as generated file.
+
+In addition apply some cleanups: Drop the absolution pathes, clean the
+dist directory as well, support lib64 for sitelib at uninstall.
+
+Signed-off-by: Alon Bar-Lev <alon.barlev at gmail.com>
+(cherry picked from commit 49195c487e6c923f7137f092b982e7d833d98de6)
+---
+ configure.ac | 2 +-
+ lang/python/MANIFEST.in | 1 +
+ lang/python/Makefile.am | 38 +-
+ lang/python/gpg/__init__.py | 121 --
+ lang/python/gpg/callbacks.py | 49 -
+ lang/python/gpg/constants/__init__.py | 142 ---
+ lang/python/gpg/constants/create.py | 25 -
+ lang/python/gpg/constants/data/__init__.py | 6 -
+ lang/python/gpg/constants/data/encoding.py | 23 -
+ lang/python/gpg/constants/event.py | 23 -
+ lang/python/gpg/constants/import.py | 23 -
+ lang/python/gpg/constants/keylist/__init__.py | 6 -
+ lang/python/gpg/constants/keylist/mode.py | 23 -
+ lang/python/gpg/constants/keysign.py | 25 -
+ lang/python/gpg/constants/md.py | 23 -
+ lang/python/gpg/constants/pk.py | 23 -
+ lang/python/gpg/constants/protocol.py | 23 -
+ lang/python/gpg/constants/sig/__init__.py | 6 -
+ lang/python/gpg/constants/sig/mode.py | 23 -
+ lang/python/gpg/constants/sig/notation.py | 25 -
+ lang/python/gpg/constants/sigsum.py | 23 -
+ lang/python/gpg/constants/status.py | 124 --
+ lang/python/gpg/constants/tofu/__init__.py | 24 -
+ lang/python/gpg/constants/tofu/policy.py | 25 -
+ lang/python/gpg/constants/validity.py | 23 -
+ lang/python/gpg/core.py | 1490 -------------------------
+ lang/python/gpg/errors.py | 128 ---
+ lang/python/gpg/results.py | 118 --
+ lang/python/gpg/util.py | 53 -
+ lang/python/gpg/version.py.in | 68 --
+ lang/python/setup.py.in | 23 +-
+ lang/python/src/__init__.py | 121 ++
+ lang/python/src/callbacks.py | 49 +
+ lang/python/src/constants/__init__.py | 142 +++
+ lang/python/src/constants/create.py | 25 +
+ lang/python/src/constants/data/__init__.py | 6 +
+ lang/python/src/constants/data/encoding.py | 23 +
+ lang/python/src/constants/event.py | 23 +
+ lang/python/src/constants/import.py | 23 +
+ lang/python/src/constants/keylist/__init__.py | 6 +
+ lang/python/src/constants/keylist/mode.py | 23 +
+ lang/python/src/constants/keysign.py | 25 +
+ lang/python/src/constants/md.py | 23 +
+ lang/python/src/constants/pk.py | 23 +
+ lang/python/src/constants/protocol.py | 23 +
+ lang/python/src/constants/sig/__init__.py | 6 +
+ lang/python/src/constants/sig/mode.py | 23 +
+ lang/python/src/constants/sig/notation.py | 25 +
+ lang/python/src/constants/sigsum.py | 23 +
+ lang/python/src/constants/status.py | 124 ++
+ lang/python/src/constants/tofu/__init__.py | 24 +
+ lang/python/src/constants/tofu/policy.py | 25 +
+ lang/python/src/constants/validity.py | 23 +
+ lang/python/src/core.py | 1490 +++++++++++++++++++++++++
+ lang/python/src/errors.py | 128 +++
+ lang/python/src/results.py | 118 ++
+ lang/python/src/util.py | 53 +
+ lang/python/version.py.in | 68 ++
+ 58 files changed, 2689 insertions(+), 2705 deletions(-)
+ delete mode 100644 lang/python/gpg/__init__.py
+ delete mode 100644 lang/python/gpg/callbacks.py
+ delete mode 100644 lang/python/gpg/constants/__init__.py
+ delete mode 100644 lang/python/gpg/constants/create.py
+ delete mode 100644 lang/python/gpg/constants/data/__init__.py
+ delete mode 100644 lang/python/gpg/constants/data/encoding.py
+ delete mode 100644 lang/python/gpg/constants/event.py
+ delete mode 100644 lang/python/gpg/constants/import.py
+ delete mode 100644 lang/python/gpg/constants/keylist/__init__.py
+ delete mode 100644 lang/python/gpg/constants/keylist/mode.py
+ delete mode 100644 lang/python/gpg/constants/keysign.py
+ delete mode 100644 lang/python/gpg/constants/md.py
+ delete mode 100644 lang/python/gpg/constants/pk.py
+ delete mode 100644 lang/python/gpg/constants/protocol.py
+ delete mode 100644 lang/python/gpg/constants/sig/__init__.py
+ delete mode 100644 lang/python/gpg/constants/sig/mode.py
+ delete mode 100644 lang/python/gpg/constants/sig/notation.py
+ delete mode 100644 lang/python/gpg/constants/sigsum.py
+ delete mode 100644 lang/python/gpg/constants/status.py
+ delete mode 100644 lang/python/gpg/constants/tofu/__init__.py
+ delete mode 100644 lang/python/gpg/constants/tofu/policy.py
+ delete mode 100644 lang/python/gpg/constants/validity.py
+ delete mode 100644 lang/python/gpg/core.py
+ delete mode 100644 lang/python/gpg/errors.py
+ delete mode 100644 lang/python/gpg/results.py
+ delete mode 100644 lang/python/gpg/util.py
+ delete mode 100644 lang/python/gpg/version.py.in
+ create mode 100644 lang/python/src/__init__.py
+ create mode 100644 lang/python/src/callbacks.py
+ create mode 100644 lang/python/src/constants/__init__.py
+ create mode 100644 lang/python/src/constants/create.py
+ create mode 100644 lang/python/src/constants/data/__init__.py
+ create mode 100644 lang/python/src/constants/data/encoding.py
+ create mode 100644 lang/python/src/constants/event.py
+ create mode 100644 lang/python/src/constants/import.py
+ create mode 100644 lang/python/src/constants/keylist/__init__.py
+ create mode 100644 lang/python/src/constants/keylist/mode.py
+ create mode 100644 lang/python/src/constants/keysign.py
+ create mode 100644 lang/python/src/constants/md.py
+ create mode 100644 lang/python/src/constants/pk.py
+ create mode 100644 lang/python/src/constants/protocol.py
+ create mode 100644 lang/python/src/constants/sig/__init__.py
+ create mode 100644 lang/python/src/constants/sig/mode.py
+ create mode 100644 lang/python/src/constants/sig/notation.py
+ create mode 100644 lang/python/src/constants/sigsum.py
+ create mode 100644 lang/python/src/constants/status.py
+ create mode 100644 lang/python/src/constants/tofu/__init__.py
+ create mode 100644 lang/python/src/constants/tofu/policy.py
+ create mode 100644 lang/python/src/constants/validity.py
+ create mode 100644 lang/python/src/core.py
+ create mode 100644 lang/python/src/errors.py
+ create mode 100644 lang/python/src/results.py
+ create mode 100644 lang/python/src/util.py
+ create mode 100644 lang/python/version.py.in
+
+diff --git a/configure.ac b/configure.ac
+index 7ba07cc..e77cc23 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -878,7 +878,7 @@ AC_CONFIG_FILES([lang/Makefile lang/cl/Makefile lang/cl/gpgme.asd])
+ AM_COND_IF([HAVE_DOXYGEN], [AC_CONFIG_FILES([lang/qt/doc/Doxyfile])])
+ AC_CONFIG_FILES(lang/qt/doc/Makefile)
+ AC_CONFIG_FILES([lang/python/Makefile
+- lang/python/gpg/version.py
++ lang/python/version.py
+ lang/python/tests/Makefile])
+ AC_CONFIG_FILES([lang/python/setup.py], [chmod a+x lang/python/setup.py])
+ AC_OUTPUT
+diff --git a/lang/python/MANIFEST.in b/lang/python/MANIFEST.in
+index ff38172..c34e84a 100644
+--- a/lang/python/MANIFEST.in
++++ b/lang/python/MANIFEST.in
+@@ -1,4 +1,5 @@
+ recursive-include examples *.py
+ include gpgme.i
+ include helpers.c helpers.h private.h
++include version.py
+ recursive-include gpg *.py
+diff --git a/lang/python/Makefile.am b/lang/python/Makefile.am
+index 42beeee..4ebd214 100644
+--- a/lang/python/Makefile.am
++++ b/lang/python/Makefile.am
+@@ -22,28 +22,19 @@ EXTRA_DIST = \
+ gpgme.i \
+ helpers.c helpers.h private.h \
+ examples \
+- gpg
++ src
+
+ SUBDIRS = . tests
+
+-COPY_FILES_GPG = \
+- $(srcdir)/gpg/callbacks.py \
+- $(srcdir)/gpg/constants \
+- $(srcdir)/gpg/core.py \
+- $(srcdir)/gpg/errors.py \
+- $(srcdir)/gpg/__init__.py \
+- $(srcdir)/gpg/results.py \
+- $(srcdir)/gpg/util.py
+-
+ .PHONY: prepare
+ prepare: copystamp
+
+ # For VPATH builds we need to copy some files because Python's
+ # distutils are not VPATH-aware.
+ copystamp:
+- ln -sf "$(abs_top_srcdir)/src/data.h" .
+- ln -sf "$(abs_top_builddir)/config.h" .
+- if test $(srcdir) != . ; then cp -R $(COPY_FILES_GPG) gpg ; fi
++ ln -sf "$(top_srcdir)/src/data.h" .
++ ln -sf "$(top_builddir)/config.h" .
++ ln -sf "$(srcdir)/src" gpg
+ touch $@
+
+ all-local: copystamp
+@@ -51,7 +42,7 @@ all-local: copystamp
+ PYTHON="$$1" ; shift ; \
+ CFLAGS="$(CFLAGS)" \
+ srcdir="$(srcdir)" \
+- abs_top_builddir="$(abs_top_builddir)" \
++ top_builddir="$(top_builddir)" \
+ $$PYTHON setup.py build --verbose --build-base=python$${VERSION}-gpg ; \
+ done
+
+@@ -59,7 +50,7 @@ python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz.asc: copystamp
+ $(MKDIR_P) python$(PYTHON_VERSION)-gpg-dist
+ CFLAGS="$(CFLAGS)" \
+ srcdir="$(srcdir)" \
+- abs_top_builddir="$(abs_top_builddir)" \
++ top_builddir="$(top_builddir)" \
+ $(PYTHON) setup.py sdist --verbose --dist-dir=python$(PYTHON_VERSION)-gpg-dist \
+ --manifest=python$(PYTHON_VERSION)-gpg-dist/MANIFEST
+ gpg2 --detach-sign --armor python$(PYTHON_VERSION)-gpg-dist/gpg-$(VERSION).tar.gz
+@@ -75,6 +66,7 @@ upload: python$(PYTHON_VERSION)-gpg-dist/gpg-$(VERSION).tar.gz \
+ CLEANFILES = copystamp \
+ config.h \
+ data.h \
++ gpg \
+ files.txt \
+ install_files.txt
+
+@@ -84,22 +76,16 @@ CLEANFILES = copystamp \
+ # permissions.
+ clean-local:
+ rm -rf -- build
+- if test $(srcdir) != . ; then \
+- find gpg -type d ! -perm -200 -exec chmod u+w {} ';' ; \
+- for FILE in $(COPY_FILES_GPG); do \
+- rm -rf -- gpg/$$(basename $$FILE) ; \
+- done \
+- fi
+ for VERSION in $(PYTHON_VERSIONS); do \
+- find python$${VERSION}-gpg -type d ! -perm -200 -exec chmod u+w {} ';' ; \
+- rm -rf -- python$${VERSION}-gpg ; \
++ find python$${VERSION}-gpg* -type d ! -perm -200 -exec chmod u+w {} ';' ; \
++ rm -rf -- python$${VERSION}-gpg* ; \
+ done
+
+ install-exec-local:
+ set -e ; set $(PYTHONS); for VERSION in $(PYTHON_VERSIONS); do \
+ PYTHON="$$1" ; shift ; \
+ srcdir="$(srcdir)" \
+- abs_top_builddir="$(abs_top_builddir)" \
++ top_builddir="$(top_builddir)" \
+ $$PYTHON setup.py \
+ build \
+ --build-base=python$${VERSION}-gpg \
+@@ -110,6 +96,6 @@ install-exec-local:
+
+ uninstall-local:
+ GV=$$(echo $(VERSION) | tr - _); for PV in $(PYTHON_VERSIONS); do \
+- rm -rf -- "$(DESTDIR)$(prefix)/lib/python$$PV/site-packages/gpg" \
+-"$(DESTDIR)$(prefix)/lib/python$$PV/site-packages/gpg-$$GV-py$$PV.egg-info" ; \
++ rm -rf -- "$(DESTDIR)$(prefix)"/lib*/python$$PV/site-packages/gpg \
++"$(DESTDIR)$(prefix)"/lib*/python$$PV/site-packages/gpg-$$GV-py$$PV.egg-info ; \
+ done
+diff --git a/lang/python/gpg/__init__.py b/lang/python/gpg/__init__.py
+deleted file mode 100644
+index 385b17e..0000000
+--- a/lang/python/gpg/__init__.py
++++ /dev/null
+@@ -1,121 +0,0 @@
+-# Copyright (C) 2016 g10 Code GmbH
+-# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
+-# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
+-#
+-# This library 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.
+-#
+-# This library 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 library; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+-
+-"""gpg: GnuPG Interface for Python (GPGME bindings)
+-
+-Welcome to gpg, the GnuPG Interface for Python.
+-
+-The latest release of this package may be obtained from
+-https://www.gnupg.org
+-
+-FEATURES
+---------
+-
+- * Feature-rich, full implementation of the GPGME library. Supports
+- all GPGME features. Callback functions may be written in pure
+- Python. Exceptions raised in callbacks are properly propagated.
+-
+- * Ability to sign, encrypt, decrypt, and verify data.
+-
+- * Ability to list keys, export and import keys, and manage the keyring.
+-
+- * Fully object-oriented with convenient classes and modules.
+-
+-QUICK EXAMPLE
+--------------
+-
+- >>> import gpg
+- >>> with gpg.Context() as c:
+- >>> with gpg.Context() as c:
+- ... cipher, _, _ = c.encrypt("Hello world :)".encode(),
+- ... passphrase="abc")
+- ... c.decrypt(cipher, passphrase="abc")
+- ...
+- (b'Hello world :)',
+- <gpg.results.DecryptResult object at 0x7f5ab8121080>,
+- <gpg.results.VerifyResult object at 0x7f5ab81219b0>)
+-
+-GENERAL OVERVIEW
+-----------------
+-
+-For those of you familiar with GPGME, you will be right at home here.
+-
+-The python gpg module is, for the most part, a direct interface to the C GPGME
+-library. However, it is re-packaged in a more Pythonic way --
+-object-oriented with classes and modules. Take a look at the classes
+-defined here -- they correspond directly to certain object types in GPGME
+-for C. For instance, the following C code:
+-
+-gpgme_ctx_t context;
+-gpgme_new(&context);
+-...
+-gpgme_op_encrypt(context, recp, 1, plain, cipher);
+-
+-Translates into the following Python code:
+-
+-context = core.Context()
+-...
+-context.op_encrypt(recp, 1, plain, cipher)
+-
+-The Python module automatically does error-checking and raises Python
+-exception gpg.errors.GPGMEError when GPGME signals an error. getcode()
+-and getsource() of this exception return code and source of the error.
+-
+-IMPORTANT NOTE
+---------------
+-This documentation only covers a small subset of available GPGME functions and
+-methods. Please consult the documentation for the C library
+-for comprehensive coverage.
+-
+-This library uses Python's reflection to automatically detect the methods
+-that are available for each class, and as such, most of those methods
+-do not appear explicitly anywhere. You can use dir() python built-in command
+-on an object to see what methods and fields it has but their meaning can
+-be found only in GPGME documentation.
+-
+-FOR MORE INFORMATION
+---------------------
+-GnuPG homepage: https://www.gnupg.org/
+-GPGME documentation: https://www.gnupg.org/documentation/manuals/gpgme/
+-
+-"""
+-
+-from __future__ import absolute_import, print_function, unicode_literals
+-del absolute_import, print_function, unicode_literals
+-
+-from . import core
+-from . import errors
+-from . import constants
+-from . import util
+-from . import callbacks
+-from . import version
+-from .core import Context
+-from .core import Data
+-
+-# Interface hygiene.
+-
+-# Drop the low-level gpgme that creeps in for some reason.
+-gpgme = None
+-del gpgme
+-
+-# This is a white-list of symbols. Any other will alert pyflakes.
+-_ = [Context, Data, core, errors, constants, util, callbacks, version]
+-del _
+-
+-__all__ = ["Context", "Data",
+- "core", "errors", "constants", "util", "callbacks", "version"]
+diff --git a/lang/python/gpg/callbacks.py b/lang/python/gpg/callbacks.py
+deleted file mode 100644
+index b25a9a7..0000000
+--- a/lang/python/gpg/callbacks.py
++++ /dev/null
+@@ -1,49 +0,0 @@
+-# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
+-# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
+-#
+-# This library 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.
+-#
+-# This library 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 library; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+-
+-from __future__ import absolute_import, print_function, unicode_literals
+-del absolute_import, print_function, unicode_literals
+-
+-from getpass import getpass
+-
+-def passphrase_stdin(hint, desc, prev_bad, hook=None):
+- """This is a sample callback that will read a passphrase from
+- the terminal. The hook here, if present, will be used to describe
+- why the passphrase is needed."""
+- why = ''
+- if hook != None:
+- why = ' ' + hook
+- if prev_bad:
+- why += ' (again)'
+- print("Please supply %s' password%s:" % (hint, why))
+- return getpass()
+-
+-def progress_stdout(what, type, current, total, hook=None):
+- print("PROGRESS UPDATE: what = %s, type = %d, current = %d, total = %d" %\
+- (what, type, current, total))
+-
+-def readcb_fh(count, hook):
+- """A callback for data. hook should be a Python file-like object."""
+- if count:
+- # Should return '' on EOF
+- return hook.read(count)
+- else:
+- # Wants to rewind.
+- if not hasattr(hook, 'seek'):
+- return None
+- hook.seek(0, 0)
+- return None
+diff --git a/lang/python/gpg/constants/__init__.py b/lang/python/gpg/constants/__init__.py
+deleted file mode 100644
+index 484ffd2..0000000
+--- a/lang/python/gpg/constants/__init__.py
++++ /dev/null
+@@ -1,142 +0,0 @@
+-# Constants.
+-#
+-# Copyright (C) 2016 g10 Code 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 <http://www.gnu.org/licenses/>.
+-
+-from __future__ import absolute_import, print_function, unicode_literals
+-del absolute_import, print_function, unicode_literals
+-
+-from gpg import util
+-util.process_constants('GPGME_', globals())
+-del util
+-
+-# For convenience, we import the modules here.
+-from . import data, keylist, sig, tofu # The subdirs.
+-from . import create, event, keysign, md, pk, protocol, sigsum, status, validity
+-
+-# A complication arises because 'import' is a reserved keyword.
+-# Import it as 'Import' instead.
+-globals()['Import'] = getattr(__import__('', globals(), locals(),
+- [str('import')], 1), "import")
+-
+-__all__ = ['data', 'event', 'import', 'keysign', 'keylist', 'md', 'pk',
+- 'protocol', 'sig', 'sigsum', 'status', 'tofu', 'validity', 'create']
+-
+-# GPGME 1.7 replaced gpgme_op_edit with gpgme_op_interact. We
+-# implement gpg.Context.op_edit using gpgme_op_interact, so the
+-# callbacks will be called with string keywords instead of numeric
+-# status messages. Code that is using these constants will continue
+-# to work.
+-
+-STATUS_ABORT = "ABORT"
+-STATUS_ALREADY_SIGNED = "ALREADY_SIGNED"
+-STATUS_ATTRIBUTE = "ATTRIBUTE"
+-STATUS_BACKUP_KEY_CREATED = "BACKUP_KEY_CREATED"
+-STATUS_BAD_PASSPHRASE = "BAD_PASSPHRASE"
+-STATUS_BADARMOR = "BADARMOR"
+-STATUS_BADMDC = "BADMDC"
+-STATUS_BADSIG = "BADSIG"
+-STATUS_BEGIN_DECRYPTION = "BEGIN_DECRYPTION"
+-STATUS_BEGIN_ENCRYPTION = "BEGIN_ENCRYPTION"
+-STATUS_BEGIN_SIGNING = "BEGIN_SIGNING"
+-STATUS_BEGIN_STREAM = "BEGIN_STREAM"
+-STATUS_CARDCTRL = "CARDCTRL"
+-STATUS_DECRYPTION_FAILED = "DECRYPTION_FAILED"
+-STATUS_DECRYPTION_INFO = "DECRYPTION_INFO"
+-STATUS_DECRYPTION_OKAY = "DECRYPTION_OKAY"
+-STATUS_DELETE_PROBLEM = "DELETE_PROBLEM"
+-STATUS_ENC_TO = "ENC_TO"
+-STATUS_END_DECRYPTION = "END_DECRYPTION"
+-STATUS_END_ENCRYPTION = "END_ENCRYPTION"
+-STATUS_END_STREAM = "END_STREAM"
+-STATUS_ENTER = "ENTER"
+-STATUS_ERRMDC = "ERRMDC"
+-STATUS_ERROR = "ERROR"
+-STATUS_ERRSIG = "ERRSIG"
+-STATUS_EXPKEYSIG = "EXPKEYSIG"
+-STATUS_EXPSIG = "EXPSIG"
+-STATUS_FAILURE = "FAILURE"
+-STATUS_FILE_DONE = "FILE_DONE"
+-STATUS_FILE_ERROR = "FILE_ERROR"
+-STATUS_FILE_START = "FILE_START"
+-STATUS_GET_BOOL = "GET_BOOL"
+-STATUS_GET_HIDDEN = "GET_HIDDEN"
+-STATUS_GET_LINE = "GET_LINE"
+-STATUS_GOOD_PASSPHRASE = "GOOD_PASSPHRASE"
+-STATUS_GOODMDC = "GOODMDC"
+-STATUS_GOODSIG = "GOODSIG"
+-STATUS_GOT_IT = "GOT_IT"
+-STATUS_IMPORT_OK = "IMPORT_OK"
+-STATUS_IMPORT_PROBLEM = "IMPORT_PROBLEM"
+-STATUS_IMPORT_RES = "IMPORT_RES"
+-STATUS_IMPORTED = "IMPORTED"
+-STATUS_INQUIRE_MAXLEN = "INQUIRE_MAXLEN"
+-STATUS_INV_RECP = "INV_RECP"
+-STATUS_INV_SGNR = "INV_SGNR"
+-STATUS_KEY_CONSIDERED = "KEY_CONSIDERED"
+-STATUS_KEY_CREATED = "KEY_CREATED"
+-STATUS_KEY_NOT_CREATED = "KEY_NOT_CREATED"
+-STATUS_KEYEXPIRED = "KEYEXPIRED"
+-STATUS_KEYREVOKED = "KEYREVOKED"
+-STATUS_LEAVE = "LEAVE"
+-STATUS_MISSING_PASSPHRASE = "MISSING_PASSPHRASE"
+-STATUS_MOUNTPOINT = "MOUNTPOINT"
+-STATUS_NEED_PASSPHRASE = "NEED_PASSPHRASE"
+-STATUS_NEED_PASSPHRASE_PIN = "NEED_PASSPHRASE_PIN"
+-STATUS_NEED_PASSPHRASE_SYM = "NEED_PASSPHRASE_SYM"
+-STATUS_NEWSIG = "NEWSIG"
+-STATUS_NO_PUBKEY = "NO_PUBKEY"
+-STATUS_NO_RECP = "NO_RECP"
+-STATUS_NO_SECKEY = "NO_SECKEY"
+-STATUS_NO_SGNR = "NO_SGNR"
+-STATUS_NODATA = "NODATA"
+-STATUS_NOTATION_DATA = "NOTATION_DATA"
+-STATUS_NOTATION_FLAGS = "NOTATION_FLAGS"
+-STATUS_NOTATION_NAME = "NOTATION_NAME"
+-STATUS_PINENTRY_LAUNCHED = "PINENTRY_LAUNCHED"
+-STATUS_PKA_TRUST_BAD = "PKA_TRUST_BAD"
+-STATUS_PKA_TRUST_GOOD = "PKA_TRUST_GOOD"
+-STATUS_PLAINTEXT = "PLAINTEXT"
+-STATUS_PLAINTEXT_LENGTH = "PLAINTEXT_LENGTH"
+-STATUS_POLICY_URL = "POLICY_URL"
+-STATUS_PROGRESS = "PROGRESS"
+-STATUS_REVKEYSIG = "REVKEYSIG"
+-STATUS_RSA_OR_IDEA = "RSA_OR_IDEA"
+-STATUS_SC_OP_FAILURE = "SC_OP_FAILURE"
+-STATUS_SC_OP_SUCCESS = "SC_OP_SUCCESS"
+-STATUS_SESSION_KEY = "SESSION_KEY"
+-STATUS_SHM_GET = "SHM_GET"
+-STATUS_SHM_GET_BOOL = "SHM_GET_BOOL"
+-STATUS_SHM_GET_HIDDEN = "SHM_GET_HIDDEN"
+-STATUS_SHM_INFO = "SHM_INFO"
+-STATUS_SIG_CREATED = "SIG_CREATED"
+-STATUS_SIG_ID = "SIG_ID"
+-STATUS_SIG_SUBPACKET = "SIG_SUBPACKET"
+-STATUS_SIGEXPIRED = "SIGEXPIRED"
+-STATUS_SUCCESS = "SUCCESS"
+-STATUS_TOFU_STATS = "TOFU_STATS"
+-STATUS_TOFU_STATS_LONG = "TOFU_STATS_LONG"
+-STATUS_TOFU_USER = "TOFU_USER"
+-STATUS_TRUNCATED = "TRUNCATED"
+-STATUS_TRUST_FULLY = "TRUST_FULLY"
+-STATUS_TRUST_MARGINAL = "TRUST_MARGINAL"
+-STATUS_TRUST_NEVER = "TRUST_NEVER"
+-STATUS_TRUST_ULTIMATE = "TRUST_ULTIMATE"
+-STATUS_TRUST_UNDEFINED = "TRUST_UNDEFINED"
+-STATUS_UNEXPECTED = "UNEXPECTED"
+-STATUS_USERID_HINT = "USERID_HINT"
+-STATUS_VALIDSIG = "VALIDSIG"
+diff --git a/lang/python/gpg/constants/create.py b/lang/python/gpg/constants/create.py
+deleted file mode 100644
+index 132e96d..0000000
+--- a/lang/python/gpg/constants/create.py
++++ /dev/null
+@@ -1,25 +0,0 @@
+-# Flags for key creation
+-#
+-# Copyright (C) 2017 g10 Code 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 <http://www.gnu.org/licenses/>.
+-
+-from __future__ import absolute_import, print_function, unicode_literals
+-del absolute_import, print_function, unicode_literals
+-
+-from gpg import util
+-util.process_constants('GPGME_CREATE_', globals())
+-del util
+diff --git a/lang/python/gpg/constants/data/__init__.py b/lang/python/gpg/constants/data/__init__.py
+deleted file mode 100644
+index 8274ab9..0000000
+--- a/lang/python/gpg/constants/data/__init__.py
++++ /dev/null
+@@ -1,6 +0,0 @@
+-
+-from __future__ import absolute_import, print_function, unicode_literals
+-del absolute_import, print_function, unicode_literals
+-
+-from . import encoding
+-__all__ = ['encoding']
+diff --git a/lang/python/gpg/constants/data/encoding.py b/lang/python/gpg/constants/data/encoding.py
+deleted file mode 100644
+index e76a22e..0000000
+--- a/lang/python/gpg/constants/data/encoding.py
++++ /dev/null
+@@ -1,23 +0,0 @@
+-# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
+-# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
+-#
+-# This library 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.
+-#
+-# This library 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 library; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+-
+-from __future__ import absolute_import, print_function, unicode_literals
+-del absolute_import, print_function, unicode_literals
+-
+-from gpg import util
+-util.process_constants('GPGME_DATA_ENCODING_', globals())
+-del util
+diff --git a/lang/python/gpg/constants/event.py b/lang/python/gpg/constants/event.py
+deleted file mode 100644
+index 1b14d1d..0000000
+--- a/lang/python/gpg/constants/event.py
++++ /dev/null
+@@ -1,23 +0,0 @@
+-# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
+-# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
+-#
+-# This library 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.
+-#
+-# This library 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 library; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+-
+-from __future__ import absolute_import, print_function, unicode_literals
+-del absolute_import, print_function, unicode_literals
+-
+-from gpg import util
+-util.process_constants('GPGME_EVENT_', globals())
+-del util
+diff --git a/lang/python/gpg/constants/import.py b/lang/python/gpg/constants/import.py
+deleted file mode 100644
+index 47c296c..0000000
+--- a/lang/python/gpg/constants/import.py
++++ /dev/null
+@@ -1,23 +0,0 @@
+-# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
+-# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
+-#
+-# This library 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.
+-#
+-# This library 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 library; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+-
+-from __future__ import absolute_import, print_function, unicode_literals
+-del absolute_import, print_function, unicode_literals
+-
+-from gpg import util
+-util.process_constants('GPGME_IMPORT_', globals())
+-del util
+diff --git a/lang/python/gpg/constants/keylist/__init__.py b/lang/python/gpg/constants/keylist/__init__.py
+deleted file mode 100644
+index 2ce0edf..0000000
+--- a/lang/python/gpg/constants/keylist/__init__.py
++++ /dev/null
+@@ -1,6 +0,0 @@
+-
+-from __future__ import absolute_import, print_function, unicode_literals
+-del absolute_import, print_function, unicode_literals
+-
+-from . import mode
+-__all__ = ['mode']
+diff --git a/lang/python/gpg/constants/keylist/mode.py b/lang/python/gpg/constants/keylist/mode.py
+deleted file mode 100644
+index 39e1819..0000000
+--- a/lang/python/gpg/constants/keylist/mode.py
++++ /dev/null
+@@ -1,23 +0,0 @@
+-# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
+-# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
+-#
+-# This library 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.
+-#
+-# This library 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 library; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+-
+-from __future__ import absolute_import, print_function, unicode_literals
+-del absolute_import, print_function, unicode_literals
+-
+-from gpg import util
+-util.process_constants('GPGME_KEYLIST_MODE_', globals())
+-del util
+diff --git a/lang/python/gpg/constants/keysign.py b/lang/python/gpg/constants/keysign.py
+deleted file mode 100644
+index fccdbc4..0000000
+--- a/lang/python/gpg/constants/keysign.py
++++ /dev/null
+@@ -1,25 +0,0 @@
+-# Flags for key signing
+-#
+-# Copyright (C) 2017 g10 Code 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 <http://www.gnu.org/licenses/>.
+-
+-from __future__ import absolute_import, print_function, unicode_literals
+-del absolute_import, print_function, unicode_literals
+-
+-from gpg import util
+-util.process_constants('GPGME_KEYSIGN_', globals())
+-del util
+diff --git a/lang/python/gpg/constants/md.py b/lang/python/gpg/constants/md.py
+deleted file mode 100644
+index f3e8bbd..0000000
+--- a/lang/python/gpg/constants/md.py
++++ /dev/null
+@@ -1,23 +0,0 @@
+-# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
+-# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
+-#
+-# This library 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.
+-#
+-# This library 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 library; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+-
+-from __future__ import absolute_import, print_function, unicode_literals
+-del absolute_import, print_function, unicode_literals
+-
+-from gpg import util
+-util.process_constants('GPGME_MD_', globals())
+-del util
+diff --git a/lang/python/gpg/constants/pk.py b/lang/python/gpg/constants/pk.py
+deleted file mode 100644
+index 6bf2a21..0000000
+--- a/lang/python/gpg/constants/pk.py
++++ /dev/null
+@@ -1,23 +0,0 @@
+-# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
+-# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
+-#
+-# This library 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.
+-#
+-# This library 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 library; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+-
+-from __future__ import absolute_import, print_function, unicode_literals
+-del absolute_import, print_function, unicode_literals
+-
+-from gpg import util
+-util.process_constants('GPGME_PK_', globals())
+-del util
+diff --git a/lang/python/gpg/constants/protocol.py b/lang/python/gpg/constants/protocol.py
+deleted file mode 100644
+index d086bbd..0000000
+--- a/lang/python/gpg/constants/protocol.py
++++ /dev/null
+@@ -1,23 +0,0 @@
+-# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
+-# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
+-#
+-# This library 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.
+-#
+-# This library 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 library; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+-
+-from __future__ import absolute_import, print_function, unicode_literals
+-del absolute_import, print_function, unicode_literals
+-
+-from gpg import util
+-util.process_constants('GPGME_PROTOCOL_', globals())
+-del util
+diff --git a/lang/python/gpg/constants/sig/__init__.py b/lang/python/gpg/constants/sig/__init__.py
+deleted file mode 100644
+index 39d4e6e..0000000
+--- a/lang/python/gpg/constants/sig/__init__.py
++++ /dev/null
+@@ -1,6 +0,0 @@
+-
+-from __future__ import absolute_import, print_function, unicode_literals
+-del absolute_import, print_function, unicode_literals
+-
+-from . import mode, notation
+-__all__ = ['mode', 'notation']
+diff --git a/lang/python/gpg/constants/sig/mode.py b/lang/python/gpg/constants/sig/mode.py
+deleted file mode 100644
+index 0f4f0ef..0000000
+--- a/lang/python/gpg/constants/sig/mode.py
++++ /dev/null
+@@ -1,23 +0,0 @@
+-# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
+-# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
+-#
+-# This library 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.
+-#
+-# This library 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 library; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+-
+-from __future__ import absolute_import, print_function, unicode_literals
+-del absolute_import, print_function, unicode_literals
+-
+-from gpg import util
+-util.process_constants('GPGME_SIG_MODE_', globals())
+-del util
+diff --git a/lang/python/gpg/constants/sig/notation.py b/lang/python/gpg/constants/sig/notation.py
+deleted file mode 100644
+index 9a79e01..0000000
+--- a/lang/python/gpg/constants/sig/notation.py
++++ /dev/null
+@@ -1,25 +0,0 @@
+-# Constants for signature notation data.
+-#
+-# Copyright (C) 2016 g10 Code 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 <http://www.gnu.org/licenses/>.
+-
+-from __future__ import absolute_import, print_function, unicode_literals
+-del absolute_import, print_function, unicode_literals
+-
+-from gpg import util
+-util.process_constants('GPGME_SIG_NOTATION_', globals())
+-del util
+diff --git a/lang/python/gpg/constants/sigsum.py b/lang/python/gpg/constants/sigsum.py
+deleted file mode 100644
+index 09ef9d7..0000000
+--- a/lang/python/gpg/constants/sigsum.py
++++ /dev/null
+@@ -1,23 +0,0 @@
+-# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
+-# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
+-#
+-# This library 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.
+-#
+-# This library 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 library; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+-
+-from __future__ import absolute_import, print_function, unicode_literals
+-del absolute_import, print_function, unicode_literals
+-
+-from gpg import util
+-util.process_constants('GPGME_SIGSUM_', globals())
+-del util
+diff --git a/lang/python/gpg/constants/status.py b/lang/python/gpg/constants/status.py
+deleted file mode 100644
+index a0ad073..0000000
+--- a/lang/python/gpg/constants/status.py
++++ /dev/null
+@@ -1,124 +0,0 @@
+-# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
+-# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
+-#
+-# This library 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.
+-#
+-# This library 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 library; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+-
+-from __future__ import absolute_import, print_function, unicode_literals
+-del absolute_import, print_function, unicode_literals
+-
+-# GPGME 1.7 replaced gpgme_op_edit with gpgme_op_interact. We
+-# implement gpg.Context.op_edit using gpgme_op_interact, so the
+-# callbacks will be called with string keywords instead of numeric
+-# status messages. Code that is using these constants will continue
+-# to work.
+-
+-ABORT = "ABORT"
+-ALREADY_SIGNED = "ALREADY_SIGNED"
+-ATTRIBUTE = "ATTRIBUTE"
+-BACKUP_KEY_CREATED = "BACKUP_KEY_CREATED"
+-BAD_PASSPHRASE = "BAD_PASSPHRASE"
+-BADARMOR = "BADARMOR"
+-BADMDC = "BADMDC"
+-BADSIG = "BADSIG"
+-BEGIN_DECRYPTION = "BEGIN_DECRYPTION"
+-BEGIN_ENCRYPTION = "BEGIN_ENCRYPTION"
+-BEGIN_SIGNING = "BEGIN_SIGNING"
+-BEGIN_STREAM = "BEGIN_STREAM"
+-CARDCTRL = "CARDCTRL"
+-DECRYPTION_FAILED = "DECRYPTION_FAILED"
+-DECRYPTION_INFO = "DECRYPTION_INFO"
+-DECRYPTION_OKAY = "DECRYPTION_OKAY"
+-DELETE_PROBLEM = "DELETE_PROBLEM"
+-ENC_TO = "ENC_TO"
+-END_DECRYPTION = "END_DECRYPTION"
+-END_ENCRYPTION = "END_ENCRYPTION"
+-END_STREAM = "END_STREAM"
+-ENTER = "ENTER"
+-ERRMDC = "ERRMDC"
+-ERROR = "ERROR"
+-ERRSIG = "ERRSIG"
+-EXPKEYSIG = "EXPKEYSIG"
+-EXPSIG = "EXPSIG"
+-FAILURE = "FAILURE"
+-FILE_DONE = "FILE_DONE"
+-FILE_ERROR = "FILE_ERROR"
+-FILE_START = "FILE_START"
+-GET_BOOL = "GET_BOOL"
+-GET_HIDDEN = "GET_HIDDEN"
+-GET_LINE = "GET_LINE"
+-GOOD_PASSPHRASE = "GOOD_PASSPHRASE"
+-GOODMDC = "GOODMDC"
+-GOODSIG = "GOODSIG"
+-GOT_IT = "GOT_IT"
+-IMPORT_OK = "IMPORT_OK"
+-IMPORT_PROBLEM = "IMPORT_PROBLEM"
+-IMPORT_RES = "IMPORT_RES"
+-IMPORTED = "IMPORTED"
+-INQUIRE_MAXLEN = "INQUIRE_MAXLEN"
+-INV_RECP = "INV_RECP"
+-INV_SGNR = "INV_SGNR"
+-KEY_CONSIDERED = "KEY_CONSIDERED"
+-KEY_CREATED = "KEY_CREATED"
+-KEY_NOT_CREATED = "KEY_NOT_CREATED"
+-KEYEXPIRED = "KEYEXPIRED"
+-KEYREVOKED = "KEYREVOKED"
+-LEAVE = "LEAVE"
+-MISSING_PASSPHRASE = "MISSING_PASSPHRASE"
+-MOUNTPOINT = "MOUNTPOINT"
+-NEED_PASSPHRASE = "NEED_PASSPHRASE"
+-NEED_PASSPHRASE_PIN = "NEED_PASSPHRASE_PIN"
+-NEED_PASSPHRASE_SYM = "NEED_PASSPHRASE_SYM"
+-NEWSIG = "NEWSIG"
+-NO_PUBKEY = "NO_PUBKEY"
+-NO_RECP = "NO_RECP"
+-NO_SECKEY = "NO_SECKEY"
+-NO_SGNR = "NO_SGNR"
+-NODATA = "NODATA"
+-NOTATION_DATA = "NOTATION_DATA"
+-NOTATION_FLAGS = "NOTATION_FLAGS"
+-NOTATION_NAME = "NOTATION_NAME"
+-PINENTRY_LAUNCHED = "PINENTRY_LAUNCHED"
+-PKA_TRUST_BAD = "PKA_TRUST_BAD"
+-PKA_TRUST_GOOD = "PKA_TRUST_GOOD"
+-PLAINTEXT = "PLAINTEXT"
+-PLAINTEXT_LENGTH = "PLAINTEXT_LENGTH"
+-POLICY_URL = "POLICY_URL"
+-PROGRESS = "PROGRESS"
+-REVKEYSIG = "REVKEYSIG"
+-RSA_OR_IDEA = "RSA_OR_IDEA"
+-SC_OP_FAILURE = "SC_OP_FAILURE"
+-SC_OP_SUCCESS = "SC_OP_SUCCESS"
+-SESSION_KEY = "SESSION_KEY"
+-SHM_GET = "SHM_GET"
+-SHM_GET_BOOL = "SHM_GET_BOOL"
+-SHM_GET_HIDDEN = "SHM_GET_HIDDEN"
+-SHM_INFO = "SHM_INFO"
+-SIG_CREATED = "SIG_CREATED"
+-SIG_ID = "SIG_ID"
+-SIG_SUBPACKET = "SIG_SUBPACKET"
+-SIGEXPIRED = "SIGEXPIRED"
+-SUCCESS = "SUCCESS"
+-TOFU_STATS = "TOFU_STATS"
+-TOFU_STATS_LONG = "TOFU_STATS_LONG"
+-TOFU_USER = "TOFU_USER"
+-TRUNCATED = "TRUNCATED"
+-TRUST_FULLY = "TRUST_FULLY"
+-TRUST_MARGINAL = "TRUST_MARGINAL"
+-TRUST_NEVER = "TRUST_NEVER"
+-TRUST_ULTIMATE = "TRUST_ULTIMATE"
+-TRUST_UNDEFINED = "TRUST_UNDEFINED"
+-UNEXPECTED = "UNEXPECTED"
+-USERID_HINT = "USERID_HINT"
+-VALIDSIG = "VALIDSIG"
+diff --git a/lang/python/gpg/constants/tofu/__init__.py b/lang/python/gpg/constants/tofu/__init__.py
+deleted file mode 100644
+index 819a58b..0000000
+--- a/lang/python/gpg/constants/tofu/__init__.py
++++ /dev/null
+@@ -1,24 +0,0 @@
+-# TOFU
+-#
+-# Copyright (C) 2017 g10 Code 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 <http://www.gnu.org/licenses/>.
+-
+-from __future__ import absolute_import, print_function, unicode_literals
+-del absolute_import, print_function, unicode_literals
+-
+-from . import policy
+-__all__ = ['policy']
+diff --git a/lang/python/gpg/constants/tofu/policy.py b/lang/python/gpg/constants/tofu/policy.py
+deleted file mode 100644
+index 5a61f06..0000000
+--- a/lang/python/gpg/constants/tofu/policy.py
++++ /dev/null
+@@ -1,25 +0,0 @@
+-# TOFU policies
+-#
+-# Copyright (C) 2017 g10 Code 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 <http://www.gnu.org/licenses/>.
+-
+-from __future__ import absolute_import, print_function, unicode_literals
+-del absolute_import, print_function, unicode_literals
+-
+-from gpg import util
+-util.process_constants('GPGME_TOFU_POLICY_', globals())
+-del util
+diff --git a/lang/python/gpg/constants/validity.py b/lang/python/gpg/constants/validity.py
+deleted file mode 100644
+index d3c5345..0000000
+--- a/lang/python/gpg/constants/validity.py
++++ /dev/null
+@@ -1,23 +0,0 @@
+-# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
+-# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
+-#
+-# This library 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.
+-#
+-# This library 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 library; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+-
+-from __future__ import absolute_import, print_function, unicode_literals
+-del absolute_import, print_function, unicode_literals
+-
+-from gpg import util
+-util.process_constants('GPGME_VALIDITY_', globals())
+-del util
+diff --git a/lang/python/gpg/core.py b/lang/python/gpg/core.py
+deleted file mode 100644
+index 632f4ca..0000000
+--- a/lang/python/gpg/core.py
++++ /dev/null
+@@ -1,1490 +0,0 @@
+-# Copyright (C) 2016-2017 g10 Code GmbH
+-# Copyright (C) 2004,2008 Igor Belyi <belyi at users.sourceforge.net>
+-# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
+-#
+-# This library 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.
+-#
+-# This library 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 library; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+-
+-"""Core functionality
+-
+-Core functionality of GPGME wrapped in a object-oriented fashion.
+-Provides the 'Context' class for performing cryptographic operations,
+-and the 'Data' class describing buffers of data.
+-
+-"""
+-
+-from __future__ import absolute_import, print_function, unicode_literals
+-del absolute_import, print_function, unicode_literals
+-
+-import re
+-import os
+-import warnings
+-import weakref
+-from . import gpgme
+-from .errors import errorcheck, GPGMEError
+-from . import constants
+-from . import errors
+-from . import util
+-
+-class GpgmeWrapper(object):
+- """Base wrapper class
+-
+- Not to be instantiated directly.
+-
+- """
+-
+- def __init__(self, wrapped):
+- self._callback_excinfo = None
+- self.wrapped = wrapped
+-
+- def __repr__(self):
+- return '<{}/{!r}>'.format(super(GpgmeWrapper, self).__repr__(),
+- self.wrapped)
+-
+- def __str__(self):
+- acc = ['{}.{}'.format(__name__, self.__class__.__name__)]
+- flags = [f for f in self._boolean_properties if getattr(self, f)]
+- if flags:
+- acc.append('({})'.format(' '.join(flags)))
+-
+- return '<{}>'.format(' '.join(acc))
+-
+- def __hash__(self):
+- return hash(repr(self.wrapped))
+-
+- def __eq__(self, other):
+- if other == None:
+- return False
+- else:
+- return repr(self.wrapped) == repr(other.wrapped)
+-
+- @property
+- def _ctype(self):
+- """The name of the c type wrapped by this class
+-
+- Must be set by child classes.
+-
+- """
+- raise NotImplementedError()
+-
+- @property
+- def _cprefix(self):
+- """The common prefix of c functions wrapped by this class
+-
+- Must be set by child classes.
+-
+- """
+- raise NotImplementedError()
+-
+- def _errorcheck(self, name):
+- """Must be implemented by child classes.
+-
+- This function must return a trueish value for all c functions
+- returning gpgme_error_t."""
+- raise NotImplementedError()
+-
+- """The set of all boolean properties"""
+- _boolean_properties = set()
+-
+- def __wrap_boolean_property(self, key, do_set=False, value=None):
+- get_func = getattr(gpgme,
+- "{}get_{}".format(self._cprefix, key))
+- set_func = getattr(gpgme,
+- "{}set_{}".format(self._cprefix, key))
+- def get(slf):
+- return bool(get_func(slf.wrapped))
+- def set_(slf, value):
+- set_func(slf.wrapped, bool(value))
+-
+- p = property(get, set_, doc="{} flag".format(key))
+- setattr(self.__class__, key, p)
+-
+- if do_set:
+- set_(self, bool(value))
+- else:
+- return get(self)
+-
+- _munge_docstring = re.compile(r'gpgme_([^(]*)\(([^,]*), (.*\) -> .*)')
+- def __getattr__(self, key):
+- """On-the-fly generation of wrapper methods and properties"""
+- if key[0] == '_' or self._cprefix == None:
+- return None
+-
+- if key in self._boolean_properties:
+- return self.__wrap_boolean_property(key)
+-
+- name = self._cprefix + key
+- func = getattr(gpgme, name)
+-
+- if self._errorcheck(name):
+- def _funcwrap(slf, *args):
+- result = func(slf.wrapped, *args)
+- if slf._callback_excinfo:
+- gpgme.gpg_raise_callback_exception(slf)
+- return errorcheck(result, "Invocation of " + name)
+- else:
+- def _funcwrap(slf, *args):
+- result = func(slf.wrapped, *args)
+- if slf._callback_excinfo:
+- gpgme.gpg_raise_callback_exception(slf)
+- return result
+-
+- doc = self._munge_docstring.sub(r'\2.\1(\3', getattr(func, "__doc__"))
+- _funcwrap.__doc__ = doc
+-
+- # Monkey-patch the class.
+- setattr(self.__class__, key, _funcwrap)
+-
+- # Bind the method to 'self'.
+- def wrapper(*args):
+- return _funcwrap(self, *args)
+- wrapper.__doc__ = doc
+-
+- return wrapper
+-
+- def __setattr__(self, key, value):
+- """On-the-fly generation of properties"""
+- if key in self._boolean_properties:
+- self.__wrap_boolean_property(key, True, value)
+- else:
+- super(GpgmeWrapper, self).__setattr__(key, value)
+-
+-class Context(GpgmeWrapper):
+- """Context for cryptographic operations
+-
+- All cryptographic operations in GPGME are performed within a
+- context, which contains the internal state of the operation as
+- well as configuration parameters. By using several contexts you
+- can run several cryptographic operations in parallel, with
+- different configuration.
+-
+- Access to a context must be synchronized.
+-
+- """
+-
+- def __init__(self, armor=False, textmode=False, offline=False,
+- signers=[], pinentry_mode=constants.PINENTRY_MODE_DEFAULT,
+- protocol=constants.PROTOCOL_OpenPGP,
+- wrapped=None, home_dir=None):
+- """Construct a context object
+-
+- Keyword arguments:
+- armor -- enable ASCII armoring (default False)
+- textmode -- enable canonical text mode (default False)
+- offline -- do not contact external key sources (default False)
+- signers -- list of keys used for signing (default [])
+- pinentry_mode -- pinentry mode (default PINENTRY_MODE_DEFAULT)
+- protocol -- protocol to use (default PROTOCOL_OpenPGP)
+- home_dir -- state directory (default is the engine default)
+-
+- """
+- if wrapped:
+- self.own = False
+- else:
+- tmp = gpgme.new_gpgme_ctx_t_p()
+- errorcheck(gpgme.gpgme_new(tmp))
+- wrapped = gpgme.gpgme_ctx_t_p_value(tmp)
+- gpgme.delete_gpgme_ctx_t_p(tmp)
+- self.own = True
+- super(Context, self).__init__(wrapped)
+- self.armor = armor
+- self.textmode = textmode
+- self.offline = offline
+- self.signers = signers
+- self.pinentry_mode = pinentry_mode
+- self.protocol = protocol
+- self.home_dir = home_dir
+-
+- def __repr__(self):
+- return (
+- "Context(armor={0.armor}, "
+- "textmode={0.textmode}, offline={0.offline}, "
+- "signers={0.signers}, pinentry_mode={0.pinentry_mode}, "
+- "protocol={0.protocol}, home_dir={0.home_dir}"
+- ")").format(self)
+-
+- def encrypt(self, plaintext, recipients=[], sign=True, sink=None,
+- passphrase=None, always_trust=False, add_encrypt_to=False,
+- prepare=False, expect_sign=False, compress=True):
+- """Encrypt data
+-
+- Encrypt the given plaintext for the given recipients. If the
+- list of recipients is empty, the data is encrypted
+- symmetrically with a passphrase.
+-
+- The passphrase can be given as parameter, using a callback
+- registered at the context, or out-of-band via pinentry.
+-
+- Keyword arguments:
+- recipients -- list of keys to encrypt to
+- sign -- sign plaintext (default True)
+- sink -- write result to sink instead of returning it
+- passphrase -- for symmetric encryption
+- always_trust -- always trust the keys (default False)
+- add_encrypt_to -- encrypt to configured additional keys (default False)
+- prepare -- (ui) prepare for encryption (default False)
+- expect_sign -- (ui) prepare for signing (default False)
+- compress -- compress plaintext (default True)
+-
+- Returns:
+- ciphertext -- the encrypted data (or None if sink is given)
+- result -- additional information about the encryption
+- sign_result -- additional information about the signature(s)
+-
+- Raises:
+- InvalidRecipients -- if encryption using a particular key failed
+- InvalidSigners -- if signing using a particular key failed
+- GPGMEError -- as signaled by the underlying library
+-
+- """
+- ciphertext = sink if sink else Data()
+- flags = 0
+- flags |= always_trust * constants.ENCRYPT_ALWAYS_TRUST
+- flags |= (not add_encrypt_to) * constants.ENCRYPT_NO_ENCRYPT_TO
+- flags |= prepare * constants.ENCRYPT_PREPARE
+- flags |= expect_sign * constants.ENCRYPT_EXPECT_SIGN
+- flags |= (not compress) * constants.ENCRYPT_NO_COMPRESS
+-
+- if passphrase != None:
+- old_pinentry_mode = self.pinentry_mode
+- old_passphrase_cb = getattr(self, '_passphrase_cb', None)
+- self.pinentry_mode = constants.PINENTRY_MODE_LOOPBACK
+- def passphrase_cb(hint, desc, prev_bad, hook=None):
+- return passphrase
+- self.set_passphrase_cb(passphrase_cb)
+-
+- try:
+- if sign:
+- self.op_encrypt_sign(recipients, flags, plaintext, ciphertext)
+- else:
+- self.op_encrypt(recipients, flags, plaintext, ciphertext)
+- except errors.GPGMEError as e:
+- if e.getcode() == errors.UNUSABLE_PUBKEY:
+- result = self.op_encrypt_result()
+- if result.invalid_recipients:
+- raise errors.InvalidRecipients(result.invalid_recipients)
+- if e.getcode() == errors.UNUSABLE_SECKEY:
+- sig_result = self.op_sign_result()
+- if sig_result.invalid_signers:
+- raise errors.InvalidSigners(sig_result.invalid_signers)
+- raise
+- finally:
+- if passphrase != None:
+- self.pinentry_mode = old_pinentry_mode
+- if old_passphrase_cb:
+- self.set_passphrase_cb(*old_passphrase_cb[1:])
+-
+- result = self.op_encrypt_result()
+- assert not result.invalid_recipients
+- sig_result = self.op_sign_result() if sign else None
+- assert not sig_result or not sig_result.invalid_signers
+-
+- cipherbytes = None
+- if not sink:
+- ciphertext.seek(0, os.SEEK_SET)
+- cipherbytes = ciphertext.read()
+- return cipherbytes, result, sig_result
+-
+- def decrypt(self, ciphertext, sink=None, passphrase=None, verify=True):
+- """Decrypt data
+-
+- Decrypt the given ciphertext and verify any signatures. If
+- VERIFY is an iterable of keys, the ciphertext must be signed
+- by all those keys, otherwise an error is raised.
+-
+- If the ciphertext is symmetrically encrypted using a
+- passphrase, that passphrase can be given as parameter, using a
+- callback registered at the context, or out-of-band via
+- pinentry.
+-
+- Keyword arguments:
+- sink -- write result to sink instead of returning it
+- passphrase -- for symmetric decryption
+- verify -- check signatures (default True)
+-
+- Returns:
+- plaintext -- the decrypted data (or None if sink is given)
+- result -- additional information about the decryption
+- verify_result -- additional information about the signature(s)
+-
+- Raises:
+- UnsupportedAlgorithm -- if an unsupported algorithm was used
+- BadSignatures -- if a bad signature is encountered
+- MissingSignatures -- if expected signatures are missing or bad
+- GPGMEError -- as signaled by the underlying library
+-
+- """
+- plaintext = sink if sink else Data()
+-
+- if passphrase != None:
+- old_pinentry_mode = self.pinentry_mode
+- old_passphrase_cb = getattr(self, '_passphrase_cb', None)
+- self.pinentry_mode = constants.PINENTRY_MODE_LOOPBACK
+- def passphrase_cb(hint, desc, prev_bad, hook=None):
+- return passphrase
+- self.set_passphrase_cb(passphrase_cb)
+-
+- try:
+- if verify:
+- self.op_decrypt_verify(ciphertext, plaintext)
+- else:
+- self.op_decrypt(ciphertext, plaintext)
+- finally:
+- if passphrase != None:
+- self.pinentry_mode = old_pinentry_mode
+- if old_passphrase_cb:
+- self.set_passphrase_cb(*old_passphrase_cb[1:])
+-
+- result = self.op_decrypt_result()
+- verify_result = self.op_verify_result() if verify else None
+- if result.unsupported_algorithm:
+- raise errors.UnsupportedAlgorithm(result.unsupported_algorithm)
+-
+- if verify:
+- if any(s.status != errors.NO_ERROR
+- for s in verify_result.signatures):
+- raise errors.BadSignatures(verify_result)
+-
+- if verify and verify != True:
+- missing = list()
+- for key in verify:
+- ok = False
+- for subkey in key.subkeys:
+- for sig in verify_result.signatures:
+- if sig.summary & constants.SIGSUM_VALID == 0:
+- continue
+- if subkey.can_sign and subkey.fpr == sig.fpr:
+- ok = True
+- break
+- if ok:
+- break
+- if not ok:
+- missing.append(key)
+- if missing:
+- raise errors.MissingSignatures(verify_result, missing)
+-
+- plainbytes = None
+- if not sink:
+- plaintext.seek(0, os.SEEK_SET)
+- plainbytes = plaintext.read()
+- return plainbytes, result, verify_result
+-
+- def sign(self, data, sink=None, mode=constants.SIG_MODE_NORMAL):
+- """Sign data
+-
+- Sign the given data with either the configured default local
+- key, or the 'signers' keys of this context.
+-
+- Keyword arguments:
+- mode -- signature mode (default: normal, see below)
+- sink -- write result to sink instead of returning it
+-
+- Returns:
+- either
+- signed_data -- encoded data and signature (normal mode)
+- signature -- only the signature data (detached mode)
+- cleartext -- data and signature as text (cleartext mode)
+- (or None if sink is given)
+- result -- additional information about the signature(s)
+-
+- Raises:
+- InvalidSigners -- if signing using a particular key failed
+- GPGMEError -- as signaled by the underlying library
+-
+- """
+- signeddata = sink if sink else Data()
+-
+- try:
+- self.op_sign(data, signeddata, mode)
+- except errors.GPGMEError as e:
+- if e.getcode() == errors.UNUSABLE_SECKEY:
+- result = self.op_sign_result()
+- if result.invalid_signers:
+- raise errors.InvalidSigners(result.invalid_signers)
+- raise
+-
+- result = self.op_sign_result()
+- assert not result.invalid_signers
+-
+- signedbytes = None
+- if not sink:
+- signeddata.seek(0, os.SEEK_SET)
+- signedbytes = signeddata.read()
+- return signedbytes, result
+-
+- def verify(self, signed_data, signature=None, sink=None, verify=[]):
+- """Verify signatures
+-
+- Verify signatures over data. If VERIFY is an iterable of
+- keys, the ciphertext must be signed by all those keys,
+- otherwise an error is raised.
+-
+- Keyword arguments:
+- signature -- detached signature data
+- sink -- write result to sink instead of returning it
+-
+- Returns:
+- data -- the plain data
+- (or None if sink is given, or we verified a detached signature)
+- result -- additional information about the signature(s)
+-
+- Raises:
+- BadSignatures -- if a bad signature is encountered
+- MissingSignatures -- if expected signatures are missing or bad
+- GPGMEError -- as signaled by the underlying library
+-
+- """
+- if signature:
+- # Detached signature, we don't return the plain text.
+- data = None
+- else:
+- data = sink if sink else Data()
+-
+- if signature:
+- self.op_verify(signature, signed_data, None)
+- else:
+- self.op_verify(signed_data, None, data)
+-
+- result = self.op_verify_result()
+- if any(s.status != errors.NO_ERROR for s in result.signatures):
+- raise errors.BadSignatures(result)
+-
+- missing = list()
+- for key in verify:
+- ok = False
+- for subkey in key.subkeys:
+- for sig in result.signatures:
+- if sig.summary & constants.SIGSUM_VALID == 0:
+- continue
+- if subkey.can_sign and subkey.fpr == sig.fpr:
+- ok = True
+- break
+- if ok:
+- break
+- if not ok:
+- missing.append(key)
+- if missing:
+- raise errors.MissingSignatures(result, missing)
+-
+- plainbytes = None
+- if data and not sink:
+- data.seek(0, os.SEEK_SET)
+- plainbytes = data.read()
+- return plainbytes, result
+-
+- def keylist(self, pattern=None, secret=False,
+- mode=constants.keylist.mode.LOCAL,
+- source=None):
+- """List keys
+-
+- Keyword arguments:
+- pattern -- return keys matching pattern (default: all keys)
+- secret -- return only secret keys (default: False)
+- mode -- keylist mode (default: list local keys)
+- source -- read keys from source instead from the keyring
+- (all other options are ignored in this case)
+-
+- Returns:
+- -- an iterator returning key objects
+-
+- Raises:
+- GPGMEError -- as signaled by the underlying library
+- """
+- if not source:
+- self.set_keylist_mode(mode)
+- self.op_keylist_start(pattern, secret)
+- else:
+- # Automatic wrapping of SOURCE is not possible here,
+- # because the object must not be deallocated until the
+- # iteration over the results ends.
+- if not isinstance(source, Data):
+- source = Data(file=source)
+- self.op_keylist_from_data_start(source, 0)
+-
+- key = self.op_keylist_next()
+- while key:
+- yield key
+- key = self.op_keylist_next()
+- self.op_keylist_end()
+-
+- def create_key(self, userid, algorithm=None, expires_in=0, expires=True,
+- sign=False, encrypt=False, certify=False, authenticate=False,
+- passphrase=None, force=False):
+- """Create a primary key
+-
+- Create a primary key for the user id USERID.
+-
+- ALGORITHM may be used to specify the public key encryption
+- algorithm for the new key. By default, a reasonable default
+- is chosen. You may use "future-default" to select an
+- algorithm that will be the default in a future implementation
+- of the engine. ALGORITHM may be a string like "rsa", or
+- "rsa2048" to explicitly request an algorithm and a key size.
+-
+- EXPIRES_IN specifies the expiration time of the key in number
+- of seconds since the keys creation. By default, a reasonable
+- expiration time is chosen. If you want to create a key that
+- does not expire, use the keyword argument EXPIRES.
+-
+- SIGN, ENCRYPT, CERTIFY, and AUTHENTICATE can be used to
+- request the capabilities of the new key. If you don't request
+- any, a reasonable set of capabilities is selected, and in case
+- of OpenPGP, a subkey with a reasonable set of capabilities is
+- created.
+-
+- If PASSPHRASE is None (the default), then the key will not be
+- protected with a passphrase. If PASSPHRASE is a string, it
+- will be used to protect the key. If PASSPHRASE is True, the
+- passphrase must be supplied using a passphrase callback or
+- out-of-band with a pinentry.
+-
+- Keyword arguments:
+- algorithm -- public key algorithm, see above (default: reasonable)
+- expires_in -- expiration time in seconds (default: reasonable)
+- expires -- whether or not the key should expire (default: True)
+- sign -- request the signing capability (see above)
+- encrypt -- request the encryption capability (see above)
+- certify -- request the certification capability (see above)
+- authenticate -- request the authentication capability (see above)
+- passphrase -- protect the key with a passphrase (default: no passphrase)
+- force -- force key creation even if a key with the same userid exists
+- (default: False)
+-
+- Returns:
+- -- an object describing the result of the key creation
+-
+- Raises:
+- GPGMEError -- as signaled by the underlying library
+-
+- """
+- if util.is_a_string(passphrase):
+- old_pinentry_mode = self.pinentry_mode
+- old_passphrase_cb = getattr(self, '_passphrase_cb', None)
+- self.pinentry_mode = constants.PINENTRY_MODE_LOOPBACK
+- def passphrase_cb(hint, desc, prev_bad, hook=None):
+- return passphrase
+- self.set_passphrase_cb(passphrase_cb)
+-
+- try:
+- self.op_createkey(userid, algorithm,
+- 0, # reserved
+- expires_in,
+- None, # extrakey
+- ((constants.create.SIGN if sign else 0)
+- | (constants.create.ENCR if encrypt else 0)
+- | (constants.create.CERT if certify else 0)
+- | (constants.create.AUTH if authenticate else 0)
+- | (constants.create.NOPASSWD if passphrase == None else 0)
+- | (0 if expires else constants.create.NOEXPIRE)
+- | (constants.create.FORCE if force else 0)))
+- finally:
+- if util.is_a_string(passphrase):
+- self.pinentry_mode = old_pinentry_mode
+- if old_passphrase_cb:
+- self.set_passphrase_cb(*old_passphrase_cb[1:])
+-
+- return self.op_genkey_result()
+-
+- def create_subkey(self, key, algorithm=None, expires_in=0, expires=True,
+- sign=False, encrypt=False, authenticate=False, passphrase=None):
+- """Create a subkey
+-
+- Create a subkey for the given KEY. As subkeys are a concept
+- of OpenPGP, calling this is only valid for the OpenPGP
+- protocol.
+-
+- ALGORITHM may be used to specify the public key encryption
+- algorithm for the new subkey. By default, a reasonable
+- default is chosen. You may use "future-default" to select an
+- algorithm that will be the default in a future implementation
+- of the engine. ALGORITHM may be a string like "rsa", or
+- "rsa2048" to explicitly request an algorithm and a key size.
+-
+- EXPIRES_IN specifies the expiration time of the subkey in
+- number of seconds since the subkeys creation. By default, a
+- reasonable expiration time is chosen. If you want to create a
+- subkey that does not expire, use the keyword argument EXPIRES.
+-
+- SIGN, ENCRYPT, and AUTHENTICATE can be used to request the
+- capabilities of the new subkey. If you don't request any, an
+- encryption subkey is generated.
+-
+- If PASSPHRASE is None (the default), then the subkey will not
+- be protected with a passphrase. If PASSPHRASE is a string, it
+- will be used to protect the subkey. If PASSPHRASE is True,
+- the passphrase must be supplied using a passphrase callback or
+- out-of-band with a pinentry.
+-
+- Keyword arguments:
+- algorithm -- public key algorithm, see above (default: reasonable)
+- expires_in -- expiration time in seconds (default: reasonable)
+- expires -- whether or not the subkey should expire (default: True)
+- sign -- request the signing capability (see above)
+- encrypt -- request the encryption capability (see above)
+- authenticate -- request the authentication capability (see above)
+- passphrase -- protect the subkey with a passphrase (default: no passphrase)
+-
+- Returns:
+- -- an object describing the result of the subkey creation
+-
+- Raises:
+- GPGMEError -- as signaled by the underlying library
+-
+- """
+- if util.is_a_string(passphrase):
+- old_pinentry_mode = self.pinentry_mode
+- old_passphrase_cb = getattr(self, '_passphrase_cb', None)
+- self.pinentry_mode = constants.PINENTRY_MODE_LOOPBACK
+- def passphrase_cb(hint, desc, prev_bad, hook=None):
+- return passphrase
+- self.set_passphrase_cb(passphrase_cb)
+-
+- try:
+- self.op_createsubkey(key, algorithm,
+- 0, # reserved
+- expires_in,
+- ((constants.create.SIGN if sign else 0)
+- | (constants.create.ENCR if encrypt else 0)
+- | (constants.create.AUTH if authenticate else 0)
+- | (constants.create.NOPASSWD
+- if passphrase == None else 0)
+- | (0 if expires else constants.create.NOEXPIRE)))
+- finally:
+- if util.is_a_string(passphrase):
+- self.pinentry_mode = old_pinentry_mode
+- if old_passphrase_cb:
+- self.set_passphrase_cb(*old_passphrase_cb[1:])
+-
+- return self.op_genkey_result()
+-
+- def key_add_uid(self, key, uid):
+- """Add a UID
+-
+- Add the uid UID to the given KEY. Calling this function is
+- only valid for the OpenPGP protocol.
+-
+- Raises:
+- GPGMEError -- as signaled by the underlying library
+-
+- """
+- self.op_adduid(key, uid, 0)
+-
+- def key_revoke_uid(self, key, uid):
+- """Revoke a UID
+-
+- Revoke the uid UID from the given KEY. Calling this function
+- is only valid for the OpenPGP protocol.
+-
+- Raises:
+- GPGMEError -- as signaled by the underlying library
+-
+- """
+- self.op_revuid(key, uid, 0)
+-
+- def key_sign(self, key, uids=None, expires_in=False, local=False):
+- """Sign a key
+-
+- Sign a key with the current set of signing keys. Calling this
+- function is only valid for the OpenPGP protocol.
+-
+- If UIDS is None (the default), then all UIDs are signed. If
+- it is a string, then only the matching UID is signed. If it
+- is a list of strings, then all matching UIDs are signed. Note
+- that a case-sensitive exact string comparison is done.
+-
+- EXPIRES_IN specifies the expiration time of the signature in
+- seconds. If EXPIRES_IN is False, the signature does not
+- expire.
+-
+- Keyword arguments:
+- uids -- user ids to sign, see above (default: sign all)
+- expires_in -- validity period of the signature in seconds
+- (default: do not expire)
+- local -- create a local, non-exportable signature
+- (default: False)
+-
+- Raises:
+- GPGMEError -- as signaled by the underlying library
+-
+- """
+- flags = 0
+- if uids == None or util.is_a_string(uids):
+- pass#through unchanged
+- else:
+- flags |= constants.keysign.LFSEP
+- uids = "\n".join(uids)
+-
+- if not expires_in:
+- flags |= constants.keysign.NOEXPIRE
+-
+- if local:
+- flags |= constants.keysign.LOCAL
+-
+- self.op_keysign(key, uids, expires_in, flags)
+-
+- def key_tofu_policy(self, key, policy):
+- """Set a keys' TOFU policy
+-
+- Set the TOFU policy associated with KEY to POLICY. Calling
+- this function is only valid for the OpenPGP protocol.
+-
+- Raises:
+- GPGMEError -- as signaled by the underlying library
+-
+- """
+- self.op_tofu_policy(key, policy)
+-
+- def assuan_transact(self, command,
+- data_cb=None, inquire_cb=None, status_cb=None):
+- """Issue a raw assuan command
+-
+- This function can be used to issue a raw assuan command to the
+- engine.
+-
+- If command is a string or bytes, it will be used as-is. If it
+- is an iterable of strings, it will be properly escaped and
+- joined into an well-formed assuan command.
+-
+- Keyword arguments:
+- data_cb -- a callback receiving data lines
+- inquire_cb -- a callback providing more information
+- status_cb -- a callback receiving status lines
+-
+- Returns:
+- result -- the result of command as GPGMEError
+-
+- Raises:
+- GPGMEError -- as signaled by the underlying library
+-
+- """
+-
+- if util.is_a_string(command) or isinstance(command, bytes):
+- cmd = command
+- else:
+- cmd = " ".join(util.percent_escape(f) for f in command)
+-
+- errptr = gpgme.new_gpgme_error_t_p()
+-
+- err = gpgme.gpgme_op_assuan_transact_ext(
+- self.wrapped,
+- cmd,
+- (weakref.ref(self), data_cb) if data_cb else None,
+- (weakref.ref(self), inquire_cb) if inquire_cb else None,
+- (weakref.ref(self), status_cb) if status_cb else None,
+- errptr)
+-
+- if self._callback_excinfo:
+- gpgme.gpg_raise_callback_exception(self)
+-
+- errorcheck(err)
+-
+- status = gpgme.gpgme_error_t_p_value(errptr)
+- gpgme.delete_gpgme_error_t_p(errptr)
+-
+- return GPGMEError(status) if status != 0 else None
+-
+- def interact(self, key, func, sink=None, flags=0, fnc_value=None):
+- """Interact with the engine
+-
+- This method can be used to edit keys and cards interactively.
+- KEY is the key to edit, FUNC is called repeatedly with two
+- unicode arguments, 'keyword' and 'args'. See the GPGME manual
+- for details.
+-
+- Keyword arguments:
+- sink -- if given, additional output is written here
+- flags -- use constants.INTERACT_CARD to edit a card
+-
+- Raises:
+- GPGMEError -- as signaled by the underlying library
+-
+- """
+- if key == None:
+- raise ValueError("First argument cannot be None")
+-
+- if sink == None:
+- sink = Data()
+-
+- if fnc_value:
+- opaquedata = (weakref.ref(self), func, fnc_value)
+- else:
+- opaquedata = (weakref.ref(self), func)
+-
+- result = gpgme.gpgme_op_interact(self.wrapped, key, flags,
+- opaquedata, sink)
+- if self._callback_excinfo:
+- gpgme.gpg_raise_callback_exception(self)
+- errorcheck(result)
+-
+- @property
+- def signers(self):
+- """Keys used for signing"""
+- return [self.signers_enum(i) for i in range(self.signers_count())]
+- @signers.setter
+- def signers(self, signers):
+- old = self.signers
+- self.signers_clear()
+- try:
+- for key in signers:
+- self.signers_add(key)
+- except:
+- self.signers = old
+- raise
+-
+- @property
+- def pinentry_mode(self):
+- """Pinentry mode"""
+- return self.get_pinentry_mode()
+- @pinentry_mode.setter
+- def pinentry_mode(self, value):
+- self.set_pinentry_mode(value)
+-
+- @property
+- def protocol(self):
+- """Protocol to use"""
+- return self.get_protocol()
+- @protocol.setter
+- def protocol(self, value):
+- errorcheck(gpgme.gpgme_engine_check_version(value))
+- self.set_protocol(value)
+-
+- @property
+- def home_dir(self):
+- """Engine's home directory"""
+- return self.engine_info.home_dir
+- @home_dir.setter
+- def home_dir(self, value):
+- self.set_engine_info(self.protocol, home_dir=value)
+-
+- _ctype = 'gpgme_ctx_t'
+- _cprefix = 'gpgme_'
+-
+- def _errorcheck(self, name):
+- """This function should list all functions returning gpgme_error_t"""
+- # The list of functions is created using:
+- #
+- # $ grep '^gpgme_error_t ' obj/lang/python/python3.5-gpg/gpgme.h \
+- # | grep -v _op_ | awk "/\(gpgme_ctx/ { printf (\"'%s',\\n\", \$2) } "
+- return ((name.startswith('gpgme_op_')
+- and not name.endswith('_result'))
+- or name in {
+- 'gpgme_new',
+- 'gpgme_set_ctx_flag',
+- 'gpgme_set_protocol',
+- 'gpgme_set_sub_protocol',
+- 'gpgme_set_keylist_mode',
+- 'gpgme_set_pinentry_mode',
+- 'gpgme_set_locale',
+- 'gpgme_ctx_set_engine_info',
+- 'gpgme_signers_add',
+- 'gpgme_sig_notation_add',
+- 'gpgme_set_sender',
+- 'gpgme_cancel',
+- 'gpgme_cancel_async',
+- 'gpgme_get_key',
+- })
+-
+- _boolean_properties = {'armor', 'textmode', 'offline'}
+-
+- def __del__(self):
+- if not gpgme:
+- # At interpreter shutdown, gpgme is set to NONE.
+- return
+-
+- self._free_passcb()
+- self._free_progresscb()
+- self._free_statuscb()
+- if self.own and self.wrapped and gpgme.gpgme_release:
+- gpgme.gpgme_release(self.wrapped)
+- self.wrapped = None
+-
+- # Implement the context manager protocol.
+- def __enter__(self):
+- return self
+- def __exit__(self, type, value, tb):
+- self.__del__()
+-
+- def op_keylist_all(self, *args, **kwargs):
+- self.op_keylist_start(*args, **kwargs)
+- key = self.op_keylist_next()
+- while key:
+- yield key
+- key = self.op_keylist_next()
+- self.op_keylist_end()
+-
+- def op_keylist_next(self):
+- """Returns the next key in the list created
+- by a call to op_keylist_start(). The object returned
+- is of type Key."""
+- ptr = gpgme.new_gpgme_key_t_p()
+- try:
+- errorcheck(gpgme.gpgme_op_keylist_next(self.wrapped, ptr))
+- key = gpgme.gpgme_key_t_p_value(ptr)
+- except errors.GPGMEError as excp:
+- key = None
+- if excp.getcode() != errors.EOF:
+- raise excp
+- gpgme.delete_gpgme_key_t_p(ptr)
+- if key:
+- key.__del__ = lambda self: gpgme.gpgme_key_unref(self)
+- return key
+-
+- def get_key(self, fpr, secret=False):
+- """Get a key given a fingerprint
+-
+- Keyword arguments:
+- secret -- to request a secret key
+-
+- Returns:
+- -- the matching key
+-
+- Raises:
+- KeyError -- if the key was not found
+- GPGMEError -- as signaled by the underlying library
+-
+- """
+- ptr = gpgme.new_gpgme_key_t_p()
+-
+- try:
+- errorcheck(gpgme.gpgme_get_key(self.wrapped, fpr, ptr, secret))
+- except errors.GPGMEError as e:
+- if e.getcode() == errors.EOF:
+- raise errors.KeyNotFound(fpr)
+- raise e
+-
+- key = gpgme.gpgme_key_t_p_value(ptr)
+- gpgme.delete_gpgme_key_t_p(ptr)
+- assert key
+- key.__del__ = lambda self: gpgme.gpgme_key_unref(self)
+- return key
+-
+- def op_trustlist_all(self, *args, **kwargs):
+- self.op_trustlist_start(*args, **kwargs)
+- trust = self.op_trustlist_next()
+- while trust:
+- yield trust
+- trust = self.op_trustlist_next()
+- self.op_trustlist_end()
+-
+- def op_trustlist_next(self):
+- """Returns the next trust item in the list created
+- by a call to op_trustlist_start(). The object returned
+- is of type TrustItem."""
+- ptr = gpgme.new_gpgme_trust_item_t_p()
+- try:
+- errorcheck(gpgme.gpgme_op_trustlist_next(self.wrapped, ptr))
+- trust = gpgme.gpgme_trust_item_t_p_value(ptr)
+- except errors.GPGMEError as excp:
+- trust = None
+- if excp.getcode() != errors.EOF:
+- raise
+- gpgme.delete_gpgme_trust_item_t_p(ptr)
+- return trust
+-
+- def set_passphrase_cb(self, func, hook=None):
+- """Sets the passphrase callback to the function specified by func.
+-
+- When the system needs a passphrase, it will call func with three args:
+- hint, a string describing the key it needs the passphrase for;
+- desc, a string describing the passphrase it needs;
+- prev_bad, a boolean equal True if this is a call made after
+- unsuccessful previous attempt.
+-
+- If hook has a value other than None it will be passed into the func
+- as a forth argument.
+-
+- Please see the GPGME manual for more information.
+- """
+- if func == None:
+- hookdata = None
+- else:
+- if hook == None:
+- hookdata = (weakref.ref(self), func)
+- else:
+- hookdata = (weakref.ref(self), func, hook)
+- gpgme.gpg_set_passphrase_cb(self, hookdata)
+-
+- def _free_passcb(self):
+- if gpgme.gpg_set_passphrase_cb:
+- self.set_passphrase_cb(None)
+-
+- def set_progress_cb(self, func, hook=None):
+- """Sets the progress meter callback to the function specified by FUNC.
+- If FUNC is None, the callback will be cleared.
+-
+- This function will be called to provide an interactive update
+- of the system's progress. The function will be called with
+- three arguments, type, total, and current. If HOOK is not
+- None, it will be supplied as fourth argument.
+-
+- Please see the GPGME manual for more information.
+-
+- """
+- if func == None:
+- hookdata = None
+- else:
+- if hook == None:
+- hookdata = (weakref.ref(self), func)
+- else:
+- hookdata = (weakref.ref(self), func, hook)
+- gpgme.gpg_set_progress_cb(self, hookdata)
+-
+- def _free_progresscb(self):
+- if gpgme.gpg_set_progress_cb:
+- self.set_progress_cb(None)
+-
+- def set_status_cb(self, func, hook=None):
+- """Sets the status callback to the function specified by FUNC. If
+- FUNC is None, the callback will be cleared.
+-
+- The function will be called with two arguments, keyword and
+- args. If HOOK is not None, it will be supplied as third
+- argument.
+-
+- Please see the GPGME manual for more information.
+-
+- """
+- if func == None:
+- hookdata = None
+- else:
+- if hook == None:
+- hookdata = (weakref.ref(self), func)
+- else:
+- hookdata = (weakref.ref(self), func, hook)
+- gpgme.gpg_set_status_cb(self, hookdata)
+-
+- def _free_statuscb(self):
+- if gpgme.gpg_set_status_cb:
+- self.set_status_cb(None)
+-
+- @property
+- def engine_info(self):
+- """Configuration of the engine currently in use"""
+- p = self.protocol
+- infos = [i for i in self.get_engine_info() if i.protocol == p]
+- assert len(infos) == 1
+- return infos[0]
+-
+- def get_engine_info(self):
+- """Get engine configuration
+-
+- Returns information about all configured and installed
+- engines.
+-
+- Returns:
+- infos -- a list of engine infos
+-
+- """
+- return gpgme.gpgme_ctx_get_engine_info(self.wrapped)
+-
+- def set_engine_info(self, proto, file_name=None, home_dir=None):
+- """Change engine configuration
+-
+- Changes the configuration of the crypto engine implementing
+- the protocol 'proto' for the context.
+-
+- Keyword arguments:
+- file_name -- engine program file name (unchanged if None)
+- home_dir -- configuration directory (unchanged if None)
+-
+- """
+- self.ctx_set_engine_info(proto, file_name, home_dir)
+-
+- def wait(self, hang):
+- """Wait for asynchronous call to finish. Wait forever if hang is True.
+- Raises an exception on errors.
+-
+- Please read the GPGME manual for more information.
+-
+- """
+- ptr = gpgme.new_gpgme_error_t_p()
+- gpgme.gpgme_wait(self.wrapped, ptr, hang)
+- status = gpgme.gpgme_error_t_p_value(ptr)
+- gpgme.delete_gpgme_error_t_p(ptr)
+- errorcheck(status)
+-
+- def op_edit(self, key, func, fnc_value, out):
+- """Start key editing using supplied callback function
+-
+- Note: This interface is deprecated and will be removed with
+- GPGME 1.8. Please use .interact instead. Furthermore, we
+- implement this using gpgme_op_interact, so callbacks will get
+- called with string keywords instead of numeric status
+- messages. Code that is using constants.STATUS_X or
+- constants.status.X will continue to work, whereas code using
+- magic numbers will break as a result.
+-
+- """
+- warnings.warn("Call to deprecated method op_edit.",
+- category=DeprecationWarning)
+- return self.interact(key, func, sink=out, fnc_value=fnc_value)
+-
+-
+-class Data(GpgmeWrapper):
+- """Data buffer
+-
+- A lot of data has to be exchanged between the user and the crypto
+- engine, like plaintext messages, ciphertext, signatures and
+- information about the keys. The technical details about
+- exchanging the data information are completely abstracted by
+- GPGME. The user provides and receives the data via `gpgme_data_t'
+- objects, regardless of the communication protocol between GPGME
+- and the crypto engine in use.
+-
+- This Data class is the implementation of the GpgmeData objects.
+-
+- Please see the information about __init__ for instantiation.
+-
+- """
+-
+- _ctype = 'gpgme_data_t'
+- _cprefix = 'gpgme_data_'
+-
+- def _errorcheck(self, name):
+- """This function should list all functions returning gpgme_error_t"""
+- # This list is compiled using
+- #
+- # $ grep -v '^gpgme_error_t ' obj/lang/python/python3.5-gpg/gpgme.h \
+- # | awk "/\(gpgme_data_t/ { printf (\"'%s',\\n\", \$2) } " | sed "s/'\\*/'/"
+- return name not in {
+- 'gpgme_data_read',
+- 'gpgme_data_write',
+- 'gpgme_data_seek',
+- 'gpgme_data_release',
+- 'gpgme_data_release_and_get_mem',
+- 'gpgme_data_get_encoding',
+- 'gpgme_data_get_file_name',
+- 'gpgme_data_identify',
+- }
+-
+- def __init__(self, string=None, file=None, offset=None,
+- length=None, cbs=None, copy=True):
+- """Initialize a new gpgme_data_t object.
+-
+- If no args are specified, make it an empty object.
+-
+- If string alone is specified, initialize it with the data
+- contained there.
+-
+- If file, offset, and length are all specified, file must
+- be either a filename or a file-like object, and the object
+- will be initialized by reading the specified chunk from the file.
+-
+- If cbs is specified, it MUST be a tuple of the form:
+-
+- (read_cb, write_cb, seek_cb, release_cb[, hook])
+-
+- where the first four items are functions implementing reading,
+- writing, seeking the data, and releasing any resources once
+- the data object is deallocated. The functions must match the
+- following prototypes:
+-
+- def read(amount, hook=None):
+- return <a b"bytes" object>
+-
+- def write(data, hook=None):
+- return <the number of bytes written>
+-
+- def seek(offset, whence, hook=None):
+- return <the new file position>
+-
+- def release(hook=None):
+- <return value and exceptions are ignored>
+-
+- The functions may be bound methods. In that case, you can
+- simply use the 'self' reference instead of using a hook.
+-
+- If file is specified without any other arguments, then
+- it must be a filename, and the object will be initialized from
+- that file.
+-
+- """
+- super(Data, self).__init__(None)
+- self.data_cbs = None
+-
+- if cbs != None:
+- self.new_from_cbs(*cbs)
+- elif string != None:
+- self.new_from_mem(string, copy)
+- elif file != None and offset != None and length != None:
+- self.new_from_filepart(file, offset, length)
+- elif file != None:
+- if util.is_a_string(file):
+- self.new_from_file(file, copy)
+- else:
+- self.new_from_fd(file)
+- else:
+- self.new()
+-
+- def __del__(self):
+- if not gpgme:
+- # At interpreter shutdown, gpgme is set to NONE.
+- return
+-
+- if self.wrapped != None and gpgme.gpgme_data_release:
+- gpgme.gpgme_data_release(self.wrapped)
+- if self._callback_excinfo:
+- gpgme.gpg_raise_callback_exception(self)
+- self.wrapped = None
+- self._free_datacbs()
+-
+- # Implement the context manager protocol.
+- def __enter__(self):
+- return self
+- def __exit__(self, type, value, tb):
+- self.__del__()
+-
+- def _free_datacbs(self):
+- self._data_cbs = None
+-
+- def new(self):
+- tmp = gpgme.new_gpgme_data_t_p()
+- errorcheck(gpgme.gpgme_data_new(tmp))
+- self.wrapped = gpgme.gpgme_data_t_p_value(tmp)
+- gpgme.delete_gpgme_data_t_p(tmp)
+-
+- def new_from_mem(self, string, copy=True):
+- tmp = gpgme.new_gpgme_data_t_p()
+- errorcheck(gpgme.gpgme_data_new_from_mem(tmp,string,len(string),copy))
+- self.wrapped = gpgme.gpgme_data_t_p_value(tmp)
+- gpgme.delete_gpgme_data_t_p(tmp)
+-
+- def new_from_file(self, filename, copy=True):
+- tmp = gpgme.new_gpgme_data_t_p()
+- try:
+- errorcheck(gpgme.gpgme_data_new_from_file(tmp, filename, copy))
+- except errors.GPGMEError as e:
+- if e.getcode() == errors.INV_VALUE and not copy:
+- raise ValueError("delayed reads are not yet supported")
+- else:
+- raise e
+- self.wrapped = gpgme.gpgme_data_t_p_value(tmp)
+- gpgme.delete_gpgme_data_t_p(tmp)
+-
+- def new_from_cbs(self, read_cb, write_cb, seek_cb, release_cb, hook=None):
+- tmp = gpgme.new_gpgme_data_t_p()
+- if hook != None:
+- hookdata = (weakref.ref(self),
+- read_cb, write_cb, seek_cb, release_cb, hook)
+- else:
+- hookdata = (weakref.ref(self),
+- read_cb, write_cb, seek_cb, release_cb)
+- gpgme.gpg_data_new_from_cbs(self, hookdata, tmp)
+- self.wrapped = gpgme.gpgme_data_t_p_value(tmp)
+- gpgme.delete_gpgme_data_t_p(tmp)
+-
+- def new_from_filepart(self, file, offset, length):
+- """This wraps the GPGME gpgme_data_new_from_filepart() function.
+- The argument "file" may be:
+-
+- * a string specifying a file name, or
+- * a file-like object supporting the fileno() and the mode attribute.
+-
+- """
+-
+- tmp = gpgme.new_gpgme_data_t_p()
+- filename = None
+- fp = None
+-
+- if util.is_a_string(file):
+- filename = file
+- else:
+- fp = gpgme.fdopen(file.fileno(), file.mode)
+- if fp == None:
+- raise ValueError("Failed to open file from %s arg %s" % \
+- (str(type(file)), str(file)))
+-
+- errorcheck(gpgme.gpgme_data_new_from_filepart(tmp, filename, fp,
+- offset, length))
+- self.wrapped = gpgme.gpgme_data_t_p_value(tmp)
+- gpgme.delete_gpgme_data_t_p(tmp)
+-
+- def new_from_fd(self, file):
+- """This wraps the GPGME gpgme_data_new_from_fd() function. The
+- argument "file" must be a file-like object, supporting the
+- fileno() method.
+-
+- """
+- tmp = gpgme.new_gpgme_data_t_p()
+- errorcheck(gpgme.gpgme_data_new_from_fd(tmp, file.fileno()))
+- self.wrapped = gpgme.gpgme_data_t_p_value(tmp)
+- gpgme.delete_gpgme_data_t_p(tmp)
+-
+- def new_from_stream(self, file):
+- """This wrap around gpgme_data_new_from_stream is an alias for
+- new_from_fd() method since in python there's not difference
+- between file stream and file descriptor"""
+- self.new_from_fd(file)
+-
+- def write(self, buffer):
+- """Write buffer given as string or bytes.
+-
+- If a string is given, it is implicitly encoded using UTF-8."""
+- written = gpgme.gpgme_data_write(self.wrapped, buffer)
+- if written < 0:
+- if self._callback_excinfo:
+- gpgme.gpg_raise_callback_exception(self)
+- else:
+- raise GPGMEError.fromSyserror()
+- return written
+-
+- def read(self, size = -1):
+- """Read at most size bytes, returned as bytes.
+-
+- If the size argument is negative or omitted, read until EOF is reached.
+-
+- Returns the data read, or the empty string if there was no data
+- to read before EOF was reached."""
+-
+- if size == 0:
+- return ''
+-
+- if size > 0:
+- try:
+- result = gpgme.gpgme_data_read(self.wrapped, size)
+- except:
+- if self._callback_excinfo:
+- gpgme.gpg_raise_callback_exception(self)
+- else:
+- raise
+- return result
+- else:
+- chunks = []
+- while True:
+- try:
+- result = gpgme.gpgme_data_read(self.wrapped, 4096)
+- except:
+- if self._callback_excinfo:
+- gpgme.gpg_raise_callback_exception(self)
+- else:
+- raise
+- if len(result) == 0:
+- break
+- chunks.append(result)
+- return b''.join(chunks)
+-
+-def pubkey_algo_string(subkey):
+- """Return short algorithm string
+-
+- Return a public key algorithm string (e.g. "rsa2048") for a given
+- SUBKEY.
+-
+- Returns:
+- algo - a string
+-
+- """
+- return gpgme.gpgme_pubkey_algo_string(subkey)
+-
+-def pubkey_algo_name(algo):
+- """Return name of public key algorithm
+-
+- Return the name of the public key algorithm for a given numeric
+- algorithm id ALGO (cf. RFC4880).
+-
+- Returns:
+- algo - a string
+-
+- """
+- return gpgme.gpgme_pubkey_algo_name(algo)
+-
+-def hash_algo_name(algo):
+- """Return name of hash algorithm
+-
+- Return the name of the hash algorithm for a given numeric
+- algorithm id ALGO (cf. RFC4880).
+-
+- Returns:
+- algo - a string
+-
+- """
+- return gpgme.gpgme_hash_algo_name(algo)
+-
+-def get_protocol_name(proto):
+- """Get protocol description
+-
+- Get the string describing protocol PROTO.
+-
+- Returns:
+- proto - a string
+-
+- """
+- return gpgme.gpgme_get_protocol_name(proto)
+-
+-def addrspec_from_uid(uid):
+- """Return the address spec
+-
+- Return the addr-spec (cf. RFC2822 section 4.3) from a user id UID.
+-
+- Returns:
+- addr_spec - a string
+-
+- """
+- return gpgme.gpgme_addrspec_from_uid(uid)
+-
+-def check_version(version=None):
+- return gpgme.gpgme_check_version(version)
+-
+-# check_version also makes sure that several subsystems are properly
+-# initialized, and it must be run at least once before invoking any
+-# other function. We do it here so that the user does not have to do
+-# it unless she really wants to check for a certain version.
+-check_version()
+-
+-def engine_check_version (proto):
+- try:
+- errorcheck(gpgme.gpgme_engine_check_version(proto))
+- return True
+- except errors.GPGMEError:
+- return False
+-
+-def get_engine_info():
+- ptr = gpgme.new_gpgme_engine_info_t_p()
+- try:
+- errorcheck(gpgme.gpgme_get_engine_info(ptr))
+- info = gpgme.gpgme_engine_info_t_p_value(ptr)
+- except errors.GPGMEError:
+- info = None
+- gpgme.delete_gpgme_engine_info_t_p(ptr)
+- return info
+-
+-def set_engine_info(proto, file_name, home_dir=None):
+- """Changes the default configuration of the crypto engine implementing
+- the protocol 'proto'. 'file_name' is the file name of
+- the executable program implementing this protocol. 'home_dir' is the
+- directory name of the configuration directory (engine's default is
+- used if omitted)."""
+- errorcheck(gpgme.gpgme_set_engine_info(proto, file_name, home_dir))
+-
+-def set_locale(category, value):
+- """Sets the default locale used by contexts"""
+- errorcheck(gpgme.gpgme_set_locale(None, category, value))
+-
+-def wait(hang):
+- """Wait for asynchronous call on any Context to finish.
+- Wait forever if hang is True.
+-
+- For finished anynch calls it returns a tuple (status, context):
+- status - status return by asnynchronous call.
+- context - context which caused this call to return.
+-
+- Please read the GPGME manual of more information."""
+- ptr = gpgme.new_gpgme_error_t_p()
+- context = gpgme.gpgme_wait(None, ptr, hang)
+- status = gpgme.gpgme_error_t_p_value(ptr)
+- gpgme.delete_gpgme_error_t_p(ptr)
+- if context == None:
+- errorcheck(status)
+- else:
+- context = Context(context)
+- return (status, context)
+diff --git a/lang/python/gpg/errors.py b/lang/python/gpg/errors.py
+deleted file mode 100644
+index 1ce139e..0000000
+--- a/lang/python/gpg/errors.py
++++ /dev/null
+@@ -1,128 +0,0 @@
+-# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
+-# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
+-#
+-# This library 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.
+-#
+-# This library 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 library; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+-
+-from __future__ import absolute_import, print_function, unicode_literals
+-del absolute_import, print_function, unicode_literals
+-
+-from . import gpgme
+-from . import util
+-
+-# To appease static analysis tools, we define some constants here.
+-# They are overwritten with the proper values by process_constants.
+-NO_ERROR = None
+-EOF = None
+-
+-util.process_constants('GPG_ERR_', globals())
+-del util
+-
+-class GpgError(Exception):
+- pass
+-
+-class GPGMEError(GpgError):
+- def __init__(self, error = None, message = None):
+- self.error = error
+- self.message = message
+-
+- @classmethod
+- def fromSyserror(cls):
+- return cls(gpgme.gpgme_err_code_from_syserror())
+-
+- def getstring(self):
+- message = "%s: %s" % (gpgme.gpgme_strsource(self.error),
+- gpgme.gpgme_strerror(self.error))
+- if self.message != None:
+- message = "%s: %s" % (self.message, message)
+- return message
+-
+- def getcode(self):
+- return gpgme.gpgme_err_code(self.error)
+-
+- def getsource(self):
+- return gpgme.gpgme_err_source(self.error)
+-
+- def __str__(self):
+- return self.getstring()
+-
+-def errorcheck(retval, extradata = None):
+- if retval:
+- raise GPGMEError(retval, extradata)
+-
+-class KeyNotFound(GPGMEError, KeyError):
+- """Raised if a key was not found
+-
+- GPGME indicates this condition with EOF, which is not very
+- idiomatic. We raise this error that is both a GPGMEError
+- indicating EOF, and a KeyError.
+-
+- """
+- def __init__(self, keystr):
+- self.keystr = keystr
+- GPGMEError.__init__(self, EOF)
+- def __str__(self):
+- return self.keystr
+-
+-# These errors are raised in the idiomatic interface code.
+-
+-class EncryptionError(GpgError):
+- pass
+-
+-class InvalidRecipients(EncryptionError):
+- def __init__(self, recipients):
+- self.recipients = recipients
+- def __str__(self):
+- return ", ".join("{}: {}".format(r.fpr,
+- gpgme.gpgme_strerror(r.reason))
+- for r in self.recipients)
+-
+-class DeryptionError(GpgError):
+- pass
+-
+-class UnsupportedAlgorithm(DeryptionError):
+- def __init__(self, algorithm):
+- self.algorithm = algorithm
+- def __str__(self):
+- return self.algorithm
+-
+-class SigningError(GpgError):
+- pass
+-
+-class InvalidSigners(SigningError):
+- def __init__(self, signers):
+- self.signers = signers
+- def __str__(self):
+- return ", ".join("{}: {}".format(s.fpr,
+- gpgme.gpgme_strerror(s.reason))
+- for s in self.signers)
+-
+-class VerificationError(GpgError):
+- pass
+-
+-class BadSignatures(VerificationError):
+- def __init__(self, result):
+- self.result = result
+- def __str__(self):
+- return ", ".join("{}: {}".format(s.fpr,
+- gpgme.gpgme_strerror(s.status))
+- for s in self.result.signatures
+- if s.status != NO_ERROR)
+-
+-class MissingSignatures(VerificationError):
+- def __init__(self, result, missing):
+- self.result = result
+- self.missing = missing
+- def __str__(self):
+- return ", ".join(k.subkeys[0].fpr for k in self.missing)
+diff --git a/lang/python/gpg/results.py b/lang/python/gpg/results.py
+deleted file mode 100644
+index 46ebeec..0000000
+--- a/lang/python/gpg/results.py
++++ /dev/null
+@@ -1,118 +0,0 @@
+-# Robust result objects
+-#
+-# Copyright (C) 2016 g10 Code 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 <http://www.gnu.org/licenses/>.
+-
+-from __future__ import absolute_import, print_function, unicode_literals
+-del absolute_import, print_function, unicode_literals
+-
+-"""Robust result objects
+-
+-Results returned by the underlying library are fragile, i.e. they are
+-only valid until the next operation is performed in the context.
+-
+-We cannot arbitrarily constrain the lifetime of Python objects, we
+-therefore create deep copies of the results.
+-
+-"""
+-
+-class Result(object):
+- """Result object
+-
+- Describes the result of an operation.
+-
+- """
+-
+- """Convert to types"""
+- _type = {}
+-
+- """Map functions over list attributes"""
+- _map = {}
+-
+- """Automatically copy unless blacklisted"""
+- _blacklist = {
+- 'acquire', 'append', 'disown', 'next', 'own', 'this', 'thisown',
+- }
+- def __init__(self, fragile):
+- for key, func in self._type.items():
+- if hasattr(fragile, key):
+- setattr(self, key, func(getattr(fragile, key)))
+-
+- for key, func in self._map.items():
+- if hasattr(fragile, key):
+- setattr(self, key, list(map(func, getattr(fragile, key))))
+-
+- for key in dir(fragile):
+- if key.startswith('_') or key in self._blacklist:
+- continue
+- if hasattr(self, key):
+- continue
+-
+- setattr(self, key, getattr(fragile, key))
+-
+- def __repr__(self):
+- return '{}({})'.format(
+- self.__class__.__name__,
+- ', '.join('{}={!r}'.format(k, getattr(self, k))
+- for k in dir(self) if not k.startswith('_')))
+-
+-class InvalidKey(Result):
+- pass
+-
+-class EncryptResult(Result):
+- _map = dict(invalid_recipients=InvalidKey)
+-
+-class Recipient(Result):
+- pass
+-
+-class DecryptResult(Result):
+- _type = dict(wrong_key_usage=bool)
+- _map = dict(recipients=Recipient)
+-
+-class NewSignature(Result):
+- pass
+-
+-class SignResult(Result):
+- _map = dict(invalid_signers=InvalidKey, signatures=NewSignature)
+-
+-class Notation(Result):
+- pass
+-
+-class Signature(Result):
+- _type = dict(wrong_key_usage=bool, chain_model=bool)
+- _map = dict(notations=Notation)
+-
+-class VerifyResult(Result):
+- _map = dict(signatures=Signature)
+-
+-class ImportStatus(Result):
+- pass
+-
+-class ImportResult(Result):
+- _map = dict(imports=ImportStatus)
+-
+-class GenkeyResult(Result):
+- _type = dict(primary=bool, sub=bool)
+-
+-class KeylistResult(Result):
+- _type = dict(truncated=bool)
+-
+-class VFSMountResult(Result):
+- pass
+-
+-class EngineInfo(Result):
+- pass
+diff --git a/lang/python/gpg/util.py b/lang/python/gpg/util.py
+deleted file mode 100644
+index e4fca4c..0000000
+--- a/lang/python/gpg/util.py
++++ /dev/null
+@@ -1,53 +0,0 @@
+-# Copyright (C) 2016 g10 Code GmbH
+-# Copyright (C) 2004,2008 Igor Belyi <belyi at users.sourceforge.net>
+-# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
+-#
+-# This library 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.
+-#
+-# This library 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 library; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+-
+-from __future__ import absolute_import, print_function, unicode_literals
+-del absolute_import, print_function, unicode_literals
+-
+-import sys
+-
+-def process_constants(prefix, scope):
+- """Called by the constant modules to load up the constants from the C
+- library starting with PREFIX. Matching constants will be inserted
+- into SCOPE with PREFIX stripped from the names. Returns the names
+- of inserted constants.
+-
+- """
+- from . import gpgme
+- index = len(prefix)
+- constants = {identifier[index:]: getattr(gpgme, identifier)
+- for identifier in dir(gpgme)
+- if identifier.startswith(prefix)}
+- scope.update(constants)
+- return list(constants.keys())
+-
+-def percent_escape(s):
+- return ''.join(
+- '%{0:2x}'.format(ord(c))
+- if c == '+' or c == '"' or c == '%' or ord(c) <= 0x20 else c
+- for c in s)
+-
+-# Python2/3 compatibility
+-if sys.version_info[0] == 3:
+- # Python3
+- def is_a_string(x):
+- return isinstance(x, str)
+-else:
+- # Python2
+- def is_a_string(x):
+- return isinstance(x, basestring)
+diff --git a/lang/python/gpg/version.py.in b/lang/python/gpg/version.py.in
+deleted file mode 100644
+index 1a1baf0..0000000
+--- a/lang/python/gpg/version.py.in
++++ /dev/null
+@@ -1,68 +0,0 @@
+-# Copyright (C) 2016 g10 Code GmbH
+-# Copyright (C) 2015 Ben McGinnes <ben at adversary.org>
+-# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
+-#
+-# This library 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.
+-#
+-# This library 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 library; if not, write to the Free Software
+-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+-
+-from __future__ import absolute_import, print_function
+-del absolute_import, print_function
+-
+-from . import gpgme
+-
+-productname = 'gpg'
+-versionstr = "@VERSION@"
+-gpgme_versionstr = gpgme.GPGME_VERSION
+-in_tree_build = bool(gpgme.cvar.gpg_in_tree_build)
+-
+-versionlist = versionstr.split(".")
+-major = versionlist[0]
+-minor = versionlist[1]
+-patch = versionlist[2]
+-
+-copyright = """\
+-Copyright (C) 2016 g10 Code GmbH
+-Copyright (C) 2015 Ben McGinnes
+-Copyright (C) 2014-2015 Martin Albrecht
+-Copyright (C) 2004-2008 Igor Belyi
+-Copyright (C) 2002 John Goerzen"""
+-
+-author = "The GnuPG hackers"
+-author_email = "gnupg-devel at gnupg.org"
+-
+-description = "Python support for GPGME GnuPG cryptography library"
+-homepage = "https://gnupg.org"
+-
+-license = """Copyright (C) 2016 g10 Code GmbH
+-Copyright (C) 2015 Ben McGinnes <ben at adversary.org>
+-Copyright (C) 2014, 2015 Martin Albrecht <martinralbrecht at googlemail.com>
+-Copyright (C) 2004, 2008 Igor Belyi <belyi at users.sourceforge.net>
+-Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
+-
+-This library 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.
+-
+-This library 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 library; if not, write to the Free Software
+-Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"""
+-
+-# Interface hygiene. Keep this at the end.
+-del gpgme
+diff --git a/lang/python/setup.py.in b/lang/python/setup.py.in
+index 2114aaf..5d94c70 100755
+--- a/lang/python/setup.py.in
++++ b/lang/python/setup.py.in
+@@ -38,18 +38,17 @@ gpgme_config = ["gpgme-config"] + gpgme_config_flags
+ gpgme_h = ""
+ include_dirs = [os.getcwd()]
+ library_dirs = []
+-vpath_build = os.environ.get('srcdir', '.') != '.'
+ in_tree = False
+ extra_swig_opts = []
+ extra_macros = dict()
+
+-abs_top_builddir = os.environ.get("abs_top_builddir")
+-if abs_top_builddir:
++top_builddir = os.environ.get("top_builddir")
++if top_builddir:
+ # In-tree build.
+ in_tree = True
+- gpgme_config = [os.path.join(abs_top_builddir, "src/gpgme-config")] + gpgme_config_flags
+- gpgme_h = os.path.join(abs_top_builddir, "src/gpgme.h")
+- library_dirs = [os.path.join(abs_top_builddir, "src/.libs")] # XXX uses libtool internals
++ gpgme_config = [os.path.join(top_builddir, "src/gpgme-config")] + gpgme_config_flags
++ gpgme_h = os.path.join(top_builddir, "src/gpgme.h")
++ library_dirs = [os.path.join(top_builddir, "src/.libs")] # XXX uses libtool internals
+ extra_macros.update(
+ HAVE_CONFIG_H=1,
+ HAVE_DATA_H=1,
+@@ -210,23 +209,21 @@ class BuildExtFirstHack(build):
+ # Copy due to http://bugs.python.org/issue2624
+ # Avoid creating in srcdir
+ for source, target in ((in_srcdir(n), in_build_base(n))
+- for n in ('gpgme.i', 'helpers.c')):
++ for n in ('gpgme.i', 'helpers.c', 'private.h', 'helpers.h')):
+ if not up_to_date(source, target):
+ shutil.copy2(source, target)
+
+- def run(self):
+- self._generate()
+-
+ # Append generated files via build_base
+ if not os.path.exists(os.path.join(self.build_lib, "gpg")):
+ os.makedirs(os.path.join(self.build_lib, "gpg"))
++ shutil.copy2("version.py", os.path.join(self.build_lib, "gpg"))
++
++ def run(self):
++ self._generate()
+
+ swig_sources.append(os.path.join(self.build_base, 'gpgme.i'))
+ swig_opts.extend(['-I' + self.build_base,
+- '-I' + in_srcdir('.'),
+ '-outdir', os.path.join(self.build_lib, 'gpg')])
+- if vpath_build:
+- include_dirs.append(in_srcdir('.'))
+ include_dirs.append(self.build_base)
+
+ self.run_command('build_ext')
+diff --git a/lang/python/src/__init__.py b/lang/python/src/__init__.py
+new file mode 100644
+index 0000000..385b17e
+--- /dev/null
++++ b/lang/python/src/__init__.py
+@@ -0,0 +1,121 @@
++# Copyright (C) 2016 g10 Code GmbH
++# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
++# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
++#
++# This library 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.
++#
++# This library 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 library; if not, write to the Free Software
++# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++
++"""gpg: GnuPG Interface for Python (GPGME bindings)
++
++Welcome to gpg, the GnuPG Interface for Python.
++
++The latest release of this package may be obtained from
++https://www.gnupg.org
++
++FEATURES
++--------
++
++ * Feature-rich, full implementation of the GPGME library. Supports
++ all GPGME features. Callback functions may be written in pure
++ Python. Exceptions raised in callbacks are properly propagated.
++
++ * Ability to sign, encrypt, decrypt, and verify data.
++
++ * Ability to list keys, export and import keys, and manage the keyring.
++
++ * Fully object-oriented with convenient classes and modules.
++
++QUICK EXAMPLE
++-------------
++
++ >>> import gpg
++ >>> with gpg.Context() as c:
++ >>> with gpg.Context() as c:
++ ... cipher, _, _ = c.encrypt("Hello world :)".encode(),
++ ... passphrase="abc")
++ ... c.decrypt(cipher, passphrase="abc")
++ ...
++ (b'Hello world :)',
++ <gpg.results.DecryptResult object at 0x7f5ab8121080>,
++ <gpg.results.VerifyResult object at 0x7f5ab81219b0>)
++
++GENERAL OVERVIEW
++----------------
++
++For those of you familiar with GPGME, you will be right at home here.
++
++The python gpg module is, for the most part, a direct interface to the C GPGME
++library. However, it is re-packaged in a more Pythonic way --
++object-oriented with classes and modules. Take a look at the classes
++defined here -- they correspond directly to certain object types in GPGME
++for C. For instance, the following C code:
++
++gpgme_ctx_t context;
++gpgme_new(&context);
++...
++gpgme_op_encrypt(context, recp, 1, plain, cipher);
++
++Translates into the following Python code:
++
++context = core.Context()
++...
++context.op_encrypt(recp, 1, plain, cipher)
++
++The Python module automatically does error-checking and raises Python
++exception gpg.errors.GPGMEError when GPGME signals an error. getcode()
++and getsource() of this exception return code and source of the error.
++
++IMPORTANT NOTE
++--------------
++This documentation only covers a small subset of available GPGME functions and
++methods. Please consult the documentation for the C library
++for comprehensive coverage.
++
++This library uses Python's reflection to automatically detect the methods
++that are available for each class, and as such, most of those methods
++do not appear explicitly anywhere. You can use dir() python built-in command
++on an object to see what methods and fields it has but their meaning can
++be found only in GPGME documentation.
++
++FOR MORE INFORMATION
++--------------------
++GnuPG homepage: https://www.gnupg.org/
++GPGME documentation: https://www.gnupg.org/documentation/manuals/gpgme/
++
++"""
++
++from __future__ import absolute_import, print_function, unicode_literals
++del absolute_import, print_function, unicode_literals
++
++from . import core
++from . import errors
++from . import constants
++from . import util
++from . import callbacks
++from . import version
++from .core import Context
++from .core import Data
++
++# Interface hygiene.
++
++# Drop the low-level gpgme that creeps in for some reason.
++gpgme = None
++del gpgme
++
++# This is a white-list of symbols. Any other will alert pyflakes.
++_ = [Context, Data, core, errors, constants, util, callbacks, version]
++del _
++
++__all__ = ["Context", "Data",
++ "core", "errors", "constants", "util", "callbacks", "version"]
+diff --git a/lang/python/src/callbacks.py b/lang/python/src/callbacks.py
+new file mode 100644
+index 0000000..b25a9a7
+--- /dev/null
++++ b/lang/python/src/callbacks.py
+@@ -0,0 +1,49 @@
++# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
++# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
++#
++# This library 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.
++#
++# This library 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 library; if not, write to the Free Software
++# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++
++from __future__ import absolute_import, print_function, unicode_literals
++del absolute_import, print_function, unicode_literals
++
++from getpass import getpass
++
++def passphrase_stdin(hint, desc, prev_bad, hook=None):
++ """This is a sample callback that will read a passphrase from
++ the terminal. The hook here, if present, will be used to describe
++ why the passphrase is needed."""
++ why = ''
++ if hook != None:
++ why = ' ' + hook
++ if prev_bad:
++ why += ' (again)'
++ print("Please supply %s' password%s:" % (hint, why))
++ return getpass()
++
++def progress_stdout(what, type, current, total, hook=None):
++ print("PROGRESS UPDATE: what = %s, type = %d, current = %d, total = %d" %\
++ (what, type, current, total))
++
++def readcb_fh(count, hook):
++ """A callback for data. hook should be a Python file-like object."""
++ if count:
++ # Should return '' on EOF
++ return hook.read(count)
++ else:
++ # Wants to rewind.
++ if not hasattr(hook, 'seek'):
++ return None
++ hook.seek(0, 0)
++ return None
+diff --git a/lang/python/src/constants/__init__.py b/lang/python/src/constants/__init__.py
+new file mode 100644
+index 0000000..484ffd2
+--- /dev/null
++++ b/lang/python/src/constants/__init__.py
+@@ -0,0 +1,142 @@
++# Constants.
++#
++# Copyright (C) 2016 g10 Code 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 <http://www.gnu.org/licenses/>.
++
++from __future__ import absolute_import, print_function, unicode_literals
++del absolute_import, print_function, unicode_literals
++
++from gpg import util
++util.process_constants('GPGME_', globals())
++del util
++
++# For convenience, we import the modules here.
++from . import data, keylist, sig, tofu # The subdirs.
++from . import create, event, keysign, md, pk, protocol, sigsum, status, validity
++
++# A complication arises because 'import' is a reserved keyword.
++# Import it as 'Import' instead.
++globals()['Import'] = getattr(__import__('', globals(), locals(),
++ [str('import')], 1), "import")
++
++__all__ = ['data', 'event', 'import', 'keysign', 'keylist', 'md', 'pk',
++ 'protocol', 'sig', 'sigsum', 'status', 'tofu', 'validity', 'create']
++
++# GPGME 1.7 replaced gpgme_op_edit with gpgme_op_interact. We
++# implement gpg.Context.op_edit using gpgme_op_interact, so the
++# callbacks will be called with string keywords instead of numeric
++# status messages. Code that is using these constants will continue
++# to work.
++
++STATUS_ABORT = "ABORT"
++STATUS_ALREADY_SIGNED = "ALREADY_SIGNED"
++STATUS_ATTRIBUTE = "ATTRIBUTE"
++STATUS_BACKUP_KEY_CREATED = "BACKUP_KEY_CREATED"
++STATUS_BAD_PASSPHRASE = "BAD_PASSPHRASE"
++STATUS_BADARMOR = "BADARMOR"
++STATUS_BADMDC = "BADMDC"
++STATUS_BADSIG = "BADSIG"
++STATUS_BEGIN_DECRYPTION = "BEGIN_DECRYPTION"
++STATUS_BEGIN_ENCRYPTION = "BEGIN_ENCRYPTION"
++STATUS_BEGIN_SIGNING = "BEGIN_SIGNING"
++STATUS_BEGIN_STREAM = "BEGIN_STREAM"
++STATUS_CARDCTRL = "CARDCTRL"
++STATUS_DECRYPTION_FAILED = "DECRYPTION_FAILED"
++STATUS_DECRYPTION_INFO = "DECRYPTION_INFO"
++STATUS_DECRYPTION_OKAY = "DECRYPTION_OKAY"
++STATUS_DELETE_PROBLEM = "DELETE_PROBLEM"
++STATUS_ENC_TO = "ENC_TO"
++STATUS_END_DECRYPTION = "END_DECRYPTION"
++STATUS_END_ENCRYPTION = "END_ENCRYPTION"
++STATUS_END_STREAM = "END_STREAM"
++STATUS_ENTER = "ENTER"
++STATUS_ERRMDC = "ERRMDC"
++STATUS_ERROR = "ERROR"
++STATUS_ERRSIG = "ERRSIG"
++STATUS_EXPKEYSIG = "EXPKEYSIG"
++STATUS_EXPSIG = "EXPSIG"
++STATUS_FAILURE = "FAILURE"
++STATUS_FILE_DONE = "FILE_DONE"
++STATUS_FILE_ERROR = "FILE_ERROR"
++STATUS_FILE_START = "FILE_START"
++STATUS_GET_BOOL = "GET_BOOL"
++STATUS_GET_HIDDEN = "GET_HIDDEN"
++STATUS_GET_LINE = "GET_LINE"
++STATUS_GOOD_PASSPHRASE = "GOOD_PASSPHRASE"
++STATUS_GOODMDC = "GOODMDC"
++STATUS_GOODSIG = "GOODSIG"
++STATUS_GOT_IT = "GOT_IT"
++STATUS_IMPORT_OK = "IMPORT_OK"
++STATUS_IMPORT_PROBLEM = "IMPORT_PROBLEM"
++STATUS_IMPORT_RES = "IMPORT_RES"
++STATUS_IMPORTED = "IMPORTED"
++STATUS_INQUIRE_MAXLEN = "INQUIRE_MAXLEN"
++STATUS_INV_RECP = "INV_RECP"
++STATUS_INV_SGNR = "INV_SGNR"
++STATUS_KEY_CONSIDERED = "KEY_CONSIDERED"
++STATUS_KEY_CREATED = "KEY_CREATED"
++STATUS_KEY_NOT_CREATED = "KEY_NOT_CREATED"
++STATUS_KEYEXPIRED = "KEYEXPIRED"
++STATUS_KEYREVOKED = "KEYREVOKED"
++STATUS_LEAVE = "LEAVE"
++STATUS_MISSING_PASSPHRASE = "MISSING_PASSPHRASE"
++STATUS_MOUNTPOINT = "MOUNTPOINT"
++STATUS_NEED_PASSPHRASE = "NEED_PASSPHRASE"
++STATUS_NEED_PASSPHRASE_PIN = "NEED_PASSPHRASE_PIN"
++STATUS_NEED_PASSPHRASE_SYM = "NEED_PASSPHRASE_SYM"
++STATUS_NEWSIG = "NEWSIG"
++STATUS_NO_PUBKEY = "NO_PUBKEY"
++STATUS_NO_RECP = "NO_RECP"
++STATUS_NO_SECKEY = "NO_SECKEY"
++STATUS_NO_SGNR = "NO_SGNR"
++STATUS_NODATA = "NODATA"
++STATUS_NOTATION_DATA = "NOTATION_DATA"
++STATUS_NOTATION_FLAGS = "NOTATION_FLAGS"
++STATUS_NOTATION_NAME = "NOTATION_NAME"
++STATUS_PINENTRY_LAUNCHED = "PINENTRY_LAUNCHED"
++STATUS_PKA_TRUST_BAD = "PKA_TRUST_BAD"
++STATUS_PKA_TRUST_GOOD = "PKA_TRUST_GOOD"
++STATUS_PLAINTEXT = "PLAINTEXT"
++STATUS_PLAINTEXT_LENGTH = "PLAINTEXT_LENGTH"
++STATUS_POLICY_URL = "POLICY_URL"
++STATUS_PROGRESS = "PROGRESS"
++STATUS_REVKEYSIG = "REVKEYSIG"
++STATUS_RSA_OR_IDEA = "RSA_OR_IDEA"
++STATUS_SC_OP_FAILURE = "SC_OP_FAILURE"
++STATUS_SC_OP_SUCCESS = "SC_OP_SUCCESS"
++STATUS_SESSION_KEY = "SESSION_KEY"
++STATUS_SHM_GET = "SHM_GET"
++STATUS_SHM_GET_BOOL = "SHM_GET_BOOL"
++STATUS_SHM_GET_HIDDEN = "SHM_GET_HIDDEN"
++STATUS_SHM_INFO = "SHM_INFO"
++STATUS_SIG_CREATED = "SIG_CREATED"
++STATUS_SIG_ID = "SIG_ID"
++STATUS_SIG_SUBPACKET = "SIG_SUBPACKET"
++STATUS_SIGEXPIRED = "SIGEXPIRED"
++STATUS_SUCCESS = "SUCCESS"
++STATUS_TOFU_STATS = "TOFU_STATS"
++STATUS_TOFU_STATS_LONG = "TOFU_STATS_LONG"
++STATUS_TOFU_USER = "TOFU_USER"
++STATUS_TRUNCATED = "TRUNCATED"
++STATUS_TRUST_FULLY = "TRUST_FULLY"
++STATUS_TRUST_MARGINAL = "TRUST_MARGINAL"
++STATUS_TRUST_NEVER = "TRUST_NEVER"
++STATUS_TRUST_ULTIMATE = "TRUST_ULTIMATE"
++STATUS_TRUST_UNDEFINED = "TRUST_UNDEFINED"
++STATUS_UNEXPECTED = "UNEXPECTED"
++STATUS_USERID_HINT = "USERID_HINT"
++STATUS_VALIDSIG = "VALIDSIG"
+diff --git a/lang/python/src/constants/create.py b/lang/python/src/constants/create.py
+new file mode 100644
+index 0000000..132e96d
+--- /dev/null
++++ b/lang/python/src/constants/create.py
+@@ -0,0 +1,25 @@
++# Flags for key creation
++#
++# Copyright (C) 2017 g10 Code 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 <http://www.gnu.org/licenses/>.
++
++from __future__ import absolute_import, print_function, unicode_literals
++del absolute_import, print_function, unicode_literals
++
++from gpg import util
++util.process_constants('GPGME_CREATE_', globals())
++del util
+diff --git a/lang/python/src/constants/data/__init__.py b/lang/python/src/constants/data/__init__.py
+new file mode 100644
+index 0000000..8274ab9
+--- /dev/null
++++ b/lang/python/src/constants/data/__init__.py
+@@ -0,0 +1,6 @@
++
++from __future__ import absolute_import, print_function, unicode_literals
++del absolute_import, print_function, unicode_literals
++
++from . import encoding
++__all__ = ['encoding']
+diff --git a/lang/python/src/constants/data/encoding.py b/lang/python/src/constants/data/encoding.py
+new file mode 100644
+index 0000000..e76a22e
+--- /dev/null
++++ b/lang/python/src/constants/data/encoding.py
+@@ -0,0 +1,23 @@
++# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
++# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
++#
++# This library 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.
++#
++# This library 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 library; if not, write to the Free Software
++# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++
++from __future__ import absolute_import, print_function, unicode_literals
++del absolute_import, print_function, unicode_literals
++
++from gpg import util
++util.process_constants('GPGME_DATA_ENCODING_', globals())
++del util
+diff --git a/lang/python/src/constants/event.py b/lang/python/src/constants/event.py
+new file mode 100644
+index 0000000..1b14d1d
+--- /dev/null
++++ b/lang/python/src/constants/event.py
+@@ -0,0 +1,23 @@
++# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
++# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
++#
++# This library 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.
++#
++# This library 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 library; if not, write to the Free Software
++# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++
++from __future__ import absolute_import, print_function, unicode_literals
++del absolute_import, print_function, unicode_literals
++
++from gpg import util
++util.process_constants('GPGME_EVENT_', globals())
++del util
+diff --git a/lang/python/src/constants/import.py b/lang/python/src/constants/import.py
+new file mode 100644
+index 0000000..47c296c
+--- /dev/null
++++ b/lang/python/src/constants/import.py
+@@ -0,0 +1,23 @@
++# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
++# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
++#
++# This library 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.
++#
++# This library 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 library; if not, write to the Free Software
++# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++
++from __future__ import absolute_import, print_function, unicode_literals
++del absolute_import, print_function, unicode_literals
++
++from gpg import util
++util.process_constants('GPGME_IMPORT_', globals())
++del util
+diff --git a/lang/python/src/constants/keylist/__init__.py b/lang/python/src/constants/keylist/__init__.py
+new file mode 100644
+index 0000000..2ce0edf
+--- /dev/null
++++ b/lang/python/src/constants/keylist/__init__.py
+@@ -0,0 +1,6 @@
++
++from __future__ import absolute_import, print_function, unicode_literals
++del absolute_import, print_function, unicode_literals
++
++from . import mode
++__all__ = ['mode']
+diff --git a/lang/python/src/constants/keylist/mode.py b/lang/python/src/constants/keylist/mode.py
+new file mode 100644
+index 0000000..39e1819
+--- /dev/null
++++ b/lang/python/src/constants/keylist/mode.py
+@@ -0,0 +1,23 @@
++# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
++# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
++#
++# This library 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.
++#
++# This library 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 library; if not, write to the Free Software
++# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++
++from __future__ import absolute_import, print_function, unicode_literals
++del absolute_import, print_function, unicode_literals
++
++from gpg import util
++util.process_constants('GPGME_KEYLIST_MODE_', globals())
++del util
+diff --git a/lang/python/src/constants/keysign.py b/lang/python/src/constants/keysign.py
+new file mode 100644
+index 0000000..fccdbc4
+--- /dev/null
++++ b/lang/python/src/constants/keysign.py
+@@ -0,0 +1,25 @@
++# Flags for key signing
++#
++# Copyright (C) 2017 g10 Code 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 <http://www.gnu.org/licenses/>.
++
++from __future__ import absolute_import, print_function, unicode_literals
++del absolute_import, print_function, unicode_literals
++
++from gpg import util
++util.process_constants('GPGME_KEYSIGN_', globals())
++del util
+diff --git a/lang/python/src/constants/md.py b/lang/python/src/constants/md.py
+new file mode 100644
+index 0000000..f3e8bbd
+--- /dev/null
++++ b/lang/python/src/constants/md.py
+@@ -0,0 +1,23 @@
++# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
++# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
++#
++# This library 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.
++#
++# This library 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 library; if not, write to the Free Software
++# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++
++from __future__ import absolute_import, print_function, unicode_literals
++del absolute_import, print_function, unicode_literals
++
++from gpg import util
++util.process_constants('GPGME_MD_', globals())
++del util
+diff --git a/lang/python/src/constants/pk.py b/lang/python/src/constants/pk.py
+new file mode 100644
+index 0000000..6bf2a21
+--- /dev/null
++++ b/lang/python/src/constants/pk.py
+@@ -0,0 +1,23 @@
++# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
++# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
++#
++# This library 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.
++#
++# This library 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 library; if not, write to the Free Software
++# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++
++from __future__ import absolute_import, print_function, unicode_literals
++del absolute_import, print_function, unicode_literals
++
++from gpg import util
++util.process_constants('GPGME_PK_', globals())
++del util
+diff --git a/lang/python/src/constants/protocol.py b/lang/python/src/constants/protocol.py
+new file mode 100644
+index 0000000..d086bbd
+--- /dev/null
++++ b/lang/python/src/constants/protocol.py
+@@ -0,0 +1,23 @@
++# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
++# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
++#
++# This library 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.
++#
++# This library 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 library; if not, write to the Free Software
++# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++
++from __future__ import absolute_import, print_function, unicode_literals
++del absolute_import, print_function, unicode_literals
++
++from gpg import util
++util.process_constants('GPGME_PROTOCOL_', globals())
++del util
+diff --git a/lang/python/src/constants/sig/__init__.py b/lang/python/src/constants/sig/__init__.py
+new file mode 100644
+index 0000000..39d4e6e
+--- /dev/null
++++ b/lang/python/src/constants/sig/__init__.py
+@@ -0,0 +1,6 @@
++
++from __future__ import absolute_import, print_function, unicode_literals
++del absolute_import, print_function, unicode_literals
++
++from . import mode, notation
++__all__ = ['mode', 'notation']
+diff --git a/lang/python/src/constants/sig/mode.py b/lang/python/src/constants/sig/mode.py
+new file mode 100644
+index 0000000..0f4f0ef
+--- /dev/null
++++ b/lang/python/src/constants/sig/mode.py
+@@ -0,0 +1,23 @@
++# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
++# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
++#
++# This library 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.
++#
++# This library 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 library; if not, write to the Free Software
++# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++
++from __future__ import absolute_import, print_function, unicode_literals
++del absolute_import, print_function, unicode_literals
++
++from gpg import util
++util.process_constants('GPGME_SIG_MODE_', globals())
++del util
+diff --git a/lang/python/src/constants/sig/notation.py b/lang/python/src/constants/sig/notation.py
+new file mode 100644
+index 0000000..9a79e01
+--- /dev/null
++++ b/lang/python/src/constants/sig/notation.py
+@@ -0,0 +1,25 @@
++# Constants for signature notation data.
++#
++# Copyright (C) 2016 g10 Code 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 <http://www.gnu.org/licenses/>.
++
++from __future__ import absolute_import, print_function, unicode_literals
++del absolute_import, print_function, unicode_literals
++
++from gpg import util
++util.process_constants('GPGME_SIG_NOTATION_', globals())
++del util
+diff --git a/lang/python/src/constants/sigsum.py b/lang/python/src/constants/sigsum.py
+new file mode 100644
+index 0000000..09ef9d7
+--- /dev/null
++++ b/lang/python/src/constants/sigsum.py
+@@ -0,0 +1,23 @@
++# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
++# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
++#
++# This library 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.
++#
++# This library 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 library; if not, write to the Free Software
++# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++
++from __future__ import absolute_import, print_function, unicode_literals
++del absolute_import, print_function, unicode_literals
++
++from gpg import util
++util.process_constants('GPGME_SIGSUM_', globals())
++del util
+diff --git a/lang/python/src/constants/status.py b/lang/python/src/constants/status.py
+new file mode 100644
+index 0000000..a0ad073
+--- /dev/null
++++ b/lang/python/src/constants/status.py
+@@ -0,0 +1,124 @@
++# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
++# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
++#
++# This library 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.
++#
++# This library 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 library; if not, write to the Free Software
++# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++
++from __future__ import absolute_import, print_function, unicode_literals
++del absolute_import, print_function, unicode_literals
++
++# GPGME 1.7 replaced gpgme_op_edit with gpgme_op_interact. We
++# implement gpg.Context.op_edit using gpgme_op_interact, so the
++# callbacks will be called with string keywords instead of numeric
++# status messages. Code that is using these constants will continue
++# to work.
++
++ABORT = "ABORT"
++ALREADY_SIGNED = "ALREADY_SIGNED"
++ATTRIBUTE = "ATTRIBUTE"
++BACKUP_KEY_CREATED = "BACKUP_KEY_CREATED"
++BAD_PASSPHRASE = "BAD_PASSPHRASE"
++BADARMOR = "BADARMOR"
++BADMDC = "BADMDC"
++BADSIG = "BADSIG"
++BEGIN_DECRYPTION = "BEGIN_DECRYPTION"
++BEGIN_ENCRYPTION = "BEGIN_ENCRYPTION"
++BEGIN_SIGNING = "BEGIN_SIGNING"
++BEGIN_STREAM = "BEGIN_STREAM"
++CARDCTRL = "CARDCTRL"
++DECRYPTION_FAILED = "DECRYPTION_FAILED"
++DECRYPTION_INFO = "DECRYPTION_INFO"
++DECRYPTION_OKAY = "DECRYPTION_OKAY"
++DELETE_PROBLEM = "DELETE_PROBLEM"
++ENC_TO = "ENC_TO"
++END_DECRYPTION = "END_DECRYPTION"
++END_ENCRYPTION = "END_ENCRYPTION"
++END_STREAM = "END_STREAM"
++ENTER = "ENTER"
++ERRMDC = "ERRMDC"
++ERROR = "ERROR"
++ERRSIG = "ERRSIG"
++EXPKEYSIG = "EXPKEYSIG"
++EXPSIG = "EXPSIG"
++FAILURE = "FAILURE"
++FILE_DONE = "FILE_DONE"
++FILE_ERROR = "FILE_ERROR"
++FILE_START = "FILE_START"
++GET_BOOL = "GET_BOOL"
++GET_HIDDEN = "GET_HIDDEN"
++GET_LINE = "GET_LINE"
++GOOD_PASSPHRASE = "GOOD_PASSPHRASE"
++GOODMDC = "GOODMDC"
++GOODSIG = "GOODSIG"
++GOT_IT = "GOT_IT"
++IMPORT_OK = "IMPORT_OK"
++IMPORT_PROBLEM = "IMPORT_PROBLEM"
++IMPORT_RES = "IMPORT_RES"
++IMPORTED = "IMPORTED"
++INQUIRE_MAXLEN = "INQUIRE_MAXLEN"
++INV_RECP = "INV_RECP"
++INV_SGNR = "INV_SGNR"
++KEY_CONSIDERED = "KEY_CONSIDERED"
++KEY_CREATED = "KEY_CREATED"
++KEY_NOT_CREATED = "KEY_NOT_CREATED"
++KEYEXPIRED = "KEYEXPIRED"
++KEYREVOKED = "KEYREVOKED"
++LEAVE = "LEAVE"
++MISSING_PASSPHRASE = "MISSING_PASSPHRASE"
++MOUNTPOINT = "MOUNTPOINT"
++NEED_PASSPHRASE = "NEED_PASSPHRASE"
++NEED_PASSPHRASE_PIN = "NEED_PASSPHRASE_PIN"
++NEED_PASSPHRASE_SYM = "NEED_PASSPHRASE_SYM"
++NEWSIG = "NEWSIG"
++NO_PUBKEY = "NO_PUBKEY"
++NO_RECP = "NO_RECP"
++NO_SECKEY = "NO_SECKEY"
++NO_SGNR = "NO_SGNR"
++NODATA = "NODATA"
++NOTATION_DATA = "NOTATION_DATA"
++NOTATION_FLAGS = "NOTATION_FLAGS"
++NOTATION_NAME = "NOTATION_NAME"
++PINENTRY_LAUNCHED = "PINENTRY_LAUNCHED"
++PKA_TRUST_BAD = "PKA_TRUST_BAD"
++PKA_TRUST_GOOD = "PKA_TRUST_GOOD"
++PLAINTEXT = "PLAINTEXT"
++PLAINTEXT_LENGTH = "PLAINTEXT_LENGTH"
++POLICY_URL = "POLICY_URL"
++PROGRESS = "PROGRESS"
++REVKEYSIG = "REVKEYSIG"
++RSA_OR_IDEA = "RSA_OR_IDEA"
++SC_OP_FAILURE = "SC_OP_FAILURE"
++SC_OP_SUCCESS = "SC_OP_SUCCESS"
++SESSION_KEY = "SESSION_KEY"
++SHM_GET = "SHM_GET"
++SHM_GET_BOOL = "SHM_GET_BOOL"
++SHM_GET_HIDDEN = "SHM_GET_HIDDEN"
++SHM_INFO = "SHM_INFO"
++SIG_CREATED = "SIG_CREATED"
++SIG_ID = "SIG_ID"
++SIG_SUBPACKET = "SIG_SUBPACKET"
++SIGEXPIRED = "SIGEXPIRED"
++SUCCESS = "SUCCESS"
++TOFU_STATS = "TOFU_STATS"
++TOFU_STATS_LONG = "TOFU_STATS_LONG"
++TOFU_USER = "TOFU_USER"
++TRUNCATED = "TRUNCATED"
++TRUST_FULLY = "TRUST_FULLY"
++TRUST_MARGINAL = "TRUST_MARGINAL"
++TRUST_NEVER = "TRUST_NEVER"
++TRUST_ULTIMATE = "TRUST_ULTIMATE"
++TRUST_UNDEFINED = "TRUST_UNDEFINED"
++UNEXPECTED = "UNEXPECTED"
++USERID_HINT = "USERID_HINT"
++VALIDSIG = "VALIDSIG"
+diff --git a/lang/python/src/constants/tofu/__init__.py b/lang/python/src/constants/tofu/__init__.py
+new file mode 100644
+index 0000000..819a58b
+--- /dev/null
++++ b/lang/python/src/constants/tofu/__init__.py
+@@ -0,0 +1,24 @@
++# TOFU
++#
++# Copyright (C) 2017 g10 Code 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 <http://www.gnu.org/licenses/>.
++
++from __future__ import absolute_import, print_function, unicode_literals
++del absolute_import, print_function, unicode_literals
++
++from . import policy
++__all__ = ['policy']
+diff --git a/lang/python/src/constants/tofu/policy.py b/lang/python/src/constants/tofu/policy.py
+new file mode 100644
+index 0000000..5a61f06
+--- /dev/null
++++ b/lang/python/src/constants/tofu/policy.py
+@@ -0,0 +1,25 @@
++# TOFU policies
++#
++# Copyright (C) 2017 g10 Code 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 <http://www.gnu.org/licenses/>.
++
++from __future__ import absolute_import, print_function, unicode_literals
++del absolute_import, print_function, unicode_literals
++
++from gpg import util
++util.process_constants('GPGME_TOFU_POLICY_', globals())
++del util
+diff --git a/lang/python/src/constants/validity.py b/lang/python/src/constants/validity.py
+new file mode 100644
+index 0000000..d3c5345
+--- /dev/null
++++ b/lang/python/src/constants/validity.py
+@@ -0,0 +1,23 @@
++# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
++# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
++#
++# This library 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.
++#
++# This library 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 library; if not, write to the Free Software
++# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++
++from __future__ import absolute_import, print_function, unicode_literals
++del absolute_import, print_function, unicode_literals
++
++from gpg import util
++util.process_constants('GPGME_VALIDITY_', globals())
++del util
+diff --git a/lang/python/src/core.py b/lang/python/src/core.py
+new file mode 100644
+index 0000000..632f4ca
+--- /dev/null
++++ b/lang/python/src/core.py
+@@ -0,0 +1,1490 @@
++# Copyright (C) 2016-2017 g10 Code GmbH
++# Copyright (C) 2004,2008 Igor Belyi <belyi at users.sourceforge.net>
++# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
++#
++# This library 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.
++#
++# This library 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 library; if not, write to the Free Software
++# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++
++"""Core functionality
++
++Core functionality of GPGME wrapped in a object-oriented fashion.
++Provides the 'Context' class for performing cryptographic operations,
++and the 'Data' class describing buffers of data.
++
++"""
++
++from __future__ import absolute_import, print_function, unicode_literals
++del absolute_import, print_function, unicode_literals
++
++import re
++import os
++import warnings
++import weakref
++from . import gpgme
++from .errors import errorcheck, GPGMEError
++from . import constants
++from . import errors
++from . import util
++
++class GpgmeWrapper(object):
++ """Base wrapper class
++
++ Not to be instantiated directly.
++
++ """
++
++ def __init__(self, wrapped):
++ self._callback_excinfo = None
++ self.wrapped = wrapped
++
++ def __repr__(self):
++ return '<{}/{!r}>'.format(super(GpgmeWrapper, self).__repr__(),
++ self.wrapped)
++
++ def __str__(self):
++ acc = ['{}.{}'.format(__name__, self.__class__.__name__)]
++ flags = [f for f in self._boolean_properties if getattr(self, f)]
++ if flags:
++ acc.append('({})'.format(' '.join(flags)))
++
++ return '<{}>'.format(' '.join(acc))
++
++ def __hash__(self):
++ return hash(repr(self.wrapped))
++
++ def __eq__(self, other):
++ if other == None:
++ return False
++ else:
++ return repr(self.wrapped) == repr(other.wrapped)
++
++ @property
++ def _ctype(self):
++ """The name of the c type wrapped by this class
++
++ Must be set by child classes.
++
++ """
++ raise NotImplementedError()
++
++ @property
++ def _cprefix(self):
++ """The common prefix of c functions wrapped by this class
++
++ Must be set by child classes.
++
++ """
++ raise NotImplementedError()
++
++ def _errorcheck(self, name):
++ """Must be implemented by child classes.
++
++ This function must return a trueish value for all c functions
++ returning gpgme_error_t."""
++ raise NotImplementedError()
++
++ """The set of all boolean properties"""
++ _boolean_properties = set()
++
++ def __wrap_boolean_property(self, key, do_set=False, value=None):
++ get_func = getattr(gpgme,
++ "{}get_{}".format(self._cprefix, key))
++ set_func = getattr(gpgme,
++ "{}set_{}".format(self._cprefix, key))
++ def get(slf):
++ return bool(get_func(slf.wrapped))
++ def set_(slf, value):
++ set_func(slf.wrapped, bool(value))
++
++ p = property(get, set_, doc="{} flag".format(key))
++ setattr(self.__class__, key, p)
++
++ if do_set:
++ set_(self, bool(value))
++ else:
++ return get(self)
++
++ _munge_docstring = re.compile(r'gpgme_([^(]*)\(([^,]*), (.*\) -> .*)')
++ def __getattr__(self, key):
++ """On-the-fly generation of wrapper methods and properties"""
++ if key[0] == '_' or self._cprefix == None:
++ return None
++
++ if key in self._boolean_properties:
++ return self.__wrap_boolean_property(key)
++
++ name = self._cprefix + key
++ func = getattr(gpgme, name)
++
++ if self._errorcheck(name):
++ def _funcwrap(slf, *args):
++ result = func(slf.wrapped, *args)
++ if slf._callback_excinfo:
++ gpgme.gpg_raise_callback_exception(slf)
++ return errorcheck(result, "Invocation of " + name)
++ else:
++ def _funcwrap(slf, *args):
++ result = func(slf.wrapped, *args)
++ if slf._callback_excinfo:
++ gpgme.gpg_raise_callback_exception(slf)
++ return result
++
++ doc = self._munge_docstring.sub(r'\2.\1(\3', getattr(func, "__doc__"))
++ _funcwrap.__doc__ = doc
++
++ # Monkey-patch the class.
++ setattr(self.__class__, key, _funcwrap)
++
++ # Bind the method to 'self'.
++ def wrapper(*args):
++ return _funcwrap(self, *args)
++ wrapper.__doc__ = doc
++
++ return wrapper
++
++ def __setattr__(self, key, value):
++ """On-the-fly generation of properties"""
++ if key in self._boolean_properties:
++ self.__wrap_boolean_property(key, True, value)
++ else:
++ super(GpgmeWrapper, self).__setattr__(key, value)
++
++class Context(GpgmeWrapper):
++ """Context for cryptographic operations
++
++ All cryptographic operations in GPGME are performed within a
++ context, which contains the internal state of the operation as
++ well as configuration parameters. By using several contexts you
++ can run several cryptographic operations in parallel, with
++ different configuration.
++
++ Access to a context must be synchronized.
++
++ """
++
++ def __init__(self, armor=False, textmode=False, offline=False,
++ signers=[], pinentry_mode=constants.PINENTRY_MODE_DEFAULT,
++ protocol=constants.PROTOCOL_OpenPGP,
++ wrapped=None, home_dir=None):
++ """Construct a context object
++
++ Keyword arguments:
++ armor -- enable ASCII armoring (default False)
++ textmode -- enable canonical text mode (default False)
++ offline -- do not contact external key sources (default False)
++ signers -- list of keys used for signing (default [])
++ pinentry_mode -- pinentry mode (default PINENTRY_MODE_DEFAULT)
++ protocol -- protocol to use (default PROTOCOL_OpenPGP)
++ home_dir -- state directory (default is the engine default)
++
++ """
++ if wrapped:
++ self.own = False
++ else:
++ tmp = gpgme.new_gpgme_ctx_t_p()
++ errorcheck(gpgme.gpgme_new(tmp))
++ wrapped = gpgme.gpgme_ctx_t_p_value(tmp)
++ gpgme.delete_gpgme_ctx_t_p(tmp)
++ self.own = True
++ super(Context, self).__init__(wrapped)
++ self.armor = armor
++ self.textmode = textmode
++ self.offline = offline
++ self.signers = signers
++ self.pinentry_mode = pinentry_mode
++ self.protocol = protocol
++ self.home_dir = home_dir
++
++ def __repr__(self):
++ return (
++ "Context(armor={0.armor}, "
++ "textmode={0.textmode}, offline={0.offline}, "
++ "signers={0.signers}, pinentry_mode={0.pinentry_mode}, "
++ "protocol={0.protocol}, home_dir={0.home_dir}"
++ ")").format(self)
++
++ def encrypt(self, plaintext, recipients=[], sign=True, sink=None,
++ passphrase=None, always_trust=False, add_encrypt_to=False,
++ prepare=False, expect_sign=False, compress=True):
++ """Encrypt data
++
++ Encrypt the given plaintext for the given recipients. If the
++ list of recipients is empty, the data is encrypted
++ symmetrically with a passphrase.
++
++ The passphrase can be given as parameter, using a callback
++ registered at the context, or out-of-band via pinentry.
++
++ Keyword arguments:
++ recipients -- list of keys to encrypt to
++ sign -- sign plaintext (default True)
++ sink -- write result to sink instead of returning it
++ passphrase -- for symmetric encryption
++ always_trust -- always trust the keys (default False)
++ add_encrypt_to -- encrypt to configured additional keys (default False)
++ prepare -- (ui) prepare for encryption (default False)
++ expect_sign -- (ui) prepare for signing (default False)
++ compress -- compress plaintext (default True)
++
++ Returns:
++ ciphertext -- the encrypted data (or None if sink is given)
++ result -- additional information about the encryption
++ sign_result -- additional information about the signature(s)
++
++ Raises:
++ InvalidRecipients -- if encryption using a particular key failed
++ InvalidSigners -- if signing using a particular key failed
++ GPGMEError -- as signaled by the underlying library
++
++ """
++ ciphertext = sink if sink else Data()
++ flags = 0
++ flags |= always_trust * constants.ENCRYPT_ALWAYS_TRUST
++ flags |= (not add_encrypt_to) * constants.ENCRYPT_NO_ENCRYPT_TO
++ flags |= prepare * constants.ENCRYPT_PREPARE
++ flags |= expect_sign * constants.ENCRYPT_EXPECT_SIGN
++ flags |= (not compress) * constants.ENCRYPT_NO_COMPRESS
++
++ if passphrase != None:
++ old_pinentry_mode = self.pinentry_mode
++ old_passphrase_cb = getattr(self, '_passphrase_cb', None)
++ self.pinentry_mode = constants.PINENTRY_MODE_LOOPBACK
++ def passphrase_cb(hint, desc, prev_bad, hook=None):
++ return passphrase
++ self.set_passphrase_cb(passphrase_cb)
++
++ try:
++ if sign:
++ self.op_encrypt_sign(recipients, flags, plaintext, ciphertext)
++ else:
++ self.op_encrypt(recipients, flags, plaintext, ciphertext)
++ except errors.GPGMEError as e:
++ if e.getcode() == errors.UNUSABLE_PUBKEY:
++ result = self.op_encrypt_result()
++ if result.invalid_recipients:
++ raise errors.InvalidRecipients(result.invalid_recipients)
++ if e.getcode() == errors.UNUSABLE_SECKEY:
++ sig_result = self.op_sign_result()
++ if sig_result.invalid_signers:
++ raise errors.InvalidSigners(sig_result.invalid_signers)
++ raise
++ finally:
++ if passphrase != None:
++ self.pinentry_mode = old_pinentry_mode
++ if old_passphrase_cb:
++ self.set_passphrase_cb(*old_passphrase_cb[1:])
++
++ result = self.op_encrypt_result()
++ assert not result.invalid_recipients
++ sig_result = self.op_sign_result() if sign else None
++ assert not sig_result or not sig_result.invalid_signers
++
++ cipherbytes = None
++ if not sink:
++ ciphertext.seek(0, os.SEEK_SET)
++ cipherbytes = ciphertext.read()
++ return cipherbytes, result, sig_result
++
++ def decrypt(self, ciphertext, sink=None, passphrase=None, verify=True):
++ """Decrypt data
++
++ Decrypt the given ciphertext and verify any signatures. If
++ VERIFY is an iterable of keys, the ciphertext must be signed
++ by all those keys, otherwise an error is raised.
++
++ If the ciphertext is symmetrically encrypted using a
++ passphrase, that passphrase can be given as parameter, using a
++ callback registered at the context, or out-of-band via
++ pinentry.
++
++ Keyword arguments:
++ sink -- write result to sink instead of returning it
++ passphrase -- for symmetric decryption
++ verify -- check signatures (default True)
++
++ Returns:
++ plaintext -- the decrypted data (or None if sink is given)
++ result -- additional information about the decryption
++ verify_result -- additional information about the signature(s)
++
++ Raises:
++ UnsupportedAlgorithm -- if an unsupported algorithm was used
++ BadSignatures -- if a bad signature is encountered
++ MissingSignatures -- if expected signatures are missing or bad
++ GPGMEError -- as signaled by the underlying library
++
++ """
++ plaintext = sink if sink else Data()
++
++ if passphrase != None:
++ old_pinentry_mode = self.pinentry_mode
++ old_passphrase_cb = getattr(self, '_passphrase_cb', None)
++ self.pinentry_mode = constants.PINENTRY_MODE_LOOPBACK
++ def passphrase_cb(hint, desc, prev_bad, hook=None):
++ return passphrase
++ self.set_passphrase_cb(passphrase_cb)
++
++ try:
++ if verify:
++ self.op_decrypt_verify(ciphertext, plaintext)
++ else:
++ self.op_decrypt(ciphertext, plaintext)
++ finally:
++ if passphrase != None:
++ self.pinentry_mode = old_pinentry_mode
++ if old_passphrase_cb:
++ self.set_passphrase_cb(*old_passphrase_cb[1:])
++
++ result = self.op_decrypt_result()
++ verify_result = self.op_verify_result() if verify else None
++ if result.unsupported_algorithm:
++ raise errors.UnsupportedAlgorithm(result.unsupported_algorithm)
++
++ if verify:
++ if any(s.status != errors.NO_ERROR
++ for s in verify_result.signatures):
++ raise errors.BadSignatures(verify_result)
++
++ if verify and verify != True:
++ missing = list()
++ for key in verify:
++ ok = False
++ for subkey in key.subkeys:
++ for sig in verify_result.signatures:
++ if sig.summary & constants.SIGSUM_VALID == 0:
++ continue
++ if subkey.can_sign and subkey.fpr == sig.fpr:
++ ok = True
++ break
++ if ok:
++ break
++ if not ok:
++ missing.append(key)
++ if missing:
++ raise errors.MissingSignatures(verify_result, missing)
++
++ plainbytes = None
++ if not sink:
++ plaintext.seek(0, os.SEEK_SET)
++ plainbytes = plaintext.read()
++ return plainbytes, result, verify_result
++
++ def sign(self, data, sink=None, mode=constants.SIG_MODE_NORMAL):
++ """Sign data
++
++ Sign the given data with either the configured default local
++ key, or the 'signers' keys of this context.
++
++ Keyword arguments:
++ mode -- signature mode (default: normal, see below)
++ sink -- write result to sink instead of returning it
++
++ Returns:
++ either
++ signed_data -- encoded data and signature (normal mode)
++ signature -- only the signature data (detached mode)
++ cleartext -- data and signature as text (cleartext mode)
++ (or None if sink is given)
++ result -- additional information about the signature(s)
++
++ Raises:
++ InvalidSigners -- if signing using a particular key failed
++ GPGMEError -- as signaled by the underlying library
++
++ """
++ signeddata = sink if sink else Data()
++
++ try:
++ self.op_sign(data, signeddata, mode)
++ except errors.GPGMEError as e:
++ if e.getcode() == errors.UNUSABLE_SECKEY:
++ result = self.op_sign_result()
++ if result.invalid_signers:
++ raise errors.InvalidSigners(result.invalid_signers)
++ raise
++
++ result = self.op_sign_result()
++ assert not result.invalid_signers
++
++ signedbytes = None
++ if not sink:
++ signeddata.seek(0, os.SEEK_SET)
++ signedbytes = signeddata.read()
++ return signedbytes, result
++
++ def verify(self, signed_data, signature=None, sink=None, verify=[]):
++ """Verify signatures
++
++ Verify signatures over data. If VERIFY is an iterable of
++ keys, the ciphertext must be signed by all those keys,
++ otherwise an error is raised.
++
++ Keyword arguments:
++ signature -- detached signature data
++ sink -- write result to sink instead of returning it
++
++ Returns:
++ data -- the plain data
++ (or None if sink is given, or we verified a detached signature)
++ result -- additional information about the signature(s)
++
++ Raises:
++ BadSignatures -- if a bad signature is encountered
++ MissingSignatures -- if expected signatures are missing or bad
++ GPGMEError -- as signaled by the underlying library
++
++ """
++ if signature:
++ # Detached signature, we don't return the plain text.
++ data = None
++ else:
++ data = sink if sink else Data()
++
++ if signature:
++ self.op_verify(signature, signed_data, None)
++ else:
++ self.op_verify(signed_data, None, data)
++
++ result = self.op_verify_result()
++ if any(s.status != errors.NO_ERROR for s in result.signatures):
++ raise errors.BadSignatures(result)
++
++ missing = list()
++ for key in verify:
++ ok = False
++ for subkey in key.subkeys:
++ for sig in result.signatures:
++ if sig.summary & constants.SIGSUM_VALID == 0:
++ continue
++ if subkey.can_sign and subkey.fpr == sig.fpr:
++ ok = True
++ break
++ if ok:
++ break
++ if not ok:
++ missing.append(key)
++ if missing:
++ raise errors.MissingSignatures(result, missing)
++
++ plainbytes = None
++ if data and not sink:
++ data.seek(0, os.SEEK_SET)
++ plainbytes = data.read()
++ return plainbytes, result
++
++ def keylist(self, pattern=None, secret=False,
++ mode=constants.keylist.mode.LOCAL,
++ source=None):
++ """List keys
++
++ Keyword arguments:
++ pattern -- return keys matching pattern (default: all keys)
++ secret -- return only secret keys (default: False)
++ mode -- keylist mode (default: list local keys)
++ source -- read keys from source instead from the keyring
++ (all other options are ignored in this case)
++
++ Returns:
++ -- an iterator returning key objects
++
++ Raises:
++ GPGMEError -- as signaled by the underlying library
++ """
++ if not source:
++ self.set_keylist_mode(mode)
++ self.op_keylist_start(pattern, secret)
++ else:
++ # Automatic wrapping of SOURCE is not possible here,
++ # because the object must not be deallocated until the
++ # iteration over the results ends.
++ if not isinstance(source, Data):
++ source = Data(file=source)
++ self.op_keylist_from_data_start(source, 0)
++
++ key = self.op_keylist_next()
++ while key:
++ yield key
++ key = self.op_keylist_next()
++ self.op_keylist_end()
++
++ def create_key(self, userid, algorithm=None, expires_in=0, expires=True,
++ sign=False, encrypt=False, certify=False, authenticate=False,
++ passphrase=None, force=False):
++ """Create a primary key
++
++ Create a primary key for the user id USERID.
++
++ ALGORITHM may be used to specify the public key encryption
++ algorithm for the new key. By default, a reasonable default
++ is chosen. You may use "future-default" to select an
++ algorithm that will be the default in a future implementation
++ of the engine. ALGORITHM may be a string like "rsa", or
++ "rsa2048" to explicitly request an algorithm and a key size.
++
++ EXPIRES_IN specifies the expiration time of the key in number
++ of seconds since the keys creation. By default, a reasonable
++ expiration time is chosen. If you want to create a key that
++ does not expire, use the keyword argument EXPIRES.
++
++ SIGN, ENCRYPT, CERTIFY, and AUTHENTICATE can be used to
++ request the capabilities of the new key. If you don't request
++ any, a reasonable set of capabilities is selected, and in case
++ of OpenPGP, a subkey with a reasonable set of capabilities is
++ created.
++
++ If PASSPHRASE is None (the default), then the key will not be
++ protected with a passphrase. If PASSPHRASE is a string, it
++ will be used to protect the key. If PASSPHRASE is True, the
++ passphrase must be supplied using a passphrase callback or
++ out-of-band with a pinentry.
++
++ Keyword arguments:
++ algorithm -- public key algorithm, see above (default: reasonable)
++ expires_in -- expiration time in seconds (default: reasonable)
++ expires -- whether or not the key should expire (default: True)
++ sign -- request the signing capability (see above)
++ encrypt -- request the encryption capability (see above)
++ certify -- request the certification capability (see above)
++ authenticate -- request the authentication capability (see above)
++ passphrase -- protect the key with a passphrase (default: no passphrase)
++ force -- force key creation even if a key with the same userid exists
++ (default: False)
++
++ Returns:
++ -- an object describing the result of the key creation
++
++ Raises:
++ GPGMEError -- as signaled by the underlying library
++
++ """
++ if util.is_a_string(passphrase):
++ old_pinentry_mode = self.pinentry_mode
++ old_passphrase_cb = getattr(self, '_passphrase_cb', None)
++ self.pinentry_mode = constants.PINENTRY_MODE_LOOPBACK
++ def passphrase_cb(hint, desc, prev_bad, hook=None):
++ return passphrase
++ self.set_passphrase_cb(passphrase_cb)
++
++ try:
++ self.op_createkey(userid, algorithm,
++ 0, # reserved
++ expires_in,
++ None, # extrakey
++ ((constants.create.SIGN if sign else 0)
++ | (constants.create.ENCR if encrypt else 0)
++ | (constants.create.CERT if certify else 0)
++ | (constants.create.AUTH if authenticate else 0)
++ | (constants.create.NOPASSWD if passphrase == None else 0)
++ | (0 if expires else constants.create.NOEXPIRE)
++ | (constants.create.FORCE if force else 0)))
++ finally:
++ if util.is_a_string(passphrase):
++ self.pinentry_mode = old_pinentry_mode
++ if old_passphrase_cb:
++ self.set_passphrase_cb(*old_passphrase_cb[1:])
++
++ return self.op_genkey_result()
++
++ def create_subkey(self, key, algorithm=None, expires_in=0, expires=True,
++ sign=False, encrypt=False, authenticate=False, passphrase=None):
++ """Create a subkey
++
++ Create a subkey for the given KEY. As subkeys are a concept
++ of OpenPGP, calling this is only valid for the OpenPGP
++ protocol.
++
++ ALGORITHM may be used to specify the public key encryption
++ algorithm for the new subkey. By default, a reasonable
++ default is chosen. You may use "future-default" to select an
++ algorithm that will be the default in a future implementation
++ of the engine. ALGORITHM may be a string like "rsa", or
++ "rsa2048" to explicitly request an algorithm and a key size.
++
++ EXPIRES_IN specifies the expiration time of the subkey in
++ number of seconds since the subkeys creation. By default, a
++ reasonable expiration time is chosen. If you want to create a
++ subkey that does not expire, use the keyword argument EXPIRES.
++
++ SIGN, ENCRYPT, and AUTHENTICATE can be used to request the
++ capabilities of the new subkey. If you don't request any, an
++ encryption subkey is generated.
++
++ If PASSPHRASE is None (the default), then the subkey will not
++ be protected with a passphrase. If PASSPHRASE is a string, it
++ will be used to protect the subkey. If PASSPHRASE is True,
++ the passphrase must be supplied using a passphrase callback or
++ out-of-band with a pinentry.
++
++ Keyword arguments:
++ algorithm -- public key algorithm, see above (default: reasonable)
++ expires_in -- expiration time in seconds (default: reasonable)
++ expires -- whether or not the subkey should expire (default: True)
++ sign -- request the signing capability (see above)
++ encrypt -- request the encryption capability (see above)
++ authenticate -- request the authentication capability (see above)
++ passphrase -- protect the subkey with a passphrase (default: no passphrase)
++
++ Returns:
++ -- an object describing the result of the subkey creation
++
++ Raises:
++ GPGMEError -- as signaled by the underlying library
++
++ """
++ if util.is_a_string(passphrase):
++ old_pinentry_mode = self.pinentry_mode
++ old_passphrase_cb = getattr(self, '_passphrase_cb', None)
++ self.pinentry_mode = constants.PINENTRY_MODE_LOOPBACK
++ def passphrase_cb(hint, desc, prev_bad, hook=None):
++ return passphrase
++ self.set_passphrase_cb(passphrase_cb)
++
++ try:
++ self.op_createsubkey(key, algorithm,
++ 0, # reserved
++ expires_in,
++ ((constants.create.SIGN if sign else 0)
++ | (constants.create.ENCR if encrypt else 0)
++ | (constants.create.AUTH if authenticate else 0)
++ | (constants.create.NOPASSWD
++ if passphrase == None else 0)
++ | (0 if expires else constants.create.NOEXPIRE)))
++ finally:
++ if util.is_a_string(passphrase):
++ self.pinentry_mode = old_pinentry_mode
++ if old_passphrase_cb:
++ self.set_passphrase_cb(*old_passphrase_cb[1:])
++
++ return self.op_genkey_result()
++
++ def key_add_uid(self, key, uid):
++ """Add a UID
++
++ Add the uid UID to the given KEY. Calling this function is
++ only valid for the OpenPGP protocol.
++
++ Raises:
++ GPGMEError -- as signaled by the underlying library
++
++ """
++ self.op_adduid(key, uid, 0)
++
++ def key_revoke_uid(self, key, uid):
++ """Revoke a UID
++
++ Revoke the uid UID from the given KEY. Calling this function
++ is only valid for the OpenPGP protocol.
++
++ Raises:
++ GPGMEError -- as signaled by the underlying library
++
++ """
++ self.op_revuid(key, uid, 0)
++
++ def key_sign(self, key, uids=None, expires_in=False, local=False):
++ """Sign a key
++
++ Sign a key with the current set of signing keys. Calling this
++ function is only valid for the OpenPGP protocol.
++
++ If UIDS is None (the default), then all UIDs are signed. If
++ it is a string, then only the matching UID is signed. If it
++ is a list of strings, then all matching UIDs are signed. Note
++ that a case-sensitive exact string comparison is done.
++
++ EXPIRES_IN specifies the expiration time of the signature in
++ seconds. If EXPIRES_IN is False, the signature does not
++ expire.
++
++ Keyword arguments:
++ uids -- user ids to sign, see above (default: sign all)
++ expires_in -- validity period of the signature in seconds
++ (default: do not expire)
++ local -- create a local, non-exportable signature
++ (default: False)
++
++ Raises:
++ GPGMEError -- as signaled by the underlying library
++
++ """
++ flags = 0
++ if uids == None or util.is_a_string(uids):
++ pass#through unchanged
++ else:
++ flags |= constants.keysign.LFSEP
++ uids = "\n".join(uids)
++
++ if not expires_in:
++ flags |= constants.keysign.NOEXPIRE
++
++ if local:
++ flags |= constants.keysign.LOCAL
++
++ self.op_keysign(key, uids, expires_in, flags)
++
++ def key_tofu_policy(self, key, policy):
++ """Set a keys' TOFU policy
++
++ Set the TOFU policy associated with KEY to POLICY. Calling
++ this function is only valid for the OpenPGP protocol.
++
++ Raises:
++ GPGMEError -- as signaled by the underlying library
++
++ """
++ self.op_tofu_policy(key, policy)
++
++ def assuan_transact(self, command,
++ data_cb=None, inquire_cb=None, status_cb=None):
++ """Issue a raw assuan command
++
++ This function can be used to issue a raw assuan command to the
++ engine.
++
++ If command is a string or bytes, it will be used as-is. If it
++ is an iterable of strings, it will be properly escaped and
++ joined into an well-formed assuan command.
++
++ Keyword arguments:
++ data_cb -- a callback receiving data lines
++ inquire_cb -- a callback providing more information
++ status_cb -- a callback receiving status lines
++
++ Returns:
++ result -- the result of command as GPGMEError
++
++ Raises:
++ GPGMEError -- as signaled by the underlying library
++
++ """
++
++ if util.is_a_string(command) or isinstance(command, bytes):
++ cmd = command
++ else:
++ cmd = " ".join(util.percent_escape(f) for f in command)
++
++ errptr = gpgme.new_gpgme_error_t_p()
++
++ err = gpgme.gpgme_op_assuan_transact_ext(
++ self.wrapped,
++ cmd,
++ (weakref.ref(self), data_cb) if data_cb else None,
++ (weakref.ref(self), inquire_cb) if inquire_cb else None,
++ (weakref.ref(self), status_cb) if status_cb else None,
++ errptr)
++
++ if self._callback_excinfo:
++ gpgme.gpg_raise_callback_exception(self)
++
++ errorcheck(err)
++
++ status = gpgme.gpgme_error_t_p_value(errptr)
++ gpgme.delete_gpgme_error_t_p(errptr)
++
++ return GPGMEError(status) if status != 0 else None
++
++ def interact(self, key, func, sink=None, flags=0, fnc_value=None):
++ """Interact with the engine
++
++ This method can be used to edit keys and cards interactively.
++ KEY is the key to edit, FUNC is called repeatedly with two
++ unicode arguments, 'keyword' and 'args'. See the GPGME manual
++ for details.
++
++ Keyword arguments:
++ sink -- if given, additional output is written here
++ flags -- use constants.INTERACT_CARD to edit a card
++
++ Raises:
++ GPGMEError -- as signaled by the underlying library
++
++ """
++ if key == None:
++ raise ValueError("First argument cannot be None")
++
++ if sink == None:
++ sink = Data()
++
++ if fnc_value:
++ opaquedata = (weakref.ref(self), func, fnc_value)
++ else:
++ opaquedata = (weakref.ref(self), func)
++
++ result = gpgme.gpgme_op_interact(self.wrapped, key, flags,
++ opaquedata, sink)
++ if self._callback_excinfo:
++ gpgme.gpg_raise_callback_exception(self)
++ errorcheck(result)
++
++ @property
++ def signers(self):
++ """Keys used for signing"""
++ return [self.signers_enum(i) for i in range(self.signers_count())]
++ @signers.setter
++ def signers(self, signers):
++ old = self.signers
++ self.signers_clear()
++ try:
++ for key in signers:
++ self.signers_add(key)
++ except:
++ self.signers = old
++ raise
++
++ @property
++ def pinentry_mode(self):
++ """Pinentry mode"""
++ return self.get_pinentry_mode()
++ @pinentry_mode.setter
++ def pinentry_mode(self, value):
++ self.set_pinentry_mode(value)
++
++ @property
++ def protocol(self):
++ """Protocol to use"""
++ return self.get_protocol()
++ @protocol.setter
++ def protocol(self, value):
++ errorcheck(gpgme.gpgme_engine_check_version(value))
++ self.set_protocol(value)
++
++ @property
++ def home_dir(self):
++ """Engine's home directory"""
++ return self.engine_info.home_dir
++ @home_dir.setter
++ def home_dir(self, value):
++ self.set_engine_info(self.protocol, home_dir=value)
++
++ _ctype = 'gpgme_ctx_t'
++ _cprefix = 'gpgme_'
++
++ def _errorcheck(self, name):
++ """This function should list all functions returning gpgme_error_t"""
++ # The list of functions is created using:
++ #
++ # $ grep '^gpgme_error_t ' obj/lang/python/python3.5-gpg/gpgme.h \
++ # | grep -v _op_ | awk "/\(gpgme_ctx/ { printf (\"'%s',\\n\", \$2) } "
++ return ((name.startswith('gpgme_op_')
++ and not name.endswith('_result'))
++ or name in {
++ 'gpgme_new',
++ 'gpgme_set_ctx_flag',
++ 'gpgme_set_protocol',
++ 'gpgme_set_sub_protocol',
++ 'gpgme_set_keylist_mode',
++ 'gpgme_set_pinentry_mode',
++ 'gpgme_set_locale',
++ 'gpgme_ctx_set_engine_info',
++ 'gpgme_signers_add',
++ 'gpgme_sig_notation_add',
++ 'gpgme_set_sender',
++ 'gpgme_cancel',
++ 'gpgme_cancel_async',
++ 'gpgme_get_key',
++ })
++
++ _boolean_properties = {'armor', 'textmode', 'offline'}
++
++ def __del__(self):
++ if not gpgme:
++ # At interpreter shutdown, gpgme is set to NONE.
++ return
++
++ self._free_passcb()
++ self._free_progresscb()
++ self._free_statuscb()
++ if self.own and self.wrapped and gpgme.gpgme_release:
++ gpgme.gpgme_release(self.wrapped)
++ self.wrapped = None
++
++ # Implement the context manager protocol.
++ def __enter__(self):
++ return self
++ def __exit__(self, type, value, tb):
++ self.__del__()
++
++ def op_keylist_all(self, *args, **kwargs):
++ self.op_keylist_start(*args, **kwargs)
++ key = self.op_keylist_next()
++ while key:
++ yield key
++ key = self.op_keylist_next()
++ self.op_keylist_end()
++
++ def op_keylist_next(self):
++ """Returns the next key in the list created
++ by a call to op_keylist_start(). The object returned
++ is of type Key."""
++ ptr = gpgme.new_gpgme_key_t_p()
++ try:
++ errorcheck(gpgme.gpgme_op_keylist_next(self.wrapped, ptr))
++ key = gpgme.gpgme_key_t_p_value(ptr)
++ except errors.GPGMEError as excp:
++ key = None
++ if excp.getcode() != errors.EOF:
++ raise excp
++ gpgme.delete_gpgme_key_t_p(ptr)
++ if key:
++ key.__del__ = lambda self: gpgme.gpgme_key_unref(self)
++ return key
++
++ def get_key(self, fpr, secret=False):
++ """Get a key given a fingerprint
++
++ Keyword arguments:
++ secret -- to request a secret key
++
++ Returns:
++ -- the matching key
++
++ Raises:
++ KeyError -- if the key was not found
++ GPGMEError -- as signaled by the underlying library
++
++ """
++ ptr = gpgme.new_gpgme_key_t_p()
++
++ try:
++ errorcheck(gpgme.gpgme_get_key(self.wrapped, fpr, ptr, secret))
++ except errors.GPGMEError as e:
++ if e.getcode() == errors.EOF:
++ raise errors.KeyNotFound(fpr)
++ raise e
++
++ key = gpgme.gpgme_key_t_p_value(ptr)
++ gpgme.delete_gpgme_key_t_p(ptr)
++ assert key
++ key.__del__ = lambda self: gpgme.gpgme_key_unref(self)
++ return key
++
++ def op_trustlist_all(self, *args, **kwargs):
++ self.op_trustlist_start(*args, **kwargs)
++ trust = self.op_trustlist_next()
++ while trust:
++ yield trust
++ trust = self.op_trustlist_next()
++ self.op_trustlist_end()
++
++ def op_trustlist_next(self):
++ """Returns the next trust item in the list created
++ by a call to op_trustlist_start(). The object returned
++ is of type TrustItem."""
++ ptr = gpgme.new_gpgme_trust_item_t_p()
++ try:
++ errorcheck(gpgme.gpgme_op_trustlist_next(self.wrapped, ptr))
++ trust = gpgme.gpgme_trust_item_t_p_value(ptr)
++ except errors.GPGMEError as excp:
++ trust = None
++ if excp.getcode() != errors.EOF:
++ raise
++ gpgme.delete_gpgme_trust_item_t_p(ptr)
++ return trust
++
++ def set_passphrase_cb(self, func, hook=None):
++ """Sets the passphrase callback to the function specified by func.
++
++ When the system needs a passphrase, it will call func with three args:
++ hint, a string describing the key it needs the passphrase for;
++ desc, a string describing the passphrase it needs;
++ prev_bad, a boolean equal True if this is a call made after
++ unsuccessful previous attempt.
++
++ If hook has a value other than None it will be passed into the func
++ as a forth argument.
++
++ Please see the GPGME manual for more information.
++ """
++ if func == None:
++ hookdata = None
++ else:
++ if hook == None:
++ hookdata = (weakref.ref(self), func)
++ else:
++ hookdata = (weakref.ref(self), func, hook)
++ gpgme.gpg_set_passphrase_cb(self, hookdata)
++
++ def _free_passcb(self):
++ if gpgme.gpg_set_passphrase_cb:
++ self.set_passphrase_cb(None)
++
++ def set_progress_cb(self, func, hook=None):
++ """Sets the progress meter callback to the function specified by FUNC.
++ If FUNC is None, the callback will be cleared.
++
++ This function will be called to provide an interactive update
++ of the system's progress. The function will be called with
++ three arguments, type, total, and current. If HOOK is not
++ None, it will be supplied as fourth argument.
++
++ Please see the GPGME manual for more information.
++
++ """
++ if func == None:
++ hookdata = None
++ else:
++ if hook == None:
++ hookdata = (weakref.ref(self), func)
++ else:
++ hookdata = (weakref.ref(self), func, hook)
++ gpgme.gpg_set_progress_cb(self, hookdata)
++
++ def _free_progresscb(self):
++ if gpgme.gpg_set_progress_cb:
++ self.set_progress_cb(None)
++
++ def set_status_cb(self, func, hook=None):
++ """Sets the status callback to the function specified by FUNC. If
++ FUNC is None, the callback will be cleared.
++
++ The function will be called with two arguments, keyword and
++ args. If HOOK is not None, it will be supplied as third
++ argument.
++
++ Please see the GPGME manual for more information.
++
++ """
++ if func == None:
++ hookdata = None
++ else:
++ if hook == None:
++ hookdata = (weakref.ref(self), func)
++ else:
++ hookdata = (weakref.ref(self), func, hook)
++ gpgme.gpg_set_status_cb(self, hookdata)
++
++ def _free_statuscb(self):
++ if gpgme.gpg_set_status_cb:
++ self.set_status_cb(None)
++
++ @property
++ def engine_info(self):
++ """Configuration of the engine currently in use"""
++ p = self.protocol
++ infos = [i for i in self.get_engine_info() if i.protocol == p]
++ assert len(infos) == 1
++ return infos[0]
++
++ def get_engine_info(self):
++ """Get engine configuration
++
++ Returns information about all configured and installed
++ engines.
++
++ Returns:
++ infos -- a list of engine infos
++
++ """
++ return gpgme.gpgme_ctx_get_engine_info(self.wrapped)
++
++ def set_engine_info(self, proto, file_name=None, home_dir=None):
++ """Change engine configuration
++
++ Changes the configuration of the crypto engine implementing
++ the protocol 'proto' for the context.
++
++ Keyword arguments:
++ file_name -- engine program file name (unchanged if None)
++ home_dir -- configuration directory (unchanged if None)
++
++ """
++ self.ctx_set_engine_info(proto, file_name, home_dir)
++
++ def wait(self, hang):
++ """Wait for asynchronous call to finish. Wait forever if hang is True.
++ Raises an exception on errors.
++
++ Please read the GPGME manual for more information.
++
++ """
++ ptr = gpgme.new_gpgme_error_t_p()
++ gpgme.gpgme_wait(self.wrapped, ptr, hang)
++ status = gpgme.gpgme_error_t_p_value(ptr)
++ gpgme.delete_gpgme_error_t_p(ptr)
++ errorcheck(status)
++
++ def op_edit(self, key, func, fnc_value, out):
++ """Start key editing using supplied callback function
++
++ Note: This interface is deprecated and will be removed with
++ GPGME 1.8. Please use .interact instead. Furthermore, we
++ implement this using gpgme_op_interact, so callbacks will get
++ called with string keywords instead of numeric status
++ messages. Code that is using constants.STATUS_X or
++ constants.status.X will continue to work, whereas code using
++ magic numbers will break as a result.
++
++ """
++ warnings.warn("Call to deprecated method op_edit.",
++ category=DeprecationWarning)
++ return self.interact(key, func, sink=out, fnc_value=fnc_value)
++
++
++class Data(GpgmeWrapper):
++ """Data buffer
++
++ A lot of data has to be exchanged between the user and the crypto
++ engine, like plaintext messages, ciphertext, signatures and
++ information about the keys. The technical details about
++ exchanging the data information are completely abstracted by
++ GPGME. The user provides and receives the data via `gpgme_data_t'
++ objects, regardless of the communication protocol between GPGME
++ and the crypto engine in use.
++
++ This Data class is the implementation of the GpgmeData objects.
++
++ Please see the information about __init__ for instantiation.
++
++ """
++
++ _ctype = 'gpgme_data_t'
++ _cprefix = 'gpgme_data_'
++
++ def _errorcheck(self, name):
++ """This function should list all functions returning gpgme_error_t"""
++ # This list is compiled using
++ #
++ # $ grep -v '^gpgme_error_t ' obj/lang/python/python3.5-gpg/gpgme.h \
++ # | awk "/\(gpgme_data_t/ { printf (\"'%s',\\n\", \$2) } " | sed "s/'\\*/'/"
++ return name not in {
++ 'gpgme_data_read',
++ 'gpgme_data_write',
++ 'gpgme_data_seek',
++ 'gpgme_data_release',
++ 'gpgme_data_release_and_get_mem',
++ 'gpgme_data_get_encoding',
++ 'gpgme_data_get_file_name',
++ 'gpgme_data_identify',
++ }
++
++ def __init__(self, string=None, file=None, offset=None,
++ length=None, cbs=None, copy=True):
++ """Initialize a new gpgme_data_t object.
++
++ If no args are specified, make it an empty object.
++
++ If string alone is specified, initialize it with the data
++ contained there.
++
++ If file, offset, and length are all specified, file must
++ be either a filename or a file-like object, and the object
++ will be initialized by reading the specified chunk from the file.
++
++ If cbs is specified, it MUST be a tuple of the form:
++
++ (read_cb, write_cb, seek_cb, release_cb[, hook])
++
++ where the first four items are functions implementing reading,
++ writing, seeking the data, and releasing any resources once
++ the data object is deallocated. The functions must match the
++ following prototypes:
++
++ def read(amount, hook=None):
++ return <a b"bytes" object>
++
++ def write(data, hook=None):
++ return <the number of bytes written>
++
++ def seek(offset, whence, hook=None):
++ return <the new file position>
++
++ def release(hook=None):
++ <return value and exceptions are ignored>
++
++ The functions may be bound methods. In that case, you can
++ simply use the 'self' reference instead of using a hook.
++
++ If file is specified without any other arguments, then
++ it must be a filename, and the object will be initialized from
++ that file.
++
++ """
++ super(Data, self).__init__(None)
++ self.data_cbs = None
++
++ if cbs != None:
++ self.new_from_cbs(*cbs)
++ elif string != None:
++ self.new_from_mem(string, copy)
++ elif file != None and offset != None and length != None:
++ self.new_from_filepart(file, offset, length)
++ elif file != None:
++ if util.is_a_string(file):
++ self.new_from_file(file, copy)
++ else:
++ self.new_from_fd(file)
++ else:
++ self.new()
++
++ def __del__(self):
++ if not gpgme:
++ # At interpreter shutdown, gpgme is set to NONE.
++ return
++
++ if self.wrapped != None and gpgme.gpgme_data_release:
++ gpgme.gpgme_data_release(self.wrapped)
++ if self._callback_excinfo:
++ gpgme.gpg_raise_callback_exception(self)
++ self.wrapped = None
++ self._free_datacbs()
++
++ # Implement the context manager protocol.
++ def __enter__(self):
++ return self
++ def __exit__(self, type, value, tb):
++ self.__del__()
++
++ def _free_datacbs(self):
++ self._data_cbs = None
++
++ def new(self):
++ tmp = gpgme.new_gpgme_data_t_p()
++ errorcheck(gpgme.gpgme_data_new(tmp))
++ self.wrapped = gpgme.gpgme_data_t_p_value(tmp)
++ gpgme.delete_gpgme_data_t_p(tmp)
++
++ def new_from_mem(self, string, copy=True):
++ tmp = gpgme.new_gpgme_data_t_p()
++ errorcheck(gpgme.gpgme_data_new_from_mem(tmp,string,len(string),copy))
++ self.wrapped = gpgme.gpgme_data_t_p_value(tmp)
++ gpgme.delete_gpgme_data_t_p(tmp)
++
++ def new_from_file(self, filename, copy=True):
++ tmp = gpgme.new_gpgme_data_t_p()
++ try:
++ errorcheck(gpgme.gpgme_data_new_from_file(tmp, filename, copy))
++ except errors.GPGMEError as e:
++ if e.getcode() == errors.INV_VALUE and not copy:
++ raise ValueError("delayed reads are not yet supported")
++ else:
++ raise e
++ self.wrapped = gpgme.gpgme_data_t_p_value(tmp)
++ gpgme.delete_gpgme_data_t_p(tmp)
++
++ def new_from_cbs(self, read_cb, write_cb, seek_cb, release_cb, hook=None):
++ tmp = gpgme.new_gpgme_data_t_p()
++ if hook != None:
++ hookdata = (weakref.ref(self),
++ read_cb, write_cb, seek_cb, release_cb, hook)
++ else:
++ hookdata = (weakref.ref(self),
++ read_cb, write_cb, seek_cb, release_cb)
++ gpgme.gpg_data_new_from_cbs(self, hookdata, tmp)
++ self.wrapped = gpgme.gpgme_data_t_p_value(tmp)
++ gpgme.delete_gpgme_data_t_p(tmp)
++
++ def new_from_filepart(self, file, offset, length):
++ """This wraps the GPGME gpgme_data_new_from_filepart() function.
++ The argument "file" may be:
++
++ * a string specifying a file name, or
++ * a file-like object supporting the fileno() and the mode attribute.
++
++ """
++
++ tmp = gpgme.new_gpgme_data_t_p()
++ filename = None
++ fp = None
++
++ if util.is_a_string(file):
++ filename = file
++ else:
++ fp = gpgme.fdopen(file.fileno(), file.mode)
++ if fp == None:
++ raise ValueError("Failed to open file from %s arg %s" % \
++ (str(type(file)), str(file)))
++
++ errorcheck(gpgme.gpgme_data_new_from_filepart(tmp, filename, fp,
++ offset, length))
++ self.wrapped = gpgme.gpgme_data_t_p_value(tmp)
++ gpgme.delete_gpgme_data_t_p(tmp)
++
++ def new_from_fd(self, file):
++ """This wraps the GPGME gpgme_data_new_from_fd() function. The
++ argument "file" must be a file-like object, supporting the
++ fileno() method.
++
++ """
++ tmp = gpgme.new_gpgme_data_t_p()
++ errorcheck(gpgme.gpgme_data_new_from_fd(tmp, file.fileno()))
++ self.wrapped = gpgme.gpgme_data_t_p_value(tmp)
++ gpgme.delete_gpgme_data_t_p(tmp)
++
++ def new_from_stream(self, file):
++ """This wrap around gpgme_data_new_from_stream is an alias for
++ new_from_fd() method since in python there's not difference
++ between file stream and file descriptor"""
++ self.new_from_fd(file)
++
++ def write(self, buffer):
++ """Write buffer given as string or bytes.
++
++ If a string is given, it is implicitly encoded using UTF-8."""
++ written = gpgme.gpgme_data_write(self.wrapped, buffer)
++ if written < 0:
++ if self._callback_excinfo:
++ gpgme.gpg_raise_callback_exception(self)
++ else:
++ raise GPGMEError.fromSyserror()
++ return written
++
++ def read(self, size = -1):
++ """Read at most size bytes, returned as bytes.
++
++ If the size argument is negative or omitted, read until EOF is reached.
++
++ Returns the data read, or the empty string if there was no data
++ to read before EOF was reached."""
++
++ if size == 0:
++ return ''
++
++ if size > 0:
++ try:
++ result = gpgme.gpgme_data_read(self.wrapped, size)
++ except:
++ if self._callback_excinfo:
++ gpgme.gpg_raise_callback_exception(self)
++ else:
++ raise
++ return result
++ else:
++ chunks = []
++ while True:
++ try:
++ result = gpgme.gpgme_data_read(self.wrapped, 4096)
++ except:
++ if self._callback_excinfo:
++ gpgme.gpg_raise_callback_exception(self)
++ else:
++ raise
++ if len(result) == 0:
++ break
++ chunks.append(result)
++ return b''.join(chunks)
++
++def pubkey_algo_string(subkey):
++ """Return short algorithm string
++
++ Return a public key algorithm string (e.g. "rsa2048") for a given
++ SUBKEY.
++
++ Returns:
++ algo - a string
++
++ """
++ return gpgme.gpgme_pubkey_algo_string(subkey)
++
++def pubkey_algo_name(algo):
++ """Return name of public key algorithm
++
++ Return the name of the public key algorithm for a given numeric
++ algorithm id ALGO (cf. RFC4880).
++
++ Returns:
++ algo - a string
++
++ """
++ return gpgme.gpgme_pubkey_algo_name(algo)
++
++def hash_algo_name(algo):
++ """Return name of hash algorithm
++
++ Return the name of the hash algorithm for a given numeric
++ algorithm id ALGO (cf. RFC4880).
++
++ Returns:
++ algo - a string
++
++ """
++ return gpgme.gpgme_hash_algo_name(algo)
++
++def get_protocol_name(proto):
++ """Get protocol description
++
++ Get the string describing protocol PROTO.
++
++ Returns:
++ proto - a string
++
++ """
++ return gpgme.gpgme_get_protocol_name(proto)
++
++def addrspec_from_uid(uid):
++ """Return the address spec
++
++ Return the addr-spec (cf. RFC2822 section 4.3) from a user id UID.
++
++ Returns:
++ addr_spec - a string
++
++ """
++ return gpgme.gpgme_addrspec_from_uid(uid)
++
++def check_version(version=None):
++ return gpgme.gpgme_check_version(version)
++
++# check_version also makes sure that several subsystems are properly
++# initialized, and it must be run at least once before invoking any
++# other function. We do it here so that the user does not have to do
++# it unless she really wants to check for a certain version.
++check_version()
++
++def engine_check_version (proto):
++ try:
++ errorcheck(gpgme.gpgme_engine_check_version(proto))
++ return True
++ except errors.GPGMEError:
++ return False
++
++def get_engine_info():
++ ptr = gpgme.new_gpgme_engine_info_t_p()
++ try:
++ errorcheck(gpgme.gpgme_get_engine_info(ptr))
++ info = gpgme.gpgme_engine_info_t_p_value(ptr)
++ except errors.GPGMEError:
++ info = None
++ gpgme.delete_gpgme_engine_info_t_p(ptr)
++ return info
++
++def set_engine_info(proto, file_name, home_dir=None):
++ """Changes the default configuration of the crypto engine implementing
++ the protocol 'proto'. 'file_name' is the file name of
++ the executable program implementing this protocol. 'home_dir' is the
++ directory name of the configuration directory (engine's default is
++ used if omitted)."""
++ errorcheck(gpgme.gpgme_set_engine_info(proto, file_name, home_dir))
++
++def set_locale(category, value):
++ """Sets the default locale used by contexts"""
++ errorcheck(gpgme.gpgme_set_locale(None, category, value))
++
++def wait(hang):
++ """Wait for asynchronous call on any Context to finish.
++ Wait forever if hang is True.
++
++ For finished anynch calls it returns a tuple (status, context):
++ status - status return by asnynchronous call.
++ context - context which caused this call to return.
++
++ Please read the GPGME manual of more information."""
++ ptr = gpgme.new_gpgme_error_t_p()
++ context = gpgme.gpgme_wait(None, ptr, hang)
++ status = gpgme.gpgme_error_t_p_value(ptr)
++ gpgme.delete_gpgme_error_t_p(ptr)
++ if context == None:
++ errorcheck(status)
++ else:
++ context = Context(context)
++ return (status, context)
+diff --git a/lang/python/src/errors.py b/lang/python/src/errors.py
+new file mode 100644
+index 0000000..1ce139e
+--- /dev/null
++++ b/lang/python/src/errors.py
+@@ -0,0 +1,128 @@
++# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
++# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
++#
++# This library 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.
++#
++# This library 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 library; if not, write to the Free Software
++# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++
++from __future__ import absolute_import, print_function, unicode_literals
++del absolute_import, print_function, unicode_literals
++
++from . import gpgme
++from . import util
++
++# To appease static analysis tools, we define some constants here.
++# They are overwritten with the proper values by process_constants.
++NO_ERROR = None
++EOF = None
++
++util.process_constants('GPG_ERR_', globals())
++del util
++
++class GpgError(Exception):
++ pass
++
++class GPGMEError(GpgError):
++ def __init__(self, error = None, message = None):
++ self.error = error
++ self.message = message
++
++ @classmethod
++ def fromSyserror(cls):
++ return cls(gpgme.gpgme_err_code_from_syserror())
++
++ def getstring(self):
++ message = "%s: %s" % (gpgme.gpgme_strsource(self.error),
++ gpgme.gpgme_strerror(self.error))
++ if self.message != None:
++ message = "%s: %s" % (self.message, message)
++ return message
++
++ def getcode(self):
++ return gpgme.gpgme_err_code(self.error)
++
++ def getsource(self):
++ return gpgme.gpgme_err_source(self.error)
++
++ def __str__(self):
++ return self.getstring()
++
++def errorcheck(retval, extradata = None):
++ if retval:
++ raise GPGMEError(retval, extradata)
++
++class KeyNotFound(GPGMEError, KeyError):
++ """Raised if a key was not found
++
++ GPGME indicates this condition with EOF, which is not very
++ idiomatic. We raise this error that is both a GPGMEError
++ indicating EOF, and a KeyError.
++
++ """
++ def __init__(self, keystr):
++ self.keystr = keystr
++ GPGMEError.__init__(self, EOF)
++ def __str__(self):
++ return self.keystr
++
++# These errors are raised in the idiomatic interface code.
++
++class EncryptionError(GpgError):
++ pass
++
++class InvalidRecipients(EncryptionError):
++ def __init__(self, recipients):
++ self.recipients = recipients
++ def __str__(self):
++ return ", ".join("{}: {}".format(r.fpr,
++ gpgme.gpgme_strerror(r.reason))
++ for r in self.recipients)
++
++class DeryptionError(GpgError):
++ pass
++
++class UnsupportedAlgorithm(DeryptionError):
++ def __init__(self, algorithm):
++ self.algorithm = algorithm
++ def __str__(self):
++ return self.algorithm
++
++class SigningError(GpgError):
++ pass
++
++class InvalidSigners(SigningError):
++ def __init__(self, signers):
++ self.signers = signers
++ def __str__(self):
++ return ", ".join("{}: {}".format(s.fpr,
++ gpgme.gpgme_strerror(s.reason))
++ for s in self.signers)
++
++class VerificationError(GpgError):
++ pass
++
++class BadSignatures(VerificationError):
++ def __init__(self, result):
++ self.result = result
++ def __str__(self):
++ return ", ".join("{}: {}".format(s.fpr,
++ gpgme.gpgme_strerror(s.status))
++ for s in self.result.signatures
++ if s.status != NO_ERROR)
++
++class MissingSignatures(VerificationError):
++ def __init__(self, result, missing):
++ self.result = result
++ self.missing = missing
++ def __str__(self):
++ return ", ".join(k.subkeys[0].fpr for k in self.missing)
+diff --git a/lang/python/src/results.py b/lang/python/src/results.py
+new file mode 100644
+index 0000000..46ebeec
+--- /dev/null
++++ b/lang/python/src/results.py
+@@ -0,0 +1,118 @@
++# Robust result objects
++#
++# Copyright (C) 2016 g10 Code 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 <http://www.gnu.org/licenses/>.
++
++from __future__ import absolute_import, print_function, unicode_literals
++del absolute_import, print_function, unicode_literals
++
++"""Robust result objects
++
++Results returned by the underlying library are fragile, i.e. they are
++only valid until the next operation is performed in the context.
++
++We cannot arbitrarily constrain the lifetime of Python objects, we
++therefore create deep copies of the results.
++
++"""
++
++class Result(object):
++ """Result object
++
++ Describes the result of an operation.
++
++ """
++
++ """Convert to types"""
++ _type = {}
++
++ """Map functions over list attributes"""
++ _map = {}
++
++ """Automatically copy unless blacklisted"""
++ _blacklist = {
++ 'acquire', 'append', 'disown', 'next', 'own', 'this', 'thisown',
++ }
++ def __init__(self, fragile):
++ for key, func in self._type.items():
++ if hasattr(fragile, key):
++ setattr(self, key, func(getattr(fragile, key)))
++
++ for key, func in self._map.items():
++ if hasattr(fragile, key):
++ setattr(self, key, list(map(func, getattr(fragile, key))))
++
++ for key in dir(fragile):
++ if key.startswith('_') or key in self._blacklist:
++ continue
++ if hasattr(self, key):
++ continue
++
++ setattr(self, key, getattr(fragile, key))
++
++ def __repr__(self):
++ return '{}({})'.format(
++ self.__class__.__name__,
++ ', '.join('{}={!r}'.format(k, getattr(self, k))
++ for k in dir(self) if not k.startswith('_')))
++
++class InvalidKey(Result):
++ pass
++
++class EncryptResult(Result):
++ _map = dict(invalid_recipients=InvalidKey)
++
++class Recipient(Result):
++ pass
++
++class DecryptResult(Result):
++ _type = dict(wrong_key_usage=bool)
++ _map = dict(recipients=Recipient)
++
++class NewSignature(Result):
++ pass
++
++class SignResult(Result):
++ _map = dict(invalid_signers=InvalidKey, signatures=NewSignature)
++
++class Notation(Result):
++ pass
++
++class Signature(Result):
++ _type = dict(wrong_key_usage=bool, chain_model=bool)
++ _map = dict(notations=Notation)
++
++class VerifyResult(Result):
++ _map = dict(signatures=Signature)
++
++class ImportStatus(Result):
++ pass
++
++class ImportResult(Result):
++ _map = dict(imports=ImportStatus)
++
++class GenkeyResult(Result):
++ _type = dict(primary=bool, sub=bool)
++
++class KeylistResult(Result):
++ _type = dict(truncated=bool)
++
++class VFSMountResult(Result):
++ pass
++
++class EngineInfo(Result):
++ pass
+diff --git a/lang/python/src/util.py b/lang/python/src/util.py
+new file mode 100644
+index 0000000..e4fca4c
+--- /dev/null
++++ b/lang/python/src/util.py
+@@ -0,0 +1,53 @@
++# Copyright (C) 2016 g10 Code GmbH
++# Copyright (C) 2004,2008 Igor Belyi <belyi at users.sourceforge.net>
++# Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
++#
++# This library 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.
++#
++# This library 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 library; if not, write to the Free Software
++# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++
++from __future__ import absolute_import, print_function, unicode_literals
++del absolute_import, print_function, unicode_literals
++
++import sys
++
++def process_constants(prefix, scope):
++ """Called by the constant modules to load up the constants from the C
++ library starting with PREFIX. Matching constants will be inserted
++ into SCOPE with PREFIX stripped from the names. Returns the names
++ of inserted constants.
++
++ """
++ from . import gpgme
++ index = len(prefix)
++ constants = {identifier[index:]: getattr(gpgme, identifier)
++ for identifier in dir(gpgme)
++ if identifier.startswith(prefix)}
++ scope.update(constants)
++ return list(constants.keys())
++
++def percent_escape(s):
++ return ''.join(
++ '%{0:2x}'.format(ord(c))
++ if c == '+' or c == '"' or c == '%' or ord(c) <= 0x20 else c
++ for c in s)
++
++# Python2/3 compatibility
++if sys.version_info[0] == 3:
++ # Python3
++ def is_a_string(x):
++ return isinstance(x, str)
++else:
++ # Python2
++ def is_a_string(x):
++ return isinstance(x, basestring)
+diff --git a/lang/python/version.py.in b/lang/python/version.py.in
+new file mode 100644
+index 0000000..1a1baf0
+--- /dev/null
++++ b/lang/python/version.py.in
+@@ -0,0 +1,68 @@
++# Copyright (C) 2016 g10 Code GmbH
++# Copyright (C) 2015 Ben McGinnes <ben at adversary.org>
++# Copyright (C) 2004 Igor Belyi <belyi at users.sourceforge.net>
++#
++# This library 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.
++#
++# This library 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 library; if not, write to the Free Software
++# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++
++from __future__ import absolute_import, print_function
++del absolute_import, print_function
++
++from . import gpgme
++
++productname = 'gpg'
++versionstr = "@VERSION@"
++gpgme_versionstr = gpgme.GPGME_VERSION
++in_tree_build = bool(gpgme.cvar.gpg_in_tree_build)
++
++versionlist = versionstr.split(".")
++major = versionlist[0]
++minor = versionlist[1]
++patch = versionlist[2]
++
++copyright = """\
++Copyright (C) 2016 g10 Code GmbH
++Copyright (C) 2015 Ben McGinnes
++Copyright (C) 2014-2015 Martin Albrecht
++Copyright (C) 2004-2008 Igor Belyi
++Copyright (C) 2002 John Goerzen"""
++
++author = "The GnuPG hackers"
++author_email = "gnupg-devel at gnupg.org"
++
++description = "Python support for GPGME GnuPG cryptography library"
++homepage = "https://gnupg.org"
++
++license = """Copyright (C) 2016 g10 Code GmbH
++Copyright (C) 2015 Ben McGinnes <ben at adversary.org>
++Copyright (C) 2014, 2015 Martin Albrecht <martinralbrecht at googlemail.com>
++Copyright (C) 2004, 2008 Igor Belyi <belyi at users.sourceforge.net>
++Copyright (C) 2002 John Goerzen <jgoerzen at complete.org>
++
++This library 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.
++
++This library 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 library; if not, write to the Free Software
++Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA"""
++
++# Interface hygiene. Keep this at the end.
++del gpgme
diff --git a/debian/patches/0008-python-support-.pydistutils.cfg-mode.patch b/debian/patches/0008-python-support-.pydistutils.cfg-mode.patch
new file mode 100644
index 0000000..e2c0d97
--- /dev/null
+++ b/debian/patches/0008-python-support-.pydistutils.cfg-mode.patch
@@ -0,0 +1,101 @@
+From: Alon Bar-Lev <alon.barlev at gmail.com>
+Date: Fri, 7 Apr 2017 17:31:47 +0300
+Subject: python: support .pydistutils.cfg mode
+
+* lang/python/setup.py.in: Do not parse arguments.
+
+--
+
+The distutils settings can come from either command-line or
+configuration file. Parsing parameters is not working in all cases.
+
+Signed-off-by: Alon Bar-Lev <alon.barlev at gmail.com>
+(cherry picked from commit 365c649ad073f2697438dc014160943ae31a1447)
+---
+ lang/python/setup.py.in | 25 ++++++++++---------------
+ 1 file changed, 10 insertions(+), 15 deletions(-)
+
+diff --git a/lang/python/setup.py.in b/lang/python/setup.py.in
+index 5d94c70..e50971c 100755
+--- a/lang/python/setup.py.in
++++ b/lang/python/setup.py.in
+@@ -19,18 +19,12 @@
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ from distutils.core import setup, Extension
+-import argparse
+ import os, os.path, sys
+ import glob
+ import re
+ import shutil
+ import subprocess
+
+-# We parse a subset of the arguments.
+-parser = argparse.ArgumentParser(add_help=False)
+-parser.add_argument('--build-base', default='')
+-options, _ = parser.parse_known_args()
+-
+ # Out-of-tree build of the gpg bindings.
+ gpg_error_config = ["gpg-error-config"]
+ gpgme_config_flags = ["--thread=pthread"]
+@@ -142,8 +136,6 @@ if uname_s.startswith("MINGW32"):
+
+ def in_srcdir(name):
+ return os.path.join(os.environ.get("srcdir", ""), name)
+-def in_build_base(name):
+- return os.path.join(options.build_base, name)
+ def up_to_date(source, target):
+ return (os.path.exists(target)
+ and os.path.getmtime(source) <= os.path.getmtime(target))
+@@ -190,6 +182,9 @@ class BuildExtFirstHack(build):
+ continue
+ sink.write(rewrite_re.sub(r'%constant long \1 = \1;'+'\n', line.strip()))
+
++ def _in_build_base(self, name):
++ return os.path.join(self.build_base, name)
++
+ def _generate(self):
+ print("Building python gpg module using {} and {}.".format(gpgme_h, gpg_error_h))
+
+@@ -197,18 +192,18 @@ class BuildExtFirstHack(build):
+ if not os.path.exists(self.build_base):
+ os.makedirs(self.build_base)
+
+- self._generate_gpgme_h(gpgme_h, in_build_base("gpgme.h"))
+- self._generate_errors_i(gpg_error_h, in_build_base("errors.i"))
++ self._generate_gpgme_h(gpgme_h, self._in_build_base("gpgme.h"))
++ self._generate_errors_i(gpg_error_h, self._in_build_base("errors.i"))
+
+ # Keep timestamp to avoid rebuild
+- for source, target in ((gpgme_h, in_build_base("gpgme.h")),
+- (gpg_error_h, in_build_base("errors.i"))):
++ for source, target in ((gpgme_h, self._in_build_base("gpgme.h")),
++ (gpg_error_h, self._in_build_base("errors.i"))):
+ if not up_to_date(source, target):
+ shutil.copystat(source, target)
+
+ # Copy due to http://bugs.python.org/issue2624
+ # Avoid creating in srcdir
+- for source, target in ((in_srcdir(n), in_build_base(n))
++ for source, target in ((in_srcdir(n), self._in_build_base(n))
+ for n in ('gpgme.i', 'helpers.c', 'private.h', 'helpers.h')):
+ if not up_to_date(source, target):
+ shutil.copy2(source, target)
+@@ -221,7 +216,7 @@ class BuildExtFirstHack(build):
+ def run(self):
+ self._generate()
+
+- swig_sources.append(os.path.join(self.build_base, 'gpgme.i'))
++ swig_sources.extend((self._in_build_base('gpgme.i'), self._in_build_base('helpers.c')))
+ swig_opts.extend(['-I' + self.build_base,
+ '-outdir', os.path.join(self.build_lib, 'gpg')])
+ include_dirs.append(self.build_base)
+@@ -230,7 +225,7 @@ class BuildExtFirstHack(build):
+ build.run(self)
+
+ py3 = [] if sys.version_info.major < 3 else ['-py3']
+-swig_sources = [in_build_base('helpers.c')]
++swig_sources = []
+ swig_opts = ['-threads'] + py3 + extra_swig_opts
+ swige = Extension("gpg._gpgme",
+ sources = swig_sources,
diff --git a/debian/patches/0009-tests-Do-not-use-check-local-magic-as-dependency.patch b/debian/patches/0009-tests-Do-not-use-check-local-magic-as-dependency.patch
new file mode 100644
index 0000000..a640ae5
--- /dev/null
+++ b/debian/patches/0009-tests-Do-not-use-check-local-magic-as-dependency.patch
@@ -0,0 +1,98 @@
+From: Alon Bar-Lev <alon.barlev at gmail.com>
+Date: Fri, 7 Apr 2017 17:32:18 +0300
+Subject: tests: Do not use check-local magic as dependency
+
+* tests/gpg/Makefile.am: Use BUILT_SOURCES instead of check-local
+and initial.test.
+* lang/qt/tests/Makefile.am: Ditto.
+
+--
+
+This fixes "make dist" failure when source tree is clean:
+ git clean -dxf
+ autoreconf -ivf
+ ./configure
+ make dist
+
+BUILT_SOURCES should be used when file as generated without explicit
+dependency. The check-local is all-am dependency, this means that it
+will be resolved also in "make dist".
+
+Signed-off-by: Alon Bar-Lev <alon.barlev at gmail.com>
+(cherry picked from commit ebefc6cbf937d14ced65f7ded79c4ba901507d23)
+---
+ lang/qt/tests/Makefile.am | 12 +++---------
+ tests/gpg/Makefile.am | 9 ++-------
+ 2 files changed, 5 insertions(+), 16 deletions(-)
+
+diff --git a/lang/qt/tests/Makefile.am b/lang/qt/tests/Makefile.am
+index 93dce07..fb45eec 100644
+--- a/lang/qt/tests/Makefile.am
++++ b/lang/qt/tests/Makefile.am
+@@ -43,12 +43,6 @@ AM_CPPFLAGS = -I$(top_srcdir)/lang/cpp/src -I$(top_builddir)/src \
+ -I$(top_srcdir)/lang/qt/src \
+ -DTOP_SRCDIR="$(top_srcdir)"
+
+-check-local: ./pubring-stamp
+-
+-# To guarantee that check-local is run before any tests we
+-# add this dependency:
+-initial.test : check-local
+-
+ support_src = t-support.h t-support.cpp
+
+ t_keylist_SOURCES = t-keylist.cpp $(support_src)
+@@ -64,7 +58,7 @@ run_keyformailboxjob_SOURCES = run-keyformailboxjob.cpp
+
+ nodist_t_keylist_SOURCES = $(moc_files)
+
+-BUILT_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
+@@ -79,7 +73,7 @@ clean-local:
+
+ export GNUPGHOME := $(abs_builddir)
+
+-./pubring-stamp: $(top_srcdir)/tests/gpg/pubdemo.asc \
++pubring-stamp: $(top_srcdir)/tests/gpg/pubdemo.asc \
+ $(top_srcdir)/tests/gpg/secdemo.asc
+ echo "ignore-invalid-option allow-loopback-pinentry" > $(abs_builddir)/gpg-agent.conf
+ echo "allow-loopback-pinentry" >> gpg-agent.conf
+@@ -90,7 +84,7 @@ export GNUPGHOME := $(abs_builddir)
+ $(GPG) --no-permission-warning \
+ --passphrase "abc" \
+ --import $(top_srcdir)/tests/gpg/secdemo.asc
+- touch ./pubring-stamp
++ touch pubring-stamp
+
+ .cpp.moc:
+ $(MOC) `test -f '$<' || echo '$(srcdir)/'`$< -o $@
+diff --git a/tests/gpg/Makefile.am b/tests/gpg/Makefile.am
+index 8e26a92..4cba303 100644
+--- a/tests/gpg/Makefile.am
++++ b/tests/gpg/Makefile.am
+@@ -60,6 +60,8 @@ EXTRA_DIST = initial.test final.test \
+ pubdemo.asc secdemo.asc cipher-1.asc cipher-2.asc \
+ geheim.txt pubkey-1.asc seckey-1.asc pinentry $(private_keys)
+
++BUILT_SOURCES = gpg.conf gpg-agent.conf pubring-stamp \
++ private-keys-v1.d/gpg-sample.stamp
+ AM_CPPFLAGS = -I$(top_builddir)/src @GPG_ERROR_CFLAGS@
+ AM_LDFLAGS = -no-install
+ LDADD = ../../src/libgpgme.la
+@@ -82,13 +84,6 @@ clean-local:
+ -$(top_srcdir)/tests/start-stop-agent --stop
+ -rm -fR private-keys-v1.d
+
+-check-local: ./gpg.conf ./gpg-agent.conf ./pubring-stamp \
+- ./private-keys-v1.d/gpg-sample.stamp
+-
+-# To guarantee that check-local is run before any tests we
+-# add this dependency:
+-initial.test : check-local
+-
+ export GNUPGHOME := $(abs_builddir)
+
+ export GPG_AGENT_INFO :=
diff --git a/debian/patches/0010-python-Remove-usage-of-PYTHON_VERSIONS.patch b/debian/patches/0010-python-Remove-usage-of-PYTHON_VERSIONS.patch
new file mode 100644
index 0000000..dac8257
--- /dev/null
+++ b/debian/patches/0010-python-Remove-usage-of-PYTHON_VERSIONS.patch
@@ -0,0 +1,111 @@
+From: Alon Bar-Lev <alon.barlev at gmail.com>
+Date: Sat, 8 Apr 2017 16:34:30 +0300
+Subject: python: Remove usage of PYTHON_VERSIONS
+
+* configure.ac: Remove PYTHON_VERSIONS subst.
+* lang/python/Makefile.am: Use basename of python as builddir prefix.
+* lang/python/tests/run-tests.py: Likewise.
+
+--
+
+Two variables needs be at sync PYTHONS and PYTHON_VERSIONS, these may go
+out of sync in some cases, for example in Gentoo where default python is
+3.4 we get:
+
+PYTHON='/usr/bin/python2'
+PYTHONS='/usr/bin/python /usr/bin/python2'
+PYTHON_VERSIONS='2.7 3.4'
+
+We can use the basename of the python interpreter to achieve similar
+effect without having to sync indexes between these two variables.
+
+Signed-off-by: Alon Bar-Lev <alon.barlev at gmail.com>
+(cherry picked from commit 25e6444b3f4601c7821beab06bc4520deacb007b)
+---
+ configure.ac | 1 -
+ lang/python/Makefile.am | 23 +++++++++++------------
+ lang/python/tests/run-tests.py | 4 ++--
+ 3 files changed, 13 insertions(+), 15 deletions(-)
+
+diff --git a/configure.ac b/configure.ac
+index e77cc23..36c547b 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -471,7 +471,6 @@ if test "$found_py" = "1" -o "$found_py2" = "1" -o "$found_py3" = "1"; then
+ fi
+
+ AC_SUBST(PYTHONS, $PYTHONS)
+- AC_SUBST(PYTHON_VERSIONS, $PYTHON_VERSIONS)
+ fi
+ fi
+
+diff --git a/lang/python/Makefile.am b/lang/python/Makefile.am
+index 4ebd214..90075f7 100644
+--- a/lang/python/Makefile.am
++++ b/lang/python/Makefile.am
+@@ -38,12 +38,11 @@ copystamp:
+ touch $@
+
+ all-local: copystamp
+- set -e ; set $(PYTHONS); for VERSION in $(PYTHON_VERSIONS); do \
+- PYTHON="$$1" ; shift ; \
++ set -e ; for PYTHON in $(PYTHONS); do \
+ CFLAGS="$(CFLAGS)" \
+ srcdir="$(srcdir)" \
+ top_builddir="$(top_builddir)" \
+- $$PYTHON setup.py build --verbose --build-base=python$${VERSION}-gpg ; \
++ $$PYTHON setup.py build --verbose --build-base="$$(basename "$${PYTHON}")-gpg" ; \
+ done
+
+ python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz.asc: copystamp
+@@ -76,26 +75,26 @@ CLEANFILES = copystamp \
+ # permissions.
+ clean-local:
+ rm -rf -- build
+- for VERSION in $(PYTHON_VERSIONS); do \
+- find python$${VERSION}-gpg* -type d ! -perm -200 -exec chmod u+w {} ';' ; \
+- rm -rf -- python$${VERSION}-gpg* ; \
++ for PYTHON in $(PYTHONS); do \
++ find "$$(basename "$${PYTHON}")-gpg" -type d ! -perm -200 -exec chmod u+w {} ';' ; \
++ rm -rf -- "$$(basename "$${PYTHON}")-gpg" ; \
+ done
+
+ install-exec-local:
+- set -e ; set $(PYTHONS); for VERSION in $(PYTHON_VERSIONS); do \
+- PYTHON="$$1" ; shift ; \
++ set -e ; for PYTHON in $(PYTHONS); do \
+ srcdir="$(srcdir)" \
+ top_builddir="$(top_builddir)" \
+ $$PYTHON setup.py \
+ build \
+- --build-base=python$${VERSION}-gpg \
++ --build-base="$$(basename "$${PYTHON}")-gpg" \
+ install \
+ --prefix "$(DESTDIR)$(prefix)" \
+ --verbose ; \
+ done
+
+ uninstall-local:
+- GV=$$(echo $(VERSION) | tr - _); for PV in $(PYTHON_VERSIONS); do \
+- rm -rf -- "$(DESTDIR)$(prefix)"/lib*/python$$PV/site-packages/gpg \
+-"$(DESTDIR)$(prefix)"/lib*/python$$PV/site-packages/gpg-$$GV-py$$PV.egg-info ; \
++ GV=$$(echo $(VERSION) | tr - _); for PYTHON in $(PYTHONS); do \
++ PLATLIB="$(prefix)/$$("$${PYTHON}" -c 'import sysconfig, os; print(os.path.relpath(sysconfig.get_path("platlib"), sysconfig.get_config_var("prefix")))')" ; \
++ rm -rf -- "$(DESTDIR)$${PLATLIB}/gpg" \
++ "$(DESTDIR)$${PLATLIB}"/gpg-$$GV-py*.egg-info ; \
+ done
+diff --git a/lang/python/tests/run-tests.py b/lang/python/tests/run-tests.py
+index 9721997..5d5294a 100644
+--- a/lang/python/tests/run-tests.py
++++ b/lang/python/tests/run-tests.py
+@@ -70,8 +70,8 @@ for interpreter in args.interpreters:
+ [interpreter, "-c", "import sys; print('{0}.{1}'.format(sys.version_info[0], sys.version_info[1]))"]).strip().decode()
+
+ pattern = os.path.join(args.builddir, "..",
+- "python{0}-gpg".format(version),
+- "lib*"+version)
++ "{0}-gpg".format(os.path.basename(interpreter)),
++ "lib*")
+ builddirs = glob.glob(pattern)
+ if len(builddirs) == 0:
+ sys.exit("Build directory matching {0!r} not found.".format(pattern))
diff --git a/debian/patches/0011-python-Remove-unneeded-stats-copy.patch b/debian/patches/0011-python-Remove-unneeded-stats-copy.patch
new file mode 100644
index 0000000..8990d90
--- /dev/null
+++ b/debian/patches/0011-python-Remove-unneeded-stats-copy.patch
@@ -0,0 +1,30 @@
+From: Alon Bar-Lev <alon.barlev at gmail.com>
+Date: Sat, 8 Apr 2017 16:34:31 +0300
+Subject: python: Remove unneeded stats copy
+
+* lang/python/setup.py.in: errors.i, gpgme.h are generated and always
+newer than the original.
+
+Signed-off-by: Alon Bar-Lev <alon.barlev at gmail.com>
+(cherry picked from commit df8433bffa9e669897243f08edf7845762250e4a)
+---
+ lang/python/setup.py.in | 6 ------
+ 1 file changed, 6 deletions(-)
+
+diff --git a/lang/python/setup.py.in b/lang/python/setup.py.in
+index e50971c..f4ce64f 100755
+--- a/lang/python/setup.py.in
++++ b/lang/python/setup.py.in
+@@ -195,12 +195,6 @@ class BuildExtFirstHack(build):
+ self._generate_gpgme_h(gpgme_h, self._in_build_base("gpgme.h"))
+ self._generate_errors_i(gpg_error_h, self._in_build_base("errors.i"))
+
+- # Keep timestamp to avoid rebuild
+- for source, target in ((gpgme_h, self._in_build_base("gpgme.h")),
+- (gpg_error_h, self._in_build_base("errors.i"))):
+- if not up_to_date(source, target):
+- shutil.copystat(source, target)
+-
+ # Copy due to http://bugs.python.org/issue2624
+ # Avoid creating in srcdir
+ for source, target in ((in_srcdir(n), self._in_build_base(n))
diff --git a/debian/patches/0012-python-Read-gpg-error.h-using-the-pre-processor.patch b/debian/patches/0012-python-Read-gpg-error.h-using-the-pre-processor.patch
new file mode 100644
index 0000000..dd1e9f4
--- /dev/null
+++ b/debian/patches/0012-python-Read-gpg-error.h-using-the-pre-processor.patch
@@ -0,0 +1,131 @@
+From: Alon Bar-Lev <alon.barlev at gmail.com>
+Date: Sat, 8 Apr 2017 16:34:32 +0300
+Subject: python: Read gpg-error.h using the pre-processor
+
+* lang/python/setup.py.in: Read gpg-error.h using the pre-processor.
+
+--
+
+The libgpg-error may be installed in multilib configuration in which
+there is a wrapper header at /usr/include that includes the actual
+header at /usr/include/*. This causes invalid errors.i generation.
+
+Let the pre-processor extract the header content instead reading it
+explicitly.
+
+Signed-off-by: Alon Bar-Lev <alon.barlev at gmail.com>
+(cherry picked from commit 7309ce6f5f7c86570953a141965d4f54cd9ad9a0)
+---
+ lang/python/setup.py.in | 60 ++++++++++++++++++++++++++++---------------------
+ 1 file changed, 35 insertions(+), 25 deletions(-)
+
+diff --git a/lang/python/setup.py.in b/lang/python/setup.py.in
+index f4ce64f..a1279f8 100755
+--- a/lang/python/setup.py.in
++++ b/lang/python/setup.py.in
+@@ -54,13 +54,6 @@ if hasattr(subprocess, "DEVNULL"):
+ else:
+ devnull = open(os.devnull, "w")
+
+-try:
+- subprocess.check_call(gpg_error_config + ['--version'],
+- stdout=devnull)
+-except:
+- sys.exit("Could not find gpg-error-config. " +
+- "Please install the libgpg-error development package.")
+-
+ try:
+ subprocess.check_call(gpgme_config + ['--version'],
+ stdout=devnull)
+@@ -84,13 +77,6 @@ if not (major > 1 or (major == 1 and minor >= 7)):
+ if not gpgme_h:
+ gpgme_h = os.path.join(getconfig("prefix")[0], "include", "gpgme.h")
+
+-gpg_error_prefix = getconfig("prefix", config=gpg_error_config)[0]
+-gpg_error_h = os.path.join(gpg_error_prefix, "include", "gpg-error.h")
+-if not os.path.exists(gpg_error_h):
+- gpg_error_h = \
+- glob.glob(os.path.join(gpg_error_prefix, "include",
+- "*", "gpg-error.h"))[0]
+-
+ define_macros = []
+ libs = getconfig('libs')
+
+@@ -150,10 +136,27 @@ def up_to_date(source, target):
+ from distutils.command.build import build
+ class BuildExtFirstHack(build):
+
++ def _read_header(self, header, cflags):
++ tmp_include = self._in_build_base("include1.h")
++ with open(tmp_include, 'w') as f:
++ f.write("#include <%s>" % header)
++ return subprocess.check_output(os.environ.get('CPP', 'cc -E').split() + cflags + [tmp_include]).decode('utf-8')
++
++ def _write_if_unchanged(self, target, content):
++ if os.path.exists(target):
++ with open(target) as f:
++ if f.read() == content:
++ return
++
++ with open(target, "w") as sink:
++ sink.write(content)
++
+ def _generate_gpgme_h(self, source_name, sink_name):
+ if up_to_date(source_name, sink_name):
+ return
+
++ print("Using gpgme.h from {}".format(source_name))
++
+ deprec_func = re.compile(r'^(.*typedef.*|.*\(.*\)|[^#]+\s+.+)'
+ + r'\s*_GPGME_DEPRECATED(_OUTSIDE_GPGME)?\(.*\);\s*',
+ re.S)
+@@ -169,31 +172,38 @@ class BuildExtFirstHack(build):
+ text = ''
+ sink.write(text)
+
+- def _generate_errors_i(self, source_name, sink_name):
+- if up_to_date(source_name, sink_name):
+- return
++ def _generate_errors_i(self):
++
++ try:
++ subprocess.check_call(gpg_error_config + ['--version'],
++ stdout=devnull)
++ except:
++ sys.exit("Could not find gpg-error-config. " +
++ "Please install the libgpg-error development package.")
++
++ gpg_error_content = self._read_header("gpg-error.h", getconfig("cflags", config=gpg_error_config))
+
+ filter_re = re.compile(r'GPG_ERR_[^ ]* =')
+ rewrite_re = re.compile(r' *(.*) = .*')
+
+- with open(sink_name, "w") as sink, open(source_name) as source:
+- for line in source:
+- if not filter_re.search(line):
+- continue
+- sink.write(rewrite_re.sub(r'%constant long \1 = \1;'+'\n', line.strip()))
++ errors_i_content = ''
++ for line in gpg_error_content.splitlines():
++ if not filter_re.search(line):
++ continue
++ errors_i_content += rewrite_re.sub(r'%constant long \1 = \1;'+'\n', line.strip())
++
++ self._write_if_unchanged(self._in_build_base("errors.i"), errors_i_content)
+
+ def _in_build_base(self, name):
+ return os.path.join(self.build_base, name)
+
+ def _generate(self):
+- print("Building python gpg module using {} and {}.".format(gpgme_h, gpg_error_h))
+-
+ # Cleanup gpgme.h from deprecated functions and typedefs.
+ if not os.path.exists(self.build_base):
+ os.makedirs(self.build_base)
+
+ self._generate_gpgme_h(gpgme_h, self._in_build_base("gpgme.h"))
+- self._generate_errors_i(gpg_error_h, self._in_build_base("errors.i"))
++ self._generate_errors_i()
+
+ # Copy due to http://bugs.python.org/issue2624
+ # Avoid creating in srcdir
diff --git a/debian/patches/0013-python-Support-alternatate-libdir-for-tests.patch b/debian/patches/0013-python-Support-alternatate-libdir-for-tests.patch
new file mode 100644
index 0000000..5126902
--- /dev/null
+++ b/debian/patches/0013-python-Support-alternatate-libdir-for-tests.patch
@@ -0,0 +1,63 @@
+From: Alon Bar-Lev <alon.barlev at gmail.com>
+Date: Sat, 8 Apr 2017 16:34:33 +0300
+Subject: python: Support alternatate libdir for tests
+
+* lang/python/tests/run-tests.py: Add --python-libdir optional
+parameter.
+
+--
+
+This will make the python tests usable for downstream that build python
+module outside of autotools build system.
+
+Signed-off-by: Alon Bar-Lev <alon.barlev at gmail.com>
+(cherry picked from commit 3cc90b67fa970e716c8672ec5c5f591fa11ab216)
+---
+ lang/python/tests/run-tests.py | 25 +++++++++++++++----------
+ 1 file changed, 15 insertions(+), 10 deletions(-)
+
+diff --git a/lang/python/tests/run-tests.py b/lang/python/tests/run-tests.py
+index 5d5294a..a47e3f0 100644
+--- a/lang/python/tests/run-tests.py
++++ b/lang/python/tests/run-tests.py
+@@ -51,6 +51,9 @@ parser.add_argument('--srcdir', type=str,
+ parser.add_argument('--builddir', type=str,
+ default=os.environ.get("abs_builddir", ""),
+ help='Location of the tests.')
++parser.add_argument('--python-libdir', type=str,
++ default=None,
++ help='Optional location of the in-tree module lib directory.')
+ parser.add_argument('--parallel', action="store_true", default=False,
+ help='Ignored. For compatibility with run-tests.scm.')
+
+@@ -69,18 +72,20 @@ for interpreter in args.interpreters:
+ version = subprocess.check_output(
+ [interpreter, "-c", "import sys; print('{0}.{1}'.format(sys.version_info[0], sys.version_info[1]))"]).strip().decode()
+
+- pattern = os.path.join(args.builddir, "..",
+- "{0}-gpg".format(os.path.basename(interpreter)),
+- "lib*")
+- builddirs = glob.glob(pattern)
+- if len(builddirs) == 0:
+- sys.exit("Build directory matching {0!r} not found.".format(pattern))
+- elif len(builddirs) > 1:
+- sys.exit("Multiple build directories matching {0!r} found: {1}".format(
+- pattern, builddirs))
++ if not args.python_libdir:
++ pattern = os.path.join(args.builddir, "..",
++ "{0}-gpg".format(os.path.basename(interpreter)),
++ "lib*")
++ libdirs = glob.glob(pattern)
++ if len(libdirs) == 0:
++ sys.exit("Build directory matching {0!r} not found.".format(pattern))
++ elif len(libdirs) > 1:
++ sys.exit("Multiple build directories matching {0!r} found: {1}".format(
++ pattern, libdirs))
++ python_libdir = libdirs[0]
+
+ env = dict(os.environ)
+- env["PYTHONPATH"] = builddirs[0]
++ env["PYTHONPATH"] = python_libdir
+
+ if not args.quiet:
+ print("Running tests using {0} ({1})...".format(interpreter, version))
diff --git a/debian/patches/0014-python-Fix-distcheck.patch b/debian/patches/0014-python-Fix-distcheck.patch
new file mode 100644
index 0000000..99126a4
--- /dev/null
+++ b/debian/patches/0014-python-Fix-distcheck.patch
@@ -0,0 +1,30 @@
+From: Justus Winter <justus at g10code.com>
+Date: Mon, 10 Apr 2017 15:20:34 +0200
+Subject: python: Fix distcheck.
+
+* lang/python/Makefile.am (uninstall-local): Explicitly request the
+scheme 'posix_prefix'. On Python2.7 the default scheme is
+'posix_local', breaking distcheck.
+
+Fixes-commit: 25e6444b3f4601c7821beab06bc4520deacb007b
+Signed-off-by: Justus Winter <justus at g10code.com>
+(cherry picked from commit db476e923415f8e458720aaafde7234b802a33ab)
+---
+ lang/python/Makefile.am | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+diff --git a/lang/python/Makefile.am b/lang/python/Makefile.am
+index 90075f7..8f80a6b 100644
+--- a/lang/python/Makefile.am
++++ b/lang/python/Makefile.am
+@@ -93,8 +93,8 @@ install-exec-local:
+ done
+
+ uninstall-local:
+- GV=$$(echo $(VERSION) | tr - _); for PYTHON in $(PYTHONS); do \
+- PLATLIB="$(prefix)/$$("$${PYTHON}" -c 'import sysconfig, os; print(os.path.relpath(sysconfig.get_path("platlib"), sysconfig.get_config_var("prefix")))')" ; \
++ set -x; GV=$$(echo $(VERSION) | tr - _); for PYTHON in $(PYTHONS); do \
++ PLATLIB="$(prefix)/$$("$${PYTHON}" -c 'import sysconfig, os; print(os.path.relpath(sysconfig.get_path("platlib", scheme="posix_prefix"), sysconfig.get_config_var("prefix")))')" ; \
+ rm -rf -- "$(DESTDIR)$${PLATLIB}/gpg" \
+ "$(DESTDIR)$${PLATLIB}"/gpg-$$GV-py*.egg-info ; \
+ done
diff --git a/debian/patches/0015-python-Prune-CLEANFILES.patch b/debian/patches/0015-python-Prune-CLEANFILES.patch
new file mode 100644
index 0000000..2ce030a
--- /dev/null
+++ b/debian/patches/0015-python-Prune-CLEANFILES.patch
@@ -0,0 +1,27 @@
+From: Justus Winter <justus at g10code.com>
+Date: Mon, 10 Apr 2017 15:24:03 +0200
+Subject: python: Prune CLEANFILES.
+
+--
+Fixes-commit: e7d9c0c3d773f826dbd2ed417d04e25c410f3374
+Signed-off-by: Justus Winter <justus at g10code.com>
+(cherry picked from commit 63bec9f48666d811b65e2ef5cc63f8b731249088)
+---
+ lang/python/Makefile.am | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+diff --git a/lang/python/Makefile.am b/lang/python/Makefile.am
+index 8f80a6b..3fa98b5 100644
+--- a/lang/python/Makefile.am
++++ b/lang/python/Makefile.am
+@@ -65,9 +65,7 @@ upload: python$(PYTHON_VERSION)-gpg-dist/gpg-$(VERSION).tar.gz \
+ CLEANFILES = copystamp \
+ config.h \
+ data.h \
+- gpg \
+- files.txt \
+- install_files.txt
++ gpg
+
+ # Remove the rest.
+ #
diff --git a/debian/patches/0016-python-fix-run-tests-missing-python_libdir.patch b/debian/patches/0016-python-fix-run-tests-missing-python_libdir.patch
new file mode 100644
index 0000000..89dea3d
--- /dev/null
+++ b/debian/patches/0016-python-fix-run-tests-missing-python_libdir.patch
@@ -0,0 +1,28 @@
+From: Alon Bar-Lev <alon.barlev at gmail.com>
+Date: Tue, 11 Apr 2017 01:55:13 +0300
+Subject: python: fix run-tests missing python_libdir
+
+* lang/python/tests/run-tests.py: Set python_libdir if --python-libdir
+is set.
+
+Signed-off-by: Alon Bar-Lev <alon.barlev at gmail.com>
+(cherry picked from commit d785c053a982bddefd7014dc6856d1af345fe9fb)
+---
+ lang/python/tests/run-tests.py | 4 +++-
+ 1 file changed, 3 insertions(+), 1 deletion(-)
+
+diff --git a/lang/python/tests/run-tests.py b/lang/python/tests/run-tests.py
+index a47e3f0..e2a363e 100644
+--- a/lang/python/tests/run-tests.py
++++ b/lang/python/tests/run-tests.py
+@@ -72,7 +72,9 @@ for interpreter in args.interpreters:
+ version = subprocess.check_output(
+ [interpreter, "-c", "import sys; print('{0}.{1}'.format(sys.version_info[0], sys.version_info[1]))"]).strip().decode()
+
+- if not args.python_libdir:
++ if args.python_libdir:
++ python_libdir = args.python_libdir
++ else:
+ pattern = os.path.join(args.builddir, "..",
+ "{0}-gpg".format(os.path.basename(interpreter)),
+ "lib*")
diff --git a/debian/patches/0017-python-use-autoconf-pre-processor-when-building-via-.patch b/debian/patches/0017-python-use-autoconf-pre-processor-when-building-via-.patch
new file mode 100644
index 0000000..826b585
--- /dev/null
+++ b/debian/patches/0017-python-use-autoconf-pre-processor-when-building-via-.patch
@@ -0,0 +1,55 @@
+From: Alon Bar-Lev <alon.barlev at gmail.com>
+Date: Tue, 11 Apr 2017 03:56:00 +0300
+Subject: python: use autoconf pre-processor when building via autoconf
+
+* configure.ac: Add AC_PROG_CPP.
+* lang/python/Makefile.am: Set CPP environment for setup.py to use.
+
+Signed-off-by: Alon Bar-Lev <alon.barlev at gmail.com>
+(cherry picked from commit a827382cafe7f1425455dcc8bf5ef049172eb493)
+---
+ configure.ac | 1 +
+ lang/python/Makefile.am | 4 ++++
+ 2 files changed, 5 insertions(+)
+
+diff --git a/configure.ac b/configure.ac
+index 36c547b..794e0d9 100644
+--- a/configure.ac
++++ b/configure.ac
+@@ -106,6 +106,7 @@ AH_VERBATIM([_REENTRANT],
+ #endif])
+
+ AC_PROG_CC
++AC_PROG_CPP
+ AC_PROG_CXX
+
+ # Note: A suitable gitlog-to-changelog script can be found in GnuPG master.
+diff --git a/lang/python/Makefile.am b/lang/python/Makefile.am
+index 3fa98b5..8d74cbd 100644
+--- a/lang/python/Makefile.am
++++ b/lang/python/Makefile.am
+@@ -39,6 +39,7 @@ copystamp:
+
+ all-local: copystamp
+ set -e ; for PYTHON in $(PYTHONS); do \
++ CPP="$(CPP)" \
+ CFLAGS="$(CFLAGS)" \
+ srcdir="$(srcdir)" \
+ top_builddir="$(top_builddir)" \
+@@ -47,6 +48,7 @@ all-local: copystamp
+
+ python$(PYTHON_VERSION)-gpg/dist/gpg-$(VERSION).tar.gz.asc: copystamp
+ $(MKDIR_P) python$(PYTHON_VERSION)-gpg-dist
++ CPP="$(CPP)" \
+ CFLAGS="$(CFLAGS)" \
+ srcdir="$(srcdir)" \
+ top_builddir="$(top_builddir)" \
+@@ -80,6 +82,8 @@ clean-local:
+
+ install-exec-local:
+ set -e ; for PYTHON in $(PYTHONS); do \
++ CPP="$(CPP)" \
++ CFLAGS="$(CFLAGS)" \
+ srcdir="$(srcdir)" \
+ top_builddir="$(top_builddir)" \
+ $$PYTHON setup.py \
diff --git a/debian/patches/0018-tests-Update-encrypted-sample-files.patch b/debian/patches/0018-tests-Update-encrypted-sample-files.patch
new file mode 100644
index 0000000..94608de
--- /dev/null
+++ b/debian/patches/0018-tests-Update-encrypted-sample-files.patch
@@ -0,0 +1,80 @@
+From: Justus Winter <justus at g10code.com>
+Date: Mon, 24 Apr 2017 15:29:07 +0200
+Subject: tests: Update encrypted sample files.
+
+* tests/gpg/cipher-1.asc: Update file.
+* tests/gpg/cipher-2.asc: Likewise.
+--
+Convert the plaintext to UTF-8 and re-create the encrypted file.
+
+Fixes-commit: a11450eb048df79a3f2b00ebef6d7cab07ad5054
+Signed-off-by: Justus Winter <justus at g10code.com>
+(cherry picked from commit ac28e66f46132ae4a854d04b2f17acd4d55e4296)
+---
+ tests/gpg/cipher-1.asc | 22 ++++++++++------------
+ tests/gpg/cipher-2.asc | 25 ++++++++++++-------------
+ 2 files changed, 22 insertions(+), 25 deletions(-)
+
+diff --git a/tests/gpg/cipher-1.asc b/tests/gpg/cipher-1.asc
+index f0a8ca4..fbb6d58 100644
+--- a/tests/gpg/cipher-1.asc
++++ b/tests/gpg/cipher-1.asc
+@@ -1,15 +1,13 @@
+ -----BEGIN PGP MESSAGE-----
+-Version: GnuPG v1.0.4-2 (GNU/Linux)
+-Comment: For info see http://www.gnupg.org
+
+-hQEOA2rm1+5GqHH4EAP/Tcqiuhvrjj+RFBKnWn2A7f1ztV17U2EngYFy8TbZYGNp
+-JoMNdpA7GNZs7iqc/x1epaZDKfaQwWEtARZmK/4nlhB48N+oZeKTm7PXIkRPqrCZ
+-3fxJjCJaU0yrNGuO345DOr0QwDImVhubVEkfgs8yXK2Szx2G8X3LmiaILHAqA2oD
+-/1ZqjY8k+ovrLL/qe8un/NTwzSjKIPVGR6mhLFXmj8fnp2kSsbo+Bhh4MczTRR6l
+-SA32z25vcakKu2qn5Wa4yDcx9NcMt8RHXzmfMDLj6UFq99QqKeLK2ywcIpY9p/GL
+-fQyaf7r3HTVugBSaoOzegLJ+L7MfWohrStkMeLnJQnro0nYBjADVcUQuSS4N3lst
+-Df3XrxxA/iJvxt4F9K27u4tp5U1HDg1CIxVrkMs92LBri3S6ZtfjdoqQ7QghFwGP
+-Kw1lKiWayM6NH9rcCKSgk4kl4P/2l3f78XeFgiywN7UGeSoH3BLMSv9gSxl5KrAz
+-d2imhTMrfEvZ
+-=y4ng
++hQEOA2rm1+5GqHH4EAP/XKz8pdonnZg2dqJhjdas4vQHPxspxLhgf7OuYigodBpI
++l7srTvqtuRsDFNorgURW6DjPqfGqpZsn2uf8enUskunHVMQFBILX38d+G5SkisqF
++uOZUlmh0ZfVocCBGYt8ZPfa9ObmitPmZvhCReCHFlTj588ZjofKuNjmfw+QfmNcD
++/j4z4ijv6dKHQCm7EAjnOsCw9SbrAVpRXjibN7KT+w6QT6m+5w9k4RfhkTOlqrHq
++5d3ZyxLctdTkXlk0hXz1Mey4AEKTtlZGvrQVIhaX4hcB4NFJB0fZJ/pnKypi1H6q
++0bSBq2p6kCzJuNvrEr4wk4B1NsOTBacUSffXLrfsEH2F0ngBzN7d/KHBImu81F8w
++x96f6dELyYetV0UwhyFrPrA3lBQf9q5cNDqPiCHooUFOudQ5t0h7VtSU3fyaYoit
++cJGPFkIxhv+VAbEW/h5muEg3KO1iEqLP4RK3y0Jjy4pyEauAgviM68Vjf4OVvgta
++/IblIrp1FHxoCpA=
++=sEuD
+ -----END PGP MESSAGE-----
+diff --git a/tests/gpg/cipher-2.asc b/tests/gpg/cipher-2.asc
+index 210f3e9..f7c85f3 100644
+--- a/tests/gpg/cipher-2.asc
++++ b/tests/gpg/cipher-2.asc
+@@ -1,16 +1,15 @@
+ -----BEGIN PGP MESSAGE-----
+-Version: GnuPG v1.0.6 (GNU/Linux)
+-Comment: Weitere Infos: siehe http://www.gnupg.org
+
+-hQEOA++dwnahcsiBEAP9HgkC1ElQwZRX1X/MBF54Q28dpXKr84IviO4QcbnnhmYk
+-2IlaNe6mr8R7kNM1aqJFK3fnobqnSWwM/VBObMqqYnzZSfclCNsy66sojQJxwXcz
+-DKQKi69BLaC6aTMnX048tOl8pJpR72fkffUOUa5ywDHVVVUClDG3XkIrfM1du3YD
+-/A6vFSrRylupKhQBxdtSUx5IDmpDYwG2vqqbYKoMaQ4pPSKLYV2zskU+pQWRlk6y
+-nwPGY5h9eGz0xYHMPxhe9VnwljeTEDwz5U4CHF3wQ8h5WBxOVx5QN/H/UyjpmoJT
+-ddrIu+8GgajhBVKVYAOqr577exkiSDA60/JrYbKZBvzL0sAJAUu+HoeMPJ+5/RYF
+-pLSdz/3MbVDRJJqzV2TJnEBvFtPa6urzx99P0u4xr+RJMFmR9/99YXhYz7+Y6d/B
+-44F6B3YouYxiK39IoOUcYPZTwb5kaudD5a3mU3XxEhSDUpnyvowPiKQO1T8CPd2u
+-2HsD3KeaOc2VFE0gnvqECvUTQfSCZCk/kil8XVAMHZrEA0bWAYiaHfHEOB8SRCy8
+-rW0wsON4uDXmZpUkfOjFoYZdpJI7fDKkb5uYUzFZDasharEaXb1X/5xSAclx
+-=+eYk
++hQEOA++dwnahcsiBEAQAqaF1yuTJ26FmJHndyaHUjazx7j8/Z/Ht3O+jSAOaoJFR
++84rK4Tte0JQYTCl3XxwSEwr48OAtyeTstLjabGAvBoHrXVP3xC0U7kBalZm2lwcq
++A8dDDoa3uMkWi1OJ3e2o79/z6SdTHEgRIRomAku1JaXFGTd8OsFhW782RpKUBOID
++/jMs9o2sa/gDhWVaeC3SaQovl2xb45ev0nMibED916BQvv3NkH5/EzeM6v788h63
++4yUkWWNr0/bnJ21chlxIbvICjHfuGAEDw+i4HhK/nLBL3Ep4ADtLP7OPZJHlcQgI
++g8mAztasBxTGGUuFYvRT0X7sbaSPxLR26vbTCYAo/P/80sA4AYGhBuYPsRN4JzX9
++QaSrToKjPbaZqq+nHQYCvi6m5xAjMT0HVdXejMtZMKwv4TRm7IVCimtIZqrlvw7c
++Kj+ZcDGq9qb7urnzC5mdAZkXyNtZxmMKYFI0ci7zMnflvIM87JrVEjZbjjiXlcVy
++mSxhufOOweLJARkJ4mKVq1tr8REu8/ots4fDzUIAITM3z8pKA7doWAH2VTo0Idmc
++wYOoTLkiq1Z8fxeryB6U66C831PDiWe7W0usRSVo5rZ7laLZeOGl33fAAZCNLTgv
++tOPWWg5rCpRTVXgQ6Edl7DtzKI1z4EJbuEUs6shW+OT3bNISiDz2am8remU=
++=9AEU
+ -----END PGP MESSAGE-----
diff --git a/debian/patches/0019-doc-Improve-doc-on-passphrase_cb-pinentry-mode.patch b/debian/patches/0019-doc-Improve-doc-on-passphrase_cb-pinentry-mode.patch
new file mode 100644
index 0000000..9ca850a
--- /dev/null
+++ b/debian/patches/0019-doc-Improve-doc-on-passphrase_cb-pinentry-mode.patch
@@ -0,0 +1,45 @@
+From: Andre Heinecke <aheinecke at intevation.de>
+Date: Mon, 24 Apr 2017 16:46:09 +0200
+Subject: doc: Improve doc on passphrase_cb / pinentry mode
+
+* doc/gpgme.texi (Passphrase Callback): Mention pinentry_mode and
+restrictions.
+(Pinentry Mode): Fix wording and clarify versions that need
+loopback mode for passphrase_cb to work.
+
+(cherry picked from commit 8d61aba1fe0379ba14494f8ae2011ba531554ef4)
+---
+ doc/gpgme.texi | 12 ++++++++----
+ 1 file changed, 8 insertions(+), 4 deletions(-)
+
+diff --git a/doc/gpgme.texi b/doc/gpgme.texi
+index 40423cf..bc40430 100644
+--- a/doc/gpgme.texi
++++ b/doc/gpgme.texi
+@@ -2597,11 +2597,11 @@ Return a Pinentry error @code{No Pinentry}.
+
+ @item GPGME_PINENTRY_MODE_LOOPBACK
+ Redirect Pinentry queries to the caller.
+-This enables the use of @code{gpgme_set_passphrase_cb} whis pinentry
+-queries redirected to gpgme.
++This enables the use of @code{gpgme_set_passphrase_cb} because pinentry
++queries are redirected to gpgme.
+
+-Note: This mode requires @code{allow-loopback-pinentry} to be enabled
+-in the @file{gpg-agent.conf} or an agent started with that option.
++Note: For 2.1.0 - 2.1.12 this mode requires @code{allow-loopback-pinentry}
++to be enabled in the @file{gpg-agent.conf} or an agent started with that option.
+
+ @end table
+ @end deftp
+@@ -2763,6 +2763,10 @@ character before returning from the callback.
+ If an error occurs, return the corresponding @code{gpgme_error_t}
+ value. You can use the error code @code{GPG_ERR_CANCELED} to abort
+ the operation. Otherwise, return @code{0}.
++
++Note: The passphrase_cb only works with GnuPG 1.x and 2.1.x and not
++with the 2.0.x series. See @code{gpgme_set_pinentry_mode} for more
++details on 2.1.x usage.
+ @end deftp
+
+ @deftypefun void gpgme_set_passphrase_cb (@w{gpgme_ctx_t @var{ctx}}, @w{gpgme_passphrase_cb_t @var{passfunc}}, @w{void *@var{hook_value}})
diff --git a/debian/patches/0020-core-Don-t-split-gpgconf-strings-on-comma.patch b/debian/patches/0020-core-Don-t-split-gpgconf-strings-on-comma.patch
new file mode 100644
index 0000000..14eae07
--- /dev/null
+++ b/debian/patches/0020-core-Don-t-split-gpgconf-strings-on-comma.patch
@@ -0,0 +1,40 @@
+From: Andre Heinecke <aheinecke at intevation.de>
+Date: Wed, 5 Apr 2017 18:23:48 +0200
+Subject: core: Don't split gpgconf strings on comma
+
+* src/engine-gpgconf.c (gpgconf_parse_option): Don't split
+strings on comma.
+
+--
+This only affects values where the main type is string. Values
+with the alt_type string but another main type are still split
+to keep lists (e.g. groups) working.
+
+(cherry picked from commit df4eb611e33dcab7bebf07b13734c7db7ccf40da)
+---
+ src/engine-gpgconf.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+diff --git a/src/engine-gpgconf.c b/src/engine-gpgconf.c
+index 6f7c8ac..af5f110 100644
+--- a/src/engine-gpgconf.c
++++ b/src/engine-gpgconf.c
+@@ -399,7 +399,7 @@ gpgconf_parse_option (gpgme_conf_opt_t opt,
+ gpgme_conf_arg_t *arg_p, char *line)
+ {
+ gpgme_error_t err;
+- char *mark;
++ char *mark = NULL;
+
+ if (!line[0])
+ return 0;
+@@ -408,7 +408,8 @@ gpgconf_parse_option (gpgme_conf_opt_t opt,
+ {
+ gpgme_conf_arg_t arg;
+
+- mark = strchr (line, ',');
++ if (opt->type != GPGME_CONF_STRING)
++ mark = strchr (line, ',');
+ if (mark)
+ *mark = '\0';
+
diff --git a/debian/patches/0021-qt-tests-Don-t-use-internal-API.patch b/debian/patches/0021-qt-tests-Don-t-use-internal-API.patch
new file mode 100644
index 0000000..0ff21ad
--- /dev/null
+++ b/debian/patches/0021-qt-tests-Don-t-use-internal-API.patch
@@ -0,0 +1,153 @@
+From: Andre Heinecke <aheinecke at intevation.de>
+Date: Wed, 10 May 2017 10:18:41 +0200
+Subject: qt, tests: Don't use internal API
+
+* lang/qt/tests/t-encrypt.cpp, lang/qt/tests/t-tofuinfo.cpp:
+Only use exported API.
+
+--
+With the Job::Context hack we no longer need to use internal API.
+
+(cherry picked from commit b56f398eff4e3e70dea714c3174a5512dd9bcf33)
+---
+ lang/qt/tests/t-encrypt.cpp | 29 +++++++++++++++--------------
+ lang/qt/tests/t-tofuinfo.cpp | 7 ++++---
+ 2 files changed, 19 insertions(+), 17 deletions(-)
+
+diff --git a/lang/qt/tests/t-encrypt.cpp b/lang/qt/tests/t-encrypt.cpp
+index a2d8dc4..d9b5a5c 100644
+--- a/lang/qt/tests/t-encrypt.cpp
++++ b/lang/qt/tests/t-encrypt.cpp
+@@ -41,10 +41,10 @@
+ #include "encryptjob.h"
+ #include "signencryptjob.h"
+ #include "signingresult.h"
+-#include "qgpgmeencryptjob.h"
++#include "encryptjob.h"
+ #include "encryptionresult.h"
+ #include "decryptionresult.h"
+-#include "qgpgmedecryptjob.h"
++#include "decryptjob.h"
+ #include "qgpgmebackend.h"
+ #include "keylistresult.h"
+ #include "engineinfo.h"
+@@ -105,11 +105,11 @@ private Q_SLOTS:
+ if (!decryptSupported()) {
+ return;
+ }
+- auto ctx = Context::createForProtocol(OpenPGP);
++ auto decJob = openpgp()->decryptJob();
++ auto ctx = Job::context(decJob);
+ TestPassphraseProvider provider;
+ ctx->setPassphraseProvider(&provider);
+ ctx->setPinentryMode(Context::PinentryLoopback);
+- auto decJob = new QGpgMEDecryptJob(ctx);
+ QByteArray plainText;
+ auto decResult = decJob->exec(cipherText, plainText);
+ QVERIFY(!decResult.error());
+@@ -176,13 +176,13 @@ private Q_SLOTS:
+ if (!decryptSupported()) {
+ return;
+ }
+- auto ctx = Context::createForProtocol(OpenPGP);
++ auto job = openpgp()->encryptJob();
++ auto ctx = Job::context(job);
+ TestPassphraseProvider provider;
+ ctx->setPassphraseProvider(&provider);
+ ctx->setPinentryMode(Context::PinentryLoopback);
+ ctx->setArmor(true);
+ ctx->setTextMode(true);
+- auto job = new QGpgMEEncryptJob(ctx);
+ QByteArray cipherText;
+ auto result = job->exec(std::vector<Key>(), QStringLiteral("Hello symmetric World").toUtf8(), Context::AlwaysTrust, cipherText);
+ delete job;
+@@ -192,10 +192,10 @@ private Q_SLOTS:
+
+ killAgent(mDir.path());
+
+- auto ctx2 = Context::createForProtocol(OpenPGP);
++ auto decJob = openpgp()->decryptJob();
++ auto ctx2 = Job::context(decJob);
+ ctx2->setPassphraseProvider(&provider);
+ ctx2->setPinentryMode(Context::PinentryLoopback);
+- auto decJob = new QGpgMEDecryptJob(ctx2);
+ QByteArray plainText;
+ auto decResult = decJob->exec(cipherText, plainText);
+ QVERIFY(!result.error());
+@@ -239,13 +239,14 @@ private:
+ if (!decryptSupported()) {
+ return;
+ }
+- auto ctx = Context::createForProtocol(OpenPGP);
++
++ auto decJob = openpgp()->decryptJob();
++ auto ctx = Job::context(decJob);
+ TestPassphraseProvider provider;
+ ctx->setPassphraseProvider(&provider);
+ ctx->setPinentryMode(Context::PinentryLoopback);
+ ctx->setDecryptionFlags(Context::DecryptUnwrap);
+
+- auto decJob = new QGpgMEDecryptJob(ctx);
+ QByteArray plainText;
+ auto decResult = decJob->exec(cipherText, plainText);
+
+@@ -283,12 +284,12 @@ private:
+ QVERIFY(keys.size() == 1);
+ delete listjob;
+
+- auto ctx = Context::createForProtocol(OpenPGP);
++ auto job = openpgp()->encryptJob();
++ auto ctx = Job::context(job);
+ ctx->setPassphraseProvider(new TestPassphraseProvider);
+ ctx->setPinentryMode(Context::PinentryLoopback);
+ ctx->setArmor(true);
+ ctx->setTextMode(true);
+- auto job = new QGpgMEEncryptJob(ctx);
+ QByteArray cipherText;
+ printf("Before exec, flags: %x\n", Context::Symmetric | Context::AlwaysTrust);
+ auto result = job->exec(keys, QStringLiteral("Hello symmetric World").toUtf8(),
+@@ -311,11 +312,11 @@ private:
+ agentConf.write("allow-loopback-pinentry");
+ agentConf.close();
+
+- auto ctx2 = Context::createForProtocol(OpenPGP);
++ auto decJob = openpgp()->decryptJob();
++ auto ctx2 = Job::context(decJob);
+ ctx2->setPassphraseProvider(new TestPassphraseProvider);
+ ctx2->setPinentryMode(Context::PinentryLoopback);
+ ctx2->setTextMode(true);
+- auto decJob = new QGpgMEDecryptJob(ctx2);
+ QByteArray plainText;
+ auto decResult = decJob->exec(cipherText, plainText);
+ QVERIFY(!decResult.error());
+diff --git a/lang/qt/tests/t-tofuinfo.cpp b/lang/qt/tests/t-tofuinfo.cpp
+index e16b1fd..2dd25ea 100644
+--- a/lang/qt/tests/t-tofuinfo.cpp
++++ b/lang/qt/tests/t-tofuinfo.cpp
+@@ -47,10 +47,11 @@
+ #include "importresult.h"
+ #include "keylistjob.h"
+ #include "keylistresult.h"
+-#include "qgpgmesignjob.h"
++#include "signjob.h"
+ #include "key.h"
+ #include "t-support.h"
+ #include "engineinfo.h"
++#include "context.h"
+ #include <iostream>
+
+ using namespace QGpgME;
+@@ -133,11 +134,11 @@ private:
+
+ void signAndVerify(const QString &what, const GpgME::Key &key, int expected)
+ {
+- Context *ctx = Context::createForProtocol(OpenPGP);
++ auto job = openpgp()->signJob();
++ auto ctx = Job::context(job);
+ TestPassphraseProvider provider;
+ ctx->setPassphraseProvider(&provider);
+ ctx->setPinentryMode(Context::PinentryLoopback);
+- auto *job = new QGpgMESignJob(ctx);
+
+ std::vector<Key> keys;
+ keys.push_back(key);
diff --git a/debian/patches/0022-qt-Undeprecate-API-that-I-find-useful.patch b/debian/patches/0022-qt-Undeprecate-API-that-I-find-useful.patch
new file mode 100644
index 0000000..f17de30
--- /dev/null
+++ b/debian/patches/0022-qt-Undeprecate-API-that-I-find-useful.patch
@@ -0,0 +1,92 @@
+From: Andre Heinecke <aheinecke at intevation.de>
+Date: Wed, 10 May 2017 10:22:23 +0200
+Subject: qt: Undeprecate API that I find useful
+
+* lang/qt/src/decryptjob.h,
+lang/qt/src/decryptverifyjob.h,
+lang/qt/src/signencryptjob.h,
+lang/qt/src/verifydetachedjob.h,
+lang/qt/src/verifyopaquejob.h: Undeprecate ByteArray based API.
+
+--
+While an IODevice may be more performant the ByteArray API is
+a very easy way to get started with QGpgME as it allows you
+basically to encrypt / decrypt any QString.
+
+This also fixes a ton of deprecation warnings in KDE where this
+API is used all over the place.
+
+(cherry picked from commit cc2ef3d07c0d261bb9e8e8c0f2706e1a08e4ec53)
+---
+ lang/qt/src/decryptjob.h | 2 +-
+ lang/qt/src/decryptverifyjob.h | 2 +-
+ lang/qt/src/signencryptjob.h | 2 +-
+ lang/qt/src/verifydetachedjob.h | 2 +-
+ lang/qt/src/verifyopaquejob.h | 2 +-
+ 5 files changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/lang/qt/src/decryptjob.h b/lang/qt/src/decryptjob.h
+index c4fc86f..f59d753 100644
+--- a/lang/qt/src/decryptjob.h
++++ b/lang/qt/src/decryptjob.h
+@@ -75,7 +75,7 @@ public:
+ Starts the decryption operation. \a cipherText is the data to
+ decrypt.
+ */
+- virtual QGPGME_DEPRECATED_EXPORT GpgME::Error start(const QByteArray &cipherText) = 0;
++ virtual GpgME::Error start(const QByteArray &cipherText) = 0;
+
+ /*!
+ \overload
+diff --git a/lang/qt/src/decryptverifyjob.h b/lang/qt/src/decryptverifyjob.h
+index 97af008..f64d747 100644
+--- a/lang/qt/src/decryptverifyjob.h
++++ b/lang/qt/src/decryptverifyjob.h
+@@ -76,7 +76,7 @@ public:
+ Starts the combined decryption and verification operation.
+ \a cipherText is the data to decrypt and later verify.
+ */
+- virtual QGPGME_DEPRECATED_EXPORT GpgME::Error start(const QByteArray &cipherText) = 0;
++ virtual GpgME::Error start(const QByteArray &cipherText) = 0;
+
+ /*!
+ \overload
+diff --git a/lang/qt/src/signencryptjob.h b/lang/qt/src/signencryptjob.h
+index 4e07744..427912d 100644
+--- a/lang/qt/src/signencryptjob.h
++++ b/lang/qt/src/signencryptjob.h
+@@ -94,7 +94,7 @@ public:
+ \em recipient keys will not be performed, but full validity
+ assumed for all \em recipient keys without further checks.
+ */
+- virtual QGPGME_DEPRECATED_EXPORT GpgME::Error start(const std::vector<GpgME::Key> &signers,
++ virtual GpgME::Error start(const std::vector<GpgME::Key> &signers,
+ const std::vector<GpgME::Key> &recipients,
+ const QByteArray &plainText,
+ bool alwaysTrust = false) = 0;
+diff --git a/lang/qt/src/verifydetachedjob.h b/lang/qt/src/verifydetachedjob.h
+index b339a8c..792fec8 100644
+--- a/lang/qt/src/verifydetachedjob.h
++++ b/lang/qt/src/verifydetachedjob.h
+@@ -77,7 +77,7 @@ public:
+ signature data, while \a signedData contains the data over
+ which the signature was made.
+ */
+- virtual QGPGME_DEPRECATED_EXPORT GpgME::Error start(const QByteArray &signature,
++ virtual GpgME::Error start(const QByteArray &signature,
+ const QByteArray &signedData) = 0;
+
+ /*!
+diff --git a/lang/qt/src/verifyopaquejob.h b/lang/qt/src/verifyopaquejob.h
+index f064049..4d62576 100644
+--- a/lang/qt/src/verifyopaquejob.h
++++ b/lang/qt/src/verifyopaquejob.h
+@@ -76,7 +76,7 @@ public:
+ signature data, while \a signedData contains the data over
+ which the signature was made.
+ */
+- virtual QGPGME_DEPRECATED_EXPORT GpgME::Error start(const QByteArray &signedData) = 0;
++ virtual GpgME::Error start(const QByteArray &signedData) = 0;
+
+ /*!
+ \overload
diff --git a/debian/patches/0023-qt-Add-a-missing-include-functional.patch b/debian/patches/0023-qt-Add-a-missing-include-functional.patch
new file mode 100644
index 0000000..2699363
--- /dev/null
+++ b/debian/patches/0023-qt-Add-a-missing-include-functional.patch
@@ -0,0 +1,33 @@
+From: Andre Heinecke <aheinecke at intevation.de>
+Date: Wed, 10 May 2017 10:24:18 +0200
+Subject: qt: Add a missing include <functional>
+MIME-Version: 1.0
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: 8bit
+
+* lang/qt/src/qgpgmenewcryptoconfig.cpp: Include functional.
+
+--
+This is intended to fix compilation against the c++ stdlib from
+Gentoo / GCC 7.
+
+Patch provided by Martin Väth.
+GnuPG-Bug-Id: T3151
+
+(cherry picked from commit 5e27bf98b4c48cf6a239bcc94b7b67515ff339e7)
+---
+ lang/qt/src/qgpgmenewcryptoconfig.cpp | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/lang/qt/src/qgpgmenewcryptoconfig.cpp b/lang/qt/src/qgpgmenewcryptoconfig.cpp
+index 6901eef..87d2d38 100644
+--- a/lang/qt/src/qgpgmenewcryptoconfig.cpp
++++ b/lang/qt/src/qgpgmenewcryptoconfig.cpp
+@@ -48,6 +48,7 @@
+
+ #include <sstream>
+ #include <string>
++#include <functional>
+ #include <cassert>
+ #include <functional>
+
diff --git a/debian/patches/0024-qt-Stop-agent-on-clean.patch b/debian/patches/0024-qt-Stop-agent-on-clean.patch
new file mode 100644
index 0000000..3982c25
--- /dev/null
+++ b/debian/patches/0024-qt-Stop-agent-on-clean.patch
@@ -0,0 +1,24 @@
+From: Justus Winter <justus at g10code.com>
+Date: Wed, 10 May 2017 15:23:56 +0200
+Subject: qt: Stop agent on clean.
+
+* lang/qt/tests/Makefile.am (clean-local): Stop agent.
+
+Signed-off-by: Justus Winter <justus at g10code.com>
+(cherry picked from commit 093b5497b7231590ce91ccf73ba64ebc0757f9e9)
+---
+ lang/qt/tests/Makefile.am | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/lang/qt/tests/Makefile.am b/lang/qt/tests/Makefile.am
+index fb45eec..fbcb63e 100644
+--- a/lang/qt/tests/Makefile.am
++++ b/lang/qt/tests/Makefile.am
+@@ -69,6 +69,7 @@ CLEANFILES = secring.gpg pubring.gpg pubring.kbx trustdb.gpg dirmngr.conf \
+ gpg.conf tofu.db
+
+ clean-local:
++ -$(top_srcdir)/tests/start-stop-agent --stop
+ -rm -fR private-keys-v1.d crls.d
+
+ export GNUPGHOME := $(abs_builddir)
diff --git a/debian/patches/0025-tests-Harmonize-test-suites.patch b/debian/patches/0025-tests-Harmonize-test-suites.patch
new file mode 100644
index 0000000..cb5cbad
--- /dev/null
+++ b/debian/patches/0025-tests-Harmonize-test-suites.patch
@@ -0,0 +1,93 @@
+From: Justus Winter <justus at g10code.com>
+Date: Wed, 10 May 2017 15:49:54 +0200
+Subject: tests: Harmonize test suites.
+
+* lang/python/tests/Makefile.am: Create test environment as part of
+'make all'.
+* tests/gpg/Makefile.am: Make sure the private keystore is created
+first.
+* tests/gpgsm/Makefile.am: Create test environment as part of
+'make all'. Make sure the private keystore is created
+first.
+
+Signed-off-by: Justus Winter <justus at g10code.com>
+(cherry picked from commit 15adff073bb89dc032d8342cfbbdad2850943f52)
+---
+ lang/python/tests/Makefile.am | 5 ++++-
+ tests/gpg/Makefile.am | 2 +-
+ tests/gpgsm/Makefile.am | 11 ++++++++---
+ 3 files changed, 13 insertions(+), 5 deletions(-)
+
+diff --git a/lang/python/tests/Makefile.am b/lang/python/tests/Makefile.am
+index 9c19a13..b62b524 100644
+--- a/lang/python/tests/Makefile.am
++++ b/lang/python/tests/Makefile.am
+@@ -71,7 +71,7 @@ check: xcheck
+
+ .PHONY: xcheck
+
+-xcheck: ./pubring-stamp
++xcheck:
+ $(TESTS_ENVIRONMENT) $(PYTHON) $(srcdir)/run-tests.py \
+ --interpreters="$(PYTHONS)" --srcdir=$(srcdir) $(TESTFLAGS) \
+ $(XTESTS)
+@@ -93,6 +93,9 @@ clean-local:
+ -rm -fR -- private-keys-v1.d openpgp-revocs.d S.gpg-agent sshcontrol
+
+
++BUILT_SOURCES = gpg.conf gpg-agent.conf pubring-stamp \
++ private-keys-v1.d/gpg-sample.stamp
++
+ ./private-keys-v1.d/gpg-sample.stamp: $(private_keys)
+ $(MKDIR_P) ./private-keys-v1.d
+ for k in $(private_keys); do \
+diff --git a/tests/gpg/Makefile.am b/tests/gpg/Makefile.am
+index 4cba303..2ac032b 100644
+--- a/tests/gpg/Makefile.am
++++ b/tests/gpg/Makefile.am
+@@ -95,7 +95,7 @@ export GPG_AGENT_INFO :=
+ done
+ echo x > ./private-keys-v1.d/gpg-sample.stamp
+
+-./pubring-stamp: $(srcdir)/pubdemo.asc
++./pubring-stamp: $(srcdir)/pubdemo.asc ./private-keys-v1.d/gpg-sample.stamp
+ $(GPG) --batch --no-permission-warning \
+ --import $(srcdir)/pubdemo.asc
+ -$(GPG) --batch --no-permission-warning \
+diff --git a/tests/gpgsm/Makefile.am b/tests/gpgsm/Makefile.am
+index 46d6a9b..76e4938 100644
+--- a/tests/gpgsm/Makefile.am
++++ b/tests/gpgsm/Makefile.am
+@@ -44,7 +44,7 @@ noinst_PROGRAMS = $(c_tests) t-genkey cms-keylist cms-decrypt
+
+ key_id = 32100C27173EF6E9C4E9A25D3D69F86D37A4F939
+
+-CLEANFILES = pubring.kbx pubring.kbx~ gpgsm.conf trustlist.txt \
++CLEANFILES = pubring-stamp pubring.kbx pubring.kbx~ gpgsm.conf trustlist.txt \
+ random_seed S.gpg-agent
+
+ clean-local:
+@@ -62,16 +62,21 @@ export GNUPGHOME := $(abs_builddir)
+
+ export GPG_AGENT_INFO :=
+
+-./pubring.kbx: $(srcdir)/cert_g10code_test1.der
++BUILT_SOURCES = gpgsm.conf trustlist.txt pubring-stamp \
++ private-keys-v1.d/gpg-sample.stamp
++
++./pubring-stamp: $(srcdir)/cert_g10code_test1.der ./private-keys-v1.d/gpg-sample.stamp
+ $(GPGSM) --import $(srcdir)/cert_g10code_test1.der
++ touch pubring-stamp
+
+ ./gpgsm.conf:
+ echo disable-crl-checks > ./gpgsm.conf
+ echo faked-system-time 1008241200 >> ./gpgsm.conf
+
+-./private-keys-v1.d/$(key_id).key: $(srcdir)/$(key_id)
++./private-keys-v1.d/gpg-sample.stamp: $(srcdir)/$(key_id)
+ $(MKDIR_P) ./private-keys-v1.d
+ cp $(srcdir)/$(key_id) private-keys-v1.d/$(key_id).key
++ echo x > ./private-keys-v1.d/gpg-sample.stamp
+
+ ./trustlist.txt:
+ echo $(key_id) > ./trustlist.txt
diff --git a/debian/patches/0026-tests-Make-sure-to-kill-all-previously-running-daemo.patch b/debian/patches/0026-tests-Make-sure-to-kill-all-previously-running-daemo.patch
new file mode 100644
index 0000000..206fd7a
--- /dev/null
+++ b/debian/patches/0026-tests-Make-sure-to-kill-all-previously-running-daemo.patch
@@ -0,0 +1,73 @@
+From: Justus Winter <justus at g10code.com>
+Date: Wed, 10 May 2017 15:52:12 +0200
+Subject: tests: Make sure to kill all previously running daemons.
+
+* lang/python/tests/Makefile.am: Kill all previously running daemons
+before creating the private key store.
+* lang/qt/tests/Makefile.am: Likewise.
+* tests/gpg/Makefile.am: Likewise.
+* tests/gpgsm/Makefile.am: Likewise.
+--
+
+Now that the daemons sockets are no longer created in the GNUPGHOME,
+we cannot rely on cleaning the build directory to make sure they are
+shut down. Therefore, we explicitly kill any running daemons when
+creating the test environment.
+
+Signed-off-by: Justus Winter <justus at g10code.com>
+(cherry picked from commit a226eca84670ef4e171c3a54e7caefb3a89254a4)
+---
+ lang/python/tests/Makefile.am | 1 +
+ lang/qt/tests/Makefile.am | 1 +
+ tests/gpg/Makefile.am | 1 +
+ tests/gpgsm/Makefile.am | 1 +
+ 4 files changed, 4 insertions(+)
+
+diff --git a/lang/python/tests/Makefile.am b/lang/python/tests/Makefile.am
+index b62b524..790d8f8 100644
+--- a/lang/python/tests/Makefile.am
++++ b/lang/python/tests/Makefile.am
+@@ -97,6 +97,7 @@ BUILT_SOURCES = gpg.conf gpg-agent.conf pubring-stamp \
+ private-keys-v1.d/gpg-sample.stamp
+
+ ./private-keys-v1.d/gpg-sample.stamp: $(private_keys)
++ -gpgconf --kill all
+ $(MKDIR_P) ./private-keys-v1.d
+ for k in $(private_keys); do \
+ cp $$k private-keys-v1.d/$${k#$(test_srcdir)/}.key; \
+diff --git a/lang/qt/tests/Makefile.am b/lang/qt/tests/Makefile.am
+index fbcb63e..6f3936b 100644
+--- a/lang/qt/tests/Makefile.am
++++ b/lang/qt/tests/Makefile.am
+@@ -76,6 +76,7 @@ export GNUPGHOME := $(abs_builddir)
+
+ pubring-stamp: $(top_srcdir)/tests/gpg/pubdemo.asc \
+ $(top_srcdir)/tests/gpg/secdemo.asc
++ -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
+diff --git a/tests/gpg/Makefile.am b/tests/gpg/Makefile.am
+index 2ac032b..ba23aff 100644
+--- a/tests/gpg/Makefile.am
++++ b/tests/gpg/Makefile.am
+@@ -89,6 +89,7 @@ export GNUPGHOME := $(abs_builddir)
+ export GPG_AGENT_INFO :=
+
+ ./private-keys-v1.d/gpg-sample.stamp: $(srcdir)/$(private_keys)
++ -gpgconf --kill all
+ $(MKDIR_P) ./private-keys-v1.d
+ for k in $(private_keys); do \
+ cp $(srcdir)/$$k private-keys-v1.d/$$k.key; \
+diff --git a/tests/gpgsm/Makefile.am b/tests/gpgsm/Makefile.am
+index 76e4938..adae6a2 100644
+--- a/tests/gpgsm/Makefile.am
++++ b/tests/gpgsm/Makefile.am
+@@ -74,6 +74,7 @@ BUILT_SOURCES = gpgsm.conf trustlist.txt pubring-stamp \
+ echo faked-system-time 1008241200 >> ./gpgsm.conf
+
+ ./private-keys-v1.d/gpg-sample.stamp: $(srcdir)/$(key_id)
++ -gpgconf --kill all
+ $(MKDIR_P) ./private-keys-v1.d
+ cp $(srcdir)/$(key_id) private-keys-v1.d/$(key_id).key
+ echo x > ./private-keys-v1.d/gpg-sample.stamp
diff --git a/debian/patches/0027-python-Fix-test-environment-creation.patch b/debian/patches/0027-python-Fix-test-environment-creation.patch
new file mode 100644
index 0000000..1276861
--- /dev/null
+++ b/debian/patches/0027-python-Fix-test-environment-creation.patch
@@ -0,0 +1,25 @@
+From: Justus Winter <justus at g10code.com>
+Date: Wed, 10 May 2017 16:19:52 +0200
+Subject: python: Fix test environment creation.
+
+* lang/python/tests/Makefile.am (pubring-stamp): Do not depend on the
+configuration files, this can trigger superfluous rebuilds.
+
+Signed-off-by: Justus Winter <justus at g10code.com>
+(cherry picked from commit 6b4dd3b929ac23271bfa96edb9dbb142eca0a30f)
+---
+ lang/python/tests/Makefile.am | 1 -
+ 1 file changed, 1 deletion(-)
+
+diff --git a/lang/python/tests/Makefile.am b/lang/python/tests/Makefile.am
+index 790d8f8..d0dde1d 100644
+--- a/lang/python/tests/Makefile.am
++++ b/lang/python/tests/Makefile.am
+@@ -105,7 +105,6 @@ BUILT_SOURCES = gpg.conf gpg-agent.conf pubring-stamp \
+ echo x > ./private-keys-v1.d/gpg-sample.stamp
+
+ ./pubring-stamp: $(test_srcdir)/pubdemo.asc \
+- ./gpg.conf ./gpg-agent.conf \
+ ./private-keys-v1.d/gpg-sample.stamp
+ $(GPG) --batch --no-permission-warning \
+ --import $(test_srcdir)/pubdemo.asc
diff --git a/debian/patches/0028-tests-Remove-remnants-of-check-local.patch b/debian/patches/0028-tests-Remove-remnants-of-check-local.patch
new file mode 100644
index 0000000..6ccf789
--- /dev/null
+++ b/debian/patches/0028-tests-Remove-remnants-of-check-local.patch
@@ -0,0 +1,35 @@
+From: Justus Winter <justus at g10code.com>
+Date: Wed, 10 May 2017 16:31:58 +0200
+Subject: tests: Remove remnants of 'check-local'.
+
+* tests/gpgsm/Makefile.am (check-local): Drop rule and the dependency
+on it.
+--
+Previously, the test environment was created during 'make check'.
+Nowadays we create it using 'BUILT_SOURCES' during 'make all'. Drop
+remnant of the previous method.
+
+Signed-off-by: Justus Winter <justus at g10code.com>
+(cherry picked from commit a9b4c0ad0d1085ff76742e44cf0cf926e89d1f4c)
+---
+ tests/gpgsm/Makefile.am | 7 -------
+ 1 file changed, 7 deletions(-)
+
+diff --git a/tests/gpgsm/Makefile.am b/tests/gpgsm/Makefile.am
+index adae6a2..c73faea 100644
+--- a/tests/gpgsm/Makefile.am
++++ b/tests/gpgsm/Makefile.am
+@@ -51,13 +51,6 @@ clean-local:
+ -$(top_srcdir)/tests/start-stop-agent --stop
+ -rm -fR private-keys-v1.d
+
+-check-local: ./pubring.kbx ./gpgsm.conf \
+- ./private-keys-v1.d/$(key_id).key ./trustlist.txt
+-
+-# To guarantee that check-local is run before any tests we add this
+-# dependency:
+-initial.test : check-local
+-
+ export GNUPGHOME := $(abs_builddir)
+
+ export GPG_AGENT_INFO :=
diff --git a/debian/patches/0029-python-Fix-build-in-certain-cases.patch b/debian/patches/0029-python-Fix-build-in-certain-cases.patch
new file mode 100644
index 0000000..fb6b68f
--- /dev/null
+++ b/debian/patches/0029-python-Fix-build-in-certain-cases.patch
@@ -0,0 +1,30 @@
+From: Justus Winter <justus at g10code.com>
+Date: Thu, 18 May 2017 11:42:13 +0200
+Subject: python: Fix build in certain cases.
+
+* lang/python/setup.py.in: Prepend the Python build dir to the list of
+include directories so that it takes precedence over any other include
+directory.
+--
+Fixes the build in case an older 'gpgme.h' is installed and is picked
+up by the compiler when compiling the Python module.
+
+Signed-off-by: Justus Winter <justus at g10code.com>
+(cherry picked from commit 84a203e60b9935bd8536cd2832fbc55d7f011341)
+---
+ lang/python/setup.py.in | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/lang/python/setup.py.in b/lang/python/setup.py.in
+index a1279f8..f9dda20 100755
+--- a/lang/python/setup.py.in
++++ b/lang/python/setup.py.in
+@@ -223,7 +223,7 @@ class BuildExtFirstHack(build):
+ swig_sources.extend((self._in_build_base('gpgme.i'), self._in_build_base('helpers.c')))
+ swig_opts.extend(['-I' + self.build_base,
+ '-outdir', os.path.join(self.build_lib, 'gpg')])
+- include_dirs.append(self.build_base)
++ include_dirs.insert(0, self.build_base)
+
+ self.run_command('build_ext')
+ build.run(self)
diff --git a/debian/patches/0030-core-Sort-the-status-table.patch b/debian/patches/0030-core-Sort-the-status-table.patch
new file mode 100644
index 0000000..0e0bf8f
--- /dev/null
+++ b/debian/patches/0030-core-Sort-the-status-table.patch
@@ -0,0 +1,85 @@
+From: Justus Winter <justus at g10code.com>
+Date: Tue, 30 May 2017 12:09:23 +0200
+Subject: core: Sort the status table.
+
+--
+Signed-off-by: Justus Winter <justus at g10code.com>
+(cherry picked from commit 92574406fb5fd456bc5bf85c7d906f06c4680632)
+---
+ src/status-table.c | 20 ++++++++++----------
+ 1 file changed, 10 insertions(+), 10 deletions(-)
+
+diff --git a/src/status-table.c b/src/status-table.c
+index c9bf357..6451134 100644
+--- a/src/status-table.c
++++ b/src/status-table.c
+@@ -43,10 +43,10 @@ static struct status_table_s status_table[] =
+ { "ALREADY_SIGNED", GPGME_STATUS_ALREADY_SIGNED },
+ { "ATTRIBUTE", GPGME_STATUS_ATTRIBUTE },
+ { "BACKUP_KEY_CREATED", GPGME_STATUS_BACKUP_KEY_CREATED },
+- { "BAD_PASSPHRASE", GPGME_STATUS_BAD_PASSPHRASE },
+ { "BADARMOR", GPGME_STATUS_BADARMOR },
+ { "BADMDC", GPGME_STATUS_BADMDC },
+ { "BADSIG", GPGME_STATUS_BADSIG },
++ { "BAD_PASSPHRASE", GPGME_STATUS_BAD_PASSPHRASE },
+ { "BEGIN_DECRYPTION", GPGME_STATUS_BEGIN_DECRYPTION },
+ { "BEGIN_ENCRYPTION", GPGME_STATUS_BEGIN_ENCRYPTION },
+ { "BEGIN_SIGNING", GPGME_STATUS_BEGIN_SIGNING },
+@@ -73,22 +73,22 @@ static struct status_table_s status_table[] =
+ { "GET_BOOL", GPGME_STATUS_GET_BOOL },
+ { "GET_HIDDEN", GPGME_STATUS_GET_HIDDEN },
+ { "GET_LINE", GPGME_STATUS_GET_LINE },
+- { "GOOD_PASSPHRASE", GPGME_STATUS_GOOD_PASSPHRASE },
+ { "GOODMDC", GPGME_STATUS_GOODMDC },
+ { "GOODSIG", GPGME_STATUS_GOODSIG },
++ { "GOOD_PASSPHRASE", GPGME_STATUS_GOOD_PASSPHRASE },
+ { "GOT_IT", GPGME_STATUS_GOT_IT },
++ { "IMPORTED", GPGME_STATUS_IMPORTED },
+ { "IMPORT_OK", GPGME_STATUS_IMPORT_OK },
+ { "IMPORT_PROBLEM", GPGME_STATUS_IMPORT_PROBLEM },
+ { "IMPORT_RES", GPGME_STATUS_IMPORT_RES },
+- { "IMPORTED", GPGME_STATUS_IMPORTED },
+ { "INQUIRE_MAXLEN", GPGME_STATUS_INQUIRE_MAXLEN },
+ { "INV_RECP", GPGME_STATUS_INV_RECP },
+ { "INV_SGNR", GPGME_STATUS_INV_SGNR },
++ { "KEYEXPIRED", GPGME_STATUS_KEYEXPIRED },
++ { "KEYREVOKED", GPGME_STATUS_KEYREVOKED },
+ { "KEY_CONSIDERED", GPGME_STATUS_KEY_CONSIDERED },
+ { "KEY_CREATED", GPGME_STATUS_KEY_CREATED },
+ { "KEY_NOT_CREATED", GPGME_STATUS_KEY_NOT_CREATED },
+- { "KEYEXPIRED", GPGME_STATUS_KEYEXPIRED },
+- { "KEYREVOKED", GPGME_STATUS_KEYREVOKED },
+ { "LEAVE", GPGME_STATUS_LEAVE },
+ { "MISSING_PASSPHRASE", GPGME_STATUS_MISSING_PASSPHRASE },
+ { "MOUNTPOINT", GPGME_STATUS_MOUNTPOINT },
+@@ -96,14 +96,14 @@ static struct status_table_s status_table[] =
+ { "NEED_PASSPHRASE_PIN", GPGME_STATUS_NEED_PASSPHRASE_PIN },
+ { "NEED_PASSPHRASE_SYM", GPGME_STATUS_NEED_PASSPHRASE_SYM },
+ { "NEWSIG", GPGME_STATUS_NEWSIG },
+- { "NO_PUBKEY", GPGME_STATUS_NO_PUBKEY },
+- { "NO_RECP", GPGME_STATUS_NO_RECP },
+- { "NO_SECKEY", GPGME_STATUS_NO_SECKEY },
+- { "NO_SGNR", GPGME_STATUS_NO_SGNR },
+ { "NODATA", GPGME_STATUS_NODATA },
+ { "NOTATION_DATA", GPGME_STATUS_NOTATION_DATA },
+ { "NOTATION_FLAGS", GPGME_STATUS_NOTATION_FLAGS },
+ { "NOTATION_NAME", GPGME_STATUS_NOTATION_NAME },
++ { "NO_PUBKEY", GPGME_STATUS_NO_PUBKEY },
++ { "NO_RECP", GPGME_STATUS_NO_RECP },
++ { "NO_SECKEY", GPGME_STATUS_NO_SECKEY },
++ { "NO_SGNR", GPGME_STATUS_NO_SGNR },
+ { "PINENTRY_LAUNCHED", GPGME_STATUS_PINENTRY_LAUNCHED},
+ { "PKA_TRUST_BAD", GPGME_STATUS_PKA_TRUST_BAD },
+ { "PKA_TRUST_GOOD", GPGME_STATUS_PKA_TRUST_GOOD },
+@@ -120,10 +120,10 @@ static struct status_table_s status_table[] =
+ { "SHM_GET_BOOL", GPGME_STATUS_SHM_GET_BOOL },
+ { "SHM_GET_HIDDEN", GPGME_STATUS_SHM_GET_HIDDEN },
+ { "SHM_INFO", GPGME_STATUS_SHM_INFO },
++ { "SIGEXPIRED", GPGME_STATUS_SIGEXPIRED },
+ { "SIG_CREATED", GPGME_STATUS_SIG_CREATED },
+ { "SIG_ID", GPGME_STATUS_SIG_ID },
+ { "SIG_SUBPACKET", GPGME_STATUS_SIG_SUBPACKET },
+- { "SIGEXPIRED", GPGME_STATUS_SIGEXPIRED },
+ { "SUCCESS", GPGME_STATUS_SUCCESS },
+ { "TOFU_STATS", GPGME_STATUS_TOFU_STATS },
+ { "TOFU_STATS_LONG", GPGME_STATUS_TOFU_STATS_LONG },
diff --git a/debian/patches/0031-cpp-Fix-CMake-config-library-name-for-GPGME.patch b/debian/patches/0031-cpp-Fix-CMake-config-library-name-for-GPGME.patch
new file mode 100644
index 0000000..a4c5675
--- /dev/null
+++ b/debian/patches/0031-cpp-Fix-CMake-config-library-name-for-GPGME.patch
@@ -0,0 +1,28 @@
+From: Andre Heinecke <aheinecke at intevation.de>
+Date: Mon, 12 Jun 2017 15:25:33 +0200
+Subject: cpp: Fix CMake config library name for GPGME
+
+* lang/cpp/src/GpgmeppConfig.cmake.in.in: The link library
+is of course also dynamic.
+
+--
+GnuPG-Bug-Id: T3181
+
+(cherry picked from commit 5c53c702605b8d4458a14e6303cdc13f3b106efa)
+---
+ lang/cpp/src/GpgmeppConfig.cmake.in.in | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/lang/cpp/src/GpgmeppConfig.cmake.in.in b/lang/cpp/src/GpgmeppConfig.cmake.in.in
+index 7f42f31..73f5eaa 100644
+--- a/lang/cpp/src/GpgmeppConfig.cmake.in.in
++++ b/lang/cpp/src/GpgmeppConfig.cmake.in.in
+@@ -63,7 +63,7 @@ add_library(Gpgmepp SHARED IMPORTED)
+
+ set_target_properties(Gpgmepp PROPERTIES
+ INTERFACE_INCLUDE_DIRECTORIES "@resolved_includedir@/gpgme++;@resolved_includedir@"
+- INTERFACE_LINK_LIBRARIES "pthread;@resolved_libdir@/libgpgme.so;@LIBASSUAN_LIBS@"
++ INTERFACE_LINK_LIBRARIES "pthread;@resolved_libdir@/libgpgme at libsuffix@;@LIBASSUAN_LIBS@"
+ IMPORTED_LOCATION "@resolved_libdir@/libgpgmepp at libsuffix@"
+ )
+
diff --git a/debian/patches/0032-Fix-some-shadow-warnings.patch b/debian/patches/0032-Fix-some-shadow-warnings.patch
new file mode 100644
index 0000000..bc08684
--- /dev/null
+++ b/debian/patches/0032-Fix-some-shadow-warnings.patch
@@ -0,0 +1,38 @@
+From: Andre Heinecke <aheinecke at intevation.de>
+Date: Mon, 10 Jul 2017 17:26:46 +0200
+Subject: Fix some shadow warnings
+
+--
+Warnings in headers hurt downstream.
+
+(cherry picked from commit 3b9123b4c64828acd2d2048f40d7631513aeab36)
+---
+ lang/cpp/src/configuration.h | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/lang/cpp/src/configuration.h b/lang/cpp/src/configuration.h
+index 288a410..5e481a9 100644
+--- a/lang/cpp/src/configuration.h
++++ b/lang/cpp/src/configuration.h
+@@ -93,8 +93,8 @@ class GPGMEPP_EXPORT Component
+ {
+ public:
+ Component() : comp() {}
+- explicit Component(const shared_gpgme_conf_comp_t &comp)
+- : comp(comp) {}
++ explicit Component(const shared_gpgme_conf_comp_t &gpgme_comp)
++ : comp(gpgme_comp) {}
+
+ // copy ctor is ok
+
+@@ -144,8 +144,8 @@ class GPGMEPP_EXPORT Option
+ {
+ public:
+ Option() : comp(), opt(0) {}
+- Option(const shared_gpgme_conf_comp_t &comp, gpgme_conf_opt_t opt)
+- : comp(comp), opt(opt) {}
++ Option(const shared_gpgme_conf_comp_t &gpgme_comp, gpgme_conf_opt_t gpgme_opt)
++ : comp(gpgme_comp), opt(gpgme_opt) {}
+
+ const Option &operator=(const Option &other)
+ {
diff --git a/debian/patches/0033-core-Simplify-parsing-of-STATUS_ERROR-in-decrypt.c.patch b/debian/patches/0033-core-Simplify-parsing-of-STATUS_ERROR-in-decrypt.c.patch
new file mode 100644
index 0000000..10f7f90
--- /dev/null
+++ b/debian/patches/0033-core-Simplify-parsing-of-STATUS_ERROR-in-decrypt.c.patch
@@ -0,0 +1,127 @@
+From: Werner Koch <wk at gnupg.org>
+Date: Wed, 12 Jul 2017 15:59:12 +0200
+Subject: core: Simplify parsing of STATUS_ERROR in decrypt.c
+
+* src/decrypt.c (_gpgme_decrypt_status_handler): Factor some code out
+to ...
+(parse_status_error): new. Modernize parsing.
+
+Signed-off-by: Werner Koch <wk at gnupg.org>
+(cherry picked from commit 87703dbb86ac8fd8abd23170f8038ea6e3dbde28)
+---
+ src/conversion.c | 2 +-
+ src/decrypt.c | 80 +++++++++++++++++++++++++++-----------------------------
+ 2 files changed, 40 insertions(+), 42 deletions(-)
+
+diff --git a/src/conversion.c b/src/conversion.c
+index 92dd214..5b84f67 100644
+--- a/src/conversion.c
++++ b/src/conversion.c
+@@ -374,7 +374,7 @@ _gpgme_encode_percent_string (const char *src, char **destp, size_t len)
+
+
+ /* Split a string into space delimited fields and remove leading and
+- * trailing spaces from each field. A pointer to the each field is
++ * trailing spaces from each field. A pointer to each field is
+ * stored in ARRAY. Stop splitting at ARRAYSIZE fields. The function
+ * modifies STRING. The number of parsed fields is returned.
+ */
+diff --git a/src/decrypt.c b/src/decrypt.c
+index f30f80f..cfad35e 100644
+--- a/src/decrypt.c
++++ b/src/decrypt.c
+@@ -124,7 +124,43 @@ gpgme_op_decrypt_result (gpgme_ctx_t ctx)
+ return &opd->result;
+ }
+
++
+
++/* Parse the ARGS of an error status line and record some error
++ * conditions at OPD. Returns 0 on success. */
++static gpgme_error_t
++parse_status_error (char *args, op_data_t opd)
++{
++ gpgme_error_t err;
++ char *field[3];
++ int nfields;
++
++ nfields = _gpgme_split_fields (args, field, DIM (field));
++ if (nfields < 1)
++ return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Required arg missing. */
++ err = nfields < 2 ? 0 : atoi (field[1]);
++
++ if (!strcmp (field[0], "decrypt.algorithm"))
++ {
++ if (gpg_err_code (err) == GPG_ERR_UNSUPPORTED_ALGORITHM
++ && nfields > 2
++ && strcmp (field[2], "?"))
++ {
++ opd->result.unsupported_algorithm = strdup (field[2]);
++ if (!opd->result.unsupported_algorithm)
++ return gpg_error_from_syserror ();
++ }
++ }
++ else if (!strcmp (field[0], "decrypt.keyusage"))
++ {
++ if (gpg_err_code (err) == GPG_ERR_WRONG_KEY_USAGE)
++ opd->result.wrong_key_usage = 1;
++ }
++
++ return 0;
++}
++
++
+ static gpgme_error_t
+ parse_enc_to (char *args, gpgme_recipient_t *recp, gpgme_protocol_t protocol)
+ {
+@@ -230,47 +266,9 @@ _gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code,
+ /* Note that this is an informational status code which should
+ not lead to an error return unless it is something not
+ related to the backend. */
+- {
+- const char d_alg[] = "decrypt.algorithm";
+- const char k_alg[] = "decrypt.keyusage";
+-
+- if (!strncmp (args, d_alg, sizeof (d_alg) - 1))
+- {
+- args += sizeof (d_alg) - 1;
+- while (*args == ' ')
+- args++;
+-
+- if (gpg_err_code (atoi (args)) == GPG_ERR_UNSUPPORTED_ALGORITHM)
+- {
+- char *end;
+-
+- while (*args && *args != ' ')
+- args++;
+- while (*args == ' ')
+- args++;
+-
+- end = strchr (args, ' ');
+- if (end)
+- *end = '\0';
+-
+- if (!(*args == '?' && *(args + 1) == '\0'))
+- {
+- opd->result.unsupported_algorithm = strdup (args);
+- if (!opd->result.unsupported_algorithm)
+- return gpg_error_from_syserror ();
+- }
+- }
+- }
+- else if (!strncmp (args, k_alg, sizeof (k_alg) - 1))
+- {
+- args += sizeof (k_alg) - 1;
+- while (*args == ' ')
+- args++;
+-
+- if (gpg_err_code (atoi (args)) == GPG_ERR_WRONG_KEY_USAGE)
+- opd->result.wrong_key_usage = 1;
+- }
+- }
++ err = parse_status_error (args, opd);
++ if (err)
++ return err;
+ break;
+
+ case GPGME_STATUS_ENC_TO:
diff --git a/debian/patches/0034-core-Return-CANCELED-and-BAD_PASSPHRASE-error-code-o.patch b/debian/patches/0034-core-Return-CANCELED-and-BAD_PASSPHRASE-error-code-o.patch
new file mode 100644
index 0000000..7b99d0c
--- /dev/null
+++ b/debian/patches/0034-core-Return-CANCELED-and-BAD_PASSPHRASE-error-code-o.patch
@@ -0,0 +1,78 @@
+From: Werner Koch <wk at gnupg.org>
+Date: Wed, 12 Jul 2017 17:55:43 +0200
+Subject: core: Return CANCELED and BAD_PASSPHRASE error code on decryption.
+
+* src/decrypt.c (op_data_t): Add field pkdecrypt_failed.
+(_gpgme_decrypt_status_handler): Consult new field.
+(parse_status_error): Handle some error codes.
+--
+
+The idea is to return only a limited set of error codes because a user
+won't be able to understand the more esoteric codes.
+
+GnuPG-bug-id: 3270
+Signed-off-by: Werner Koch <wk at gnupg.org>
+(cherry picked from commit d37bc7e025cdc6228da45b2b527e9f3bfef71c71)
+---
+ src/decrypt.c | 33 ++++++++++++++++++++++++++++++++-
+ 1 file changed, 32 insertions(+), 1 deletion(-)
+
+diff --git a/src/decrypt.c b/src/decrypt.c
+index cfad35e..3d77aae 100644
+--- a/src/decrypt.c
++++ b/src/decrypt.c
+@@ -43,7 +43,11 @@ typedef struct
+ gpg_error_t failure_code;
+
+ int okay;
++
++ /* A flag telling that the a decryption failed and an optional error
++ * code to further specify the failure. */
+ int failed;
++ gpg_error_t pkdecrypt_failed;
+
+ /* A pointer to the next pointer of the last recipient in the list.
+ This makes appending new invalid signers painless while
+@@ -156,6 +160,31 @@ parse_status_error (char *args, op_data_t opd)
+ if (gpg_err_code (err) == GPG_ERR_WRONG_KEY_USAGE)
+ opd->result.wrong_key_usage = 1;
+ }
++ else if (!strcmp (field[0], "pkdecrypt_failed"))
++ {
++ switch (gpg_err_code (err))
++ {
++ case GPG_ERR_CANCELED:
++ case GPG_ERR_FULLY_CANCELED:
++ /* It is better to return with a cancel error code than the
++ * general decryption failed error code. */
++ opd->pkdecrypt_failed = gpg_err_make (gpg_err_source (err),
++ GPG_ERR_CANCELED);
++ break;
++
++ case GPG_ERR_BAD_PASSPHRASE:
++ /* A bad passphrase is severe enough that we return this
++ * error code. */
++ opd->pkdecrypt_failed = err;
++ break;
++
++ default:
++ /* For now all other error codes are ignored and the
++ * standard DECRYPT_FAILED is returned. */
++ break;
++ }
++ }
++
+
+ return 0;
+ }
+@@ -242,7 +271,9 @@ _gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code,
+ case GPGME_STATUS_EOF:
+ /* FIXME: These error values should probably be attributed to
+ the underlying crypto engine (as error source). */
+- if (opd->failed)
++ if (opd->failed && opd->pkdecrypt_failed)
++ return opd->pkdecrypt_failed;
++ else if (opd->failed)
+ return gpg_error (GPG_ERR_DECRYPT_FAILED);
+ else if (!opd->okay)
+ return gpg_error (GPG_ERR_NO_DATA);
diff --git a/debian/patches/0035-core-Return-NO_SECKEY-error-code-on-decryption.patch b/debian/patches/0035-core-Return-NO_SECKEY-error-code-on-decryption.patch
new file mode 100644
index 0000000..55d906f
--- /dev/null
+++ b/debian/patches/0035-core-Return-NO_SECKEY-error-code-on-decryption.patch
@@ -0,0 +1,86 @@
+From: Werner Koch <wk at gnupg.org>
+Date: Wed, 12 Jul 2017 18:30:49 +0200
+Subject: core: Return NO_SECKEY error code on decryption
+
+* src/decrypt.c (op_data_t): Add flag any_no_seckey.
+(_gpgme_decrypt_status_handler): Consult that flag.
+(_gpgme_decrypt_status_handler): Set that flag.
+--
+
+The NO_SECKEY is emitted instead of an "S ERROR pkdecrypt_failed" if
+gpg knowns that a key has been encrypted to that key (cf. "S ENC_TO").
+it is not fool proffof but in the majority of cases we can provide a
+better error message than just DECRYPTION_FAILED.
+
+GnuPG-bug-id: 3270
+Signed-off-by: Werner Koch <wk at gnupg.org>
+(cherry picked from commit ad0c5ab4cd8d3a1b11b37dc137b75a67aa26da37)
+---
+ doc/gpgme.texi | 2 +-
+ src/decrypt.c | 13 +++++++++++--
+ 2 files changed, 12 insertions(+), 3 deletions(-)
+
+diff --git a/doc/gpgme.texi b/doc/gpgme.texi
+index bc40430..31929d3 100644
+--- a/doc/gpgme.texi
++++ b/doc/gpgme.texi
+@@ -4893,7 +4893,7 @@ if @var{ctx}, @var{cipher} or @var{plain} is not a valid pointer,
+ @code{GPG_ERR_NO_DATA} if @var{cipher} does not contain any data to
+ decrypt, @code{GPG_ERR_DECRYPT_FAILED} if @var{cipher} is not a valid
+ cipher text, @code{GPG_ERR_BAD_PASSPHRASE} if the passphrase for the
+-secret key could not be retrieved, and passes through any errors that
++secret key could not be retrieved, and passes through some errors that
+ are reported by the crypto engine support routines.
+ @end deftypefun
+
+diff --git a/src/decrypt.c b/src/decrypt.c
+index 3d77aae..118ed70 100644
+--- a/src/decrypt.c
++++ b/src/decrypt.c
+@@ -1,6 +1,6 @@
+ /* decrypt.c - Decrypt function.
+ Copyright (C) 2000 Werner Koch (dd9jn)
+- Copyright (C) 2001, 2002, 2003, 2004 g10 Code GmbH
++ Copyright (C) 2001, 2002, 2003, 2004, 2017 g10 Code GmbH
+
+ This file is part of GPGME.
+
+@@ -49,6 +49,13 @@ typedef struct
+ int failed;
+ gpg_error_t pkdecrypt_failed;
+
++ /* At least one secret key is not available. gpg issues NO_SECKEY
++ * status lines for each key the message has been encrypted to but
++ * that secret key is not available. This can't be done for hidden
++ * recipients, though. We track it here to allow for a better error
++ * message that the general DECRYPTION_FAILED. */
++ int any_no_seckey;
++
+ /* A pointer to the next pointer of the last recipient in the list.
+ This makes appending new invalid signers painless while
+ preserving the order. */
+@@ -273,6 +280,8 @@ _gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code,
+ the underlying crypto engine (as error source). */
+ if (opd->failed && opd->pkdecrypt_failed)
+ return opd->pkdecrypt_failed;
++ else if (opd->failed && opd->any_no_seckey)
++ return gpg_error (GPG_ERR_NO_SECKEY);
+ else if (opd->failed)
+ return gpg_error (GPG_ERR_DECRYPT_FAILED);
+ else if (!opd->okay)
+@@ -319,7 +328,6 @@ _gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code,
+ case GPGME_STATUS_NO_SECKEY:
+ {
+ gpgme_recipient_t rec = opd->result.recipients;
+-
+ while (rec)
+ {
+ if (!strcmp (rec->keyid, args))
+@@ -332,6 +340,7 @@ _gpgme_decrypt_status_handler (void *priv, gpgme_status_code_t code,
+ /* FIXME: Is this ok? */
+ if (!rec)
+ return trace_gpg_error (GPG_ERR_INV_ENGINE);
++ opd->any_no_seckey = 1;
+ }
+ break;
+
diff --git a/debian/patches/0036-tests-Fix-printf-compiler-warning-for-an-error-case.patch b/debian/patches/0036-tests-Fix-printf-compiler-warning-for-an-error-case.patch
new file mode 100644
index 0000000..5999d0e
--- /dev/null
+++ b/debian/patches/0036-tests-Fix-printf-compiler-warning-for-an-error-case.patch
@@ -0,0 +1,25 @@
+From: Werner Koch <wk at gnupg.org>
+Date: Wed, 12 Jul 2017 18:32:50 +0200
+Subject: tests: Fix printf compiler warning for an error case.
+
+* tests/gpg/t-keylist.c (main): Cast DIM to int.
+
+Signed-off-by: Werner Koch <wk at gnupg.org>
+(cherry picked from commit 46d2e48105e0929ec38dd4106004dd60d941df9a)
+---
+ tests/gpg/t-keylist.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/tests/gpg/t-keylist.c b/tests/gpg/t-keylist.c
+index 8a32f9b..0417011 100644
+--- a/tests/gpg/t-keylist.c
++++ b/tests/gpg/t-keylist.c
+@@ -569,7 +569,7 @@ main (int argc, char **argv)
+ if (keys[i].fpr)
+ {
+ fprintf (stderr, "Less keys (%d) returned than expected (%d)\n",
+- i, DIM (keys) - 1);
++ i, (int)(DIM (keys) - 1));
+ exit (1);
+ }
+
diff --git a/debian/patches/0037-Sync-autogen.sh.patch b/debian/patches/0037-Sync-autogen.sh.patch
new file mode 100644
index 0000000..f93d19e
--- /dev/null
+++ b/debian/patches/0037-Sync-autogen.sh.patch
@@ -0,0 +1,245 @@
+From: Justus Winter <justus at g10code.com>
+Date: Thu, 13 Jul 2017 15:30:38 +0200
+Subject: Sync 'autogen.sh'.
+
+--
+Signed-off-by: Justus Winter <justus at g10code.com>
+(cherry picked from commit 2446138e3c8c45fd4bd64eba84edd32fcd009699)
+---
+ autogen.sh | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++++---------
+ 1 file changed, 116 insertions(+), 20 deletions(-)
+
+diff --git a/autogen.sh b/autogen.sh
+index 91f35a6..e5ba5bf 100755
+--- a/autogen.sh
++++ b/autogen.sh
+@@ -1,6 +1,6 @@
+ #! /bin/sh
+ # autogen.sh
+-# Copyright (C) 2003, 2014 g10 Code GmbH
++# Copyright (C) 2003, 2014, 2017 g10 Code GmbH
+ #
+ # This file is free software; as a special exception the author gives
+ # unlimited permission to copy and/or distribute it, with or without
+@@ -15,7 +15,7 @@
+ # configure it for the respective package. It is maintained as part of
+ # GnuPG and source copied by other packages.
+ #
+-# Version: 2014-01-10
++# Version: 2017-01-17
+
+ configure_ac="configure.ac"
+
+@@ -41,7 +41,7 @@ fatal () {
+
+ info () {
+ if [ -z "${SILENT}" ]; then
+- echo "autogen.sh:" "$*"
++ echo "autogen.sh:" "$*" >&2
+ fi
+ }
+
+@@ -70,14 +70,27 @@ MSGMERGE=${GETTEXT_PREFIX}${MSGMERGE:-msgmerge}${GETTEXT_SUFFIX}
+ DIE=no
+ FORCE=
+ SILENT=
++PRINT_HOST=no
++PRINT_BUILD=no
+ tmp=$(dirname "$0")
+ tsdir=$(cd "${tmp}"; pwd)
++version_parts=3
+
+ if [ -n "${AUTOGEN_SH_SILENT}" ]; then
+ SILENT=" --silent"
+ fi
+ if test x"$1" = x"--help"; then
+- echo "usage: ./autogen.sh [--silent] [--force] [--build-TYPE] [ARGS]"
++ echo "usage: ./autogen.sh [OPTIONS] [ARGS]"
++ echo " Options:"
++ echo " --silent Silent operation"
++ echo " --force Pass --force to autoconf"
++ echo " --find-version Helper for configure.ac"
++ echo " --build-TYPE Configure to cross build for TYPE"
++ echo " --print-host Print only the host triplet"
++ echo " --print-build Print only the build platform triplet"
++ echo ""
++ echo " ARGS are passed to configure in --build-TYPE mode."
++ echo " Configuration for this script is expected in autogen.rc"
+ exit 0
+ fi
+ if test x"$1" = x"--silent"; then
+@@ -88,6 +101,14 @@ if test x"$1" = x"--force"; then
+ FORCE=" --force"
+ shift
+ fi
++if test x"$1" = x"--print-host"; then
++ PRINT_HOST=yes
++ shift
++fi
++if test x"$1" = x"--print-build"; then
++ PRINT_BUILD=yes
++ shift
++fi
+
+
+ # Reject unsafe characters in $HOME, $tsdir and cwd. We consider spaces
+@@ -133,6 +154,11 @@ amd64_toolprefixes=
+ myhost=""
+ myhostsub=""
+ case "$1" in
++ --find-version)
++ myhost="find-version"
++ SILENT=" --silent"
++ shift
++ ;;
+ --build-w32)
+ myhost="w32"
+ shift
+@@ -172,16 +198,81 @@ if [ -f "$HOME/.gnupg-autogen.rc" ]; then
+ . "$HOME/.gnupg-autogen.rc"
+ fi
+
++
++# **** FIND VERSION ****
++# This is a helper for the configure.ac M4 magic
++# Called
++# ./autogen.sh --find-version PACKAGE MAJOR MINOR [MICRO]
++# returns a complete version string with automatic beta numbering.
++if [ "$myhost" = "find-version" ]; then
++ package="$1"
++ major="$2"
++ minor="$3"
++ micro="$4"
++
++ if [ -z "$package" -o -z "$major" -o -z "$minor" ]; then
++ echo "usage: ./autogen.sh --find-version PACKAGE MAJOR MINOR [MICRO]" >&2
++ exit 1
++ fi
++
++ case "$version_parts" in
++ 2)
++ matchstr1="$package-$major.[0-9]*"
++ matchstr2="$package-$major-base"
++ vers="$major.$minor"
++ ;;
++ *)
++ matchstr1="$package-$major.$minor.[0-9]*"
++ matchstr2="$package-$major.$minor-base"
++ vers="$major.$minor.$micro"
++ ;;
++ esac
++
++ beta=no
++ if [ -e .git ]; then
++ ingit=yes
++ tmp=$(git describe --match "${matchstr1}" --long 2>/dev/null)
++ tmp=$(echo "$tmp" | sed s/^"$package"//)
++ if [ -n "$tmp" ]; then
++ tmp=$(echo "$tmp" | sed s/^"$package"// \
++ | awk -F- '$3!=0 && $3 !~ /^beta/ {print"-beta"$3}')
++ else
++ tmp=$(git describe --match "${matchstr2}" --long 2>/dev/null \
++ | awk -F- '$4!=0{print"-beta"$4}')
++ fi
++ [ -n "$tmp" ] && beta=yes
++ rev=$(git rev-parse --short HEAD | tr -d '\n\r')
++ rvd=$((0x$(echo ${rev} | dd bs=1 count=4 2>/dev/null)))
++ else
++ ingit=no
++ beta=yes
++ tmp="-unknown"
++ rev="0000000"
++ rvd="0"
++ fi
++
++ echo "$package-$vers$tmp:$beta:$ingit:$vers$tmp:$vers:$tmp:$rev:$rvd:"
++ exit 0
++fi
++# **** end FIND VERSION ****
++
++
++if [ ! -f "$tsdir/build-aux/config.guess" ]; then
++ fatal "$tsdir/build-aux/config.guess not found"
++ exit 1
++fi
++build=`$tsdir/build-aux/config.guess`
++if [ $PRINT_BUILD = yes ]; then
++ echo "$build"
++ exit 0
++fi
++
++
++
+ # ******************
+ # W32 build script
+ # ******************
+ if [ "$myhost" = "w32" ]; then
+- if [ ! -f "$tsdir/build-aux/config.guess" ]; then
+- fatal "$tsdir/build-aux/config.guess not found"
+- exit 1
+- fi
+- build=`$tsdir/build-aux/config.guess`
+-
+ case $myhostsub in
+ ce)
+ w32root="$w32ce_root"
+@@ -222,6 +313,10 @@ if [ "$myhost" = "w32" ]; then
+ fi
+ die_p
+ fi
++ if [ $PRINT_HOST = yes ]; then
++ echo "$host"
++ exit 0
++ fi
+
+ if [ -f "$tsdir/config.log" ]; then
+ if ! head $tsdir/config.log | grep "$host" >/dev/null; then
+@@ -232,7 +327,8 @@ if [ "$myhost" = "w32" ]; then
+
+ $tsdir/configure --enable-maintainer-mode ${SILENT} \
+ --prefix=${w32root} \
+- --host=${host} --build=${build} \
++ --host=${host} --build=${build} SYSROOT=${w32root} \
++ PKG_CONFIG_LIBDIR=${w32root}/lib/pkgconfig \
+ ${configure_opts} ${extraoptions} "$@"
+ rc=$?
+ exit $rc
+@@ -242,13 +338,6 @@ fi
+ # ***** AMD64 cross build script *******
+ # Used to cross-compile for AMD64 (for testing)
+ if [ "$myhost" = "amd64" ]; then
+- shift
+- if [ ! -f $tsdir/build-aux/config.guess ]; then
+- echo "$tsdir/build-aux/config.guess not found" >&2
+- exit 1
+- fi
+- build=`$tsdir/build-aux/config.guess`
+-
+ [ -z "$amd64root" ] && amd64root="$HOME/amd64root"
+ info "Using $amd64root as standard install directory"
+ replace_sysroot
+@@ -269,6 +358,10 @@ if [ "$myhost" = "amd64" ]; then
+ echo "Stop." >&2
+ exit 1
+ fi
++ if [ $PRINT_HOST = yes ]; then
++ echo "$host"
++ exit 0
++ fi
+
+ if [ -f "$tsdir/config.log" ]; then
+ if ! head $tsdir/config.log | grep "$host" >/dev/null; then
+@@ -341,8 +434,11 @@ fi
+
+ # Check the git setup.
+ if [ -d .git ]; then
+- CP="cp -a"
+- [ -z "${SILENT}" ] && CP="$CP -v"
++ CP="cp -p"
++ # If we have a GNU cp we can add -v
++ if cp --version >/dev/null 2>/dev/null; then
++ [ -z "${SILENT}" ] && CP="$CP -v"
++ fi
+ if [ -f .git/hooks/pre-commit.sample -a ! -f .git/hooks/pre-commit ] ; then
+ [ -z "${SILENT}" ] && cat <<EOF
+ *** Activating trailing whitespace git pre-commit hook. ***
diff --git a/debian/patches/0038-tests-Make-agent-spawning-more-robust.patch b/debian/patches/0038-tests-Make-agent-spawning-more-robust.patch
new file mode 100644
index 0000000..bda4f66
--- /dev/null
+++ b/debian/patches/0038-tests-Make-agent-spawning-more-robust.patch
@@ -0,0 +1,54 @@
+From: Justus Winter <justus at g10code.com>
+Date: Thu, 13 Jul 2017 15:41:11 +0200
+Subject: tests: Make agent spawning more robust.
+
+* tests/gpgsm/Makefile.am (gpgsm.conf): Add agent-program directive.
+* tests/start-stop-agent: Update agent-program directive.
+--
+Update the 'agent-program' configuration directive to point to the
+same agent that we are starting. Previously, it was possible that a
+different agent was started if 'make check' was run with a different
+PATH.
+
+Signed-off-by: Justus Winter <justus at g10code.com>
+(cherry picked from commit 9cd2b58dfb2c21cce64520cf4c726859b583d14e)
+---
+ tests/gpgsm/Makefile.am | 1 +
+ tests/start-stop-agent | 12 ++++++++++++
+ 2 files changed, 13 insertions(+)
+
+diff --git a/tests/gpgsm/Makefile.am b/tests/gpgsm/Makefile.am
+index c73faea..a5667a8 100644
+--- a/tests/gpgsm/Makefile.am
++++ b/tests/gpgsm/Makefile.am
+@@ -65,6 +65,7 @@ BUILT_SOURCES = gpgsm.conf trustlist.txt pubring-stamp \
+ ./gpgsm.conf:
+ echo disable-crl-checks > ./gpgsm.conf
+ echo faked-system-time 1008241200 >> ./gpgsm.conf
++ echo "agent-program `which $(GPG_AGENT)`|--debug-quick-random" >> ./gpg.conf
+
+ ./private-keys-v1.d/gpg-sample.stamp: $(srcdir)/$(key_id)
+ -gpgconf --kill all
+diff --git a/tests/start-stop-agent b/tests/start-stop-agent
+index 3ce6f22..9514e0a 100755
+--- a/tests/start-stop-agent
++++ b/tests/start-stop-agent
+@@ -30,6 +30,18 @@ if [ "$1" = "--stop" ]; then
+ exit 0
+ fi
+
++# Update 'agent-program' in the configuration files to make sure we
++# will always start exactly this agent again if we ever need to.
++for F in gpg.conf gpgsm.conf
++do
++ if test -f "$GNUPGHOME/$F"
++ then
++ mv "$GNUPGHOME/$F" "$GNUPGHOME/$F~"
++ sed -e "s#^agent-program.*#agent-program ${GPG_AGENT}|--debug-quick-random#" \
++ >"$GNUPGHOME/$F" <"$GNUPGHOME/$F~"
++ fi
++done
++
+ if [ "$(gpg-connect-agent --no-autostart getval\ $token /bye 2>/dev/null | head -1)" \
+ = "D set" ]; then
+ echo "gpg-agent already running" >&2
diff --git a/debian/patches/0039-tests-Fix-blunder.patch b/debian/patches/0039-tests-Fix-blunder.patch
new file mode 100644
index 0000000..e42f5da
--- /dev/null
+++ b/debian/patches/0039-tests-Fix-blunder.patch
@@ -0,0 +1,25 @@
+From: Justus Winter <justus at g10code.com>
+Date: Thu, 13 Jul 2017 16:04:07 +0200
+Subject: tests: Fix blunder.
+
+--
+Fixes-commit: 9cd2b58dfb2c21cce64520cf4c726859b583d14e
+Signed-off-by: Justus Winter <justus at g10code.com>
+(cherry picked from commit ceed4a74540cf2c316b65cf62a9926c65019bb81)
+---
+ tests/gpgsm/Makefile.am | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/tests/gpgsm/Makefile.am b/tests/gpgsm/Makefile.am
+index a5667a8..274ba9d 100644
+--- a/tests/gpgsm/Makefile.am
++++ b/tests/gpgsm/Makefile.am
+@@ -65,7 +65,7 @@ BUILT_SOURCES = gpgsm.conf trustlist.txt pubring-stamp \
+ ./gpgsm.conf:
+ echo disable-crl-checks > ./gpgsm.conf
+ echo faked-system-time 1008241200 >> ./gpgsm.conf
+- echo "agent-program `which $(GPG_AGENT)`|--debug-quick-random" >> ./gpg.conf
++ echo "agent-program `which $(GPG_AGENT)`|--debug-quick-random" >> ./gpgsm.conf
+
+ ./private-keys-v1.d/gpg-sample.stamp: $(srcdir)/$(key_id)
+ -gpgconf --kill all
diff --git a/debian/patches/0040-tests-Fix-distcheck.patch b/debian/patches/0040-tests-Fix-distcheck.patch
new file mode 100644
index 0000000..037d48d
--- /dev/null
+++ b/debian/patches/0040-tests-Fix-distcheck.patch
@@ -0,0 +1,25 @@
+From: Justus Winter <justus at g10code.com>
+Date: Thu, 13 Jul 2017 16:06:27 +0200
+Subject: tests: Fix distcheck.
+
+* tests/start-stop-agent: Remove backup file.
+
+Fixes-commit: 9cd2b58dfb2c21cce64520cf4c726859b583d14e
+Signed-off-by: Justus Winter <justus at g10code.com>
+(cherry picked from commit 7fde780cc1773c6d3902d4f49a8d4aadbf3223d4)
+---
+ tests/start-stop-agent | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/tests/start-stop-agent b/tests/start-stop-agent
+index 9514e0a..7901374 100755
+--- a/tests/start-stop-agent
++++ b/tests/start-stop-agent
+@@ -39,6 +39,7 @@ do
+ mv "$GNUPGHOME/$F" "$GNUPGHOME/$F~"
+ sed -e "s#^agent-program.*#agent-program ${GPG_AGENT}|--debug-quick-random#" \
+ >"$GNUPGHOME/$F" <"$GNUPGHOME/$F~"
++ rm "$GNUPGHOME/$F~"
+ fi
+ done
+
diff --git a/debian/patches/0041-core-Fix-status-parsing-for-decrypt-verify.patch b/debian/patches/0041-core-Fix-status-parsing-for-decrypt-verify.patch
new file mode 100644
index 0000000..8384627
--- /dev/null
+++ b/debian/patches/0041-core-Fix-status-parsing-for-decrypt-verify.patch
@@ -0,0 +1,61 @@
+From: Andre Heinecke <aheinecke at intevation.de>
+Date: Thu, 27 Jul 2017 14:20:58 +0200
+Subject: core: Fix status parsing for decrypt + verify
+
+* src/decrypt.c (parse_status_error): Don't modify args.
+
+--
+Otherwise chained status handlers will not see the full args
+which resulted in a parse error in the verify status handler.
+
+GnuPG-Bug-Id: T3310
+(cherry picked from commit d3796e4504a2b4f422de17d78f3acfe8dd199c9c)
+---
+ src/decrypt.c | 18 +++++++++++++++---
+ 1 file changed, 15 insertions(+), 3 deletions(-)
+
+diff --git a/src/decrypt.c b/src/decrypt.c
+index 118ed70..9717c7a 100644
+--- a/src/decrypt.c
++++ b/src/decrypt.c
+@@ -145,10 +145,18 @@ parse_status_error (char *args, op_data_t opd)
+ gpgme_error_t err;
+ char *field[3];
+ int nfields;
++ char *args2;
+
+- nfields = _gpgme_split_fields (args, field, DIM (field));
++ if (!args)
++ return trace_gpg_error (GPG_ERR_INV_ENGINE);
++
++ args2 = strdup (args); /* Split modifies the input string. */
++ nfields = _gpgme_split_fields (args2, field, DIM (field));
+ if (nfields < 1)
+- return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Required arg missing. */
++ {
++ free (args2);
++ return trace_gpg_error (GPG_ERR_INV_ENGINE); /* Required arg missing. */
++ }
+ err = nfields < 2 ? 0 : atoi (field[1]);
+
+ if (!strcmp (field[0], "decrypt.algorithm"))
+@@ -159,7 +167,10 @@ parse_status_error (char *args, op_data_t opd)
+ {
+ opd->result.unsupported_algorithm = strdup (field[2]);
+ if (!opd->result.unsupported_algorithm)
+- return gpg_error_from_syserror ();
++ {
++ free (args2);
++ return gpg_error_from_syserror ();
++ }
+ }
+ }
+ else if (!strcmp (field[0], "decrypt.keyusage"))
+@@ -193,6 +204,7 @@ parse_status_error (char *args, op_data_t opd)
+ }
+
+
++ free (args2);
+ return 0;
+ }
+
diff --git a/debian/patches/0042-doc-Add-more-tofu-documentation.patch b/debian/patches/0042-doc-Add-more-tofu-documentation.patch
new file mode 100644
index 0000000..01ee037
--- /dev/null
+++ b/debian/patches/0042-doc-Add-more-tofu-documentation.patch
@@ -0,0 +1,104 @@
+From: Marcus Brinkmann <marcus.brinkmann at ruhr-uni-bochum.de>
+Date: Thu, 10 Aug 2017 16:10:40 +0200
+Subject: doc: Add more tofu documentation.
+
+* doc/gpgme.texi (gpgme_tofu_info_t): Document structure.
+(gpgme_sigsum_t): Document GPGME_SIGSUM_TOFU_CONFLICT.
+
+Signed-off-by: Marcus Brinkmann <mb at g10code.com>
+GnuPG-bug-id: 2816
+(cherry picked from commit 274609baceda3378b21f84c3ae6a44806dad2dba)
+---
+ doc/gpgme.texi | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 72 insertions(+)
+
+diff --git a/doc/gpgme.texi b/doc/gpgme.texi
+index 31929d3..37760af 100644
+--- a/doc/gpgme.texi
++++ b/doc/gpgme.texi
+@@ -3276,6 +3276,75 @@ Reserved for the time of the last update of this user ID.
+ @end deftp
+
+
++ at deftp {Data type} gpgme_tofu_info_t
++
++The @code{gpgme_tofu_info_t} type is a pointer to a tofu info
++structure. Tofu info structures are one component of a
++ at code{gpgme_user_id_t} object, and provide information from the TOFU
++database pertaining to the user ID.
++
++The tofu info structure has the following members:
++
++ at table @code
++ at item gpgme_key_sig_t next
++This is a pointer to the next tofu info structure in the linked
++list, or @code{NULL} if this is the last element.
++
++ at item unsigned int validity : 3
++This is the TOFU validity. It can have the following values:
++
++ at table @code
++ at item 0
++The value @code{0} indicates a conflict.
++
++ at item 1
++The value @code{1} indicates a key without history.
++
++ at item 2
++The value @code{2} indicates a key with too little history.
++
++ at item 3
++The value @code{3} indicates a key with enough history for basic trust.
++
++ at item 4
++The value @code{4} indicates a key with a lot of history.
++
++ at end table
++
++ at item unsigned int policy : 4
++This is the TOFU policy, see @code{gpgme_tofu_policy_t}.
++
++ at item unsigned short signcount
++This is the number of signatures seen for this binding (or
++ at code{USHRT_MAX} if there are more than that).
++
++ at item unsigned short encrcount
++This is the number of encryptions done with this binding (or
++ at code{USHRT_MAX} if there are more than that).
++
++ at item unsigned long signfirst
++Number of seconds since Epoch when the first signature was seen with
++this binding.
++
++ at item unsigned long signlast
++Number of seconds since Epoch when the last signature was seen with
++this binding.
++
++ at item unsigned long encrfirst
++Number of seconds since Epoch when the first encryption was done with
++this binding.
++
++ at item unsigned long encrlast
++Number of seconds since Epoch when the last encryption was done with
++this binding.
++
++ at item char *description
++A human-readable string summarizing the TOFU data (or NULL).
++
++ at end table
++ at end deftp
++
++
+ @deftp {Data type} gpgme_key_sig_t
+
+ The @code{gpgme_key_sig_t} type is a pointer to a key signature structure.
+@@ -5196,6 +5265,9 @@ The defined bits are:
+
+ @item GPGME_SIGSUM_SYS_ERROR
+ A system error occured.
++
++ @item GPGME_SIGSUM_TOFU_CONFLICT
++ A TOFU conflict was detected.
+ @end table
+
+ @item char *fpr
diff --git a/debian/patches/0043-doc-Clarify-import-keys-operation.patch b/debian/patches/0043-doc-Clarify-import-keys-operation.patch
new file mode 100644
index 0000000..1443cf9
--- /dev/null
+++ b/debian/patches/0043-doc-Clarify-import-keys-operation.patch
@@ -0,0 +1,92 @@
+From: Marcus Brinkmann <marcus.brinkmann at ruhr-uni-bochum.de>
+Date: Thu, 10 Aug 2017 16:50:11 +0200
+Subject: doc: Clarify import keys operation.
+
+* doc/gpgme.texi (gpgme_op_import_start): Fix grammar.
+(gpgme_op_import_keys): Clarify some wording and fix result.
+* src/import.c (gpgme_op_import_keys): Clarify comment.
+
+Signed-off-by: Marcus Brinkmann <mb at g10code.com>
+GnuPG-bug-id: 3215
+(cherry picked from commit dfb3ca85680534b3885ab04d3fba4752c5a6f998)
+---
+ doc/gpgme.texi | 28 +++++++++++++---------------
+ src/import.c | 13 ++++++-------
+ 2 files changed, 19 insertions(+), 22 deletions(-)
+
+diff --git a/doc/gpgme.texi b/doc/gpgme.texi
+index 37760af..bf84629 100644
+--- a/doc/gpgme.texi
++++ b/doc/gpgme.texi
+@@ -4449,34 +4449,32 @@ The function @code{gpgme_op_import_start} initiates a
+
+ The function returns the error code @code{GPG_ERR_NO_ERROR} if the
+ import could be started successfully, @code{GPG_ERR_INV_VALUE} if
+- at var{keydata} if @var{ctx} or @var{keydata} is not a valid pointer,
+-and @code{GPG_ERR_NO_DATA} if @var{keydata} is an empty data buffer.
++ at var{ctx} or @var{keydata} is not a valid pointer, and
++ at code{GPG_ERR_NO_DATA} if @var{keydata} is an empty data buffer.
+ @end deftypefun
+
+ @deftypefun gpgme_error_t gpgme_op_import_keys (@w{gpgme_ctx_t @var{ctx}}, @w{gpgme_key_t *@var{keys}})
+-The function @code{gpgme_op_import_keys} adds the keys described by the
+- at code{NULL} terminated array @var{keys} to the key ring of the crypto
+-engine used by @var{ctx}. This function is the general interface to
+-move a key from one crypto engine to another as long as they are
+-compatible. In particular it is used to actually import and make keys
+-permanent which have been retrieved from an external source (i.e. using
+- at code{GPGME_KEYLIST_MODE_EXTERN}). @footnote{Thus it is a replacement
+-for the usual workaround of exporting and then importing a key to make
+-an X.509 key permanent.}
++The function @code{gpgme_op_import_keys} adds the keys described by
++the @code{NULL} terminated array @var{keys} to the key ring of the
++crypto engine used by @var{ctx}. It is used to actually import and
++make keys permanent which have been retrieved from an external source
++(i.e. using @code{GPGME_KEYLIST_MODE_EXTERN}). @footnote{Thus it is a
++replacement for the usual workaround of exporting and then importing a
++key to make an X.509 key permanent.}
+
+ Only keys of the currently selected protocol of @var{ctx} are
+ considered for import. Other keys specified by the @var{keys} are
+ ignored. As of now all considered keys must have been retrieved using
+-the same method, that is the used key listing mode must be identical.
++the same method, i.e. the used key listing mode must be identical.
+
+ After the operation completed successfully, the result can be
+ retrieved with @code{gpgme_op_import_result}.
+
+ The function returns the error code @code{GPG_ERR_NO_ERROR} if the
+ import was completed successfully, @code{GPG_ERR_INV_VALUE} if
+- at var{keydata} if @var{ctx} or @var{keydata} is not a valid pointer,
+- at code{GPG_ERR_CONFLICT} if the key listing mode does not match, and
+- at code{GPG_ERR_NO_DATA} if no keys are considered for export.
++ at var{ctx} is not a valid pointer, @code{GPG_ERR_CONFLICT} if the key
++listing mode does not match, and @code{GPG_ERR_NO_DATA} if no keys are
++considered for export.
+ @end deftypefun
+
+ @deftypefun gpgme_error_t gpgme_op_import_keys_start (@w{gpgme_ctx_t @var{ctx}}, @w{gpgme_key_t *@var{keys}})
+diff --git a/src/import.c b/src/import.c
+index 4173fe9..386ca72 100644
+--- a/src/import.c
++++ b/src/import.c
+@@ -392,13 +392,12 @@ gpgme_op_import_keys_start (gpgme_ctx_t ctx, gpgme_key_t *keys)
+ }
+
+
+-/* Import the keys from the array KEYS into the keyring. This
+- function allows to move a key from one engine to another as long as
+- they are compatible. In particular it is used to actually import
+- keys retrieved from an external source (i.e. using
+- GPGME_KEYLIST_MODE_EXTERN). It replaces the old workaround of
+- exporting and then importing a key as used to make an X.509 key
+- permanent. This function automagically does the right thing.
++/* Import the keys from the array KEYS into the keyring. In
++ particular it is used to actually import keys retrieved from an
++ external source (i.e. using GPGME_KEYLIST_MODE_EXTERN). It
++ replaces the old workaround of exporting and then importing a key
++ as used to make an X.509 key permanent. This function
++ automagically does the right thing.
+
+ KEYS is a NULL terminated array of gpgme key objects. The result
+ is the usual import result structure. Only keys matching the
diff --git a/debian/patches/0044-doc-Clarify-import-keys-operation-further.patch b/debian/patches/0044-doc-Clarify-import-keys-operation-further.patch
new file mode 100644
index 0000000..e96db15
--- /dev/null
+++ b/debian/patches/0044-doc-Clarify-import-keys-operation-further.patch
@@ -0,0 +1,42 @@
+From: Marcus Brinkmann <marcus.brinkmann at ruhr-uni-bochum.de>
+Date: Wed, 16 Aug 2017 15:39:17 +0200
+Subject: doc: Clarify import keys operation further.
+
+* doc/gpgme.texi (gpgme_op_import_keys): Further clarifications.
+
+Signed-off-by: Marcus Brinkmann <mb at g10code.com>
+GnuPG-bug-id: 3215
+(cherry picked from commit 0ee7f4f178284dae153a59be710bc994820369e5)
+---
+ doc/gpgme.texi | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+diff --git a/doc/gpgme.texi b/doc/gpgme.texi
+index bf84629..4829426 100644
+--- a/doc/gpgme.texi
++++ b/doc/gpgme.texi
+@@ -4458,9 +4458,12 @@ The function @code{gpgme_op_import_keys} adds the keys described by
+ the @code{NULL} terminated array @var{keys} to the key ring of the
+ crypto engine used by @var{ctx}. It is used to actually import and
+ make keys permanent which have been retrieved from an external source
+-(i.e. using @code{GPGME_KEYLIST_MODE_EXTERN}). @footnote{Thus it is a
++(i.e. using @code{GPGME_KEYLIST_MODE_EXTERN}) earlier. The external
++keylisting must have been made with the same context configuration (in
++particular the same home directory). @footnote{Thus it is a
+ replacement for the usual workaround of exporting and then importing a
+-key to make an X.509 key permanent.}
++key to make an X.509 key permanent.} Note that for OpenPGP this may
++require another access to the keyserver over the network.
+
+ Only keys of the currently selected protocol of @var{ctx} are
+ considered for import. Other keys specified by the @var{keys} are
+@@ -4470,6 +4473,9 @@ the same method, i.e. the used key listing mode must be identical.
+ After the operation completed successfully, the result can be
+ retrieved with @code{gpgme_op_import_result}.
+
++To move keys from one home directory to another, export and import the
++keydata using @code{gpgme_op_export} and @code{gpgme_op_import}.
++
+ The function returns the error code @code{GPG_ERR_NO_ERROR} if the
+ import was completed successfully, @code{GPG_ERR_INV_VALUE} if
+ @var{ctx} is not a valid pointer, @code{GPG_ERR_CONFLICT} if the key
diff --git a/debian/patches/series b/debian/patches/series
index 057bc06..765e1ef 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,2 +1,44 @@
0001-avoid-identifying-as-beta.patch
0002-qt-pass-fmt-to-gpgrt_asprintf.patch
+0003-tests-Make-error-message-more-helpful.patch
+0004-python-build-Reinstate-prepare-target.patch
+0005-python-Generate-files-into-build-directory.patch
+0006-python-Fix-vpath-builds-fix-distcheck.patch
+0007-python-simplify-build-some-fixups.patch
+0008-python-support-.pydistutils.cfg-mode.patch
+0009-tests-Do-not-use-check-local-magic-as-dependency.patch
+0010-python-Remove-usage-of-PYTHON_VERSIONS.patch
+0011-python-Remove-unneeded-stats-copy.patch
+0012-python-Read-gpg-error.h-using-the-pre-processor.patch
+0013-python-Support-alternatate-libdir-for-tests.patch
+0014-python-Fix-distcheck.patch
+0015-python-Prune-CLEANFILES.patch
+0016-python-fix-run-tests-missing-python_libdir.patch
+0017-python-use-autoconf-pre-processor-when-building-via-.patch
+0018-tests-Update-encrypted-sample-files.patch
+0019-doc-Improve-doc-on-passphrase_cb-pinentry-mode.patch
+0020-core-Don-t-split-gpgconf-strings-on-comma.patch
+0021-qt-tests-Don-t-use-internal-API.patch
+0022-qt-Undeprecate-API-that-I-find-useful.patch
+0023-qt-Add-a-missing-include-functional.patch
+0024-qt-Stop-agent-on-clean.patch
+0025-tests-Harmonize-test-suites.patch
+0026-tests-Make-sure-to-kill-all-previously-running-daemo.patch
+0027-python-Fix-test-environment-creation.patch
+0028-tests-Remove-remnants-of-check-local.patch
+0029-python-Fix-build-in-certain-cases.patch
+0030-core-Sort-the-status-table.patch
+0031-cpp-Fix-CMake-config-library-name-for-GPGME.patch
+0032-Fix-some-shadow-warnings.patch
+0033-core-Simplify-parsing-of-STATUS_ERROR-in-decrypt.c.patch
+0034-core-Return-CANCELED-and-BAD_PASSPHRASE-error-code-o.patch
+0035-core-Return-NO_SECKEY-error-code-on-decryption.patch
+0036-tests-Fix-printf-compiler-warning-for-an-error-case.patch
+0037-Sync-autogen.sh.patch
+0038-tests-Make-agent-spawning-more-robust.patch
+0039-tests-Fix-blunder.patch
+0040-tests-Fix-distcheck.patch
+0041-core-Fix-status-parsing-for-decrypt-verify.patch
+0042-doc-Add-more-tofu-documentation.patch
+0043-doc-Clarify-import-keys-operation.patch
+0044-doc-Clarify-import-keys-operation-further.patch
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-gnupg/gpgme.git
More information about the Pkg-gnupg-commit
mailing list