summaryrefslogtreecommitdiff
path: root/lang/python
diff options
context:
space:
mode:
authorJinWang An <jinwang.an@samsung.com>2021-12-01 16:54:36 +0900
committerJinWang An <jinwang.an@samsung.com>2021-12-01 16:54:36 +0900
commite158cb38f461261d019c653a5f5e0ca9ddab8d6d (patch)
tree3872a21bc5b5797ee3c705509aace3393b0de251 /lang/python
parentfd5caec0dccd1229c2b9dd5220c8e2b1ef966d0e (diff)
downloadgpgme-e158cb38f461261d019c653a5f5e0ca9ddab8d6d.tar.gz
gpgme-e158cb38f461261d019c653a5f5e0ca9ddab8d6d.tar.bz2
gpgme-e158cb38f461261d019c653a5f5e0ca9ddab8d6d.zip
Imported Upstream version 1.7.0upstream/1.7.0
Diffstat (limited to 'lang/python')
-rw-r--r--lang/python/MANIFEST.in4
-rw-r--r--lang/python/Makefile.am106
-rw-r--r--lang/python/Makefile.in798
-rw-r--r--lang/python/README59
-rw-r--r--lang/python/examples/assuan.py28
-rw-r--r--lang/python/examples/decryption-filter.py32
-rwxr-xr-xlang/python/examples/delkey.py33
-rwxr-xr-xlang/python/examples/encrypt-to-all.py53
-rwxr-xr-xlang/python/examples/exportimport.py61
-rwxr-xr-xlang/python/examples/genkey.py44
-rw-r--r--lang/python/examples/inter-edit.py56
-rwxr-xr-xlang/python/examples/sign.py28
-rwxr-xr-xlang/python/examples/signverify.py41
-rwxr-xr-xlang/python/examples/simple.py47
-rw-r--r--lang/python/examples/testCMSgetkey.py35
-rwxr-xr-xlang/python/examples/verifydetails.py79
-rwxr-xr-xlang/python/gpgme-h-clean.py53
-rw-r--r--lang/python/gpgme.i625
-rw-r--r--lang/python/helpers.c1169
-rw-r--r--lang/python/helpers.h39
-rw-r--r--lang/python/private.h54
-rw-r--r--lang/python/pyme/__init__.py125
-rw-r--r--lang/python/pyme/__pycache__/__init__.cpython-34.pycbin0 -> 4633 bytes
-rw-r--r--lang/python/pyme/__pycache__/version.cpython-34.pycbin0 -> 1997 bytes
-rw-r--r--lang/python/pyme/callbacks.py49
-rw-r--r--lang/python/pyme/constants/__init__.py114
-rw-r--r--lang/python/pyme/constants/data/__init__.py6
-rw-r--r--lang/python/pyme/constants/data/encoding.py22
-rw-r--r--lang/python/pyme/constants/event.py22
-rw-r--r--lang/python/pyme/constants/import.py22
-rw-r--r--lang/python/pyme/constants/keylist/__init__.py6
-rw-r--r--lang/python/pyme/constants/keylist/mode.py22
-rw-r--r--lang/python/pyme/constants/md.py22
-rw-r--r--lang/python/pyme/constants/pk.py22
-rw-r--r--lang/python/pyme/constants/protocol.py22
-rw-r--r--lang/python/pyme/constants/sig/__init__.py6
-rw-r--r--lang/python/pyme/constants/sig/mode.py22
-rw-r--r--lang/python/pyme/constants/sigsum.py22
-rw-r--r--lang/python/pyme/constants/status.py124
-rw-r--r--lang/python/pyme/constants/validity.py22
-rw-r--r--lang/python/pyme/core.py1145
-rw-r--r--lang/python/pyme/errors.py111
-rw-r--r--lang/python/pyme/gpgme.py125
-rw-r--r--lang/python/pyme/results.py118
-rw-r--r--lang/python/pyme/util.py53
-rw-r--r--lang/python/pyme/version.py68
-rw-r--r--lang/python/pyme/version.py.in68
-rwxr-xr-xlang/python/setup.py.in190
-rw-r--r--lang/python/tests/Makefile.am114
-rw-r--r--lang/python/tests/Makefile.in620
-rw-r--r--lang/python/tests/encrypt-only.asc33
-rwxr-xr-xlang/python/tests/final.py27
-rwxr-xr-xlang/python/tests/initial.py41
-rw-r--r--lang/python/tests/run-tests.py90
-rw-r--r--lang/python/tests/sign-only.asc33
-rw-r--r--lang/python/tests/support.py69
-rwxr-xr-xlang/python/tests/t-callbacks.py257
-rwxr-xr-xlang/python/tests/t-data.py129
-rwxr-xr-xlang/python/tests/t-decrypt-verify.py77
-rwxr-xr-xlang/python/tests/t-decrypt.py45
-rwxr-xr-xlang/python/tests/t-edit.py72
-rwxr-xr-xlang/python/tests/t-encrypt-large.py66
-rwxr-xr-xlang/python/tests/t-encrypt-sign.py98
-rwxr-xr-xlang/python/tests/t-encrypt-sym.py86
-rwxr-xr-xlang/python/tests/t-encrypt.py65
-rwxr-xr-xlang/python/tests/t-export.py40
-rwxr-xr-xlang/python/tests/t-file-name.py45
-rwxr-xr-xlang/python/tests/t-idiomatic.py84
-rwxr-xr-xlang/python/tests/t-import.py79
-rwxr-xr-xlang/python/tests/t-keylist.py246
-rwxr-xr-xlang/python/tests/t-protocol-assuan.py69
-rwxr-xr-xlang/python/tests/t-sig-notation.py79
-rwxr-xr-xlang/python/tests/t-sign.py122
-rwxr-xr-xlang/python/tests/t-signers.py98
-rwxr-xr-xlang/python/tests/t-trustlist.py43
-rwxr-xr-xlang/python/tests/t-verify.py195
-rwxr-xr-xlang/python/tests/t-wait.py45
-rwxr-xr-xlang/python/tests/t-wrapper.py25
78 files changed, 9064 insertions, 0 deletions
diff --git a/lang/python/MANIFEST.in b/lang/python/MANIFEST.in
new file mode 100644
index 0000000..eefdb83
--- /dev/null
+++ b/lang/python/MANIFEST.in
@@ -0,0 +1,4 @@
+recursive-include examples *.py
+include gpgme-h-clean.py gpgme.i
+include helpers.c helpers.h private.h
+recursive-include pyme *.py
diff --git a/lang/python/Makefile.am b/lang/python/Makefile.am
new file mode 100644
index 0000000..2271ce0
--- /dev/null
+++ b/lang/python/Makefile.am
@@ -0,0 +1,106 @@
+# Makefile.am for the Python bindings.
+# 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/>.
+
+EXTRA_DIST = \
+ README \
+ MANIFEST.in \
+ gpgme.i \
+ helpers.c helpers.h private.h \
+ gpgme-h-clean.py \
+ examples \
+ pyme
+
+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_PYME = \
+ $(srcdir)/pyme/callbacks.py \
+ $(srcdir)/pyme/constants \
+ $(srcdir)/pyme/core.py \
+ $(srcdir)/pyme/errors.py \
+ $(srcdir)/pyme/__init__.py \
+ $(srcdir)/pyme/results.py \
+ $(srcdir)/pyme/util.py
+
+# For VPATH builds we need to copy some files because Python's
+# distutils are not VPATH-aware.
+copystamp: $(COPY_FILES) $(COPY_FILES_PYME)
+ if test "$(srcdir)" != "$(builddir)" ; then \
+ cp -R $(COPY_FILES) . ; \
+ cp -R $(COPY_FILES_PYME) pyme ; \
+ fi
+ touch $@
+
+all-local: copystamp
+ for PYTHON in $(PYTHONS); do \
+ CFLAGS="$(CFLAGS) -I$(top_srcdir)" \
+ $$PYTHON setup.py build --verbose ; \
+ done
+
+dist/pyme3-$(VERSION).tar.gz dist/pyme3-$(VERSION).tar.gz.asc: copystamp
+ CFLAGS="$(CFLAGS) -I$(top_srcdir)" \
+ $(PYTHON) setup.py sdist --verbose
+ gpg2 --detach-sign --armor dist/pyme3-$(VERSION).tar.gz
+
+.PHONY: sdist
+sdist: dist/pyme3-$(VERSION).tar.gz dist/pyme3-$(VERSION).tar.gz.asc
+
+.PHONY: upload
+upload: dist/pyme3-$(VERSION).tar.gz dist/pyme3-$(VERSION).tar.gz.asc
+ twine upload $^
+
+CLEANFILES = gpgme.h errors.i gpgme_wrap.c pyme/gpgme.py \
+ copystamp
+
+# Remove the rest.
+#
+# 'make distclean' clears the write bit, breaking rm -rf. Fix the
+# permissions.
+clean-local:
+ rm -rf -- build
+ if test "$(srcdir)" != "$(builddir)" ; then \
+ find . -type d ! -perm -200 -exec chmod u+w {} ';' ; \
+ for F in $(COPY_FILES); do rm -rf -- `basename $$F` ; done ; \
+ for F in $(COPY_FILES_PYME); do \
+ rm -rf -- pyme/`basename $$F` ; \
+ done ; \
+ fi
+
+install-exec-local:
+ rm -f install_files.txt
+ for PYTHON in $(PYTHONS); do \
+ $$PYTHON setup.py install \
+ --prefix $(DESTDIR)$(prefix) \
+ --record files.txt \
+ --verbose ; \
+ cat files.txt >> install_files.txt ; \
+ rm files.txt ; \
+ done
+ $(MKDIR_P) $(DESTDIR)$(pythondir)/pyme
+ mv install_files.txt $(DESTDIR)$(pythondir)/pyme
+
+uninstall-local:
+ xargs <$(DESTDIR)$(pythondir)/pyme/install_files.txt -- rm -rf --
+ rm -rf -- $(DESTDIR)$(pythondir)/pyme
diff --git a/lang/python/Makefile.in b/lang/python/Makefile.in
new file mode 100644
index 0000000..6e6cfe6
--- /dev/null
+++ b/lang/python/Makefile.in
@@ -0,0 +1,798 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Makefile.am for the Python bindings.
+# 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/>.
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = lang/python
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+ $(top_srcdir)/build-aux/mkinstalldirs $(srcdir)/setup.py.in \
+ README
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4/ax_pkg_swig.m4 \
+ $(top_srcdir)/m4/ax_python_devel.m4 \
+ $(top_srcdir)/m4/glib-2.0.m4 $(top_srcdir)/m4/glibc21.m4 \
+ $(top_srcdir)/m4/gnupg-ttyname.m4 \
+ $(top_srcdir)/m4/gpg-error.m4 $(top_srcdir)/m4/libassuan.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkg.m4 \
+ $(top_srcdir)/m4/qt.m4 $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES = setup.py
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
+ ctags-recursive dvi-recursive html-recursive info-recursive \
+ install-data-recursive install-dvi-recursive \
+ install-exec-recursive install-html-recursive \
+ install-info-recursive install-pdf-recursive \
+ install-ps-recursive install-recursive installcheck-recursive \
+ installdirs-recursive pdf-recursive ps-recursive \
+ tags-recursive uninstall-recursive
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+RECURSIVE_CLEAN_TARGETS = mostlyclean-recursive clean-recursive \
+ distclean-recursive maintainer-clean-recursive
+am__recursive_targets = \
+ $(RECURSIVE_TARGETS) \
+ $(RECURSIVE_CLEAN_TARGETS) \
+ $(am__extra_recursive_targets)
+AM_RECURSIVE_TARGETS = $(am__recursive_targets:-recursive=) TAGS CTAGS \
+ distdir
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+ETAGS = etags
+CTAGS = ctags
+DIST_SUBDIRS = $(SUBDIRS)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+am__relativize = \
+ dir0=`pwd`; \
+ sed_first='s,^\([^/]*\)/.*$$,\1,'; \
+ sed_rest='s,^[^/]*/*,,'; \
+ sed_last='s,^.*/\([^/]*\)$$,\1,'; \
+ sed_butlast='s,/*[^/]*$$,,'; \
+ while test -n "$$dir1"; do \
+ first=`echo "$$dir1" | sed -e "$$sed_first"`; \
+ if test "$$first" != "."; then \
+ if test "$$first" = ".."; then \
+ dir2=`echo "$$dir0" | sed -e "$$sed_last"`/"$$dir2"; \
+ dir0=`echo "$$dir0" | sed -e "$$sed_butlast"`; \
+ else \
+ first2=`echo "$$dir2" | sed -e "$$sed_first"`; \
+ if test "$$first2" = "$$first"; then \
+ dir2=`echo "$$dir2" | sed -e "$$sed_rest"`; \
+ else \
+ dir2="../$$dir2"; \
+ fi; \
+ dir0="$$dir0"/"$$first"; \
+ fi; \
+ fi; \
+ dir1=`echo "$$dir1" | sed -e "$$sed_rest"`; \
+ done; \
+ reldir="$$dir2"
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BUILD_FILEVERSION = @BUILD_FILEVERSION@
+BUILD_REVISION = @BUILD_REVISION@
+BUILD_TIMESTAMP = @BUILD_TIMESTAMP@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DOXYGEN = @DOXYGEN@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ENABLED_LANGUAGES = @ENABLED_LANGUAGES@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GITLOG_TO_CHANGELOG = @GITLOG_TO_CHANGELOG@
+GLIBC21 = @GLIBC21@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GPGME_CONFIG_API_VERSION = @GPGME_CONFIG_API_VERSION@
+GPGME_CONFIG_AVAIL_LANG = @GPGME_CONFIG_AVAIL_LANG@
+GPGME_CONFIG_CFLAGS = @GPGME_CONFIG_CFLAGS@
+GPGME_CONFIG_HOST = @GPGME_CONFIG_HOST@
+GPGME_CONFIG_LIBS = @GPGME_CONFIG_LIBS@
+GPGME_QTTEST_CFLAGS = @GPGME_QTTEST_CFLAGS@
+GPGME_QTTEST_LIBS = @GPGME_QTTEST_LIBS@
+GPGME_QT_CFLAGS = @GPGME_QT_CFLAGS@
+GPGME_QT_LIBS = @GPGME_QT_LIBS@
+GPG_ERROR_CFLAGS = @GPG_ERROR_CFLAGS@
+GPG_ERROR_CONFIG = @GPG_ERROR_CONFIG@
+GPG_ERROR_LIBS = @GPG_ERROR_LIBS@
+GPG_ERROR_MT_CFLAGS = @GPG_ERROR_MT_CFLAGS@
+GPG_ERROR_MT_LIBS = @GPG_ERROR_MT_LIBS@
+GRAPHVIZ = @GRAPHVIZ@
+GREP = @GREP@
+HAVE_CXX11 = @HAVE_CXX11@
+HAVE_DOT = @HAVE_DOT@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBASSUAN_CFLAGS = @LIBASSUAN_CFLAGS@
+LIBASSUAN_CONFIG = @LIBASSUAN_CONFIG@
+LIBASSUAN_LIBS = @LIBASSUAN_LIBS@
+LIBGPGMEPP_LT_AGE = @LIBGPGMEPP_LT_AGE@
+LIBGPGMEPP_LT_CURRENT = @LIBGPGMEPP_LT_CURRENT@
+LIBGPGMEPP_LT_REVISION = @LIBGPGMEPP_LT_REVISION@
+LIBGPGME_LT_AGE = @LIBGPGME_LT_AGE@
+LIBGPGME_LT_CURRENT = @LIBGPGME_LT_CURRENT@
+LIBGPGME_LT_REVISION = @LIBGPGME_LT_REVISION@
+LIBOBJS = @LIBOBJS@
+LIBQGPGME_LT_AGE = @LIBQGPGME_LT_AGE@
+LIBQGPGME_LT_CURRENT = @LIBQGPGME_LT_CURRENT@
+LIBQGPGME_LT_REVISION = @LIBQGPGME_LT_REVISION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MOC = @MOC@
+MOC2 = @MOC2@
+NEED__FILE_OFFSET_BITS = @NEED__FILE_OFFSET_BITS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PYTHON = @PYTHON@
+PYTHONS = @PYTHONS@
+PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@
+PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@
+PYTHON_LDFLAGS = @PYTHON_LDFLAGS@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_SITE_PKG = @PYTHON_SITE_PKG@
+PYTHON_VERSION = @PYTHON_VERSION@
+PYTHON_VERSIONS = @PYTHON_VERSIONS@
+QTCHOOSER = @QTCHOOSER@
+RANLIB = @RANLIB@
+RC = @RC@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SWIG = @SWIG@
+SWIG_LIB = @SWIG_LIB@
+SYSROOT = @SYSROOT@
+VERSION = @VERSION@
+VERSION_NUMBER = @VERSION_NUMBER@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+emacs_local_vars_begin = @emacs_local_vars_begin@
+emacs_local_vars_end = @emacs_local_vars_end@
+emacs_local_vars_read_only = @emacs_local_vars_read_only@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+EXTRA_DIST = \
+ README \
+ MANIFEST.in \
+ gpgme.i \
+ helpers.c helpers.h private.h \
+ gpgme-h-clean.py \
+ examples \
+ pyme
+
+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_PYME = \
+ $(srcdir)/pyme/callbacks.py \
+ $(srcdir)/pyme/constants \
+ $(srcdir)/pyme/core.py \
+ $(srcdir)/pyme/errors.py \
+ $(srcdir)/pyme/__init__.py \
+ $(srcdir)/pyme/results.py \
+ $(srcdir)/pyme/util.py
+
+CLEANFILES = gpgme.h errors.i gpgme_wrap.c pyme/gpgme.py \
+ copystamp
+
+all: all-recursive
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu lang/python/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu lang/python/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+setup.py: $(top_builddir)/config.status $(srcdir)/setup.py.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+
+# This directory's subdirectories are mostly independent; you can cd
+# into them and run 'make' without going through this Makefile.
+# To change the values of 'make' variables: instead of editing Makefiles,
+# (1) if the variable is set in 'config.status', edit 'config.status'
+# (which will cause the Makefiles to be regenerated when you run 'make');
+# (2) otherwise, pass the desired values on the 'make' command line.
+$(am__recursive_targets):
+ @fail=; \
+ if $(am__make_keepgoing); then \
+ failcom='fail=yes'; \
+ else \
+ failcom='exit 1'; \
+ fi; \
+ dot_seen=no; \
+ target=`echo $@ | sed s/-recursive//`; \
+ case "$@" in \
+ distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \
+ *) list='$(SUBDIRS)' ;; \
+ esac; \
+ for subdir in $$list; do \
+ echo "Making $$target in $$subdir"; \
+ if test "$$subdir" = "."; then \
+ dot_seen=yes; \
+ local_target="$$target-am"; \
+ else \
+ local_target="$$target"; \
+ fi; \
+ ($(am__cd) $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \
+ || eval $$failcom; \
+ done; \
+ if test "$$dot_seen" = "no"; then \
+ $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \
+ fi; test -z "$$fail"
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-recursive
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ if ($(ETAGS) --etags-include --version) >/dev/null 2>&1; then \
+ include_option=--etags-include; \
+ empty_fix=.; \
+ else \
+ include_option=--include; \
+ empty_fix=; \
+ fi; \
+ list='$(SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ test ! -f $$subdir/TAGS || \
+ set "$$@" "$$include_option=$$here/$$subdir/TAGS"; \
+ fi; \
+ done; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-recursive
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-recursive
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+ @list='$(DIST_SUBDIRS)'; for subdir in $$list; do \
+ if test "$$subdir" = .; then :; else \
+ $(am__make_dryrun) \
+ || test -d "$(distdir)/$$subdir" \
+ || $(MKDIR_P) "$(distdir)/$$subdir" \
+ || exit 1; \
+ dir1=$$subdir; dir2="$(distdir)/$$subdir"; \
+ $(am__relativize); \
+ new_distdir=$$reldir; \
+ dir1=$$subdir; dir2="$(top_distdir)"; \
+ $(am__relativize); \
+ new_top_distdir=$$reldir; \
+ echo " (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) top_distdir="$$new_top_distdir" distdir="$$new_distdir" \\"; \
+ echo " am__remove_distdir=: am__skip_length_check=: am__skip_mode_fix=: distdir)"; \
+ ($(am__cd) $$subdir && \
+ $(MAKE) $(AM_MAKEFLAGS) \
+ top_distdir="$$new_top_distdir" \
+ distdir="$$new_distdir" \
+ am__remove_distdir=: \
+ am__skip_length_check=: \
+ am__skip_mode_fix=: \
+ distdir) \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-recursive
+all-am: Makefile all-local
+installdirs: installdirs-recursive
+installdirs-am:
+install: install-recursive
+install-exec: install-exec-recursive
+install-data: install-data-recursive
+uninstall: uninstall-recursive
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-recursive
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-recursive
+
+clean-am: clean-generic clean-libtool clean-local mostlyclean-am
+
+distclean: distclean-recursive
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic distclean-tags
+
+dvi: dvi-recursive
+
+dvi-am:
+
+html: html-recursive
+
+html-am:
+
+info: info-recursive
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-recursive
+
+install-dvi-am:
+
+install-exec-am: install-exec-local
+
+install-html: install-html-recursive
+
+install-html-am:
+
+install-info: install-info-recursive
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-recursive
+
+install-pdf-am:
+
+install-ps: install-ps-recursive
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-recursive
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-recursive
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-recursive
+
+pdf-am:
+
+ps: ps-recursive
+
+ps-am:
+
+uninstall-am: uninstall-local
+
+.MAKE: $(am__recursive_targets) install-am install-strip
+
+.PHONY: $(am__recursive_targets) CTAGS GTAGS TAGS all all-am all-local \
+ check check-am clean clean-generic clean-libtool clean-local \
+ cscopelist-am ctags ctags-am distclean distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-exec-local install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ installdirs-am maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \
+ ps ps-am tags tags-am uninstall uninstall-am uninstall-local
+
+
+# For VPATH builds we need to copy some files because Python's
+# distutils are not VPATH-aware.
+copystamp: $(COPY_FILES) $(COPY_FILES_PYME)
+ if test "$(srcdir)" != "$(builddir)" ; then \
+ cp -R $(COPY_FILES) . ; \
+ cp -R $(COPY_FILES_PYME) pyme ; \
+ fi
+ touch $@
+
+all-local: copystamp
+ for PYTHON in $(PYTHONS); do \
+ CFLAGS="$(CFLAGS) -I$(top_srcdir)" \
+ $$PYTHON setup.py build --verbose ; \
+ done
+
+dist/pyme3-$(VERSION).tar.gz dist/pyme3-$(VERSION).tar.gz.asc: copystamp
+ CFLAGS="$(CFLAGS) -I$(top_srcdir)" \
+ $(PYTHON) setup.py sdist --verbose
+ gpg2 --detach-sign --armor dist/pyme3-$(VERSION).tar.gz
+
+.PHONY: sdist
+sdist: dist/pyme3-$(VERSION).tar.gz dist/pyme3-$(VERSION).tar.gz.asc
+
+.PHONY: upload
+upload: dist/pyme3-$(VERSION).tar.gz dist/pyme3-$(VERSION).tar.gz.asc
+ twine upload $^
+
+# Remove the rest.
+#
+# 'make distclean' clears the write bit, breaking rm -rf. Fix the
+# permissions.
+clean-local:
+ rm -rf -- build
+ if test "$(srcdir)" != "$(builddir)" ; then \
+ find . -type d ! -perm -200 -exec chmod u+w {} ';' ; \
+ for F in $(COPY_FILES); do rm -rf -- `basename $$F` ; done ; \
+ for F in $(COPY_FILES_PYME); do \
+ rm -rf -- pyme/`basename $$F` ; \
+ done ; \
+ fi
+
+install-exec-local:
+ rm -f install_files.txt
+ for PYTHON in $(PYTHONS); do \
+ $$PYTHON setup.py install \
+ --prefix $(DESTDIR)$(prefix) \
+ --record files.txt \
+ --verbose ; \
+ cat files.txt >> install_files.txt ; \
+ rm files.txt ; \
+ done
+ $(MKDIR_P) $(DESTDIR)$(pythondir)/pyme
+ mv install_files.txt $(DESTDIR)$(pythondir)/pyme
+
+uninstall-local:
+ xargs <$(DESTDIR)$(pythondir)/pyme/install_files.txt -- rm -rf --
+ rm -rf -- $(DESTDIR)$(pythondir)/pyme
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/lang/python/README b/lang/python/README
new file mode 100644
index 0000000..98c007e
--- /dev/null
+++ b/lang/python/README
@@ -0,0 +1,59 @@
+PyME - GPGME for Python -*- org -*-
+=======================
+
+PyME is a python interface to the GPGME library:
+https://www.gnupg.org/related_software/gpgme/
+
+PyME offers two interfaces, one is a high-level, curated, and
+idiomatic interface that is implemented as a shim on top of the
+low-level interface automatically created using SWIG.
+
+This way we make simple things easy, while still providing the entire
+functionality of the underlying library.
+
+* Mailing List
+
+For general discussion and help see the gnupg-users mailing list:
+https://lists.gnupg.org/mailman/listinfo/gnupg-users
+
+For development see the gnupg-devel mailing list:
+https://lists.gnupg.org/mailman/listinfo/gnupg-devel
+
+* Bugs
+
+Please report bugs using our bug tracker using the category 'gpgme',
+and topic 'python':
+https://bugs.gnupg.org/gnupg/
+
+* Authors
+
+PyME has been created by John Goerzen, and maintained, developed, and
+cherished by Igor Belyi, Martin Albrecht, Ben McGinnes, and everyone
+who contributed to it in any way.
+
+In 2016 we merged a port of PyME to into the GPGME repository, and
+development will continue there. Please see the VCS history for the
+list of contributors, and if you do find bugs, or want to contribute,
+please get in touch and help maintain PyME.
+
+Please see the section 'History' further down this document for
+references to previous versions.
+
+* History
+
+ - The bindings have been merged into the GPGME repository in 2016.
+
+ - The latest version of PyME for Python 3.2 and above (as of
+ May, 2015) is v0.9.1.
+ https://git.gnupg.org/gpgme.git/lang/py3-pyme
+
+ - The latest version of PyME for Python 2.6 and 2.7 (as of this
+ writing) is v0.9.0. https://bitbucket.org/malb/pyme
+
+ - A previous version of PyME v0.8.0 can be found on sourceforge:
+ http://pyme.sourceforge.net/
+
+ - A previous version of PyME v0.5.1 which works with GPGME v0.3.15
+ can be found on John Goerzen's PyME page:
+ http://quux.org/devel/pyme/
+ http://www.complete.org/JohnGoerzen
diff --git a/lang/python/examples/assuan.py b/lang/python/examples/assuan.py
new file mode 100644
index 0000000..22960d3
--- /dev/null
+++ b/lang/python/examples/assuan.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2016 g10 Code GmbH
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+"""Demonstrate the use of the Assuan protocol engine"""
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+import pyme
+
+with pyme.Context(protocol=pyme.constants.PROTOCOL_ASSUAN) as c:
+ # Invoke the pinentry to get a confirmation.
+ err = c.assuan_transact(['GET_CONFIRMATION', 'Hello there'])
+ print("You chose {}.".format("cancel" if err else "ok"))
diff --git a/lang/python/examples/decryption-filter.py b/lang/python/examples/decryption-filter.py
new file mode 100644
index 0000000..3007c2b
--- /dev/null
+++ b/lang/python/examples/decryption-filter.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2016 g10 Code GmbH
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+"""A decryption filter
+
+This demonstrates decryption using pyme3 in three lines of code. To
+be used like this:
+
+./decryption-filter.py <message.gpg >message.plain
+
+"""
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+import sys
+import pyme
+pyme.Context().decrypt(sys.stdin, sink=sys.stdout)
diff --git a/lang/python/examples/delkey.py b/lang/python/examples/delkey.py
new file mode 100755
index 0000000..a02f412
--- /dev/null
+++ b/lang/python/examples/delkey.py
@@ -0,0 +1,33 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2004,2008 Igor Belyi <belyi@users.sourceforge.net>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+# Sample of key deletion
+# It deletes keys for joe@example.org generated by genkey.py script
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+import pyme
+
+with pyme.Context() as c:
+ # Note: We must not modify the key store during iteration,
+ # therefore, we explicitly make a list.
+ keys = list(c.keylist("joe+pyme@example.org"))
+
+ for k in keys:
+ c.op_delete(k, True)
diff --git a/lang/python/examples/encrypt-to-all.py b/lang/python/examples/encrypt-to-all.py
new file mode 100755
index 0000000..35873bd
--- /dev/null
+++ b/lang/python/examples/encrypt-to-all.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2008 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+"""
+This program will try to encrypt a simple message to each key on your
+keyring. If your keyring has any invalid keys on it, those keys will
+be skipped and it will re-try the encryption."""
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+import sys
+import pyme
+
+with pyme.Context(armor=True) as c:
+ recipients = list()
+ for key in c.keylist():
+ valid = 0
+ if any(sk.can_encrypt for sk in key.subkeys):
+ recipients.append(key)
+ print("Adding recipient {0}.".format(key.uids[0].uid))
+
+ ciphertext = None
+ while not ciphertext:
+ print("Encrypting to %d recipients" % len(recipients))
+ try:
+ ciphertext, _, _ = c.encrypt(b'This is my message.',
+ recipients=recipients)
+ except pyme.errors.InvalidRecipients as e:
+ print("Encryption failed for these keys:\n{0!s}".format(e))
+
+ # filter out the bad keys
+ bad_keys = {bad.fpr for bad in e.recipients}
+ recipients = [r for r in recipients
+ if not r.subkeys[0].fpr in bad_keys]
+
+ sys.stdout.buffer.write(ciphertext)
diff --git a/lang/python/examples/exportimport.py b/lang/python/examples/exportimport.py
new file mode 100755
index 0000000..bc946bc
--- /dev/null
+++ b/lang/python/examples/exportimport.py
@@ -0,0 +1,61 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2004,2008 Igor Belyi <belyi@users.sourceforge.net>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+# Sample of export and import of keys
+# It uses keys for joe+pyme@example.org generated by genkey.py script
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+import sys
+import os
+import pyme
+
+user = "joe+pyme@example.org"
+
+with pyme.Context(armor=True) as c, pyme.Data() as expkey:
+ print(" - Export %s's public keys - " % user)
+ c.op_export(user, 0, expkey)
+
+ # print out exported data to see how it looks in armor.
+ expkey.seek(0, os.SEEK_SET)
+ expstring = expkey.read()
+ if expstring:
+ sys.stdout.buffer.write(expstring)
+ else:
+ sys.exit("No %s's keys to export!" % user)
+
+# delete keys to ensure that they came from our imported data. Note
+# that if joe's key has private part as well we can only delete both
+# of them.
+with pyme.Context() as c:
+ # Note: We must not modify the key store during iteration,
+ # therfore, we explicitly make a list.
+ keys = list(c.keylist(user))
+
+ for k in keys:
+ c.op_delete(k, True)
+
+with pyme.Context() as c:
+ print(" - Import exported keys - ")
+ c.op_import(expstring)
+ result = c.op_import_result()
+ if result:
+ print(result)
+ else:
+ sys.exit(" - No import result - ")
diff --git a/lang/python/examples/genkey.py b/lang/python/examples/genkey.py
new file mode 100755
index 0000000..ee70303
--- /dev/null
+++ b/lang/python/examples/genkey.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+import pyme
+
+# This is the example from the GPGME manual.
+
+parms = """<GnupgKeyParms format="internal">
+Key-Type: RSA
+Key-Length: 2048
+Subkey-Type: RSA
+Subkey-Length: 2048
+Name-Real: Joe Tester
+Name-Comment: with stupid passphrase
+Name-Email: joe+pyme@example.org
+Passphrase: Crypt0R0cks
+Expire-Date: 2020-12-31
+</GnupgKeyParms>
+"""
+
+with pyme.Context() as c:
+ c.set_progress_cb(pyme.callbacks.progress_stdout)
+ c.op_genkey(parms, None, None)
+ print("Generated key with fingerprint {0}.".format(
+ c.op_genkey_result().fpr))
diff --git a/lang/python/examples/inter-edit.py b/lang/python/examples/inter-edit.py
new file mode 100644
index 0000000..39d6f17
--- /dev/null
+++ b/lang/python/examples/inter-edit.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2005 Igor Belyi <belyi@users.sourceforge.net>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+"""Simple interactive editor to test editor scripts"""
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+import sys
+import pyme
+
+if len(sys.argv) != 2:
+ sys.exit("Usage: %s <Gpg key pattern>\n" % sys.argv[0])
+
+name = sys.argv[1]
+
+with pyme.Context() as c:
+ keys = list(c.keylist(name))
+ if len(keys) == 0:
+ sys.exit("No key matching {}.".format(name))
+ if len(keys) > 1:
+ sys.exit("More than one key matching {}.".format(name))
+
+ key = keys[0]
+ print("Editing key {} ({}):".format(key.uids[0].uid, key.subkeys[0].fpr))
+
+ def edit_fnc(keyword, args):
+ print("Status: {} ({}), args: {} > ".format(
+ keyword, status, args), end='', flush=True)
+
+ if not 'GET' in keyword:
+ # no prompt
+ print()
+ return None
+
+ try:
+ return input()
+ except EOFError:
+ return "quit"
+
+ c.interact(key, edit_fnc, sink=sys.stdout)
diff --git a/lang/python/examples/sign.py b/lang/python/examples/sign.py
new file mode 100755
index 0000000..2f235ba
--- /dev/null
+++ b/lang/python/examples/sign.py
@@ -0,0 +1,28 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+import sys
+import pyme
+from pyme.constants.sig import mode
+
+with pyme.Context() as c:
+ signed, _ = c.sign(b"Test message", mode=mode.CLEAR)
+ sys.stdout.buffer.write(signed)
diff --git a/lang/python/examples/signverify.py b/lang/python/examples/signverify.py
new file mode 100755
index 0000000..03bc0a6
--- /dev/null
+++ b/lang/python/examples/signverify.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2004,2008 Igor Belyi <belyi@users.sourceforge.net>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+# Sample of unattended signing/verifying of a message.
+# It uses keys for joe+pyme@example.org generated by genkey.py script
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+import sys
+import pyme
+from pyme.constants.sig import mode
+
+user = "joe+pyme"
+
+with pyme.Context(pinentry_mode=pyme.constants.PINENTRY_MODE_LOOPBACK) as c:
+ keys = list(c.keylist(user))
+ if len(keys) == 0:
+ sys.exit("No key matching {}.".format(user))
+
+ c.signers = keys[:1]
+ c.set_passphrase_cb(lambda *args: "Crypt0R0cks")
+ signed_data, _ = c.sign(b"Test message", mode=mode.CLEAR)
+
+ data, result = c.verify(signed_data, verify=keys[:1])
+ print("Data: {!r}\nSignature: {!s}".format(data, result.signatures[0]))
diff --git a/lang/python/examples/simple.py b/lang/python/examples/simple.py
new file mode 100755
index 0000000..5598487
--- /dev/null
+++ b/lang/python/examples/simple.py
@@ -0,0 +1,47 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2005 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@complete.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+import sys
+import pyme
+
+with pyme.Context(armor=True) as c:
+ recipients = []
+ print("Enter name of your recipient(s), end with a blank line.")
+ while True:
+ line = input()
+ if not line:
+ break
+ new = list(c.keylist(line))
+ if not new:
+ print("Matched no known keys.")
+ else:
+ print("Adding {}.".format(", ".join(k.uids[0].name for k in new)))
+ recipients.extend(new)
+
+ if not recipients:
+ sys.exit("No recipients.")
+
+ print("Encrypting for {}.".format(", ".join(k.uids[0].name
+ for k in recipients)))
+
+ ciphertext, _, _ = c.encrypt(b"This is my message,", recipients)
+ sys.stdout.buffer.write(ciphertext)
diff --git a/lang/python/examples/testCMSgetkey.py b/lang/python/examples/testCMSgetkey.py
new file mode 100644
index 0000000..4467b6c
--- /dev/null
+++ b/lang/python/examples/testCMSgetkey.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2008 Bernhard Reiter <bernhard@intevation.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+"""A test applicaton for the CMS protocol."""
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+import sys
+import pyme
+
+if len(sys.argv) != 2:
+ sys.exit("fingerprint or unique key ID for gpgme_get_key()")
+
+with pyme.Context(protocol=pyme.constants.PROTOCOL_CMS) as c:
+ key = c.get_key(sys.argv[1], False)
+
+ print("got key: ", key.subkeys[0].fpr)
+ for uid in key.uids:
+ print(uid.uid)
diff --git a/lang/python/examples/verifydetails.py b/lang/python/examples/verifydetails.py
new file mode 100755
index 0000000..fa34926
--- /dev/null
+++ b/lang/python/examples/verifydetails.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2004,2008 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (c) 2008 Bernhard Reiter <bernhard@intevation.de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <http://www.gnu.org/licenses/>.
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+import sys
+from pyme import core
+from pyme.constants import protocol
+
+def print_engine_infos():
+ print("gpgme version:", core.check_version(None))
+ print("engines:")
+
+ for engine in core.get_engine_info():
+ print(engine.file_name, engine.version)
+
+ for proto in [protocol.OpenPGP, protocol.CMS]:
+ print("Have {}? {}".format(core.get_protocol_name(proto),
+ core.engine_check_version(proto)))
+
+
+def verifyprintdetails(filename, detached_sig_filename=None):
+ """Verify a signature, print a lot of details."""
+ with core.Context() as c:
+
+ # Verify.
+ data, result = c.verify(open(filename),
+ open(detached_sig_filename)
+ if detached_sig_filename else None)
+
+ # List results for all signatures. Status equal 0 means "Ok".
+ for index, sign in enumerate(result.signatures):
+ print("signature", index, ":")
+ print(" summary: %#0x" % (sign.summary))
+ print(" status: %#0x" % (sign.status))
+ print(" timestamp: ", sign.timestamp)
+ print(" fingerprint:", sign.fpr)
+ print(" uid: ", c.get_key(sign.fpr, 0).uids[0].uid)
+
+ # Print "unsigned" text if inline signature
+ if data:
+ sys.stdout.buffer.write(data)
+
+def main():
+ print_engine_infos()
+ print()
+
+ argc = len(sys.argv)
+ if argc < 2 or argc > 3:
+ sys.exit(
+ "Usage: {} <filename>[ <detached_signature_filename>]".format(
+ sys.argv[0]))
+
+ if argc == 2:
+ print("trying to verify file {}.".format(sys.argv[1]))
+ verifyprintdetails(sys.argv[1])
+ if argc == 3:
+ print("trying to verify signature {1} for file {0}.".format(*sys.argv))
+ verifyprintdetails(sys.argv[1], sys.argv[2])
+
+if __name__ == "__main__":
+ main()
diff --git a/lang/python/gpgme-h-clean.py b/lang/python/gpgme-h-clean.py
new file mode 100755
index 0000000..52f8676
--- /dev/null
+++ b/lang/python/gpgme-h-clean.py
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2004,2008 Igor Belyi <belyi@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/gpgme.i b/lang/python/gpgme.i
new file mode 100644
index 0000000..84addae
--- /dev/null
+++ b/lang/python/gpgme.i
@@ -0,0 +1,625 @@
+/*
+# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2004,2008 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@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
+*/
+%module gpgme
+%include "cpointer.i"
+%include "cstring.i"
+
+/* Generate doc strings for all methods.
+
+ This will generate docstrings of the form
+
+ gpgme_op_encrypt(ctx, recp, flags, plain, cipher) -> gpgme_error_t
+
+ which we transform into
+
+ ctx.op_encrypt(recp, flags, plain, cipher) -> gpgme_error_t
+
+ for automagically wrapped functions. */
+%feature("autodoc", "0");
+
+
+/* Allow use of Unicode objects, bytes, and None for strings. */
+%typemap(in) const char *(PyObject *encodedInput = NULL) {
+ if ($input == Py_None)
+ $1 = NULL;
+ else if (PyUnicode_Check($input))
+ {
+ encodedInput = PyUnicode_AsUTF8String($input);
+ if (encodedInput == NULL)
+ return NULL;
+ $1 = PyBytes_AsString(encodedInput);
+ }
+ else if (PyBytes_Check($input))
+ $1 = PyBytes_AsString($input);
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "arg %d: expected str, bytes, or None, got %s",
+ $argnum, $input->ob_type->tp_name);
+ return NULL;
+ }
+}
+%typemap(freearg) const char * {
+ Py_XDECREF(encodedInput$argnum);
+}
+
+/* Likewise for a list of strings. */
+%typemap(in) const char *[] (void *vector = NULL,
+ size_t size,
+ PyObject **pyVector = NULL) {
+ /* Check if is a list */
+ if (PyList_Check($input)) {
+ size_t i, j;
+ size = PyList_Size($input);
+ $1 = (char **) (vector = malloc((size+1) * sizeof(char *)));
+ pyVector = calloc(sizeof *pyVector, size);
+
+ for (i = 0; i < size; i++) {
+ PyObject *o = PyList_GetItem($input,i);
+ if (PyUnicode_Check(o))
+ {
+ pyVector[i] = PyUnicode_AsUTF8String(o);
+ if (pyVector[i] == NULL)
+ {
+ free(vector);
+ for (j = 0; j < i; j++)
+ Py_XDECREF(pyVector[j]);
+ return NULL;
+ }
+ $1[i] = PyBytes_AsString(pyVector[i]);
+ }
+ else if (PyString_Check(o))
+ $1[i] = PyString_AsString(o);
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "arg %d: list must contain only str or bytes, got %s "
+ "at position %d",
+ $argnum, o->ob_type->tp_name, i);
+ free($1);
+ return NULL;
+ }
+ }
+ $1[i] = NULL;
+ } else {
+ PyErr_Format(PyExc_TypeError,
+ "arg %d: expected a list of str or bytes, got %s",
+ $argnum, $input->ob_type->tp_name);
+ return NULL;
+ }
+}
+%typemap(freearg) const char *[] {
+ size_t i;
+ free(vector$argnum);
+ for (i = 0; i < size$argnum; i++)
+ Py_XDECREF(pyVector$argnum[i]);
+}
+
+// Release returned buffers as necessary.
+%typemap(newfree) char * "free($1);";
+%newobject gpgme_data_release_and_get_mem;
+
+%typemap(arginit) gpgme_key_t [] {
+ $1 = NULL;
+}
+
+%typemap(in) gpgme_key_t [] {
+ int i, numb = 0;
+ if (!PySequence_Check($input)) {
+ PyErr_Format(PyExc_ValueError, "arg %d: Expected a list of gpgme_key_t",
+ $argnum);
+ return NULL;
+ }
+ if((numb = PySequence_Length($input)) != 0) {
+ $1 = (gpgme_key_t*)malloc((numb+1)*sizeof(gpgme_key_t));
+ for(i=0; i<numb; i++) {
+ PyObject *pypointer = PySequence_GetItem($input, i);
+
+ /* input = $input, 1 = $1, 1_descriptor = $1_descriptor */
+ /* &1_descriptor = $&1_descriptor *1_descriptor = $*1_descriptor */
+
+ // Following code is from swig's python.swg
+ if ((SWIG_ConvertPtr(pypointer,(void **) &$1[i], $*1_descriptor,SWIG_POINTER_EXCEPTION | $disown )) == -1) {
+ Py_DECREF(pypointer);
+ return NULL;
+ }
+ Py_DECREF(pypointer);
+ }
+ $1[numb] = NULL;
+ }
+}
+%typemap(freearg) gpgme_key_t [] {
+ if ($1) free($1);
+}
+
+// Special handling for references to our objects.
+%typemap(in) gpgme_data_t DATAIN (gpgme_data_t wrapper = NULL,
+ PyObject *bytesio = NULL,
+ Py_buffer view, int have_view = 0) {
+ /* If we create a temporary wrapper object, we will store it in
+ wrapperN, where N is $argnum. Here in this fragment, SWIG will
+ automatically append $argnum. */
+ memset(&view, 0, sizeof view);
+ if ($input == Py_None)
+ $1 = NULL;
+ else {
+ PyObject *pypointer;
+ pypointer = _pyme_obj2gpgme_data_t($input, $argnum, &wrapper,
+ &bytesio, &view);
+ if (pypointer == NULL)
+ return NULL;
+ have_view = !! view.obj;
+
+ /* input = $input, 1 = $1, 1_descriptor = $1_descriptor */
+
+ // Following code is from swig's python.swg
+
+ if ((SWIG_ConvertPtr(pypointer,(void **) &$1, $1_descriptor,
+ SWIG_POINTER_EXCEPTION | $disown )) == -1) {
+ Py_DECREF(pypointer);
+ return NULL;
+ }
+ Py_DECREF(pypointer);
+ }
+}
+
+#if HAVE_DATA_H
+/* If we are doing an in-tree build, we can use the internal
+ representation of struct gpgme_data for an very efficient check if
+ the buffer has been modified. */
+%{
+#include "src/data.h" /* For struct gpgme_data. */
+%}
+#endif
+
+%typemap(freearg) gpgme_data_t DATAIN {
+ /* See whether we need to update the Python buffer. */
+ if (resultobj && wrapper$argnum && view$argnum.buf)
+ {
+ int dirty;
+ char *new_data = NULL;
+ size_t new_size;
+
+#if HAVE_DATA_H
+ new_data = wrapper$argnum->data.mem.buffer;
+ new_size = wrapper$argnum->data.mem.length;
+ dirty = new_data != NULL;
+#else
+ new_data = gpgme_data_release_and_get_mem (wrapper$argnum, &new_size);
+ wrapper$argnum = NULL;
+ dirty = new_size != view$argnum.len
+ || memcmp (new_data, view$argnum.buf, view$argnum.len);
+#endif
+
+ if (dirty)
+ {
+ /* The buffer is dirty. */
+ if (view$argnum.readonly)
+ {
+ Py_XDECREF(resultobj);
+ resultobj = NULL;
+ PyErr_SetString(PyExc_ValueError,
+ "cannot update read-only buffer");
+ }
+
+ /* See if we need to truncate the buffer. */
+ if (resultobj && view$argnum.len != new_size)
+ {
+ if (bytesio$argnum == NULL)
+ {
+ Py_XDECREF(resultobj);
+ resultobj = NULL;
+ PyErr_SetString(PyExc_ValueError, "cannot resize buffer");
+ }
+ else
+ {
+ PyObject *retval;
+ PyBuffer_Release(&view$argnum);
+ assert(view$argnum.obj == NULL);
+ retval = PyObject_CallMethod(bytesio$argnum, "truncate",
+ "l", (long) new_size);
+ if (retval == NULL)
+ {
+ Py_XDECREF(resultobj);
+ resultobj = NULL;
+ }
+ else
+ {
+ Py_DECREF(retval);
+
+ retval = PyObject_CallMethod(bytesio$argnum,
+ "getbuffer", NULL);
+ if (retval == NULL
+ || PyObject_GetBuffer(retval, &view$argnum,
+ PyBUF_SIMPLE|PyBUF_WRITABLE) < 0)
+ {
+ Py_XDECREF(resultobj);
+ resultobj = NULL;
+ }
+
+ Py_XDECREF(retval);
+
+ if (resultobj && view$argnum.len
+ != new_size)
+ {
+ Py_XDECREF(resultobj);
+ resultobj = NULL;
+ PyErr_Format(PyExc_ValueError,
+ "Expected buffer of length %zu, got %zi",
+ new_size,
+ view$argnum.len);
+ }
+ }
+ }
+ }
+ if (resultobj)
+ memcpy(view$argnum.buf, new_data, new_size);
+ }
+#if ! HAVE_DATA_H
+ free (new_data);
+#endif
+ }
+
+ /* Free the temporary wrapper, if any. */
+ if (wrapper$argnum)
+ gpgme_data_release(wrapper$argnum);
+ Py_XDECREF (bytesio$argnum);
+ if (have_view$argnum && view$argnum.buf)
+ PyBuffer_Release(&view$argnum);
+}
+
+%apply gpgme_data_t DATAIN {gpgme_data_t plain, gpgme_data_t cipher,
+ gpgme_data_t sig, gpgme_data_t signed_text,
+ gpgme_data_t plaintext, gpgme_data_t keydata,
+ gpgme_data_t pubkey, gpgme_data_t seckey,
+ gpgme_data_t out};
+
+/* SWIG has problems interpreting ssize_t, off_t or gpgme_error_t in
+ gpgme.h. */
+/* XXX: This is wrong at least for off_t if compiled with LFS. */
+%typemap(out) ssize_t, off_t, gpgme_error_t, gpgme_err_code_t, gpgme_err_source_t, gpg_error_t {
+ $result = PyLong_FromLong($1);
+}
+/* XXX: This is wrong at least for off_t if compiled with LFS. */
+%typemap(in) ssize_t, off_t, gpgme_error_t, gpgme_err_code_t, gpgme_err_source_t, gpg_error_t {
+ $1 = PyLong_AsLong($input);
+}
+
+// Those are for gpgme_data_read() and gpgme_strerror_r()
+%typemap(in) (void *buffer, size_t size), (char *buf, size_t buflen) {
+ $2 = PyLong_AsLong($input);
+ if ($2 < 0) {
+ PyErr_SetString(PyExc_ValueError, "Positive integer expected");
+ return NULL;
+ }
+ $1 = ($1_ltype) malloc($2+1);
+}
+%typemap(argout) (void *buffer, size_t size), (char *buf, size_t buflen) {
+ Py_XDECREF($result); /* Blow away any previous result */
+ if (result < 0) { /* Check for I/O error */
+ free($1);
+ return PyErr_SetFromErrno(PyExc_RuntimeError);
+ }
+ $result = PyBytes_FromStringAndSize($1,result);
+ free($1);
+}
+
+/* For gpgme_data_write, but should be universal. */
+%typemap(in) (const void *buffer, size_t size)(PyObject *encodedInput = NULL) {
+ Py_ssize_t ssize;
+
+ if ($input == Py_None)
+ $1 = NULL, $2 = 0;
+ else if (PyUnicode_Check($input))
+ {
+ encodedInput = PyUnicode_AsUTF8String($input);
+ if (encodedInput == NULL)
+ return NULL;
+ if (PyBytes_AsStringAndSize(encodedInput, (char **) &$1, &ssize) == -1)
+ {
+ Py_DECREF(encodedInput);
+ return NULL;
+ }
+ }
+ else if (PyBytes_Check($input))
+ PyBytes_AsStringAndSize($input, (char **) &$1, &ssize);
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "arg %d: expected str, bytes, or None, got %s",
+ $argnum, $input->ob_type->tp_name);
+ return NULL;
+ }
+
+ if (! $1)
+ $2 = 0;
+ else
+ {
+ assert (ssize >= 0);
+ $2 = (size_t) ssize;
+ }
+}
+%typemap(freearg) (const void *buffer, size_t size) {
+ Py_XDECREF(encodedInput$argnum);
+}
+
+// Make types containing 'next' field to be lists
+%ignore next;
+%typemap(out) gpgme_sig_notation_t, gpgme_subkey_t,
+ gpgme_key_sig_t, gpgme_user_id_t, gpgme_invalid_key_t,
+ gpgme_recipient_t, gpgme_new_signature_t, gpgme_signature_t,
+ gpgme_import_status_t, gpgme_conf_arg_t, gpgme_conf_opt_t,
+ gpgme_conf_comp_t, gpgme_tofu_info_t {
+ int i;
+ int size = 0;
+ $1_ltype curr;
+ for (curr = $1; curr != NULL; curr = curr->next) {
+ size++;
+ }
+ $result = PyList_New(size);
+ for (i=0,curr=$1; i<size; i++,curr=curr->next) {
+ PyObject *o = SWIG_NewPointerObj(SWIG_as_voidptr(curr), $1_descriptor, %newpointer_flags);
+ PyList_SetItem($result, i, o);
+ }
+}
+
+
+
+/* Wrap the fragile result objects into robust Python ones. */
+%typemap(out) gpgme_encrypt_result_t {
+ PyObject *fragile;
+ fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor,
+ %newpointer_flags);
+ $result = _pyme_wrap_result(fragile, "EncryptResult");
+ Py_DECREF(fragile);
+}
+
+%typemap(out) gpgme_decrypt_result_t {
+ PyObject *fragile;
+ fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor,
+ %newpointer_flags);
+ $result = _pyme_wrap_result(fragile, "DecryptResult");
+ Py_DECREF(fragile);
+}
+
+%typemap(out) gpgme_sign_result_t {
+ PyObject *fragile;
+ fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor,
+ %newpointer_flags);
+ $result = _pyme_wrap_result(fragile, "SignResult");
+ Py_DECREF(fragile);
+}
+
+%typemap(out) gpgme_verify_result_t {
+ PyObject *fragile;
+ fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor,
+ %newpointer_flags);
+ $result = _pyme_wrap_result(fragile, "VerifyResult");
+ Py_DECREF(fragile);
+}
+
+%typemap(out) gpgme_import_result_t {
+ PyObject *fragile;
+ fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor,
+ %newpointer_flags);
+ $result = _pyme_wrap_result(fragile, "ImportResult");
+ Py_DECREF(fragile);
+}
+
+%typemap(out) gpgme_genkey_result_t {
+ PyObject *fragile;
+ fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor,
+ %newpointer_flags);
+ $result = _pyme_wrap_result(fragile, "GenkeyResult");
+ Py_DECREF(fragile);
+}
+
+%typemap(out) gpgme_keylist_result_t {
+ PyObject *fragile;
+ fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor,
+ %newpointer_flags);
+ $result = _pyme_wrap_result(fragile, "KeylistResult");
+ Py_DECREF(fragile);
+}
+
+%typemap(out) gpgme_vfs_mount_result_t {
+ PyObject *fragile;
+ fragile = SWIG_NewPointerObj(SWIG_as_voidptr($1), $1_descriptor,
+ %newpointer_flags);
+ $result = _pyme_wrap_result(fragile, "VFSMountResult");
+ Py_DECREF(fragile);
+}
+
+%typemap(out) gpgme_engine_info_t {
+ int i;
+ int size = 0;
+ $1_ltype curr;
+ for (curr = $1; curr != NULL; curr = curr->next) {
+ size++;
+ }
+ $result = PyList_New(size);
+ if ($result == NULL)
+ return NULL; /* raise */
+ for (i=0,curr=$1; i<size; i++,curr=curr->next) {
+ PyObject *fragile, *o;
+ fragile = SWIG_NewPointerObj(SWIG_as_voidptr(curr), $1_descriptor,
+ %newpointer_flags);
+ if (fragile == NULL)
+ {
+ Py_DECREF($result);
+ return NULL; /* raise */
+ }
+ o = _pyme_wrap_result(fragile, "EngineInfo");
+ Py_DECREF(fragile);
+ if (o == NULL)
+ {
+ Py_DECREF($result);
+ return NULL; /* raise */
+ }
+ PyList_SetItem($result, i, o);
+ }
+}
+
+
+
+/* Include mapper for interact callbacks. */
+%typemap(in) (gpgme_interact_cb_t fnc, void *fnc_value) {
+ if (! PyTuple_Check($input))
+ return PyErr_Format(PyExc_TypeError, "interact callback must be a tuple");
+ if (PyTuple_Size($input) != 2 && PyTuple_Size($input) != 3)
+ return PyErr_Format(PyExc_TypeError,
+ "interact callback must be a tuple of size 2 or 3");
+
+ $1 = (gpgme_interact_cb_t) _pyme_interact_cb;
+ $2 = $input;
+}
+
+
+
+/* The assuan protocol callbacks. */
+%typemap(in) (gpgme_assuan_data_cb_t data_cb, void *data_cb_value) {
+ if ($input == Py_None)
+ $1 = $2 = NULL;
+ else
+ {
+ if (! PyTuple_Check($input))
+ return PyErr_Format(PyExc_TypeError, "callback must be a tuple");
+ if (PyTuple_Size($input) != 2)
+ return PyErr_Format(PyExc_TypeError,
+ "callback must be a tuple of size 2");
+ if (! PyCallable_Check(PyTuple_GetItem($input, 1)))
+ return PyErr_Format(PyExc_TypeError, "second item must be callable");
+ $1 = _pyme_assuan_data_cb;
+ $2 = $input;
+ }
+}
+
+%typemap(in) (gpgme_assuan_inquire_cb_t inq_cb, void *inq_cb_value) {
+ if ($input == Py_None)
+ $1 = $2 = NULL;
+ else
+ {
+ if (! PyTuple_Check($input))
+ return PyErr_Format(PyExc_TypeError, "callback must be a tuple");
+ if (PyTuple_Size($input) != 2)
+ return PyErr_Format(PyExc_TypeError,
+ "callback must be a tuple of size 2");
+ if (! PyCallable_Check(PyTuple_GetItem($input, 1)))
+ return PyErr_Format(PyExc_TypeError, "second item must be callable");
+ $1 = _pyme_assuan_inquire_cb;
+ $2 = $input;
+ }
+}
+
+%typemap(in) (gpgme_assuan_status_cb_t stat_cb, void *stat_cb_value) {
+ if ($input == Py_None)
+ $1 = $2 = NULL;
+ else
+ {
+ if (! PyTuple_Check($input))
+ return PyErr_Format(PyExc_TypeError, "callback must be a tuple");
+ if (PyTuple_Size($input) != 2)
+ return PyErr_Format(PyExc_TypeError,
+ "callback must be a tuple of size 2");
+ if (! PyCallable_Check(PyTuple_GetItem($input, 1)))
+ return PyErr_Format(PyExc_TypeError, "second item must be callable");
+ $1 = _pyme_assuan_status_cb;
+ $2 = $input;
+ }
+}
+
+/* Include the unmodified <gpgme.h> for cc, and the cleaned-up local
+ version for SWIG. We do, however, want to hide certain fields on
+ some structs, which we provide prior to including the version for
+ SWIG. */
+%{
+#include <gpgme.h>
+%}
+
+/* This is for notations, where we want to hide the length fields, and
+ the unused bit field block. */
+struct _gpgme_sig_notation
+{
+ struct _gpgme_sig_notation *next;
+
+ /* If NAME is a null pointer, then VALUE contains a policy URL
+ rather than a notation. */
+ char *name;
+
+ /* The value of the notation data. */
+ char *value;
+
+ /* The accumulated flags. */
+ gpgme_sig_notation_flags_t flags;
+
+ /* Notation data is human-readable. */
+ unsigned int human_readable : 1;
+
+ /* Notation data is critical. */
+ unsigned int critical : 1;
+};
+
+/* Now include our local modified version. Any structs defined above
+ are ignored. */
+%include "gpgme.h"
+
+%include "errors.i"
+
+// Generating and handling pointers-to-pointers.
+
+%pointer_functions(gpgme_ctx_t, gpgme_ctx_t_p);
+%pointer_functions(gpgme_data_t, gpgme_data_t_p);
+%pointer_functions(gpgme_key_t, gpgme_key_t_p);
+%pointer_functions(gpgme_error_t, gpgme_error_t_p);
+%pointer_functions(gpgme_trust_item_t, gpgme_trust_item_t_p);
+%pointer_functions(gpgme_engine_info_t, gpgme_engine_info_t_p);
+
+// Helper functions.
+
+%{
+#include <stdio.h>
+%}
+FILE *fdopen(int fildes, const char *mode);
+
+/* We include both headers in the generated c code... */
+%{
+#include "helpers.h"
+#include "private.h"
+
+/* SWIG runtime support for helpers.c */
+PyObject *
+_pyme_wrap_gpgme_data_t(gpgme_data_t data)
+{
+ return SWIG_Python_NewPointerObj(NULL, data, SWIGTYPE_p_gpgme_data, 0);
+}
+
+gpgme_ctx_t
+_pyme_unwrap_gpgme_ctx_t(PyObject *wrapped)
+{
+ gpgme_ctx_t result;
+ if (SWIG_ConvertPtr(wrapped,
+ (void **) &result,
+ SWIGTYPE_p_gpgme_context,
+ SWIG_POINTER_EXCEPTION) == -1)
+ return NULL;
+ return result;
+}
+%}
+
+/* ... but only the public definitions here. They will be exposed to
+ the Python world, so let's be careful. */
+%include "helpers.h"
diff --git a/lang/python/helpers.c b/lang/python/helpers.c
new file mode 100644
index 0000000..f9aec91
--- /dev/null
+++ b/lang/python/helpers.c
@@ -0,0 +1,1169 @@
+/*
+# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@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
+*/
+
+#include <assert.h>
+#include <stdio.h>
+#include <gpgme.h>
+#include <stdlib.h>
+#include <string.h>
+#include "Python.h"
+
+#include "helpers.h"
+#include "private.h"
+
+/* Flag specifying whether this is an in-tree build. */
+int pyme_in_tree_build =
+#if IN_TREE_BUILD
+ 1
+#else
+ 0
+#endif
+ ;
+
+static PyObject *GPGMEError = NULL;
+
+void _pyme_exception_init(void) {
+ if (GPGMEError == NULL) {
+ PyObject *errors;
+ PyObject *from_list = PyList_New(0);
+ errors = PyImport_ImportModuleLevel("errors", PyEval_GetGlobals(),
+ PyEval_GetLocals(), from_list, 1);
+ Py_XDECREF(from_list);
+ if (errors) {
+ GPGMEError=PyDict_GetItemString(PyModule_GetDict(errors), "GPGMEError");
+ Py_XINCREF(GPGMEError);
+ }
+ }
+}
+
+static PyObject *
+_pyme_raise_exception(gpgme_error_t err)
+{
+ PyObject *e;
+
+ _pyme_exception_init();
+ if (GPGMEError == NULL)
+ return PyErr_Format(PyExc_RuntimeError, "Got gpgme_error_t %d", err);
+
+ e = PyObject_CallFunction(GPGMEError, "l", (long) err);
+ if (e == NULL)
+ return NULL;
+
+ PyErr_SetObject(GPGMEError, e);
+ Py_DECREF(e);
+
+ return NULL; /* raise */
+}
+
+gpgme_error_t _pyme_exception2code(void) {
+ gpgme_error_t err_status = gpg_error(GPG_ERR_GENERAL);
+ if (GPGMEError && PyErr_ExceptionMatches(GPGMEError)) {
+ PyObject *type = 0, *value = 0, *traceback = 0;
+ PyObject *error = 0;
+ PyErr_Fetch(&type, &value, &traceback);
+ PyErr_NormalizeException(&type, &value, &traceback);
+ error = PyObject_GetAttrString(value, "error");
+ err_status = PyLong_AsLong(error);
+ Py_DECREF(error);
+ PyErr_Restore(type, value, traceback);
+ }
+ return err_status;
+}
+
+/* Exception support for callbacks. */
+#define EXCINFO "_callback_excinfo"
+
+static void _pyme_stash_callback_exception(PyObject *weak_self)
+{
+ PyObject *self, *ptype, *pvalue, *ptraceback, *excinfo;
+
+ PyErr_Fetch(&ptype, &pvalue, &ptraceback);
+ excinfo = PyTuple_New(3);
+ PyTuple_SetItem(excinfo, 0, ptype);
+
+ if (pvalue)
+ PyTuple_SetItem(excinfo, 1, pvalue);
+ else {
+ Py_INCREF(Py_None);
+ PyTuple_SetItem(excinfo, 1, Py_None);
+ }
+
+ if (ptraceback)
+ PyTuple_SetItem(excinfo, 2, ptraceback);
+ else {
+ Py_INCREF(Py_None);
+ PyTuple_SetItem(excinfo, 2, Py_None);
+ }
+
+ self = PyWeakref_GetObject(weak_self);
+ /* self only has a borrowed reference. */
+ if (self == Py_None) {
+ /* This should not happen, as even if we're called from the data
+ release callback triggered from the wrappers destructor, the
+ object is still alive and hence the weak reference still refers
+ to the object. However, in case this ever changes, not seeing
+ any exceptions is worse than having a little extra code, so
+ here we go. */
+ fprintf(stderr,
+ "Error occurred in callback, but the wrapper object "
+ "has been deallocated.\n");
+ PyErr_Restore(ptype, pvalue, ptraceback);
+ PyErr_Print();
+ }
+ else
+ PyObject_SetAttrString(self, EXCINFO, excinfo);
+ Py_DECREF(excinfo);
+}
+
+PyObject *pyme_raise_callback_exception(PyObject *self)
+{
+ PyGILState_STATE state = PyGILState_Ensure();
+ PyObject *ptype, *pvalue, *ptraceback, *excinfo;
+
+ if (! PyObject_HasAttrString(self, EXCINFO))
+ goto leave;
+
+ excinfo = PyObject_GetAttrString(self, EXCINFO);
+ if (! PyTuple_Check(excinfo))
+ {
+ Py_DECREF(excinfo);
+ goto leave;
+ }
+
+ ptype = PyTuple_GetItem(excinfo, 0);
+ Py_INCREF(excinfo);
+
+ pvalue = PyTuple_GetItem(excinfo, 1);
+ if (pvalue == Py_None)
+ pvalue = NULL;
+ else
+ Py_INCREF(pvalue);
+
+ ptraceback = PyTuple_GetItem(excinfo, 2);
+ if (ptraceback == Py_None)
+ ptraceback = NULL;
+ else
+ Py_INCREF(ptraceback);
+
+ /* We now have references for the extracted items. */
+ Py_DECREF(excinfo);
+
+ /* Clear the exception information. It is important to do this
+ before setting the error, because setting the attribute may
+ execute python code, and the runtime system raises a SystemError
+ if an exception is set but values are returned. */
+ Py_INCREF(Py_None);
+ PyObject_SetAttrString(self, EXCINFO, Py_None);
+
+ /* Restore exception. */
+ PyErr_Restore(ptype, pvalue, ptraceback);
+ PyGILState_Release(state);
+ return NULL; /* Raise exception. */
+
+ leave:
+ Py_INCREF(Py_None);
+ PyGILState_Release(state);
+ return Py_None;
+}
+#undef EXCINFO
+
+/* Argument conversion. */
+
+/* Convert object to a pointer to gpgme type, generic version. */
+PyObject *
+_pyme_obj2gpgme_t(PyObject *input, const char *objtype, int argnum)
+{
+ PyObject *pyname = NULL, *pypointer = NULL;
+ pyname = PyObject_GetAttrString(input, "_ctype");
+ if (pyname && PyUnicode_Check(pyname))
+ {
+ PyObject *encoded = PyUnicode_AsUTF8String(pyname);
+ if (strcmp(PyBytes_AsString(encoded), objtype) != 0)
+ {
+ PyErr_Format(PyExc_TypeError,
+ "arg %d: Expected value of type %s, but got %s",
+ argnum, objtype, PyBytes_AsString(encoded));
+ Py_DECREF(encoded);
+ Py_DECREF(pyname);
+ return NULL;
+ }
+ Py_DECREF(encoded);
+ }
+ else
+ return NULL;
+
+ Py_DECREF(pyname);
+ pypointer = PyObject_GetAttrString(input, "wrapped");
+ if (pypointer == NULL) {
+ PyErr_Format(PyExc_TypeError,
+ "arg %d: Use of uninitialized Python object %s",
+ argnum, objtype);
+ return NULL;
+ }
+ return pypointer;
+}
+
+/* Convert object to a pointer to gpgme type, version for data
+ objects. Constructs a wrapper Python on the fly e.g. for file-like
+ objects with a fileno method, returning it in WRAPPER. This object
+ must be de-referenced when no longer needed. */
+PyObject *
+_pyme_obj2gpgme_data_t(PyObject *input, int argnum, gpgme_data_t *wrapper,
+ PyObject **bytesio, Py_buffer *view)
+{
+ gpgme_error_t err;
+ PyObject *data;
+ PyObject *fd;
+
+ /* See if it is a file-like object with file number. */
+ fd = PyObject_CallMethod(input, "fileno", NULL);
+ if (fd) {
+ err = gpgme_data_new_from_fd(wrapper, (int) PyLong_AsLong(fd));
+ Py_DECREF(fd);
+ if (err)
+ return _pyme_raise_exception (err);
+
+ return _pyme_wrap_gpgme_data_t(*wrapper);
+ }
+ else
+ PyErr_Clear();
+
+ /* No? Maybe it implements the buffer protocol. */
+ data = PyObject_CallMethod(input, "getbuffer", NULL);
+ if (data)
+ {
+ /* Save a reference to input, which seems to be a BytesIO
+ object. */
+ Py_INCREF(input);
+ *bytesio = input;
+ }
+ else
+ {
+ PyErr_Clear();
+
+ /* No, but maybe the user supplied a buffer object? */
+ data = input;
+ }
+
+ /* Do we have a buffer object? */
+ if (PyObject_CheckBuffer(data))
+ {
+ if (PyObject_GetBuffer(data, view, PyBUF_SIMPLE) < 0)
+ return NULL;
+
+ if (data != input)
+ Py_DECREF(data);
+
+ assert (view->obj);
+ assert (view->ndim == 1);
+ assert (view->shape == NULL);
+ assert (view->strides == NULL);
+ assert (view->suboffsets == NULL);
+
+ err = gpgme_data_new_from_mem(wrapper, view->buf, (size_t) view->len, 0);
+ if (err)
+ return _pyme_raise_exception (err);
+
+ return _pyme_wrap_gpgme_data_t(*wrapper);
+ }
+
+ /* As last resort we assume it is a wrapped data object. */
+ if (PyObject_HasAttrString(data, "_ctype"))
+ return _pyme_obj2gpgme_t(data, "gpgme_data_t", argnum);
+
+ return PyErr_Format(PyExc_TypeError,
+ "arg %d: expected pyme.Data, file, or an object "
+ "implementing the buffer protocol, got %s",
+ argnum, data->ob_type->tp_name);
+}
+
+
+
+PyObject *
+_pyme_wrap_result(PyObject *fragile, const char *classname)
+{
+ static PyObject *results;
+ PyObject *class;
+ PyObject *replacement;
+
+ if (results == NULL)
+ {
+ PyObject *from_list = PyList_New(0);
+ if (from_list == NULL)
+ return NULL;
+
+ results = PyImport_ImportModuleLevel("results", PyEval_GetGlobals(),
+ PyEval_GetLocals(), from_list, 1);
+ Py_DECREF(from_list);
+
+ if (results == NULL)
+ return NULL;
+ }
+
+ class = PyMapping_GetItemString(PyModule_GetDict(results), classname);
+ if (class == NULL)
+ return NULL;
+
+ replacement = PyObject_CallFunctionObjArgs(class, fragile, NULL);
+ Py_DECREF(class);
+ return replacement;
+}
+
+
+
+/* Callback support. */
+static gpgme_error_t pyPassphraseCb(void *hook,
+ const char *uid_hint,
+ const char *passphrase_info,
+ int prev_was_bad,
+ int fd) {
+ PyGILState_STATE state = PyGILState_Ensure();
+ PyObject *pyhook = (PyObject *) hook;
+ PyObject *self = NULL;
+ PyObject *func = NULL;
+ PyObject *args = NULL;
+ PyObject *retval = NULL;
+ PyObject *dataarg = NULL;
+ PyObject *encoded = NULL;
+ gpgme_error_t err_status = 0;
+
+ _pyme_exception_init();
+
+ assert (PyTuple_Check(pyhook));
+ assert (PyTuple_Size(pyhook) == 2 || PyTuple_Size(pyhook) == 3);
+ self = PyTuple_GetItem(pyhook, 0);
+ func = PyTuple_GetItem(pyhook, 1);
+ if (PyTuple_Size(pyhook) == 3) {
+ dataarg = PyTuple_GetItem(pyhook, 2);
+ args = PyTuple_New(4);
+ } else {
+ args = PyTuple_New(3);
+ }
+
+ if (uid_hint == NULL)
+ {
+ Py_INCREF(Py_None);
+ PyTuple_SetItem(args, 0, Py_None);
+ }
+ else
+ PyTuple_SetItem(args, 0, PyUnicode_DecodeUTF8(uid_hint, strlen (uid_hint),
+ "strict"));
+ if (PyErr_Occurred()) {
+ Py_DECREF(args);
+ err_status = gpg_error(GPG_ERR_GENERAL);
+ goto leave;
+ }
+
+ PyTuple_SetItem(args, 1, PyBytes_FromString(passphrase_info));
+ PyTuple_SetItem(args, 2, PyBool_FromLong((long)prev_was_bad));
+ if (dataarg) {
+ Py_INCREF(dataarg); /* Because GetItem doesn't give a ref but SetItem taketh away */
+ PyTuple_SetItem(args, 3, dataarg);
+ }
+
+ retval = PyObject_CallObject(func, args);
+ Py_DECREF(args);
+ if (PyErr_Occurred()) {
+ err_status = _pyme_exception2code();
+ } else {
+ if (!retval) {
+ if (write(fd, "\n", 1) < 0) {
+ err_status = gpgme_error_from_syserror ();
+ _pyme_raise_exception (err_status);
+ }
+ } else {
+ char *buf;
+ size_t len;
+ if (PyBytes_Check(retval))
+ buf = PyBytes_AsString(retval), len = PyBytes_Size(retval);
+ else if (PyUnicode_Check(retval))
+ {
+ Py_ssize_t ssize;
+ encoded = PyUnicode_AsUTF8String(retval);
+ if (encoded == NULL)
+ {
+ err_status = gpg_error(GPG_ERR_GENERAL);
+ goto leave;
+ }
+ if (PyBytes_AsStringAndSize(encoded, &buf, &ssize) == -1)
+ {
+ err_status = gpg_error(GPG_ERR_GENERAL);
+ goto leave;
+ }
+ assert (! buf || ssize >= 0);
+ len = (size_t) ssize;
+ }
+ else
+ {
+ PyErr_Format(PyExc_TypeError,
+ "expected str or bytes from passphrase callback, got %s",
+ retval->ob_type->tp_name);
+ err_status = gpg_error(GPG_ERR_GENERAL);
+ goto leave;
+ }
+
+ if (write(fd, buf, len) < 0) {
+ err_status = gpgme_error_from_syserror ();
+ _pyme_raise_exception (err_status);
+ }
+ if (! err_status && write(fd, "\n", 1) < 0) {
+ err_status = gpgme_error_from_syserror ();
+ _pyme_raise_exception (err_status);
+ }
+
+ Py_DECREF(retval);
+ }
+ }
+
+ leave:
+ if (err_status)
+ _pyme_stash_callback_exception(self);
+
+ Py_XDECREF(encoded);
+ PyGILState_Release(state);
+ return err_status;
+}
+
+PyObject *
+pyme_set_passphrase_cb(PyObject *self, PyObject *cb) {
+ PyGILState_STATE state = PyGILState_Ensure();
+ PyObject *wrapped;
+ gpgme_ctx_t ctx;
+
+ wrapped = PyObject_GetAttrString(self, "wrapped");
+ if (wrapped == NULL)
+ {
+ assert (PyErr_Occurred ());
+ PyGILState_Release(state);
+ return NULL;
+ }
+
+ ctx = _pyme_unwrap_gpgme_ctx_t(wrapped);
+ Py_DECREF(wrapped);
+ if (ctx == NULL)
+ {
+ if (cb == Py_None)
+ goto out;
+ else
+ return PyErr_Format(PyExc_RuntimeError, "wrapped is NULL");
+ }
+
+ if (cb == Py_None) {
+ gpgme_set_passphrase_cb(ctx, NULL, NULL);
+ PyObject_SetAttrString(self, "_passphrase_cb", Py_None);
+ goto out;
+ }
+
+ if (! PyTuple_Check(cb))
+ return PyErr_Format(PyExc_TypeError, "cb must be a tuple");
+ if (PyTuple_Size(cb) != 2 && PyTuple_Size(cb) != 3)
+ return PyErr_Format(PyExc_TypeError,
+ "cb must be a tuple of size 2 or 3");
+
+ gpgme_set_passphrase_cb(ctx, (gpgme_passphrase_cb_t) pyPassphraseCb,
+ (void *) cb);
+ PyObject_SetAttrString(self, "_passphrase_cb", cb);
+
+ out:
+ Py_INCREF(Py_None);
+ PyGILState_Release(state);
+ return Py_None;
+}
+
+static void pyProgressCb(void *hook, const char *what, int type, int current,
+ int total) {
+ PyGILState_STATE state = PyGILState_Ensure();
+ PyObject *func = NULL, *dataarg = NULL, *args = NULL, *retval = NULL;
+ PyObject *pyhook = (PyObject *) hook;
+ PyObject *self = NULL;
+
+ assert (PyTuple_Check(pyhook));
+ assert (PyTuple_Size(pyhook) == 2 || PyTuple_Size(pyhook) == 3);
+ self = PyTuple_GetItem(pyhook, 0);
+ func = PyTuple_GetItem(pyhook, 1);
+ if (PyTuple_Size(pyhook) == 3) {
+ dataarg = PyTuple_GetItem(pyhook, 2);
+ args = PyTuple_New(5);
+ } else {
+ args = PyTuple_New(4);
+ }
+
+ PyTuple_SetItem(args, 0, PyUnicode_DecodeUTF8(what, strlen (what),
+ "strict"));
+ if (PyErr_Occurred()) {
+ _pyme_stash_callback_exception(self);
+ Py_DECREF(args);
+ PyGILState_Release(state);
+ return;
+ }
+ PyTuple_SetItem(args, 1, PyLong_FromLong((long) type));
+ PyTuple_SetItem(args, 2, PyLong_FromLong((long) current));
+ PyTuple_SetItem(args, 3, PyLong_FromLong((long) total));
+ if (dataarg) {
+ Py_INCREF(dataarg); /* Because GetItem doesn't give a ref but SetItem taketh away */
+ PyTuple_SetItem(args, 4, dataarg);
+ }
+
+ retval = PyObject_CallObject(func, args);
+ if (PyErr_Occurred())
+ _pyme_stash_callback_exception(self);
+ Py_DECREF(args);
+ Py_XDECREF(retval);
+ PyGILState_Release(state);
+}
+
+PyObject *
+pyme_set_progress_cb(PyObject *self, PyObject *cb) {
+ PyGILState_STATE state = PyGILState_Ensure();
+ PyObject *wrapped;
+ gpgme_ctx_t ctx;
+
+ wrapped = PyObject_GetAttrString(self, "wrapped");
+ if (wrapped == NULL)
+ {
+ assert (PyErr_Occurred ());
+ PyGILState_Release(state);
+ return NULL;
+ }
+
+ ctx = _pyme_unwrap_gpgme_ctx_t(wrapped);
+ Py_DECREF(wrapped);
+ if (ctx == NULL)
+ {
+ if (cb == Py_None)
+ goto out;
+ else
+ return PyErr_Format(PyExc_RuntimeError, "wrapped is NULL");
+ }
+
+ if (cb == Py_None) {
+ gpgme_set_progress_cb(ctx, NULL, NULL);
+ PyObject_SetAttrString(self, "_progress_cb", Py_None);
+ goto out;
+ }
+
+ if (! PyTuple_Check(cb))
+ return PyErr_Format(PyExc_TypeError, "cb must be a tuple");
+ if (PyTuple_Size(cb) != 2 && PyTuple_Size(cb) != 3)
+ return PyErr_Format(PyExc_TypeError,
+ "cb must be a tuple of size 2 or 3");
+
+ gpgme_set_progress_cb(ctx, (gpgme_progress_cb_t) pyProgressCb, (void *) cb);
+ PyObject_SetAttrString(self, "_progress_cb", cb);
+
+ out:
+ Py_INCREF(Py_None);
+ PyGILState_Release(state);
+ return Py_None;
+}
+
+/* Status callbacks. */
+static gpgme_error_t pyStatusCb(void *hook, const char *keyword,
+ const char *args) {
+ PyGILState_STATE state = PyGILState_Ensure();
+ gpgme_error_t err = 0;
+ PyObject *pyhook = (PyObject *) hook;
+ PyObject *self = NULL;
+ PyObject *func = NULL;
+ PyObject *dataarg = NULL;
+ PyObject *pyargs = NULL;
+ PyObject *retval = NULL;
+
+ assert (PyTuple_Check(pyhook));
+ assert (PyTuple_Size(pyhook) == 2 || PyTuple_Size(pyhook) == 3);
+ self = PyTuple_GetItem(pyhook, 0);
+ func = PyTuple_GetItem(pyhook, 1);
+ if (PyTuple_Size(pyhook) == 3) {
+ dataarg = PyTuple_GetItem(pyhook, 2);
+ pyargs = PyTuple_New(3);
+ } else {
+ pyargs = PyTuple_New(2);
+ }
+
+ if (keyword)
+ PyTuple_SetItem(pyargs, 0, PyUnicode_DecodeUTF8(keyword, strlen (keyword),
+ "strict"));
+ else
+ {
+ Py_INCREF(Py_None);
+ PyTuple_SetItem(pyargs, 0, Py_None);
+ }
+ PyTuple_SetItem(pyargs, 1, PyUnicode_DecodeUTF8(args, strlen (args),
+ "strict"));
+ if (PyErr_Occurred()) {
+ err = gpg_error(GPG_ERR_GENERAL);
+ Py_DECREF(pyargs);
+ goto leave;
+ }
+
+ if (dataarg) {
+ Py_INCREF(dataarg);
+ PyTuple_SetItem(pyargs, 2, dataarg);
+ }
+
+ retval = PyObject_CallObject(func, pyargs);
+ if (PyErr_Occurred())
+ err = _pyme_exception2code();
+ Py_DECREF(pyargs);
+ Py_XDECREF(retval);
+
+ leave:
+ if (err)
+ _pyme_stash_callback_exception(self);
+ PyGILState_Release(state);
+ return err;
+}
+
+PyObject *
+pyme_set_status_cb(PyObject *self, PyObject *cb) {
+ PyGILState_STATE state = PyGILState_Ensure();
+ PyObject *wrapped;
+ gpgme_ctx_t ctx;
+
+ wrapped = PyObject_GetAttrString(self, "wrapped");
+ if (wrapped == NULL)
+ {
+ assert (PyErr_Occurred ());
+ PyGILState_Release(state);
+ return NULL;
+ }
+
+ ctx = _pyme_unwrap_gpgme_ctx_t(wrapped);
+ Py_DECREF(wrapped);
+ if (ctx == NULL)
+ {
+ if (cb == Py_None)
+ goto out;
+ else
+ return PyErr_Format(PyExc_RuntimeError, "wrapped is NULL");
+ }
+
+ if (cb == Py_None) {
+ gpgme_set_status_cb(ctx, NULL, NULL);
+ PyObject_SetAttrString(self, "_status_cb", Py_None);
+ goto out;
+ }
+
+ if (! PyTuple_Check(cb))
+ return PyErr_Format(PyExc_TypeError, "cb must be a tuple");
+ if (PyTuple_Size(cb) != 2 && PyTuple_Size(cb) != 3)
+ return PyErr_Format(PyExc_TypeError,
+ "cb must be a tuple of size 2 or 3");
+
+ gpgme_set_status_cb(ctx, (gpgme_status_cb_t) pyStatusCb, (void *) cb);
+ PyObject_SetAttrString(self, "_status_cb", cb);
+
+ out:
+ Py_INCREF(Py_None);
+ PyGILState_Release(state);
+ return Py_None;
+}
+
+
+
+/* Interact callbacks. */
+gpgme_error_t
+_pyme_interact_cb(void *opaque, const char *keyword,
+ const char *args, int fd)
+{
+ PyGILState_STATE state = PyGILState_Ensure();
+ PyObject *func = NULL, *dataarg = NULL, *pyargs = NULL, *retval = NULL;
+ PyObject *py_keyword;
+ PyObject *pyopaque = (PyObject *) opaque;
+ gpgme_error_t err_status = 0;
+ PyObject *self = NULL;
+
+ _pyme_exception_init();
+
+ assert (PyTuple_Check(pyopaque));
+ assert (PyTuple_Size(pyopaque) == 2 || PyTuple_Size(pyopaque) == 3);
+ self = PyTuple_GetItem(pyopaque, 0);
+ func = PyTuple_GetItem(pyopaque, 1);
+ if (PyTuple_Size(pyopaque) == 3) {
+ dataarg = PyTuple_GetItem(pyopaque, 2);
+ pyargs = PyTuple_New(3);
+ } else {
+ pyargs = PyTuple_New(2);
+ }
+
+ if (keyword)
+ py_keyword = PyUnicode_FromString(keyword);
+ else
+ {
+ Py_INCREF(Py_None);
+ py_keyword = Py_None;
+ }
+
+ PyTuple_SetItem(pyargs, 0, py_keyword);
+ PyTuple_SetItem(pyargs, 1, PyUnicode_FromString(args));
+ if (dataarg) {
+ Py_INCREF(dataarg); /* Because GetItem doesn't give a ref but SetItem taketh away */
+ PyTuple_SetItem(pyargs, 2, dataarg);
+ }
+
+ retval = PyObject_CallObject(func, pyargs);
+ Py_DECREF(pyargs);
+ if (PyErr_Occurred()) {
+ err_status = _pyme_exception2code();
+ } else {
+ if (fd>=0 && retval && PyUnicode_Check(retval)) {
+ PyObject *encoded = NULL;
+ char *buffer;
+ Py_ssize_t size;
+
+ encoded = PyUnicode_AsUTF8String(retval);
+ if (encoded == NULL)
+ {
+ err_status = gpg_error(GPG_ERR_GENERAL);
+ goto leave;
+ }
+ if (PyBytes_AsStringAndSize(encoded, &buffer, &size) == -1)
+ {
+ Py_DECREF(encoded);
+ err_status = gpg_error(GPG_ERR_GENERAL);
+ goto leave;
+ }
+
+ if (write(fd, buffer, size) < 0) {
+ err_status = gpgme_error_from_syserror ();
+ _pyme_raise_exception (err_status);
+ }
+ if (! err_status && write(fd, "\n", 1) < 0) {
+ err_status = gpgme_error_from_syserror ();
+ _pyme_raise_exception (err_status);
+ }
+ Py_DECREF(encoded);
+ }
+ }
+ leave:
+ if (err_status)
+ _pyme_stash_callback_exception(self);
+
+ Py_XDECREF(retval);
+ PyGILState_Release(state);
+ return err_status;
+}
+
+
+
+/* Data callbacks. */
+
+/* Read up to SIZE bytes into buffer BUFFER from the data object with
+ the handle HOOK. Return the number of characters read, 0 on EOF
+ and -1 on error. If an error occurs, errno is set. */
+static ssize_t pyDataReadCb(void *hook, void *buffer, size_t size)
+{
+ PyGILState_STATE state = PyGILState_Ensure();
+ ssize_t result;
+ PyObject *pyhook = (PyObject *) hook;
+ PyObject *self = NULL;
+ PyObject *func = NULL;
+ PyObject *dataarg = NULL;
+ PyObject *pyargs = NULL;
+ PyObject *retval = NULL;
+
+ assert (PyTuple_Check(pyhook));
+ assert (PyTuple_Size(pyhook) == 5 || PyTuple_Size(pyhook) == 6);
+
+ self = PyTuple_GetItem(pyhook, 0);
+ func = PyTuple_GetItem(pyhook, 1);
+ if (PyTuple_Size(pyhook) == 6) {
+ dataarg = PyTuple_GetItem(pyhook, 5);
+ pyargs = PyTuple_New(2);
+ } else {
+ pyargs = PyTuple_New(1);
+ }
+
+ PyTuple_SetItem(pyargs, 0, PyLong_FromSize_t(size));
+ if (dataarg) {
+ Py_INCREF(dataarg);
+ PyTuple_SetItem(pyargs, 1, dataarg);
+ }
+
+ retval = PyObject_CallObject(func, pyargs);
+ Py_DECREF(pyargs);
+ if (PyErr_Occurred()) {
+ _pyme_stash_callback_exception(self);
+ result = -1;
+ goto leave;
+ }
+
+ if (! PyBytes_Check(retval)) {
+ PyErr_Format(PyExc_TypeError,
+ "expected bytes from read callback, got %s",
+ retval->ob_type->tp_name);
+ _pyme_stash_callback_exception(self);
+ result = -1;
+ goto leave;
+ }
+
+ if (PyBytes_Size(retval) > size) {
+ PyErr_Format(PyExc_TypeError,
+ "expected %zu bytes from read callback, got %zu",
+ size, PyBytes_Size(retval));
+ _pyme_stash_callback_exception(self);
+ result = -1;
+ goto leave;
+ }
+
+ memcpy(buffer, PyBytes_AsString(retval), PyBytes_Size(retval));
+ result = PyBytes_Size(retval);
+
+ leave:
+ Py_XDECREF(retval);
+ PyGILState_Release(state);
+ return result;
+}
+
+/* Write up to SIZE bytes from buffer BUFFER to the data object with
+ the handle HOOK. Return the number of characters written, or -1
+ on error. If an error occurs, errno is set. */
+static ssize_t pyDataWriteCb(void *hook, const void *buffer, size_t size)
+{
+ PyGILState_STATE state = PyGILState_Ensure();
+ ssize_t result;
+ PyObject *pyhook = (PyObject *) hook;
+ PyObject *self = NULL;
+ PyObject *func = NULL;
+ PyObject *dataarg = NULL;
+ PyObject *pyargs = NULL;
+ PyObject *retval = NULL;
+
+ assert (PyTuple_Check(pyhook));
+ assert (PyTuple_Size(pyhook) == 5 || PyTuple_Size(pyhook) == 6);
+
+ self = PyTuple_GetItem(pyhook, 0);
+ func = PyTuple_GetItem(pyhook, 2);
+ if (PyTuple_Size(pyhook) == 6) {
+ dataarg = PyTuple_GetItem(pyhook, 5);
+ pyargs = PyTuple_New(2);
+ } else {
+ pyargs = PyTuple_New(1);
+ }
+
+ PyTuple_SetItem(pyargs, 0, PyBytes_FromStringAndSize(buffer, size));
+ if (dataarg) {
+ Py_INCREF(dataarg);
+ PyTuple_SetItem(pyargs, 1, dataarg);
+ }
+
+ retval = PyObject_CallObject(func, pyargs);
+ Py_DECREF(pyargs);
+ if (PyErr_Occurred()) {
+ _pyme_stash_callback_exception(self);
+ result = -1;
+ goto leave;
+ }
+
+#if PY_MAJOR_VERSION < 3
+ if (PyInt_Check(retval))
+ result = PyInt_AsSsize_t(retval);
+ else
+#endif
+ if (PyLong_Check(retval))
+ result = PyLong_AsSsize_t(retval);
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "expected int from write callback, got %s",
+ retval->ob_type->tp_name);
+ _pyme_stash_callback_exception(self);
+ result = -1;
+ }
+
+ leave:
+ Py_XDECREF(retval);
+ PyGILState_Release(state);
+ return result;
+}
+
+/* Set the current position from where the next read or write starts
+ in the data object with the handle HOOK to OFFSET, relativ to
+ WHENCE. Returns the new offset in bytes from the beginning of the
+ data object. */
+static off_t pyDataSeekCb(void *hook, off_t offset, int whence)
+{
+ PyGILState_STATE state = PyGILState_Ensure();
+ off_t result;
+ PyObject *pyhook = (PyObject *) hook;
+ PyObject *self = NULL;
+ PyObject *func = NULL;
+ PyObject *dataarg = NULL;
+ PyObject *pyargs = NULL;
+ PyObject *retval = NULL;
+
+ assert (PyTuple_Check(pyhook));
+ assert (PyTuple_Size(pyhook) == 5 || PyTuple_Size(pyhook) == 6);
+
+ self = PyTuple_GetItem(pyhook, 0);
+ func = PyTuple_GetItem(pyhook, 3);
+ if (PyTuple_Size(pyhook) == 6) {
+ dataarg = PyTuple_GetItem(pyhook, 5);
+ pyargs = PyTuple_New(3);
+ } else {
+ pyargs = PyTuple_New(2);
+ }
+
+#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64
+ PyTuple_SetItem(pyargs, 0, PyLong_FromLongLong((long long) offset));
+#else
+ PyTuple_SetItem(pyargs, 0, PyLong_FromLong((long) offset));
+#endif
+ PyTuple_SetItem(pyargs, 1, PyLong_FromLong((long) whence));
+ if (dataarg) {
+ Py_INCREF(dataarg);
+ PyTuple_SetItem(pyargs, 2, dataarg);
+ }
+
+ retval = PyObject_CallObject(func, pyargs);
+ Py_DECREF(pyargs);
+ if (PyErr_Occurred()) {
+ _pyme_stash_callback_exception(self);
+ result = -1;
+ goto leave;
+ }
+
+#if PY_MAJOR_VERSION < 3
+ if (PyInt_Check(retval))
+ result = PyInt_AsLong(retval);
+ else
+#endif
+ if (PyLong_Check(retval))
+#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64
+ result = PyLong_AsLongLong(retval);
+#else
+ result = PyLong_AsLong(retval);
+#endif
+ else {
+ PyErr_Format(PyExc_TypeError,
+ "expected int from seek callback, got %s",
+ retval->ob_type->tp_name);
+ _pyme_stash_callback_exception(self);
+ result = -1;
+ }
+
+ leave:
+ Py_XDECREF(retval);
+ PyGILState_Release(state);
+ return result;
+}
+
+/* Close the data object with the handle HOOK. */
+static void pyDataReleaseCb(void *hook)
+{
+ PyGILState_STATE state = PyGILState_Ensure();
+ PyObject *pyhook = (PyObject *) hook;
+ PyObject *self = NULL;
+ PyObject *func = NULL;
+ PyObject *dataarg = NULL;
+ PyObject *pyargs = NULL;
+ PyObject *retval = NULL;
+
+ assert (PyTuple_Check(pyhook));
+ assert (PyTuple_Size(pyhook) == 5 || PyTuple_Size(pyhook) == 6);
+
+ self = PyTuple_GetItem(pyhook, 0);
+ func = PyTuple_GetItem(pyhook, 4);
+ if (PyTuple_Size(pyhook) == 6) {
+ dataarg = PyTuple_GetItem(pyhook, 5);
+ pyargs = PyTuple_New(1);
+ } else {
+ pyargs = PyTuple_New(0);
+ }
+
+ if (dataarg) {
+ Py_INCREF(dataarg);
+ PyTuple_SetItem(pyargs, 0, dataarg);
+ }
+
+ retval = PyObject_CallObject(func, pyargs);
+ Py_XDECREF(retval);
+ Py_DECREF(pyargs);
+ if (PyErr_Occurred())
+ _pyme_stash_callback_exception(self);
+ PyGILState_Release(state);
+}
+
+PyObject *
+pyme_data_new_from_cbs(PyObject *self,
+ PyObject *pycbs,
+ gpgme_data_t *r_data)
+{
+ PyGILState_STATE state = PyGILState_Ensure();
+ static struct gpgme_data_cbs cbs = {
+ pyDataReadCb,
+ pyDataWriteCb,
+ pyDataSeekCb,
+ pyDataReleaseCb,
+ };
+ gpgme_error_t err;
+
+ if (! PyTuple_Check(pycbs))
+ return PyErr_Format(PyExc_TypeError, "pycbs must be a tuple");
+ if (PyTuple_Size(pycbs) != 5 && PyTuple_Size(pycbs) != 6)
+ return PyErr_Format(PyExc_TypeError,
+ "pycbs must be a tuple of size 5 or 6");
+
+ err = gpgme_data_new_from_cbs(r_data, &cbs, (void *) pycbs);
+ if (err)
+ return _pyme_raise_exception(err);
+
+ PyObject_SetAttrString(self, "_data_cbs", pycbs);
+
+ Py_INCREF(Py_None);
+ PyGILState_Release(state);
+ return Py_None;
+}
+
+
+
+/* The assuan callbacks. */
+
+gpgme_error_t
+_pyme_assuan_data_cb (void *hook, const void *data, size_t datalen)
+{
+ PyGILState_STATE state = PyGILState_Ensure();
+ gpgme_error_t err = 0;
+ PyObject *pyhook = (PyObject *) hook;
+ PyObject *self = NULL;
+ PyObject *func = NULL;
+ PyObject *py_data = NULL;
+ PyObject *retval = NULL;
+
+ assert (PyTuple_Check(pyhook));
+ assert (PyTuple_Size(pyhook) == 2);
+ self = PyTuple_GetItem(pyhook, 0);
+ func = PyTuple_GetItem(pyhook, 1);
+ assert (PyCallable_Check(func));
+
+ py_data = PyBytes_FromStringAndSize(data, datalen);
+ if (py_data == NULL)
+ {
+ err = _pyme_exception2code();
+ goto leave;
+ }
+
+ retval = PyObject_CallFunctionObjArgs(func, py_data, NULL);
+ if (PyErr_Occurred())
+ err = _pyme_exception2code();
+ Py_DECREF(py_data);
+ Py_XDECREF(retval);
+
+ leave:
+ if (err)
+ _pyme_stash_callback_exception(self);
+ PyGILState_Release(state);
+ return err;
+}
+
+gpgme_error_t
+_pyme_assuan_inquire_cb (void *hook, const char *name, const char *args,
+ gpgme_data_t *r_data)
+{
+ PyGILState_STATE state = PyGILState_Ensure();
+ gpgme_error_t err = 0;
+ PyObject *pyhook = (PyObject *) hook;
+ PyObject *self = NULL;
+ PyObject *func = NULL;
+ PyObject *py_name = NULL;
+ PyObject *py_args = NULL;
+ PyObject *retval = NULL;
+
+ assert (PyTuple_Check(pyhook));
+ assert (PyTuple_Size(pyhook) == 2);
+ self = PyTuple_GetItem(pyhook, 0);
+ func = PyTuple_GetItem(pyhook, 1);
+ assert (PyCallable_Check(func));
+
+ py_name = PyUnicode_FromString(name);
+ if (py_name == NULL)
+ {
+ err = _pyme_exception2code();
+ goto leave;
+ }
+
+ py_args = PyUnicode_FromString(args);
+ if (py_args == NULL)
+ {
+ err = _pyme_exception2code();
+ goto leave;
+ }
+
+ retval = PyObject_CallFunctionObjArgs(func, py_name, py_args, NULL);
+ if (PyErr_Occurred())
+ err = _pyme_exception2code();
+ Py_XDECREF(retval);
+
+ /* FIXME: Returning data is not yet implemented. */
+ *r_data = NULL;
+
+ leave:
+ Py_XDECREF(py_name);
+ Py_XDECREF(py_args);
+ if (err)
+ _pyme_stash_callback_exception(self);
+ PyGILState_Release(state);
+ return err;
+}
+
+gpgme_error_t
+_pyme_assuan_status_cb (void *hook, const char *status, const char *args)
+{
+ PyGILState_STATE state = PyGILState_Ensure();
+ gpgme_error_t err = 0;
+ PyObject *pyhook = (PyObject *) hook;
+ PyObject *self = NULL;
+ PyObject *func = NULL;
+ PyObject *py_status = NULL;
+ PyObject *py_args = NULL;
+ PyObject *retval = NULL;
+
+ assert (PyTuple_Check(pyhook));
+ assert (PyTuple_Size(pyhook) == 2);
+ self = PyTuple_GetItem(pyhook, 0);
+ func = PyTuple_GetItem(pyhook, 1);
+ assert (PyCallable_Check(func));
+
+ py_status = PyUnicode_FromString(status);
+ if (py_status == NULL)
+ {
+ err = _pyme_exception2code();
+ goto leave;
+ }
+
+ py_args = PyUnicode_FromString(args);
+ if (py_args == NULL)
+ {
+ err = _pyme_exception2code();
+ goto leave;
+ }
+
+ retval = PyObject_CallFunctionObjArgs(func, py_status, py_args, NULL);
+ if (PyErr_Occurred())
+ err = _pyme_exception2code();
+ Py_XDECREF(retval);
+
+ leave:
+ Py_XDECREF(py_status);
+ Py_XDECREF(py_args);
+ if (err)
+ _pyme_stash_callback_exception(self);
+ PyGILState_Release(state);
+ return err;
+}
diff --git a/lang/python/helpers.h b/lang/python/helpers.h
new file mode 100644
index 0000000..9200f93
--- /dev/null
+++ b/lang/python/helpers.h
@@ -0,0 +1,39 @@
+/*
+# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@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
+*/
+
+#include <gpgme.h>
+#include "Python.h"
+
+#ifdef _WIN32
+#include <windows.h>
+#define write(fd, str, sz) {DWORD written; WriteFile((HANDLE) fd, str, sz, &written, 0);}
+#endif
+
+/* Flag specifying whether this is an in-tree build. */
+extern int pyme_in_tree_build;
+
+PyObject *pyme_raise_callback_exception(PyObject *self);
+
+PyObject *pyme_set_passphrase_cb(PyObject *self, PyObject *cb);
+PyObject *pyme_set_progress_cb(PyObject *self, PyObject *cb);
+PyObject *pyme_set_status_cb(PyObject *self, PyObject *cb);
+
+PyObject *pyme_data_new_from_cbs(PyObject *self, PyObject *pycbs,
+ gpgme_data_t *r_data);
diff --git a/lang/python/private.h b/lang/python/private.h
new file mode 100644
index 0000000..3a903c1
--- /dev/null
+++ b/lang/python/private.h
@@ -0,0 +1,54 @@
+/*
+ * 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/>.
+ */
+
+#include <gpgme.h>
+
+#ifndef _PYME_PRIVATE_H_
+#define _PYME_PRIVATE_H_
+
+/* GPGME glue. Implemented in helpers.c. */
+
+void _pyme_exception_init(void);
+gpgme_error_t _pyme_exception2code(void);
+
+PyObject *_pyme_obj2gpgme_t(PyObject *input, const char *objtype, int argnum);
+PyObject *_pyme_obj2gpgme_data_t(PyObject *input, int argnum,
+ gpgme_data_t *wrapper,
+ PyObject **bytesio, Py_buffer *view);
+
+PyObject *_pyme_wrap_result(PyObject *fragile, const char *classname);
+
+gpgme_error_t _pyme_interact_cb(void *opaque, const char *keyword,
+ const char *args, int fd);
+gpgme_error_t _pyme_assuan_data_cb (void *hook,
+ const void *data, size_t datalen);
+gpgme_error_t _pyme_assuan_inquire_cb (void *hook,
+ const char *name, const char *args,
+ gpgme_data_t *r_data);
+gpgme_error_t _pyme_assuan_status_cb (void *hook,
+ const char *status, const char *args);
+
+
+
+/* SWIG runtime support. Implemented in gpgme.i. */
+
+PyObject *_pyme_wrap_gpgme_data_t(gpgme_data_t data);
+gpgme_ctx_t _pyme_unwrap_gpgme_ctx_t(PyObject *wrapped);
+
+#endif /* _PYME_PRIVATE_H_ */
diff --git a/lang/python/pyme/__init__.py b/lang/python/pyme/__init__.py
new file mode 100644
index 0000000..12c96c2
--- /dev/null
+++ b/lang/python/pyme/__init__.py
@@ -0,0 +1,125 @@
+# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@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
+
+"""Pyme: GPGME Interface for Python
+
+Welcome to PyME, the GPGME Interface for Python. "Pyme", when prounced,
+rhymes with "Pine".
+
+The latest release of this package may be obtained from
+https://www.gnupg.org
+
+Previous releases of this package for Python 2 can be obtained from
+http://pyme.sourceforge.net
+
+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 pyme
+ >>> with pyme.Context() as c:
+ >>> with pyme.Context() as c:
+ ... cipher, _, _ = c.encrypt("Hello world :)".encode(),
+ ... passphrase="abc")
+ ... c.decrypt(cipher, passphrase="abc")
+ ...
+ (b'Hello world :)',
+ <pyme.results.DecryptResult object at 0x7f5ab8121080>,
+ <pyme.results.VerifyResult object at 0x7f5ab81219b0>)
+
+GENERAL OVERVIEW
+----------------
+
+For those of you familiar with GPGME, you will be right at home here.
+
+Pyme 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 pyme.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
+--------------------
+PYME3 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/pyme/__pycache__/__init__.cpython-34.pyc b/lang/python/pyme/__pycache__/__init__.cpython-34.pyc
new file mode 100644
index 0000000..ba78937
--- /dev/null
+++ b/lang/python/pyme/__pycache__/__init__.cpython-34.pyc
Binary files differ
diff --git a/lang/python/pyme/__pycache__/version.cpython-34.pyc b/lang/python/pyme/__pycache__/version.cpython-34.pyc
new file mode 100644
index 0000000..658008a
--- /dev/null
+++ b/lang/python/pyme/__pycache__/version.cpython-34.pyc
Binary files differ
diff --git a/lang/python/pyme/callbacks.py b/lang/python/pyme/callbacks.py
new file mode 100644
index 0000000..b25a9a7
--- /dev/null
+++ b/lang/python/pyme/callbacks.py
@@ -0,0 +1,49 @@
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@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/pyme/constants/__init__.py b/lang/python/pyme/constants/__init__.py
new file mode 100644
index 0000000..96d89e4
--- /dev/null
+++ b/lang/python/pyme/constants/__init__.py
@@ -0,0 +1,114 @@
+
+from __future__ import absolute_import, print_function, unicode_literals
+del absolute_import, print_function, unicode_literals
+
+from pyme import util
+util.process_constants('GPGME_', globals())
+
+__all__ = ['data', 'event', 'import', 'keylist', 'md', 'pk',
+ 'protocol', 'sig', 'sigsum', 'status', 'validity']
+
+# GPGME 1.7 replaced gpgme_op_edit with gpgme_op_interact. We
+# implement pyme.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/pyme/constants/data/__init__.py b/lang/python/pyme/constants/data/__init__.py
new file mode 100644
index 0000000..8274ab9
--- /dev/null
+++ b/lang/python/pyme/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/pyme/constants/data/encoding.py b/lang/python/pyme/constants/data/encoding.py
new file mode 100644
index 0000000..a05dbb4
--- /dev/null
+++ b/lang/python/pyme/constants/data/encoding.py
@@ -0,0 +1,22 @@
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@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 pyme import util
+util.process_constants('GPGME_DATA_ENCODING_', globals())
diff --git a/lang/python/pyme/constants/event.py b/lang/python/pyme/constants/event.py
new file mode 100644
index 0000000..2e30c5e
--- /dev/null
+++ b/lang/python/pyme/constants/event.py
@@ -0,0 +1,22 @@
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@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 pyme import util
+util.process_constants('GPGME_EVENT_', globals())
diff --git a/lang/python/pyme/constants/import.py b/lang/python/pyme/constants/import.py
new file mode 100644
index 0000000..10e7d3c
--- /dev/null
+++ b/lang/python/pyme/constants/import.py
@@ -0,0 +1,22 @@
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@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 pyme import util
+util.process_constants('GPGME_IMPORT_', globals())
diff --git a/lang/python/pyme/constants/keylist/__init__.py b/lang/python/pyme/constants/keylist/__init__.py
new file mode 100644
index 0000000..2ce0edf
--- /dev/null
+++ b/lang/python/pyme/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/pyme/constants/keylist/mode.py b/lang/python/pyme/constants/keylist/mode.py
new file mode 100644
index 0000000..000dd79
--- /dev/null
+++ b/lang/python/pyme/constants/keylist/mode.py
@@ -0,0 +1,22 @@
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@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 pyme import util
+util.process_constants('GPGME_KEYLIST_MODE_', globals())
diff --git a/lang/python/pyme/constants/md.py b/lang/python/pyme/constants/md.py
new file mode 100644
index 0000000..dbd762c
--- /dev/null
+++ b/lang/python/pyme/constants/md.py
@@ -0,0 +1,22 @@
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@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 pyme import util
+util.process_constants('GPGME_MD_', globals())
diff --git a/lang/python/pyme/constants/pk.py b/lang/python/pyme/constants/pk.py
new file mode 100644
index 0000000..cfc5309
--- /dev/null
+++ b/lang/python/pyme/constants/pk.py
@@ -0,0 +1,22 @@
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@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 pyme import util
+util.process_constants('GPGME_PK_', globals())
diff --git a/lang/python/pyme/constants/protocol.py b/lang/python/pyme/constants/protocol.py
new file mode 100644
index 0000000..a4b6583
--- /dev/null
+++ b/lang/python/pyme/constants/protocol.py
@@ -0,0 +1,22 @@
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@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 pyme import util
+util.process_constants('GPGME_PROTOCOL_', globals())
diff --git a/lang/python/pyme/constants/sig/__init__.py b/lang/python/pyme/constants/sig/__init__.py
new file mode 100644
index 0000000..2ce0edf
--- /dev/null
+++ b/lang/python/pyme/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
+__all__ = ['mode']
diff --git a/lang/python/pyme/constants/sig/mode.py b/lang/python/pyme/constants/sig/mode.py
new file mode 100644
index 0000000..fb534bc
--- /dev/null
+++ b/lang/python/pyme/constants/sig/mode.py
@@ -0,0 +1,22 @@
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@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 pyme import util
+util.process_constants('GPGME_SIG_MODE_', globals())
diff --git a/lang/python/pyme/constants/sigsum.py b/lang/python/pyme/constants/sigsum.py
new file mode 100644
index 0000000..3d94745
--- /dev/null
+++ b/lang/python/pyme/constants/sigsum.py
@@ -0,0 +1,22 @@
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@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 pyme import util
+util.process_constants('GPGME_SIGSUM_', globals())
diff --git a/lang/python/pyme/constants/status.py b/lang/python/pyme/constants/status.py
new file mode 100644
index 0000000..a04d9aa
--- /dev/null
+++ b/lang/python/pyme/constants/status.py
@@ -0,0 +1,124 @@
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@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 pyme.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/pyme/constants/validity.py b/lang/python/pyme/constants/validity.py
new file mode 100644
index 0000000..4ecd4d3
--- /dev/null
+++ b/lang/python/pyme/constants/validity.py
@@ -0,0 +1,22 @@
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@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 pyme import util
+util.process_constants('GPGME_VALIDITY_', globals())
diff --git a/lang/python/pyme/core.py b/lang/python/pyme/core.py
new file mode 100644
index 0000000..88a086b
--- /dev/null
+++ b/lang/python/pyme/core.py
@@ -0,0 +1,1145 @@
+# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2004,2008 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@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.pyme_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.pyme_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):
+ """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)
+
+ """
+ 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
+
+ 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):
+ """List keys
+
+ Keyword arguments:
+ pattern -- return keys matching pattern (default: all keys)
+ secret -- return only secret keys
+
+ Returns:
+ -- an iterator returning key objects
+
+ Raises:
+ GPGMEError -- as signaled by the underlying library
+ """
+ return self.op_keylist_all(pattern, secret)
+
+ 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 isinstance(command, (str, 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.pyme_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.pyme_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)
+
+ _ctype = 'gpgme_ctx_t'
+ _cprefix = 'gpgme_'
+
+ def _errorcheck(self, name):
+ """This function should list all functions returning gpgme_error_t"""
+ return ((name.startswith('gpgme_op_')
+ and not name.endswith('_result'))
+ or name in {
+ 'gpgme_set_ctx_flag',
+ 'gpgme_set_protocol',
+ 'gpgme_set_sub_protocol',
+ 'gpgme_set_keylist_mode',
+ 'gpgme_set_pinentry_mode',
+ 'gpgme_set_locale',
+ 'gpgme_set_engine_info',
+ 'gpgme_signers_add',
+ 'gpgme_get_sig_key',
+ 'gpgme_sig_notation_add',
+ 'gpgme_cancel',
+ 'gpgme_cancel_async',
+ 'gpgme_cancel_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):
+ """Return the key corresponding to the fingerprint 'fpr'"""
+ ptr = gpgme.new_gpgme_key_t_p()
+ errorcheck(gpgme.gpgme_get_key(self.wrapped, fpr, ptr, secret))
+ key = gpgme.gpgme_key_t_p_value(ptr)
+ gpgme.delete_gpgme_key_t_p(ptr)
+ if 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.pyme_set_passphrase_cb(self, hookdata)
+
+ def _free_passcb(self):
+ if gpgme.pyme_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.pyme_set_progress_cb(self, hookdata)
+
+ def _free_progresscb(self):
+ if gpgme.pyme_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.pyme_set_status_cb(self, hookdata)
+
+ def _free_statuscb(self):
+ if gpgme.pyme_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)
+
+ """
+ errorcheck(gpgme.gpgme_ctx_set_engine_info(
+ self.wrapped, 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"""
+ return name not in {
+ 'gpgme_data_release_and_get_mem',
+ 'gpgme_data_get_encoding',
+ 'gpgme_data_seek',
+ 'gpgme_data_get_file_name',
+ }
+
+ 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.pyme_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.pyme_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.pyme_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.pyme_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.pyme_raise_callback_exception(self)
+ else:
+ raise
+ if len(result) == 0:
+ break
+ chunks.append(result)
+ return b''.join(chunks)
+
+def pubkey_algo_name(algo):
+ return gpgme.gpgme_pubkey_algo_name(algo)
+
+def hash_algo_name(algo):
+ return gpgme.gpgme_hash_algo_name(algo)
+
+def get_protocol_name(proto):
+ return gpgme.gpgme_get_protocol_name(proto)
+
+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/pyme/errors.py b/lang/python/pyme/errors.py
new file mode 100644
index 0000000..e26c747
--- /dev/null
+++ b/lang/python/pyme/errors.py
@@ -0,0 +1,111 @@
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@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
+
+util.process_constants('GPG_ERR_', globals())
+
+# To appease static analysis tools, we define some constants here:
+NO_ERROR = 0
+
+class PymeError(Exception):
+ pass
+
+class GPGMEError(PymeError):
+ 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)
+
+# These errors are raised in the idiomatic interface code.
+
+class EncryptionError(PymeError):
+ 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(PymeError):
+ pass
+
+class UnsupportedAlgorithm(DeryptionError):
+ def __init__(self, algorithm):
+ self.algorithm = algorithm
+ def __str__(self):
+ return self.algorithm
+
+class SigningError(PymeError):
+ 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(PymeError):
+ 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/pyme/gpgme.py b/lang/python/pyme/gpgme.py
new file mode 100644
index 0000000..ea4983d
--- /dev/null
+++ b/lang/python/pyme/gpgme.py
@@ -0,0 +1,125 @@
+# This file was automatically generated by SWIG (http://www.swig.org).
+# Version 3.0.7
+#
+# Do not make changes to this file unless you know what you are doing--modify
+# the SWIG interface file instead.
+
+
+
+
+
+from sys import version_info
+if version_info >= (2, 6, 0):
+ def swig_import_helper():
+ from os.path import dirname
+ import imp
+ fp = None
+ try:
+ fp, pathname, description = imp.find_module('_gpgme', [dirname(__file__)])
+ except ImportError:
+ import _gpgme
+ return _gpgme
+ if fp is not None:
+ try:
+ _mod = imp.load_module('_gpgme', fp, pathname, description)
+ finally:
+ fp.close()
+ return _mod
+ _gpgme = swig_import_helper()
+ del swig_import_helper
+else:
+ import _gpgme
+del version_info
+from _gpgme import *
+try:
+ _swig_property = property
+except NameError:
+ pass # Python < 2.2 doesn't have 'property'.
+
+
+def _swig_setattr_nondynamic(self, class_type, name, value, static=1):
+ if (name == "thisown"):
+ return self.this.own(value)
+ if (name == "this"):
+ if type(value).__name__ == 'SwigPyObject':
+ self.__dict__[name] = value
+ return
+ method = class_type.__swig_setmethods__.get(name, None)
+ if method:
+ return method(self, value)
+ if (not static):
+ if _newclass:
+ object.__setattr__(self, name, value)
+ else:
+ self.__dict__[name] = value
+ else:
+ raise AttributeError("You cannot add attributes to %s" % self)
+
+
+def _swig_setattr(self, class_type, name, value):
+ return _swig_setattr_nondynamic(self, class_type, name, value, 0)
+
+
+def _swig_getattr_nondynamic(self, class_type, name, static=1):
+ if (name == "thisown"):
+ return self.this.own()
+ method = class_type.__swig_getmethods__.get(name, None)
+ if method:
+ return method(self)
+ if (not static):
+ return object.__getattr__(self, name)
+ else:
+ raise AttributeError(name)
+
+def _swig_getattr(self, class_type, name):
+ return _swig_getattr_nondynamic(self, class_type, name, 0)
+
+
+def _swig_repr(self):
+ try:
+ strthis = "proxy of " + self.this.__repr__()
+ except:
+ strthis = ""
+ return "<%s.%s; %s >" % (self.__class__.__module__, self.__class__.__name__, strthis,)
+
+try:
+ _object = object
+ _newclass = 1
+except AttributeError:
+ class _object:
+ pass
+ _newclass = 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+# This file is compatible with both classic and new-style classes.
+
+
diff --git a/lang/python/pyme/results.py b/lang/python/pyme/results.py
new file mode 100644
index 0000000..3383896
--- /dev/null
+++ b/lang/python/pyme/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 __str__(self):
+ return '<{} {}>'.format(
+ self.__class__.__name__,
+ ', '.join('{}: {}'.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/pyme/util.py b/lang/python/pyme/util.py
new file mode 100644
index 0000000..e4fca4c
--- /dev/null
+++ b/lang/python/pyme/util.py
@@ -0,0 +1,53 @@
+# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2004,2008 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@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/pyme/version.py b/lang/python/pyme/version.py
new file mode 100644
index 0000000..21a0122
--- /dev/null
+++ b/lang/python/pyme/version.py
@@ -0,0 +1,68 @@
+# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2015 Ben McGinnes <ben@adversary.org>
+# Copyright (C) 2004 Igor Belyi <belyi@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 = 'pyme'
+versionstr = "1.7.0"
+gpgme_versionstr = gpgme.GPGME_VERSION
+in_tree_build = bool(gpgme.cvar.pyme_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@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@adversary.org>
+Copyright (C) 2014, 2015 Martin Albrecht <martinralbrecht@googlemail.com>
+Copyright (C) 2004, 2008 Igor Belyi <belyi@users.sourceforge.net>
+Copyright (C) 2002 John Goerzen <jgoerzen@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/pyme/version.py.in b/lang/python/pyme/version.py.in
new file mode 100644
index 0000000..cfb9510
--- /dev/null
+++ b/lang/python/pyme/version.py.in
@@ -0,0 +1,68 @@
+# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2015 Ben McGinnes <ben@adversary.org>
+# Copyright (C) 2004 Igor Belyi <belyi@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 = 'pyme'
+versionstr = "@VERSION@"
+gpgme_versionstr = gpgme.GPGME_VERSION
+in_tree_build = bool(gpgme.cvar.pyme_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@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@adversary.org>
+Copyright (C) 2014, 2015 Martin Albrecht <martinralbrecht@googlemail.com>
+Copyright (C) 2004, 2008 Igor Belyi <belyi@users.sourceforge.net>
+Copyright (C) 2002 John Goerzen <jgoerzen@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
new file mode 100755
index 0000000..31892c1
--- /dev/null
+++ b/lang/python/setup.py.in
@@ -0,0 +1,190 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2016 g10 Code GmbH
+# Copyright (C) 2004 Igor Belyi <belyi@users.sourceforge.net>
+# Copyright (C) 2002 John Goerzen <jgoerzen@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 distutils.core import setup, Extension
+import os, os.path, sys
+import glob
+import subprocess
+
+# Out-of-tree build of the pyme3 bindings.
+gpg_error_config = ["gpg-error-config"]
+gpgme_config_flags = ["--thread=pthread"]
+gpgme_config = ["gpgme-config"] + gpgme_config_flags
+gpgme_h = ""
+library_dirs = []
+in_tree = False
+extra_swig_opts = []
+extra_macros = dict()
+
+if os.path.exists("../../src/gpgme-config"):
+ # 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
+ extra_macros.update(
+ HAVE_DATA_H=1,
+ IN_TREE_BUILD=1,
+ )
+
+if hasattr(subprocess, "DEVNULL"):
+ devnull = 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)
+except:
+ sys.exit("Could not find gpgme-config. " +
+ "Please install the libgpgme development package.")
+
+def getconfig(what, config=gpgme_config):
+ confdata = subprocess.Popen(config + ["--%s" % what],
+ stdout=subprocess.PIPE).communicate()[0]
+ return [x for x in confdata.decode('utf-8').split() if x != '']
+
+version = version_raw = getconfig("version")[0]
+if '-' in version:
+ version = version.split('-')[0]
+major, minor, patch = map(int, version.split('.'))
+
+if not (major > 1 or (major == 1 and minor >= 6)):
+ sys.exit('Need at least GPGME version 1.6, found {}.'.format(version_raw))
+
+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]
+
+print("Building pyme3 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"))
+
+include_dirs = [os.getcwd()]
+define_macros = []
+libs = getconfig('libs')
+
+# Define extra_macros for both the SWIG and C code
+for k, v in extra_macros.items():
+ extra_swig_opts.append("-D{0}={1}".format(k, v))
+ define_macros.append((k, str(v)))
+
+for item in getconfig('cflags'):
+ if item.startswith("-I"):
+ include_dirs.append(item[2:])
+ elif item.startswith("-D"):
+ defitem = item[2:].split("=", 1)
+ if len(defitem)==2:
+ define_macros.append((defitem[0], defitem[1]))
+ else:
+ define_macros.append((defitem[0], None))
+
+# Adjust include and library locations in case of win32
+uname_s = os.popen("uname -s").read()
+if uname_s.startswith("MINGW32"):
+ mnts = [x.split()[0:3:2] for x in os.popen("mount").read().split("\n") if x]
+ tmplist = sorted([(len(x[1]), x[1], x[0]) for x in mnts])
+ tmplist.reverse()
+ extra_dirs = []
+ for item in include_dirs:
+ for ln, mnt, tgt in tmplist:
+ if item.startswith(mnt):
+ item = os.path.normpath(item[ln:])
+ while item[0] == os.path.sep:
+ item = item[1:]
+ extra_dirs.append(os.path.join(tgt, item))
+ break
+ include_dirs += extra_dirs
+ for item in [x[2:] for x in libs if x.startswith("-L")]:
+ for ln, mnt, tgt in tmplist:
+ if item.startswith(mnt):
+ item = os.path.normpath(item[ln:])
+ while item[0] == os.path.sep:
+ item = item[1:]
+ library_dirs.append(os.path.join(tgt, item))
+ break
+
+# 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
+# directory.
+# Bug: http://bugs.python.org/issue1016626
+# Workaround:
+# http://stackoverflow.com/questions/12491328/python-distutils-not-include-the-swig-generated-module
+from distutils.command.build import build
+class BuildExtFirstHack(build):
+ def run(self):
+ self.run_command('build_ext')
+ build.run(self)
+
+swige = Extension("pyme._gpgme", ["gpgme.i", "helpers.c"],
+ swig_opts = ['-py3', '-builtin', '-threads',
+ '-outdir', 'pyme'] + extra_swig_opts,
+ include_dirs = include_dirs,
+ define_macros = define_macros,
+ library_dirs = library_dirs,
+ extra_link_args = libs)
+
+setup(name="pyme3",
+ cmdclass={'build': BuildExtFirstHack},
+ version="@VERSION@",
+ description='Python bindings for GPGME GnuPG cryptography library',
+ # XXX add a long description
+ #long_description=long_description,
+ author='The GnuPG hackers',
+ author_email='gnupg-devel@gnupg.org',
+ url='https://www.gnupg.org',
+ ext_modules=[swige],
+ packages = ['pyme', 'pyme.constants', 'pyme.constants.data',
+ 'pyme.constants.keylist', 'pyme.constants.sig'],
+ license="LGPL2.1+ (the library), GPL2+ (tests and examples)",
+ classifiers=[
+ 'Development Status :: 4 - Beta',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: GNU Lesser General Public License v2 or later (LGPLv2+)',
+ 'Programming Language :: Python :: 2',
+ 'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 3',
+ 'Programming Language :: Python :: 3.4',
+ 'Programming Language :: Python :: 3.5',
+ 'Programming Language :: Python :: 3.6',
+ 'Operating System :: POSIX',
+ 'Operating System :: Microsoft :: Windows',
+ 'Topic :: Communications :: Email',
+ 'Topic :: Security :: Cryptography',
+ ],
+)
diff --git a/lang/python/tests/Makefile.am b/lang/python/tests/Makefile.am
new file mode 100644
index 0000000..aa88bdc
--- /dev/null
+++ b/lang/python/tests/Makefile.am
@@ -0,0 +1,114 @@
+# Makefile.am for the tests of the Python bindings.
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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/>.
+
+GPG = gpg
+GPG_AGENT = gpg-agent
+export GNUPGHOME := $(abs_builddir)
+export GPG_AGENT_INFO :=
+
+test_srcdir = $(top_srcdir)/tests/gpg
+
+TESTS_ENVIRONMENT = GNUPGHOME=$(abs_builddir) \
+ LC_ALL=C GPG_AGENT_INFO= \
+ top_srcdir=$(top_srcdir) \
+ srcdir=$(srcdir) \
+ LD_LIBRARY_PATH="../../../src/.libs:$(LD_LIBRARY_PATH)"
+
+py_tests = t-wrapper.py \
+ t-callbacks.py \
+ t-data.py \
+ t-encrypt.py \
+ t-encrypt-sym.py \
+ t-encrypt-sign.py \
+ t-sign.py \
+ t-signers.py \
+ t-decrypt.py \
+ t-verify.py \
+ t-decrypt-verify.py \
+ t-sig-notation.py \
+ t-export.py \
+ t-import.py \
+ t-trustlist.py \
+ t-edit.py \
+ t-keylist.py \
+ t-wait.py \
+ t-encrypt-large.py \
+ t-file-name.py \
+ t-idiomatic.py \
+ t-protocol-assuan.py
+
+XTESTS = initial.py $(py_tests) final.py
+EXTRA_DIST = support.py $(XTESTS) encrypt-only.asc sign-only.asc \
+ run-tests.py
+
+# XXX: Currently, one cannot override automake's 'check' target. As a
+# workaround, we avoid defining 'TESTS', thus automake will not emit
+# the 'check' target. For extra robustness, we merely define a
+# dependency on 'xcheck', so this hack should also work even if
+# automake would emit the 'check' target, as adding dependencies to
+# targets is okay.
+check: xcheck
+
+.PHONY: xcheck
+
+xcheck: ./pubring-stamp
+ $(TESTS_ENVIRONMENT) $(PYTHON) $(srcdir)/run-tests.py \
+ --interpreters="$(PYTHONS)" --srcdir=$(srcdir) $(TESTFLAGS) \
+ $(XTESTS)
+
+CLEANFILES = secring.gpg pubring.gpg pubring.kbx trustdb.gpg dirmngr.conf \
+ gpg-agent.conf pubring.kbx~ gpg.conf pubring.gpg~ \
+ random_seed .gpg-v21-migrated \
+ pubring-stamp private-keys-v1.d/gpg-sample.stamp
+
+private_keys = \
+ $(test_srcdir)/13CD0F3BDF24BE53FE192D62F18737256FF6E4FD \
+ $(test_srcdir)/76F7E2B35832976B50A27A282D9B87E44577EB66 \
+ $(test_srcdir)/A0747D5F9425E6664F4FFBEED20FBCA79FDED2BD \
+ $(test_srcdir)/13CBE3758AFE42B5E5E2AE4CED27AFA455E3F87F \
+ $(test_srcdir)/7A030357C0F253A5BBCD282FFC4E521B37558F5C
+
+clean-local:
+ -$(top_srcdir)/tests/start-stop-agent --stop
+ -rm -fR -- private-keys-v1.d openpgp-revocs.d S.gpg-agent sshcontrol
+
+
+./private-keys-v1.d/gpg-sample.stamp: $(private_keys)
+ test -d ./private-keys-v1.d || mkdir ./private-keys-v1.d
+ for k in $(private_keys); do \
+ cp $$k private-keys-v1.d/$${k#$(test_srcdir)/}.key; \
+ done
+ 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
+ -$(GPG) --batch --no-permission-warning \
+ --import $(test_srcdir)/secdemo.asc
+ echo x > ./pubring-stamp
+
+./gpg.conf:
+# This is required for t-sig-notations.
+ echo no-force-v3-sigs > ./gpg.conf
+
+./gpg-agent.conf:
+# This is required for gpg2, which does not support command fd.
+ echo pinentry-program $(abs_top_srcdir)/tests/gpg/pinentry >$@
+ echo allow-loopback-pinentry >>$@
diff --git a/lang/python/tests/Makefile.in b/lang/python/tests/Makefile.in
new file mode 100644
index 0000000..3886bf4
--- /dev/null
+++ b/lang/python/tests/Makefile.in
@@ -0,0 +1,620 @@
+# Makefile.in generated by automake 1.14.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2013 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Makefile.am for the tests of the Python bindings.
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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/>.
+VPATH = @srcdir@
+am__is_gnu_make = test -n '$(MAKEFILE_LIST)' && test -n '$(MAKELEVEL)'
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+subdir = lang/python/tests
+DIST_COMMON = $(srcdir)/Makefile.in $(srcdir)/Makefile.am \
+ $(top_srcdir)/build-aux/mkinstalldirs
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \
+ $(top_srcdir)/m4/ax_pkg_swig.m4 \
+ $(top_srcdir)/m4/ax_python_devel.m4 \
+ $(top_srcdir)/m4/glib-2.0.m4 $(top_srcdir)/m4/glibc21.m4 \
+ $(top_srcdir)/m4/gnupg-ttyname.m4 \
+ $(top_srcdir)/m4/gpg-error.m4 $(top_srcdir)/m4/libassuan.m4 \
+ $(top_srcdir)/m4/libtool.m4 $(top_srcdir)/m4/ltoptions.m4 \
+ $(top_srcdir)/m4/ltsugar.m4 $(top_srcdir)/m4/ltversion.m4 \
+ $(top_srcdir)/m4/lt~obsolete.m4 $(top_srcdir)/m4/pkg.m4 \
+ $(top_srcdir)/m4/qt.m4 $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+SOURCES =
+DIST_SOURCES =
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BUILD_FILEVERSION = @BUILD_FILEVERSION@
+BUILD_REVISION = @BUILD_REVISION@
+BUILD_TIMESTAMP = @BUILD_TIMESTAMP@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DOXYGEN = @DOXYGEN@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ENABLED_LANGUAGES = @ENABLED_LANGUAGES@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+GITLOG_TO_CHANGELOG = @GITLOG_TO_CHANGELOG@
+GLIBC21 = @GLIBC21@
+GLIB_CFLAGS = @GLIB_CFLAGS@
+GLIB_GENMARSHAL = @GLIB_GENMARSHAL@
+GLIB_LIBS = @GLIB_LIBS@
+GLIB_MKENUMS = @GLIB_MKENUMS@
+GOBJECT_QUERY = @GOBJECT_QUERY@
+GPGME_CONFIG_API_VERSION = @GPGME_CONFIG_API_VERSION@
+GPGME_CONFIG_AVAIL_LANG = @GPGME_CONFIG_AVAIL_LANG@
+GPGME_CONFIG_CFLAGS = @GPGME_CONFIG_CFLAGS@
+GPGME_CONFIG_HOST = @GPGME_CONFIG_HOST@
+GPGME_CONFIG_LIBS = @GPGME_CONFIG_LIBS@
+GPGME_QTTEST_CFLAGS = @GPGME_QTTEST_CFLAGS@
+GPGME_QTTEST_LIBS = @GPGME_QTTEST_LIBS@
+GPGME_QT_CFLAGS = @GPGME_QT_CFLAGS@
+GPGME_QT_LIBS = @GPGME_QT_LIBS@
+GPG_ERROR_CFLAGS = @GPG_ERROR_CFLAGS@
+GPG_ERROR_CONFIG = @GPG_ERROR_CONFIG@
+GPG_ERROR_LIBS = @GPG_ERROR_LIBS@
+GPG_ERROR_MT_CFLAGS = @GPG_ERROR_MT_CFLAGS@
+GPG_ERROR_MT_LIBS = @GPG_ERROR_MT_LIBS@
+GRAPHVIZ = @GRAPHVIZ@
+GREP = @GREP@
+HAVE_CXX11 = @HAVE_CXX11@
+HAVE_DOT = @HAVE_DOT@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBASSUAN_CFLAGS = @LIBASSUAN_CFLAGS@
+LIBASSUAN_CONFIG = @LIBASSUAN_CONFIG@
+LIBASSUAN_LIBS = @LIBASSUAN_LIBS@
+LIBGPGMEPP_LT_AGE = @LIBGPGMEPP_LT_AGE@
+LIBGPGMEPP_LT_CURRENT = @LIBGPGMEPP_LT_CURRENT@
+LIBGPGMEPP_LT_REVISION = @LIBGPGMEPP_LT_REVISION@
+LIBGPGME_LT_AGE = @LIBGPGME_LT_AGE@
+LIBGPGME_LT_CURRENT = @LIBGPGME_LT_CURRENT@
+LIBGPGME_LT_REVISION = @LIBGPGME_LT_REVISION@
+LIBOBJS = @LIBOBJS@
+LIBQGPGME_LT_AGE = @LIBQGPGME_LT_AGE@
+LIBQGPGME_LT_CURRENT = @LIBQGPGME_LT_CURRENT@
+LIBQGPGME_LT_REVISION = @LIBQGPGME_LT_REVISION@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+MOC = @MOC@
+MOC2 = @MOC2@
+NEED__FILE_OFFSET_BITS = @NEED__FILE_OFFSET_BITS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PYTHON = @PYTHON@
+PYTHONS = @PYTHONS@
+PYTHON_CPPFLAGS = @PYTHON_CPPFLAGS@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_EXTRA_LDFLAGS = @PYTHON_EXTRA_LDFLAGS@
+PYTHON_EXTRA_LIBS = @PYTHON_EXTRA_LIBS@
+PYTHON_LDFLAGS = @PYTHON_LDFLAGS@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_SITE_PKG = @PYTHON_SITE_PKG@
+PYTHON_VERSION = @PYTHON_VERSION@
+PYTHON_VERSIONS = @PYTHON_VERSIONS@
+QTCHOOSER = @QTCHOOSER@
+RANLIB = @RANLIB@
+RC = @RC@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SWIG = @SWIG@
+SWIG_LIB = @SWIG_LIB@
+SYSROOT = @SYSROOT@
+VERSION = @VERSION@
+VERSION_NUMBER = @VERSION_NUMBER@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+emacs_local_vars_begin = @emacs_local_vars_begin@
+emacs_local_vars_end = @emacs_local_vars_end@
+emacs_local_vars_read_only = @emacs_local_vars_read_only@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+GPG = gpg
+GPG_AGENT = gpg-agent
+test_srcdir = $(top_srcdir)/tests/gpg
+TESTS_ENVIRONMENT = GNUPGHOME=$(abs_builddir) \
+ LC_ALL=C GPG_AGENT_INFO= \
+ top_srcdir=$(top_srcdir) \
+ srcdir=$(srcdir) \
+ LD_LIBRARY_PATH="../../../src/.libs:$(LD_LIBRARY_PATH)"
+
+py_tests = t-wrapper.py \
+ t-callbacks.py \
+ t-data.py \
+ t-encrypt.py \
+ t-encrypt-sym.py \
+ t-encrypt-sign.py \
+ t-sign.py \
+ t-signers.py \
+ t-decrypt.py \
+ t-verify.py \
+ t-decrypt-verify.py \
+ t-sig-notation.py \
+ t-export.py \
+ t-import.py \
+ t-trustlist.py \
+ t-edit.py \
+ t-keylist.py \
+ t-wait.py \
+ t-encrypt-large.py \
+ t-file-name.py \
+ t-idiomatic.py \
+ t-protocol-assuan.py
+
+XTESTS = initial.py $(py_tests) final.py
+EXTRA_DIST = support.py $(XTESTS) encrypt-only.asc sign-only.asc \
+ run-tests.py
+
+CLEANFILES = secring.gpg pubring.gpg pubring.kbx trustdb.gpg dirmngr.conf \
+ gpg-agent.conf pubring.kbx~ gpg.conf pubring.gpg~ \
+ random_seed .gpg-v21-migrated \
+ pubring-stamp private-keys-v1.d/gpg-sample.stamp
+
+private_keys = \
+ $(test_srcdir)/13CD0F3BDF24BE53FE192D62F18737256FF6E4FD \
+ $(test_srcdir)/76F7E2B35832976B50A27A282D9B87E44577EB66 \
+ $(test_srcdir)/A0747D5F9425E6664F4FFBEED20FBCA79FDED2BD \
+ $(test_srcdir)/13CBE3758AFE42B5E5E2AE4CED27AFA455E3F87F \
+ $(test_srcdir)/7A030357C0F253A5BBCD282FFC4E521B37558F5C
+
+all: all-am
+
+.SUFFIXES:
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu lang/python/tests/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu lang/python/tests/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+tags TAGS:
+
+ctags CTAGS:
+
+cscope cscopelist:
+
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile
+installdirs:
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+ -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-local mostlyclean-am
+
+distclean: distclean-am
+ -rm -f Makefile
+distclean-am: clean-am distclean-generic
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am:
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am:
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-generic mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am:
+
+.MAKE: install-am install-strip
+
+.PHONY: all all-am check check-am clean clean-generic clean-libtool \
+ clean-local cscopelist-am ctags-am distclean distclean-generic \
+ distclean-libtool distdir dvi dvi-am html html-am info info-am \
+ install install-am install-data install-data-am install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags-am uninstall uninstall-am
+
+export GNUPGHOME := $(abs_builddir)
+export GPG_AGENT_INFO :=
+
+# XXX: Currently, one cannot override automake's 'check' target. As a
+# workaround, we avoid defining 'TESTS', thus automake will not emit
+# the 'check' target. For extra robustness, we merely define a
+# dependency on 'xcheck', so this hack should also work even if
+# automake would emit the 'check' target, as adding dependencies to
+# targets is okay.
+check: xcheck
+
+.PHONY: xcheck
+
+xcheck: ./pubring-stamp
+ $(TESTS_ENVIRONMENT) $(PYTHON) $(srcdir)/run-tests.py \
+ --interpreters="$(PYTHONS)" --srcdir=$(srcdir) $(TESTFLAGS) \
+ $(XTESTS)
+
+clean-local:
+ -$(top_srcdir)/tests/start-stop-agent --stop
+ -rm -fR -- private-keys-v1.d openpgp-revocs.d S.gpg-agent sshcontrol
+
+./private-keys-v1.d/gpg-sample.stamp: $(private_keys)
+ test -d ./private-keys-v1.d || mkdir ./private-keys-v1.d
+ for k in $(private_keys); do \
+ cp $$k private-keys-v1.d/$${k#$(test_srcdir)/}.key; \
+ done
+ 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
+ -$(GPG) --batch --no-permission-warning \
+ --import $(test_srcdir)/secdemo.asc
+ echo x > ./pubring-stamp
+
+./gpg.conf:
+# This is required for t-sig-notations.
+ echo no-force-v3-sigs > ./gpg.conf
+
+./gpg-agent.conf:
+# This is required for gpg2, which does not support command fd.
+ echo pinentry-program $(abs_top_srcdir)/tests/gpg/pinentry >$@
+ echo allow-loopback-pinentry >>$@
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/lang/python/tests/encrypt-only.asc b/lang/python/tests/encrypt-only.asc
new file mode 100644
index 0000000..6e068a0
--- /dev/null
+++ b/lang/python/tests/encrypt-only.asc
@@ -0,0 +1,33 @@
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v2
+
+lQPGBFd/jL0BCAD8jfoblIrlHS0shDCbSiO7RFaT6sEa/6tSPkv6XzBba9oXOkuO
+FLTkNpIwPb92U8SOS+27j7n9v6U5NW2tyZwIoeLb8lUyKnCBr22IUhTFVXf7fros
+zmPugsJaDBi9f7RL0bqiCn4EV3DGKyAukZklk1k1JV4Ec3dEPMAmL9LmnvXreEjU
+pQZZN9sJV32ew8CYkZ6AB8foFQwfxn4x0iUoKvj8kW9RsY1KMPucp4YiFhHeMZW1
+5wGAZdEIZYKyWEp4bi/wC9yn/TUR5uNWc0uVJzQvuHwaYjolPW89DinjBkPEJCBr
+RwumaOWfbu/hb51wBoUTmUr9diVw93L2ROLPABEBAAH+BwMC1bmUAoPJKI/WBiHm
+P6tSNRLdd+7etfjAFvKL7Ob2pNTrc3hbtyOLIQ9tuEaqXEyfnCms/DCg8QdkaFUv
+Nkoj0W5+G/MQuR2jIvrq/wyL/4jIw0AFbp9/V1JbSXZh2g1eJLnnykn7uPxCbDFY
+FrVeFmkhoxZ3pid6ZQSWlxXsdW+YMvbUfNIIZpbygI/alIBvbDS1YJYEBDCwFZjU
+7quE2Ufxo8dm34EHcmbpYpn4r3DUrU5AHQ2fIprLIVqHn4+NUrR8WZS9nCnIeu/z
+OaJUZ2lJFRjUC6Gpsbsw6Xwh4Ntwzyt2SsXc+UVZngjozw3yw0VpDifxMBqcd+9x
+baSc7dfbOZF2BCZOwnB7/QrFZDaqe5b3n6rTdj1va/CrJMuxbgaNAjvLpdT2EUPZ
+fHDAdPAjASofxBREv+HIKwksuPJ9cvavZU6Q4KQA7buo25hd7yjuba4WbLQhp0jH
+AT1P7SdakMhk/IFcUKFdB3ZyZZZ1JTTPa2xZn9yDa3Jb1t7IMLYLwY6EFbjvaxH5
+WEGZvOAq2iEa941mxv4miwgf7MQPx6g9u0+dXc7iZApwWs9MNfJo3J25sKhWK5Be
+Bu3w7c6nrlg40GtPuDRgaBvYWbVerJcepTA/EPfugEJtRsDJkt7wZq1H9lWHU7Ih
+Up6/+XKtBzlCIqYjorzFLnC721pcKFcPhLgvtjjNJvUsLXbr9CwnBub/eTFcfRb2
+ro60H9cOhf0fQSQyvkZWfzq0BN6rG27G1KhyprsJAmpW0fTHHkB4V19788C2sTQv
+D93VU3Nd6MWocwAYtPWmtwXPpuOAU9IcwAvVTxBeBJCXxbH3uyx1frwDXA7lf4Pb
+a8hMoMMVU+rAG1uepKI5h4seBIKP7qKEKAPloI6/Vtf7/Ump4DKprS1QpfOW+lsX
+aR48lgNR6sQXtDdFbmNyeXB0aW9uIE9ubHkgKHRlc3Qga2V5LCBkbyBub3QgdXNl
+KSA8ZW9AZXhhbXBsZS5vcmc+iQE3BBMBCAAhBQJXf4y9AhsNBQsJCAcCBhUICQoL
+AgQWAgMBAh4BAheAAAoJEJIFcnabn+Gc/KgH/07wzrsBzTqdI5L6cIqQ81Vq8ASj
+tsuYoVfFxymB8F/AxpnLMhYRuWQTcoUHQ/olG2yA0C6o4e1JPAmh6LQGwr0eRnc2
+2tr4cbnQAhXpJ8xOR6kH9eE8nGeC7tlEeeV/Wnj3SLZOXOjYjnA9bA3JX9DP3qcz
+w1sKQPEHsGkMJuT0ZadnlJ1qw8AnnNKLDlG4kIO9hz3qB8BjxFZf+j5f/nhFNv5I
+pnNdMcDwQqHVrwD6WO+Xmmdykab0awL9To0S9DG9ohcXuJiTMa8vtXFSBM0koUDk
+BWajEq+QAcDpmdFsQr4/gbzvHkAIVTQb0seJr4gpmXFZu3TMuGVD9j13GaI=
+=38ri
+-----END PGP PRIVATE KEY BLOCK-----
diff --git a/lang/python/tests/final.py b/lang/python/tests/final.py
new file mode 100755
index 0000000..8e7ab33
--- /dev/null
+++ b/lang/python/tests/final.py
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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
+
+import os
+import subprocess
+
+subprocess.check_call([os.path.join(os.getenv('top_srcdir'),
+ "tests", "start-stop-agent"), "--stop"])
diff --git a/lang/python/tests/initial.py b/lang/python/tests/initial.py
new file mode 100755
index 0000000..2d4827a
--- /dev/null
+++ b/lang/python/tests/initial.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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
+
+import os
+import subprocess
+import pyme
+import support
+support.init_gpgme(pyme.constants.PROTOCOL_OpenPGP)
+
+subprocess.check_call([os.path.join(os.getenv('top_srcdir'),
+ "tests", "start-stop-agent"), "--start"])
+
+with pyme.Context() as c:
+ alpha = c.get_key("A0FF4590BB6122EDEF6E3C542D727CC768697734", False)
+ bob = c.get_key("D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", False)
+
+ # Mark alpha as trusted. The signature verification tests expect
+ # this.
+ support.mark_key_trusted(c, alpha)
+
+ c.op_import(open(support.in_srcdir("encrypt-only.asc")))
+ c.op_import(open(support.in_srcdir("sign-only.asc")))
diff --git a/lang/python/tests/run-tests.py b/lang/python/tests/run-tests.py
new file mode 100644
index 0000000..55d3f11
--- /dev/null
+++ b/lang/python/tests/run-tests.py
@@ -0,0 +1,90 @@
+#!/usr/bin/env python
+
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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
+from __future__ import division
+from __future__ import print_function
+from __future__ import unicode_literals
+
+import argparse
+import glob
+import os
+import subprocess
+import sys
+
+class SplitAndAccumulate(argparse.Action):
+ def __call__(self, parser, namespace, values, option_string=None):
+ current = getattr(namespace, self.dest, list())
+ current.extend(values.split())
+ setattr(namespace, self.dest, current)
+
+parser = argparse.ArgumentParser(description='Run tests.')
+parser.add_argument('tests', metavar='TEST', type=str, nargs='+',
+ help='A test to run')
+parser.add_argument('-v', '--verbose', action="store_true", default=False,
+ help='Be verbose.')
+parser.add_argument('--interpreters', metavar='PYTHON', type=str,
+ default=[], action=SplitAndAccumulate,
+ help='Use these interpreters to run the tests, ' +
+ 'separated by spaces.')
+parser.add_argument('--srcdir', type=str,
+ default=os.environ.get("srcdir", ""),
+ help='Location of the tests.')
+parser.add_argument('--builddir', type=str,
+ default=os.environ.get("abs_builddir", ""),
+ help='Location of the tests.')
+
+args = parser.parse_args()
+if not args.interpreters:
+ args.interpreters = [sys.executable]
+
+out = sys.stdout if args.verbose else None
+err = sys.stderr if args.verbose else None
+
+def status_to_str(code):
+ return {0: "PASS", 77: "SKIP", 99: "ERROR"}.get(code, "FAIL")
+
+results = list()
+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()
+
+ builddirs = glob.glob(os.path.join(args.builddir, "..", "build",
+ "lib*"+version))
+ assert len(builddirs) == 1, \
+ "Expected one build directory, got {0}".format(builddirs)
+ env = dict(os.environ)
+ env["PYTHONPATH"] = builddirs[0]
+
+ print("Running tests using {0} ({1})...".format(interpreter, version))
+ for test in args.tests:
+ status = subprocess.call(
+ [interpreter, os.path.join(args.srcdir, test)],
+ env=env, stdout=out, stderr=err)
+ print("{0}: {1}".format(status_to_str(status), test))
+ results.append(status)
+
+def count(status):
+ return len(list(filter(lambda x: x == status, results)))
+def failed():
+ return len(list(filter(lambda x: x not in (0, 77, 99), results)))
+
+print("{0} tests run, {1} succeeded, {2} failed, {3} skipped.".format(
+ len(results), count(0), failed(), count(77)))
+sys.exit(len(results) - count(0))
diff --git a/lang/python/tests/sign-only.asc b/lang/python/tests/sign-only.asc
new file mode 100644
index 0000000..6e2a6f3
--- /dev/null
+++ b/lang/python/tests/sign-only.asc
@@ -0,0 +1,33 @@
+-----BEGIN PGP PRIVATE KEY BLOCK-----
+Version: GnuPG v2
+
+lQPFBFd/jO8BCADiull4EVJiKmJqclPyU6GhTlbJXw7Ch0zbFAauOWYT3ACmgr1U
+KfJlZ2sPe2EezZkVSACxgIjTCzcgKQLh/swXdhO8uEgWEIN8f07WcSVDrcRGYwDS
+KFSRsK0bfO/OQQDUsSkNQSHjcOdLnCHCinMrQi1mBZOs+Y/DXOkkEV1zbFFV7q6X
+4vX9HSWwTRQTdOV9CFZykbwM+X1YIZlVtpOAKqSNJi3P17uQF7P9zko6HWKKKQ5S
+96BfXUOIpBRl82R85/yQgeGrWlvZ2BT2ittscNQlBKqLHJ7LIeDr9ctbKlKZjHTn
+Da7NYg+PoMHspbizjSONbEzpcR/9ZUq16oJJABEBAAH+BwMC7hQZNJSmlX/W6sfL
+0wakX6kTsiCEMy2vMCRcZ769JKT234avHtkL/g7MBJEzqdG9HSEp7+LHGuOWJhfa
+20f61WvPT5ujUIy//QXJ9a8z877jCm+fHKCTDXGYLLfCkJLfr3/GfTRy6gaIGTSw
+BqZaRelPvHbMp+eiFqDkf8W/E1LO3/83k87+pXggjz4p0OasyMw8RcDmy+IKBMGG
+bzet5WIKHIhpblIzuuucQHOjtwA8vCedub3F4lcRuULe2GW6sNuCB9kjSC9g6D1d
+bJ+WYi5GiUUQARGSNXiWVoVPLpEo0i6/2bKJ7vBYGRewNp42ebVQU2bFW7uzhaIq
+4itzNTjFNTpcxX3Lo0/mzJpe7pVRJwN+HGahNGT0EtPDsT/nNTFDUq8e8nt0U9/8
+0eekg4MRBJEzE3A+wosIHPjzCkQgu98+nh79rPMbCpZVxNfLb136cTkubmHCWptN
+T2MbqK2L4hMcOxHGGOmI9SjFltNeKtTsVtkxh3Vj67UESPdN550centfasJYA0bj
+guRQfHWHJXYIfFwblIFkl8xtIVLTeWlQMEvc7oI8jcJOc2ri8Zdjj/55xxv/RvjC
+ZKzfjPpdkLYcN1zP/hETLD68u7WmiMAYCr8Eq9YQ3oKklUpWxRMCAAtmgjGGpm5P
+QQW+36s96Q3cuG8R0Z4Wo8y89FgWzCEzuAhemCdffoUA8kn0HJQaVndnExJb1Ebz
+wp+zsX/JqiOFvcKHJAWCaXkk0oXVi1aIV4tQyCPfhyjnd846K7g8UabAz51IJHvF
+CXRAmqJvu26NqjYOfWBJJxZQsPH4FjPfYx+e/MFPZa+UTKCfzaOHClrePHUDHw58
+Ez5ItcORYn51IWW33r+c4tlhW5mrjMD7FcjFOuYT4EIivd5BSnwLP0fjBz8TBVAY
+yyFO+YAXTQ+0MVNpZ24gT25seSAodGVzdCBrZXksIGRvIG5vdCB1c2UpIDxzb0Bl
+eGFtcGxlLm9yZz6JATcEEwEIACEFAld/jO8CGwMFCwkIBwIGFQgJCgsCBBYCAwEC
+HgECF4AACgkQ/tFT8S8Y9F3PAwgAvKav6+luvcAhrpBMO4z/Q8kDMtO5AW1KTEcz
+neqpj5eTVJVbYUgDuBlEXbFYtcZmYyYtJC5KQkN3bxPmehVUzGk27UYWMWbPIWyU
+riGcFL5BWWQaKSqiWUypzhNVnxYoiWVhHeJ36LICVMpLBaubgcpwCSW/j58yZo/7
+XRwf40OblXr4cevIW4Oq5GSxKOQF+DCErF6BeikC2i+NoqSxwNiIO/1NUxs8QfAI
+z8UT/bSUXr62BWLfeCIDGgXutMMPth3tKi4DlvLCzI6eYJrd8E3Rt7iUZm9IH8OQ
+Djv2DKnL/E/AP8oITItrOmICqfEWcj+Tk2Xep4pCCMNU+Pa0yg==
+=gG5b
+-----END PGP PRIVATE KEY BLOCK-----
diff --git a/lang/python/tests/support.py b/lang/python/tests/support.py
new file mode 100644
index 0000000..4d7135e
--- /dev/null
+++ b/lang/python/tests/support.py
@@ -0,0 +1,69 @@
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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
+
+import sys
+import os
+from pyme import core
+
+# known keys
+alpha = "A0FF4590BB6122EDEF6E3C542D727CC768697734"
+bob = "D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2"
+encrypt_only = "F52770D5C4DB41408D918C9F920572769B9FE19C"
+sign_only = "7CCA20CCDE5394CEE71C9F0BFED153F12F18F45D"
+
+def make_filename(name):
+ return os.path.join(os.environ['top_srcdir'], 'tests', 'gpg', name)
+
+def in_srcdir(name):
+ return os.path.join(os.environ['srcdir'], name)
+
+def init_gpgme(proto):
+ core.engine_check_version(proto)
+
+verbose = int(os.environ.get('verbose', 0)) > 1
+def print_data(data):
+ if verbose:
+ try:
+ # See if it is a file-like object.
+ data.seek(0, os.SEEK_SET)
+ data = data.read()
+ except:
+ # Hope for the best.
+ pass
+ sys.stdout.buffer.write(data)
+
+def mark_key_trusted(ctx, key):
+ class Editor(object):
+ def __init__(self):
+ self.steps = ["trust", "save"]
+ def edit(self, status, args, out):
+ if args == "keyedit.prompt":
+ result = self.steps.pop(0)
+ elif args == "edit_ownertrust.value":
+ result = "5"
+ elif args == "edit_ownertrust.set_ultimate.okay":
+ result = "Y"
+ elif args == "keyedit.save.okay":
+ result = "Y"
+ else:
+ result = None
+ return result
+ with core.Data() as sink:
+ ctx.op_edit(key, Editor().edit, sink, sink)
diff --git a/lang/python/tests/t-callbacks.py b/lang/python/tests/t-callbacks.py
new file mode 100755
index 0000000..b3b4349
--- /dev/null
+++ b/lang/python/tests/t-callbacks.py
@@ -0,0 +1,257 @@
+#!/usr/bin/env python
+
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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
+
+import os
+from pyme import core, constants
+import support
+
+support.init_gpgme(constants.PROTOCOL_OpenPGP)
+
+c = core.Context()
+c.set_pinentry_mode(constants.PINENTRY_MODE_LOOPBACK)
+
+source = core.Data("Hallo Leute\n")
+sink = core.Data()
+
+# Valid passphrases, both as string and bytes.
+for passphrase in ('foo', b'foo'):
+ def passphrase_cb(hint, desc, prev_bad, hook=None):
+ assert hook == passphrase
+ return hook
+
+ c.set_passphrase_cb(passphrase_cb, passphrase)
+ c.op_encrypt([], 0, source, sink)
+
+# Returning an invalid type.
+def passphrase_cb(hint, desc, prev_bad, hook=None):
+ return 0
+
+c.set_passphrase_cb(passphrase_cb, None)
+try:
+ c.op_encrypt([], 0, source, sink)
+except Exception as e:
+ assert type(e) == TypeError
+ assert str(e) == "expected str or bytes from passphrase callback, got int"
+else:
+ assert False, "Expected an error, got none"
+
+# Raising an exception inside callback.
+myException = Exception()
+def passphrase_cb(hint, desc, prev_bad, hook=None):
+ raise myException
+
+c.set_passphrase_cb(passphrase_cb, None)
+try:
+ c.op_encrypt([], 0, source, sink)
+except Exception as e:
+ assert e == myException
+else:
+ assert False, "Expected an error, got none"
+
+# Wrong kind of callback function.
+def bad_passphrase_cb():
+ pass
+
+c.set_passphrase_cb(bad_passphrase_cb, None)
+try:
+ c.op_encrypt([], 0, source, sink)
+except Exception as e:
+ assert type(e) == TypeError
+else:
+ assert False, "Expected an error, got none"
+
+
+
+# Test the progress callback.
+parms = """<GnupgKeyParms format="internal">
+Key-Type: RSA
+Key-Length: 1024
+Name-Real: Joe Tester
+Name-Comment: with stupid passphrase
+Name-Email: joe+pyme@example.org
+Passphrase: Crypt0R0cks
+Expire-Date: 2020-12-31
+</GnupgKeyParms>
+"""
+
+messages = []
+def progress_cb(what, typ, current, total, hook=None):
+ assert hook == messages
+ messages.append(
+ "PROGRESS UPDATE: what = {}, type = {}, current = {}, total = {}"
+ .format(what, typ, current, total))
+
+c = core.Context()
+c.set_progress_cb(progress_cb, messages)
+c.op_genkey(parms, None, None)
+assert len(messages) > 0
+
+# Test exception handling.
+def progress_cb(what, typ, current, total, hook=None):
+ raise myException
+
+c = core.Context()
+c.set_progress_cb(progress_cb, None)
+try:
+ c.op_genkey(parms, None, None)
+except Exception as e:
+ assert e == myException
+else:
+ assert False, "Expected an error, got none"
+
+
+# Test the edit callback.
+c = core.Context()
+c.set_pinentry_mode(constants.PINENTRY_MODE_LOOPBACK)
+c.set_passphrase_cb(lambda *args: "abc")
+sink = core.Data()
+alpha = c.get_key("A0FF4590BB6122EDEF6E3C542D727CC768697734", False)
+
+cookie = object()
+edit_cb_called = False
+def edit_cb(status, args, hook):
+ global edit_cb_called
+ edit_cb_called = True
+ assert hook == cookie
+ return "quit" if args == "keyedit.prompt" else None
+c.op_edit(alpha, edit_cb, cookie, sink)
+assert edit_cb_called
+
+# Test exceptions.
+c = core.Context()
+c.set_pinentry_mode(constants.PINENTRY_MODE_LOOPBACK)
+c.set_passphrase_cb(lambda *args: "abc")
+sink = core.Data()
+
+def edit_cb(status, args):
+ raise myException
+try:
+ c.op_edit(alpha, edit_cb, None, sink)
+except Exception as e:
+ assert e == myException
+else:
+ assert False, "Expected an error, got none"
+
+
+
+# Test the status callback.
+source = core.Data("Hallo Leute\n")
+sink = core.Data()
+
+status_cb_called = False
+def status_cb(keyword, args, hook=None):
+ global status_cb_called
+ status_cb_called = True
+ assert hook == cookie
+
+c = core.Context()
+c.set_status_cb(status_cb, cookie)
+c.set_ctx_flag("full-status", "1")
+c.op_encrypt([alpha], constants.ENCRYPT_ALWAYS_TRUST, source, sink)
+assert status_cb_called
+
+# Test exceptions.
+source = core.Data("Hallo Leute\n")
+sink = core.Data()
+
+def status_cb(keyword, args):
+ raise myException
+
+c = core.Context()
+c.set_status_cb(status_cb, None)
+c.set_ctx_flag("full-status", "1")
+try:
+ c.op_encrypt([alpha], constants.ENCRYPT_ALWAYS_TRUST, source, sink)
+except Exception as e:
+ assert e == myException
+else:
+ assert False, "Expected an error, got none"
+
+
+
+# Test the data callbacks.
+def read_cb(amount, hook=None):
+ assert hook == cookie
+ return 0
+def release_cb(hook=None):
+ assert hook == cookie
+data = core.Data(cbs=(read_cb, None, None, release_cb, cookie))
+try:
+ data.read()
+except Exception as e:
+ assert type(e) == TypeError
+else:
+ assert False, "Expected an error, got none"
+
+def read_cb(amount):
+ raise myException
+data = core.Data(cbs=(read_cb, None, None, lambda: None))
+try:
+ data.read()
+except Exception as e:
+ assert e == myException
+else:
+ assert False, "Expected an error, got none"
+
+
+def write_cb(what, hook=None):
+ assert hook == cookie
+ return "wrong type"
+data = core.Data(cbs=(None, write_cb, None, release_cb, cookie))
+try:
+ data.write(b'stuff')
+except Exception as e:
+ assert type(e) == TypeError
+else:
+ assert False, "Expected an error, got none"
+
+def write_cb(what):
+ raise myException
+data = core.Data(cbs=(None, write_cb, None, lambda: None))
+try:
+ data.write(b'stuff')
+except Exception as e:
+ assert e == myException
+else:
+ assert False, "Expected an error, got none"
+
+
+def seek_cb(offset, whence, hook=None):
+ assert hook == cookie
+ return "wrong type"
+data = core.Data(cbs=(None, None, seek_cb, release_cb, cookie))
+try:
+ data.seek(0, os.SEEK_SET)
+except Exception as e:
+ assert type(e) == TypeError
+else:
+ assert False, "Expected an error, got none"
+
+def seek_cb(offset, whence):
+ raise myException
+data = core.Data(cbs=(None, None, seek_cb, lambda: None))
+try:
+ data.seek(0, os.SEEK_SET)
+except Exception as e:
+ assert e == myException
+else:
+ assert False, "Expected an error, got none"
diff --git a/lang/python/tests/t-data.py b/lang/python/tests/t-data.py
new file mode 100755
index 0000000..4812a2e
--- /dev/null
+++ b/lang/python/tests/t-data.py
@@ -0,0 +1,129 @@
+#!/usr/bin/env python
+
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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
+
+import io
+import os
+import tempfile
+from pyme import core
+
+data = core.Data('Hello world!')
+assert data.read() == b'Hello world!'
+assert data.read() == b''
+
+data.seek(0, os.SEEK_SET)
+assert data.read() == b'Hello world!'
+assert data.read() == b''
+
+data = core.Data(b'Hello world!')
+assert data.read() == b'Hello world!'
+
+data = core.Data(b'Hello world!', copy=False)
+assert data.read() == b'Hello world!'
+
+data = core.Data()
+data.write('Hello world!')
+data.seek(0, os.SEEK_SET)
+assert data.read() == b'Hello world!'
+
+data = core.Data()
+data.write(b'Hello world!')
+data.seek(0, os.SEEK_SET)
+assert data.read() == b'Hello world!'
+
+binjunk = bytes(range(256))
+data = core.Data()
+data.write(binjunk)
+data.seek(0, os.SEEK_SET)
+assert data.read() == binjunk
+
+data = core.Data()
+data.set_file_name("foobar")
+assert data.get_file_name() == "foobar"
+
+# Test reading from an existing file.
+with tempfile.NamedTemporaryFile() as tmp:
+ tmp.write(binjunk)
+ tmp.flush()
+ tmp.seek(0)
+
+ # Open using name.
+ data = core.Data(file=tmp.name)
+ assert data.read() == binjunk
+
+ # Open using name, without copying.
+ if False:
+ # delayed reads are not yet supported
+ data = core.Data(file=tmp.name, copy=False)
+ assert data.read() == binjunk
+
+ # Open using stream.
+ tmp.seek(0)
+ data = core.Data(file=tmp)
+ assert data.read() == binjunk
+
+ # Open using stream, offset, and length.
+ data = core.Data(file=tmp, offset=0, length=42)
+ assert data.read() == binjunk[:42]
+
+ # Open using name, offset, and length.
+ data = core.Data(file=tmp.name, offset=23, length=42)
+ assert data.read() == binjunk[23:23+42]
+
+# Test callbacks.
+class DataObject(object):
+ def __init__(self):
+ self.buffer = io.BytesIO()
+ self.released = False
+
+ def read(self, amount, hook=None):
+ assert not self.released
+ return self.buffer.read(amount)
+
+ def write(self, data, hook=None):
+ assert not self.released
+ return self.buffer.write(data)
+
+ def seek(self, offset, whence, hook=None):
+ assert not self.released
+ return self.buffer.seek(offset, whence)
+
+ def release(self, hook=None):
+ assert not self.released
+ self.released = True
+
+do = DataObject()
+cookie = object()
+data = core.Data(cbs=(do.read, do.write, do.seek, do.release, cookie))
+data.write('Hello world!')
+data.seek(0, os.SEEK_SET)
+assert data.read() == b'Hello world!'
+del data
+assert do.released
+
+# Again, without the cookie.
+do = DataObject()
+data = core.Data(cbs=(do.read, do.write, do.seek, do.release))
+data.write('Hello world!')
+data.seek(0, os.SEEK_SET)
+assert data.read() == b'Hello world!'
+del data
+assert do.released
diff --git a/lang/python/tests/t-decrypt-verify.py b/lang/python/tests/t-decrypt-verify.py
new file mode 100755
index 0000000..a38a965
--- /dev/null
+++ b/lang/python/tests/t-decrypt-verify.py
@@ -0,0 +1,77 @@
+#!/usr/bin/env python
+
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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
+
+import pyme
+from pyme import core, constants, errors
+import support
+
+def check_verify_result(result, summary, fpr, status):
+ assert len(result.signatures) == 1, "Unexpected number of signatures"
+ sig = result.signatures[0]
+ assert sig.summary == summary, "Unexpected signature summary"
+ assert sig.fpr == fpr
+ assert errors.GPGMEError(sig.status).getcode() == status
+ assert len(sig.notations) == 0
+ assert not sig.wrong_key_usage
+ assert sig.validity == constants.VALIDITY_FULL
+ assert errors.GPGMEError(sig.validity_reason).getcode() == errors.NO_ERROR
+
+support.init_gpgme(constants.PROTOCOL_OpenPGP)
+c = core.Context()
+
+source = core.Data(file=support.make_filename("cipher-2.asc"))
+sink = core.Data()
+
+c.op_decrypt_verify(source, sink)
+result = c.op_decrypt_result()
+assert not result.unsupported_algorithm, \
+ "Unsupported algorithm: {}".format(result.unsupported_algorithm)
+
+support.print_data(sink)
+
+verify_result = c.op_verify_result()
+check_verify_result(verify_result,
+ constants.SIGSUM_VALID | constants.SIGSUM_GREEN,
+ "A0FF4590BB6122EDEF6E3C542D727CC768697734",
+ errors.NO_ERROR)
+
+# Idiomatic interface.
+with pyme.Context() as c:
+ alpha = c.get_key("A0FF4590BB6122EDEF6E3C542D727CC768697734", False)
+ bob = c.get_key("D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", False)
+ plaintext, _, verify_result = \
+ c.decrypt(open(support.make_filename("cipher-2.asc")), verify=[alpha])
+ assert plaintext.find(b'Wenn Sie dies lesen k') >= 0, \
+ 'Plaintext not found'
+ check_verify_result(verify_result,
+ constants.SIGSUM_VALID | constants.SIGSUM_GREEN,
+ "A0FF4590BB6122EDEF6E3C542D727CC768697734",
+ errors.NO_ERROR)
+
+ try:
+ c.decrypt(open(support.make_filename("cipher-2.asc")),
+ verify=[alpha, bob])
+ except errors.MissingSignatures as e:
+ assert len(e.missing) == 1
+ assert e.missing[0] == bob
+ else:
+ assert False, "Expected an error, got none"
diff --git a/lang/python/tests/t-decrypt.py b/lang/python/tests/t-decrypt.py
new file mode 100755
index 0000000..2d85bc2
--- /dev/null
+++ b/lang/python/tests/t-decrypt.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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
+
+import pyme
+from pyme import core, constants
+import support
+
+support.init_gpgme(constants.PROTOCOL_OpenPGP)
+c = core.Context()
+
+source = core.Data(file=support.make_filename("cipher-1.asc"))
+sink = core.Data()
+
+c.op_decrypt(source, sink)
+result = c.op_decrypt_result()
+assert not result.unsupported_algorithm, \
+ "Unsupported algorithm: {}".format(result.unsupported_algorithm)
+
+support.print_data(sink)
+
+# Idiomatic interface.
+with pyme.Context() as c:
+ plaintext, _, _ = c.decrypt(open(support.make_filename("cipher-1.asc")))
+ assert len(plaintext) > 0
+ assert plaintext.find(b'Wenn Sie dies lesen k') >= 0, \
+ 'Plaintext not found'
diff --git a/lang/python/tests/t-edit.py b/lang/python/tests/t-edit.py
new file mode 100755
index 0000000..18bcb94
--- /dev/null
+++ b/lang/python/tests/t-edit.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python
+
+# Copyright (C) 2005 Igor Belyi <belyi@users.sourceforge.net>
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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
+
+import sys
+import os
+from pyme import core, constants
+import support
+
+class KeyEditor(object):
+ def __init__(self):
+ self.steps = ["fpr", "expire", "1", "primary", "quit"]
+ self.step = 0
+ self.done = False
+ self.verbose = int(os.environ.get('verbose', 0)) > 1
+
+ def edit_fnc(self, status, args, out=None):
+ if args == "keyedit.prompt":
+ result = self.steps[self.step]
+ self.step += 1
+ elif args == "keyedit.save.okay":
+ result = "Y"
+ self.done = self.step == len(self.steps)
+ elif args == "keygen.valid":
+ result = "0"
+ else:
+ result = None
+
+ if self.verbose:
+ sys.stderr.write("Code: {}, args: {!r}, Returning: {!r}\n"
+ .format(status, args, result))
+
+ return result
+
+support.init_gpgme(constants.PROTOCOL_OpenPGP)
+
+c = core.Context()
+c.set_pinentry_mode(constants.PINENTRY_MODE_LOOPBACK)
+c.set_passphrase_cb(lambda *args: "abc")
+c.set_armor(True)
+
+# The deprecated interface.
+editor = KeyEditor()
+c.interact(c.get_key("A0FF4590BB6122EDEF6E3C542D727CC768697734", False),
+ editor.edit_fnc)
+assert editor.done
+
+# The deprecated interface.
+sink = core.Data()
+editor = KeyEditor()
+c.op_edit(c.get_key("A0FF4590BB6122EDEF6E3C542D727CC768697734", False),
+ editor.edit_fnc, sink, sink)
+assert editor.done
diff --git a/lang/python/tests/t-encrypt-large.py b/lang/python/tests/t-encrypt-large.py
new file mode 100755
index 0000000..b9cc3b5
--- /dev/null
+++ b/lang/python/tests/t-encrypt-large.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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
+
+import sys
+import random
+from pyme import core, constants
+import support
+
+if len(sys.argv) == 2:
+ nbytes = int(sys.argv[1])
+else:
+ nbytes = 100000
+
+support.init_gpgme(constants.PROTOCOL_OpenPGP)
+c = core.Context()
+
+ntoread = nbytes
+def read_cb(amount):
+ global ntoread
+ chunk = ntoread if ntoread < amount else amount
+ ntoread -= chunk
+ assert ntoread >= 0
+ assert chunk >= 0
+ return bytes(bytearray(random.randrange(256) for i in range(chunk)))
+
+nwritten = 0
+def write_cb(data):
+ global nwritten
+ nwritten += len(data)
+ return len(data)
+
+source = core.Data(cbs=(read_cb, None, None, lambda: None))
+sink = core.Data(cbs=(None, write_cb, None, lambda: None))
+
+keys = []
+keys.append(c.get_key("A0FF4590BB6122EDEF6E3C542D727CC768697734", False))
+keys.append(c.get_key("D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", False))
+
+c.op_encrypt(keys, constants.ENCRYPT_ALWAYS_TRUST, source, sink)
+result = c.op_encrypt_result()
+assert not result.invalid_recipients, \
+ "Invalid recipient encountered: {}".format(result.invalid_recipients.fpr)
+assert ntoread == 0
+
+if support.verbose:
+ sys.stderr.write(
+ "plaintext={} bytes, ciphertext={} bytes\n".format(nbytes, nwritten))
diff --git a/lang/python/tests/t-encrypt-sign.py b/lang/python/tests/t-encrypt-sign.py
new file mode 100755
index 0000000..a453f79
--- /dev/null
+++ b/lang/python/tests/t-encrypt-sign.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python
+
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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
+
+import sys
+import pyme
+from pyme import core, constants
+import support
+
+support.init_gpgme(constants.PROTOCOL_OpenPGP)
+c = core.Context()
+c.set_armor(True)
+
+def check_result(r, typ):
+ if r.invalid_signers:
+ sys.exit("Invalid signer found: {}".format(r.invalid_signers.fpr))
+
+ if len(r.signatures) != 1:
+ sys.exit("Unexpected number of signatures created")
+
+ signature = r.signatures[0]
+ if signature.type != typ:
+ sys.exit("Wrong type of signature created")
+
+ if signature.pubkey_algo != constants.PK_DSA:
+ sys.exit("Wrong pubkey algorithm reported: {}".format(
+ signature.pubkey_algo))
+
+ if signature.hash_algo not in (constants.MD_SHA1, constants.MD_RMD160):
+ sys.exit("Wrong hash algorithm reported: {}".format(
+ signature.hash_algo))
+
+ if signature.sig_class != 0:
+ sys.exit("Wrong signature class reported: {}".format(
+ signature.sig_class))
+
+ if signature.fpr != "A0FF4590BB6122EDEF6E3C542D727CC768697734":
+ sys.exit("Wrong fingerprint reported: {}".format(signature.fpr))
+
+keys = []
+keys.append(c.get_key("A0FF4590BB6122EDEF6E3C542D727CC768697734", False))
+keys.append(c.get_key("D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", False))
+
+for recipients in (keys, []):
+ source = core.Data("Hallo Leute\n")
+ sink = core.Data()
+
+ c.op_encrypt_sign(recipients, constants.ENCRYPT_ALWAYS_TRUST, source, sink)
+ result = c.op_encrypt_result()
+ assert not result.invalid_recipients, \
+ "Invalid recipient encountered: {}".format(
+ result.invalid_recipients.fpr)
+
+ result = c.op_sign_result()
+ check_result(result, constants.SIG_MODE_NORMAL)
+
+ support.print_data(sink)
+
+
+# Idiomatic interface.
+with pyme.Context(armor=True) as c:
+ message = "Hallo Leute\n".encode()
+ ciphertext, _, sig_result = c.encrypt(message,
+ recipients=keys,
+ always_trust=True)
+ assert len(ciphertext) > 0
+ assert ciphertext.find(b'BEGIN PGP MESSAGE') > 0, 'Marker not found'
+ check_result(sig_result, constants.SIG_MODE_NORMAL)
+
+ c.signers = [c.get_key(support.sign_only, True)]
+ c.encrypt(message, recipients=keys, always_trust=True)
+
+ c.signers = [c.get_key(support.encrypt_only, True)]
+ try:
+ c.encrypt(message, recipients=keys, always_trust=True)
+ except pyme.errors.InvalidSigners as e:
+ assert len(e.signers) == 1
+ assert support.encrypt_only.endswith(e.signers[0].fpr)
+ else:
+ assert False, "Expected an InvalidSigners error, got none"
diff --git a/lang/python/tests/t-encrypt-sym.py b/lang/python/tests/t-encrypt-sym.py
new file mode 100755
index 0000000..d577184
--- /dev/null
+++ b/lang/python/tests/t-encrypt-sym.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python
+
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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
+
+import os
+import pyme
+from pyme import core, constants
+import support
+
+support.init_gpgme(constants.PROTOCOL_OpenPGP)
+
+for passphrase in ("abc", b"abc"):
+ c = core.Context()
+ c.set_armor(True)
+ c.set_pinentry_mode(constants.PINENTRY_MODE_LOOPBACK)
+
+ source = core.Data("Hallo Leute\n")
+ cipher = core.Data()
+
+ passphrase_cb_called = 0
+ def passphrase_cb(hint, desc, prev_bad, hook=None):
+ global passphrase_cb_called
+ passphrase_cb_called += 1
+ return passphrase
+
+ c.set_passphrase_cb(passphrase_cb, None)
+
+ c.op_encrypt([], 0, source, cipher)
+ assert passphrase_cb_called == 1, \
+ "Callback called {} times".format(passphrase_cb_called)
+ support.print_data(cipher)
+
+ c = core.Context()
+ c.set_armor(True)
+ c.set_pinentry_mode(constants.PINENTRY_MODE_LOOPBACK)
+ c.set_passphrase_cb(passphrase_cb, None)
+ plain = core.Data()
+ cipher.seek(0, os.SEEK_SET)
+
+ c.op_decrypt(cipher, plain)
+ # Seems like the passphrase is cached.
+ #assert passphrase_cb_called == 2, \
+ # "Callback called {} times".format(passphrase_cb_called)
+ support.print_data(plain)
+
+ plain.seek(0, os.SEEK_SET)
+ plaintext = plain.read()
+ assert plaintext == b"Hallo Leute\n", \
+ "Wrong plaintext {!r}".format(plaintext)
+
+# Idiomatic interface.
+for passphrase in ("abc", b"abc"):
+ with pyme.Context(armor=True) as c:
+ # Check that the passphrase callback is not altered.
+ def f(*args):
+ assert False
+ c.set_passphrase_cb(f)
+
+ message = "Hallo Leute\n".encode()
+ ciphertext, _, _ = c.encrypt(message,
+ passphrase=passphrase,
+ sign=False)
+ assert ciphertext.find(b'BEGIN PGP MESSAGE') > 0, 'Marker not found'
+
+ plaintext, _, _ = c.decrypt(ciphertext, passphrase=passphrase)
+ assert plaintext == message, 'Message body not recovered'
+
+ assert c._passphrase_cb[1] == f, "Passphrase callback not restored"
diff --git a/lang/python/tests/t-encrypt.py b/lang/python/tests/t-encrypt.py
new file mode 100755
index 0000000..65e7d24
--- /dev/null
+++ b/lang/python/tests/t-encrypt.py
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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
+
+import pyme
+from pyme import core, constants
+import support
+
+support.init_gpgme(constants.PROTOCOL_OpenPGP)
+c = core.Context()
+c.set_armor(True)
+
+source = core.Data("Hallo Leute\n")
+sink = core.Data()
+
+keys = []
+keys.append(c.get_key("A0FF4590BB6122EDEF6E3C542D727CC768697734", False))
+keys.append(c.get_key("D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", False))
+
+c.op_encrypt(keys, constants.ENCRYPT_ALWAYS_TRUST, source, sink)
+result = c.op_encrypt_result()
+assert not result.invalid_recipients, \
+ "Invalid recipients: {}".format(", ".join(r.fpr for r in result.recipients))
+support.print_data(sink)
+
+# Idiomatic interface.
+with pyme.Context(armor=True) as c:
+ ciphertext, _, _ = c.encrypt("Hallo Leute\n".encode(),
+ recipients=keys,
+ sign=False,
+ always_trust=True)
+ assert len(ciphertext) > 0
+ assert ciphertext.find(b'BEGIN PGP MESSAGE') > 0, 'Marker not found'
+
+ c.encrypt("Hallo Leute\n".encode(),
+ recipients=[c.get_key(support.encrypt_only, False)],
+ sign=False, always_trust=True)
+
+ try:
+ c.encrypt("Hallo Leute\n".encode(),
+ recipients=[c.get_key(support.sign_only, False)],
+ sign=False, always_trust=True)
+ except pyme.errors.InvalidRecipients as e:
+ assert len(e.recipients) == 1
+ assert support.sign_only.endswith(e.recipients[0].fpr)
+ else:
+ assert False, "Expected an InvalidRecipients error, got none"
diff --git a/lang/python/tests/t-export.py b/lang/python/tests/t-export.py
new file mode 100755
index 0000000..db36b98
--- /dev/null
+++ b/lang/python/tests/t-export.py
@@ -0,0 +1,40 @@
+#!/usr/bin/env python
+
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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 pyme import core, constants
+import support
+
+support.init_gpgme(constants.PROTOCOL_OpenPGP)
+c = core.Context()
+c.set_armor(True)
+
+sink = core.Data()
+c.op_export_ext(['Alpha', 'Bob'], 0, sink)
+support.print_data(sink)
+
+# Again. Now using a key array.
+keys = []
+keys.append(c.get_key("0x68697734", False)) # Alpha
+keys.append(c.get_key("0xA9E3B0B2", False)) # Bob
+sink = core.Data()
+c.op_export_keys(keys, 0, sink)
+support.print_data(sink)
diff --git a/lang/python/tests/t-file-name.py b/lang/python/tests/t-file-name.py
new file mode 100755
index 0000000..e93b120
--- /dev/null
+++ b/lang/python/tests/t-file-name.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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
+
+import os
+from pyme import core, constants
+import support
+
+testname = "abcde12345"
+
+support.init_gpgme(constants.PROTOCOL_OpenPGP)
+c = core.Context()
+c.set_armor(True)
+
+source = core.Data("Hallo Leute\n")
+source.set_file_name(testname)
+cipher = core.Data()
+plain = core.Data()
+
+keys = []
+keys.append(c.get_key("A0FF4590BB6122EDEF6E3C542D727CC768697734", False))
+
+c.op_encrypt(keys, constants.ENCRYPT_ALWAYS_TRUST, source, cipher)
+cipher.seek(0, os.SEEK_SET)
+c.op_decrypt(cipher, plain)
+result = c.op_decrypt_result()
+assert result.file_name == testname
diff --git a/lang/python/tests/t-idiomatic.py b/lang/python/tests/t-idiomatic.py
new file mode 100755
index 0000000..f063206
--- /dev/null
+++ b/lang/python/tests/t-idiomatic.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python
+
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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
+
+import sys
+import io
+import os
+import tempfile
+import pyme
+import support
+
+support.init_gpgme(pyme.constants.PROTOCOL_OpenPGP)
+
+# Both Context and Data can be used as context manager:
+with pyme.Context() as c, pyme.Data() as d:
+ c.get_engine_info()
+ d.write(b"Halloechen")
+ leak_c = c
+ leak_d = d
+assert leak_c.wrapped == None
+assert leak_d.wrapped == None
+
+def sign_and_verify(source, signed, sink):
+ with pyme.Context() as c:
+ c.op_sign(source, signed, pyme.constants.SIG_MODE_NORMAL)
+ signed.seek(0, os.SEEK_SET)
+ c.op_verify(signed, None, sink)
+ result = c.op_verify_result()
+
+ assert len(result.signatures) == 1, "Unexpected number of signatures"
+ sig = result.signatures[0]
+ assert sig.summary == (pyme.constants.SIGSUM_VALID |
+ pyme.constants.SIGSUM_GREEN)
+ assert pyme.errors.GPGMEError(sig.status).getcode() == pyme.errors.NO_ERROR
+
+ sink.seek(0, os.SEEK_SET)
+ assert sink.read() == b"Hallo Leute\n"
+
+# Demonstrate automatic wrapping of file-like objects with 'fileno'
+# method.
+with tempfile.TemporaryFile() as source, \
+ tempfile.TemporaryFile() as signed, \
+ tempfile.TemporaryFile() as sink:
+ source.write(b"Hallo Leute\n")
+ source.seek(0, os.SEEK_SET)
+
+ sign_and_verify(source, signed, sink)
+
+if sys.version_info[0] == 3:
+ # Python2's io.BytesIO does not implement the buffer interface,
+ # hence we cannot use it as sink.
+
+ # XXX: Python's io.BytesIo.truncate does not work as advertised.
+ # http://bugs.python.org/issue27261
+ bio = io.BytesIO()
+ bio.truncate(1)
+ if len(bio.getvalue()) != 1:
+ # This version of Python is affected, preallocate buffer.
+ preallocate = 128*b'\x00'
+ else:
+ preallocate = b''
+
+ # Demonstrate automatic wrapping of objects implementing the buffer
+ # interface, and the use of data objects with the 'with' statement.
+ with io.BytesIO(preallocate) as signed, pyme.Data() as sink:
+ sign_and_verify(b"Hallo Leute\n", signed, sink)
diff --git a/lang/python/tests/t-import.py b/lang/python/tests/t-import.py
new file mode 100755
index 0000000..0b50d02
--- /dev/null
+++ b/lang/python/tests/t-import.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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 pyme import core, constants
+import support
+
+def check_result(result, fpr, secret):
+ assert result.considered == 1 or (secret and result.considered == 3)
+ assert result.no_user_id == 0
+ assert not ((secret and result.imported != 0)
+ or (not secret and (result.imported != 0
+ and result.imported != 1)))
+ assert result.imported_rsa == 0
+ assert not ((secret and (result.unchanged != 0 and result.unchanged != 1))
+ or (not secret and ((result.imported == 0
+ and result.unchanged != 1)
+ or (result.imported == 1
+ and result.unchanged != 0))))
+ assert result.new_user_ids == 0
+ assert result.new_sub_keys == 0
+ assert not ((secret
+ and ((result.secret_imported == 0
+ and result.new_signatures != 0)
+ or (result.secret_imported == 1
+ and result.new_signatures > 1)))
+ or (not secret and result.new_signatures != 0))
+ assert result.new_revocations == 0
+ assert not ((secret and result.secret_read != 1 and result.secret_read != 3)
+ or (not secret and result.secret_read != 0))
+ assert not ((secret and result.secret_imported != 0
+ and result.secret_imported != 1
+ and result.secret_imported != 2)
+ or (not secret and result.secret_imported != 0))
+ assert not ((secret
+ and ((result.secret_imported == 0
+ and result.secret_unchanged != 1
+ and result.secret_unchanged != 2)
+ or (result.secret_imported == 1
+ and result.secret_unchanged != 0)))
+ or (not secret and result.secret_unchanged != 0))
+ assert result.not_imported == 0
+ if secret:
+ assert not (len(result.imports) in (0, 3))
+ else:
+ assert not (len(result.imports) in (0, 2))
+
+ assert fpr == result.imports[0].fpr
+ assert len(result.imports) == 1 or fpr == result.imports[1].fpr
+ assert result.imports[0].result == 0
+
+support.init_gpgme(constants.PROTOCOL_OpenPGP)
+c = core.Context()
+
+c.op_import(core.Data(file=support.make_filename("pubkey-1.asc")))
+result = c.op_import_result()
+check_result(result, "ADAB7FCC1F4DE2616ECFA402AF82244F9CD9FD55", False)
+
+c.op_import(core.Data(file=support.make_filename("seckey-1.asc")))
+result = c.op_import_result()
+check_result(result, "ADAB7FCC1F4DE2616ECFA402AF82244F9CD9FD55", True)
diff --git a/lang/python/tests/t-keylist.py b/lang/python/tests/t-keylist.py
new file mode 100755
index 0000000..5e8b333
--- /dev/null
+++ b/lang/python/tests/t-keylist.py
@@ -0,0 +1,246 @@
+#!/usr/bin/env python
+
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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 pyme import core, constants
+import support
+
+support.init_gpgme(constants.PROTOCOL_OpenPGP)
+c = core.Context()
+
+# Check expration of keys. This test assumes three subkeys of which
+# 2 are expired; it is used with the "Whisky" test key. It has
+# already been checked that these 3 subkeys are available.
+def check_whisky(name, key):
+ sub1 = key.subkeys[2]
+ sub2 = key.subkeys[3]
+
+ assert sub1.expired and sub2.expired, \
+ "Subkey of `{}' not flagged as expired".format(name)
+ assert sub1.expires == 1129636886 and sub2.expires == 1129636939, \
+ "Subkey of `{}' has wrong expiration date".format(name)
+
+keys = [
+ [ "A0FF4590BB6122EDEF6E3C542D727CC768697734", "6AE6D7EE46A871F8",
+ [ [ "Alfa Test", "demo key", "alfa@example.net" ],
+ [ "Alpha Test", "demo key", "alpha@example.net" ],
+ [ "Alice", "demo key", "" ] ], 1 ],
+ [ "D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", "5381EA4EE29BA37F",
+ [ [ "Bob", "demo key", "" ],
+ [ "Bravo Test", "demo key", "bravo@example.net" ] ], 1 ],
+ [ "61EE841A2A27EB983B3B3C26413F4AF31AFDAB6C", "E71E72ACBC43DA60",
+ [ [ "Charlie Test", "demo key", "charlie@example.net" ] ], 1 ],
+ [ "6560C59C43D031C54D7C588EEBA9F240EB9DC9E6", "06F22880B0C45424",
+ [ [ "Delta Test", "demo key", "delta@example.net" ] ], 1 ],
+ [ "3531152DE293E26A07F504BC318C1FAEFAEF6D1B", "B5C79E1A7272144D",
+ [ [ "Echelon", "demo key", "" ],
+ [ "Echo Test", "demo key", "echo@example.net" ],
+ [ "Eve", "demo key", "" ] ], 1 ],
+ [ "56D33268F7FE693FBB594762D4BF57F37372E243", "0A32EE79EE45198E",
+ [ [ "Foxtrot Test", "demo key", "foxtrot@example.net" ] ], 1 ],
+ [ "C9C07DCC6621B9FB8D071B1D168410A48FC282E6", "247491CC9DCAD354",
+ [ [ "Golf Test", "demo key", "golf@example.net" ] ], 1 ],
+ [ "9E91CBB11E4D4135583EF90513DB965534C6E3F1", "76E26537D622AD0A",
+ [ [ "Hotel Test", "demo key", "hotel@example.net" ] ], 1 ],
+ [ "CD538D6CC9FB3D745ECDA5201FE8FC6F04259677", "C1C8EFDE61F76C73",
+ [ [ "India Test", "demo key", "india@example.net" ] ], 1 ],
+ [ "F8F1EDC73995AB739AD54B380C820C71D2699313", "BD0B108735F8F136",
+ [ [ "Juliet Test", "demo key", "juliet@example.net" ] ], 1 ],
+ [ "3FD11083779196C2ECDD9594AD1B0FAD43C2D0C7", "86CBB34A9AF64D02",
+ [ [ "Kilo Test", "demo key", "kilo@example.net" ] ], 1 ],
+ [ "1DDD28CEF714F5B03B8C246937CAB51FB79103F8", "0363B449FE56350C",
+ [ [ "Lima Test", "demo key", "lima@example.net" ] ], 1 ],
+ [ "2686AA191A278013992C72EBBE794852BE5CF886", "5F600A834F31EAE8",
+ [ [ "Mallory", "demo key", "" ],
+ [ "Mike Test", "demo key", "mike@example.net" ] ], 1 ],
+ [ "5AB9D6D7BAA1C95B3BAA3D9425B00FD430CEC684", "4C1D63308B70E472",
+ [ [ "November Test", "demo key", "november@example.net" ] ], 1 ],
+ [ "43929E89F8F79381678CAE515F6356BA6D9732AC", "FF0785712681619F",
+ [ [ "Oscar Test", "demo key", "oscar@example.net" ] ], 1 ],
+ [ "6FAA9C201E5E26DCBAEC39FD5D15E01D3FF13206", "2764E18263330D9C",
+ [ [ "Papa test", "demo key", "papa@example.net" ] ], 1 ],
+ [ "A7969DA1C3297AA96D49843F1C67EC133C661C84", "6CDCFC44A029ACF4",
+ [ [ "Quebec Test", "demo key", "quebec@example.net" ] ], 1 ],
+ [ "38FBE1E4BF6A5E1242C8F6A13BDBEDB1777FBED3", "9FAB805A11D102EA",
+ [ [ "Romeo Test", "demo key", "romeo@example.net" ] ], 1 ],
+ [ "045B2334ADD69FC221076841A5E67F7FA3AE3EA1", "93B88B0F0F1B50B4",
+ [ [ "Sierra Test", "demo key", "sierra@example.net" ] ], 1 ],
+ [ "ECAC774F4EEEB0620767044A58CB9A4C85A81F38", "97B60E01101C0402",
+ [ [ "Tango Test", "demo key", "tango@example.net" ] ], 1 ],
+ [ "0DBCAD3F08843B9557C6C4D4A94C0F75653244D6", "93079B915522BDB9",
+ [ [ "Uniform Test", "demo key", "uniform@example.net" ] ], 1 ],
+ [ "E8143C489C8D41124DC40D0B47AF4B6961F04784", "04071FB807287134",
+ [ [ "Victor Test", "demo key", "victor@example.org" ] ], 1 ],
+ [ "E8D6C90B683B0982BD557A99DEF0F7B8EC67DBDE", "D7FBB421FD6E27F6",
+ [ [ "Whisky Test", "demo key", "whisky@example.net" ] ], 3,
+ check_whisky ],
+ [ "04C1DF62EFA0EBB00519B06A8979A6C5567FB34A", "5CC6F87F41E408BE",
+ [ [ "XRay Test", "demo key", "xray@example.net" ] ], 1 ],
+ [ "ED9B316F78644A58D042655A9EEF34CD4B11B25F", "5ADFD255F7B080AD",
+ [ [ "Yankee Test", "demo key", "yankee@example.net" ] ], 1 ],
+ [ "23FD347A419429BACCD5E72D6BC4778054ACD246", "EF9DC276A172C881",
+ [ [ "Zulu Test", "demo key", "zulu@example.net" ] ], 1 ],
+]
+
+def check_global(key, uids, n_subkeys):
+ assert not key.revoked, "Key unexpectedly revoked"
+ assert not key.expired, "Key unexpectedly expired"
+ assert not key.disabled, "Key unexpectedly disabled"
+ assert not key.invalid, "Key unexpectedly invalid"
+ assert key.can_sign, "Key unexpectedly unusable for signing"
+ assert key.can_certify, "Key unexpectedly unusable for certifications"
+ assert not key.secret, "Key unexpectedly secret"
+ assert not key.protocol != constants.PROTOCOL_OpenPGP, \
+ "Key has unexpected protocol: {}".format(key.protocol)
+ assert not key.issuer_serial, \
+ "Key unexpectedly carries issuer serial: {}".format(key.issuer_serial)
+ assert not key.issuer_name, \
+ "Key unexpectedly carries issuer name: {}".format(key.issuer_name)
+ assert not key.chain_id, \
+ "Key unexpectedly carries chain ID: {}".format(key.chain_id)
+
+ # Only key Alfa is trusted
+ assert key.uids[0].name == 'Alfa Test' \
+ or key.owner_trust == constants.VALIDITY_UNKNOWN, \
+ "Key has unexpected owner trust: {}".format(key.owner_trust)
+ assert key.uids[0].name != 'Alfa Test' \
+ or key.owner_trust == constants.VALIDITY_ULTIMATE, \
+ "Key has unexpected owner trust: {}".format(key.owner_trust)
+
+ assert len(key.subkeys) - 1 == n_subkeys, \
+ "Key `{}' has unexpected number of subkeys".format(uids[0][0])
+
+
+def check_subkey(fpr, which, subkey):
+ assert not subkey.revoked, which + " key unexpectedly revoked"
+ assert not subkey.expired, which + " key unexpectedly expired"
+ assert not subkey.disabled, which + " key unexpectedly disabled"
+ assert not subkey.invalid, which + " key unexpectedly invalid"
+
+ if which == "Primary":
+ assert not subkey.can_encrypt, \
+ which + " key unexpectedly usable for encryption"
+ assert subkey.can_sign, \
+ which + " key unexpectedly unusable for signing"
+ assert subkey.can_certify, \
+ which + " key unexpectedly unusable for certifications"
+ else:
+ assert subkey.can_encrypt, \
+ which + " key unexpectedly unusable for encryption"
+ assert not subkey.can_sign, \
+ which + " key unexpectedly usable for signing"
+ assert not subkey.can_certify, \
+ which + " key unexpectedly usable for certifications"
+
+ assert not subkey.secret, which + " key unexpectedly secret"
+ assert not subkey.is_cardkey, "Public key marked as card key"
+ assert not subkey.card_number, "Public key with card number set"
+ assert not subkey.pubkey_algo != (constants.PK_DSA if which == "Primary"
+ else constants.PK_ELG_E), \
+ which + " key has unexpected public key algo: {}".\
+ format(subkey.pubkey_algo)
+ assert subkey.length == 1024, \
+ which + " key has unexpected length: {}".format(subkey.length)
+ assert fpr.endswith(subkey.keyid), \
+ which + " key has unexpected key ID: {}".format(subkey.keyid)
+ assert which == "Secondary" or subkey.fpr == fpr, \
+ which + " key has unexpected fingerprint: {}".format(subkey.fpr)
+ assert not subkey.expires, \
+ which + " key unexpectedly expires: {}".format(subkey.expires)
+
+def check_uid(which, ref, uid):
+ assert not uid.revoked, which + " user ID unexpectedly revoked"
+ assert not uid.invalid, which + " user ID unexpectedly invalid"
+ assert uid.validity == (constants.VALIDITY_UNKNOWN
+ if uid.name.split()[0]
+ not in {'Alfa', 'Alpha', 'Alice'} else
+ constants.VALIDITY_ULTIMATE), \
+ which + " user ID has unexpectedly validity: {}".format(uid.validity)
+ assert not uid.signatures, which + " user ID unexpectedly signed"
+ assert uid.name == ref[0], \
+ "Unexpected name in {} user ID: {!r}".format(which.lower(), uid.name)
+ assert uid.comment == ref[1], \
+ "Unexpected comment in {} user ID: {!r}".format(which.lower(),
+ uid.comment)
+ assert uid.email == ref[2], \
+ "Unexpected email in {} user ID: {!r}".format(which.lower(), uid.email)
+
+i = 0
+c.op_keylist_start(None, False)
+key = c.op_keylist_next ()
+while key:
+ try:
+ if len(keys[i]) == 4:
+ fpr, sec_keyid, uids, n_subkeys = keys[i]
+ misc_check = None
+ else:
+ fpr, sec_keyid, uids, n_subkeys, misc_check = keys[i]
+ except IndexError:
+ # There are more keys. We don't check for that.
+ break
+
+ # Global key flags.
+ check_global(key, uids, n_subkeys)
+ check_subkey(fpr, "Primary", key.subkeys[0])
+ check_subkey(sec_keyid, "Secondary", key.subkeys[1])
+
+ assert len(key.uids) == len(uids)
+ check_uid("First", uids[0], key.uids[0])
+ if len(key.uids) > 1:
+ check_uid("Second", uids[1], key.uids[1])
+ if len(key.uids) > 2:
+ check_uid("Third", uids[2], key.uids[2])
+
+ if misc_check:
+ misc_check (uids[0][0], key)
+ key = c.op_keylist_next ()
+ i += 1
+
+c.op_keylist_end()
+result = c.op_keylist_result()
+assert not result.truncated, "Key listing unexpectedly truncated"
+
+
+for i, key in enumerate(c.keylist()):
+ try:
+ if len(keys[i]) == 4:
+ fpr, sec_keyid, uids, n_subkeys = keys[i]
+ misc_check = None
+ else:
+ fpr, sec_keyid, uids, n_subkeys, misc_check = keys[i]
+ except IndexError:
+ # There are more keys. We don't check for that.
+ break
+
+ # Global key flags.
+ check_global(key, uids, n_subkeys)
+ check_subkey(fpr, "Primary", key.subkeys[0])
+ check_subkey(sec_keyid, "Secondary", key.subkeys[1])
+
+ assert len(key.uids) == len(uids)
+ check_uid("First", uids[0], key.uids[0])
+ if len(key.uids) > 1:
+ check_uid("Second", uids[1], key.uids[1])
+ if len(key.uids) > 2:
+ check_uid("Third", uids[2], key.uids[2])
+
+ if misc_check:
+ misc_check (uids[0][0], key)
diff --git a/lang/python/tests/t-protocol-assuan.py b/lang/python/tests/t-protocol-assuan.py
new file mode 100755
index 0000000..172c7d0
--- /dev/null
+++ b/lang/python/tests/t-protocol-assuan.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python
+
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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
+
+import pyme
+
+with pyme.Context(protocol=pyme.constants.PROTOCOL_ASSUAN) as c:
+ # Do nothing.
+ c.assuan_transact('nop')
+ c.assuan_transact('NOP')
+ c.assuan_transact(['NOP'])
+
+ err = c.assuan_transact('idontexist')
+ assert err.getsource() == pyme.errors.SOURCE_GPGAGENT
+ assert err.getcode() == pyme.errors.ASS_UNKNOWN_CMD
+
+ # Invoke the pinentry to get a confirmation.
+ c.assuan_transact(['GET_CONFIRMATION', 'Hello there'])
+
+ data = []
+ def data_cb(line):
+ data.append(line)
+
+ err = c.assuan_transact(['GETINFO', 'version'], data_cb=data_cb)
+ assert not err
+ assert len(data) == 1
+
+ data = []
+ err = c.assuan_transact(['GETINFO', 's2k_count'], data_cb=data_cb)
+ if not err:
+ assert len(data) == 1
+ assert int(data[0]) > 0
+
+ # XXX HELP sends status lines if we could use ASSUAN_CONVEY_COMMENTS.
+
+ status = []
+ def status_cb(line, args):
+ status.append((line, args))
+
+ alphas_grip = '76F7E2B35832976B50A27A282D9B87E44577EB66'
+ err = c.assuan_transact(['KEYINFO', alphas_grip], status_cb=status_cb)
+ if not err:
+ assert len(status) == 1
+ line, args = status[0]
+ assert line.startswith('KEYINFO')
+ assert args.startswith(alphas_grip)
+
+ # XXX: test these callbacks, e.g. using PRESET_PASSPHRASE
+ # XXX: once issue2428 is resolved
+ def inq_cb(name, args):
+ print("inq_cb", name, args)
diff --git a/lang/python/tests/t-sig-notation.py b/lang/python/tests/t-sig-notation.py
new file mode 100755
index 0000000..777bc0b
--- /dev/null
+++ b/lang/python/tests/t-sig-notation.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python
+
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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
+
+import os
+from pyme import core, constants
+import support
+
+expected_notations = {
+ "laughing@me": ("Just Squeeze Me", constants.SIG_NOTATION_HUMAN_READABLE),
+ "preferred-email-encoding@pgp.com": ("pgpmime",
+ constants.SIG_NOTATION_HUMAN_READABLE
+ | constants.SIG_NOTATION_CRITICAL),
+ None: ("http://www.gnu.org/policy/", 0),
+}
+
+# GnuPG prior to 2.1.13 did not report the critical flag correctly.
+with core.Context() as c:
+ version = c.engine_info.version
+ have_correct_sig_data = not (version.startswith("1.")
+ or version == "2.1.1"
+ or (version.startswith("2.1.1")
+ and version[5] < '3'))
+
+def check_result(result):
+ assert len(result.signatures) == 1, "Unexpected number of signatures"
+ sig = result.signatures[0]
+ assert len(sig.notations) == len(expected_notations)
+
+ for r in sig.notations:
+ assert not 'name_len' in dir(r)
+ assert not 'value_len' in dir(r)
+ assert r.name in expected_notations
+ value, flags = expected_notations.pop(r.name)
+
+ assert r.value == value, \
+ "Expected {!r}, got {!r}".format(value, r.value)
+ assert r.human_readable \
+ == bool(flags&constants.SIG_NOTATION_HUMAN_READABLE)
+ assert r.critical \
+ == (bool(flags&constants.SIG_NOTATION_CRITICAL)
+ if have_correct_sig_data else False)
+
+ assert len(expected_notations) == 0
+
+support.init_gpgme(constants.PROTOCOL_OpenPGP)
+
+source = core.Data("Hallo Leute\n")
+signed = core.Data()
+
+c = core.Context()
+for name, (value, flags) in expected_notations.items():
+ c.sig_notation_add(name, value, flags)
+
+c.op_sign(source, signed, constants.SIG_MODE_NORMAL)
+
+signed.seek(0, os.SEEK_SET)
+sink = core.Data()
+c.op_verify(signed, None, sink)
+result = c.op_verify_result()
+check_result(result)
diff --git a/lang/python/tests/t-sign.py b/lang/python/tests/t-sign.py
new file mode 100755
index 0000000..b0e211a
--- /dev/null
+++ b/lang/python/tests/t-sign.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python
+
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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
+
+import os
+import pyme
+from pyme import core, constants
+import support
+
+def fail(msg):
+ raise RuntimeError(msg)
+
+def check_result(r, typ):
+ if r.invalid_signers:
+ fail("Invalid signer found: {}".format(r.invalid_signers.fpr))
+
+ if len(r.signatures) != 1:
+ fail("Unexpected number of signatures created")
+
+ signature = r.signatures[0]
+ if signature.type != typ:
+ fail("Wrong type of signature created")
+
+ if signature.pubkey_algo != constants.PK_DSA:
+ fail("Wrong pubkey algorithm reported: {}".format(
+ signature.pubkey_algo))
+
+ if signature.hash_algo != constants.MD_SHA1:
+ fail("Wrong hash algorithm reported: {}".format(
+ signature.hash_algo))
+
+ if signature.sig_class != 1:
+ fail("Wrong signature class reported: {}".format(
+ signature.sig_class))
+
+ if signature.fpr != "A0FF4590BB6122EDEF6E3C542D727CC768697734":
+ fail("Wrong fingerprint reported: {}".format(signature.fpr))
+
+
+support.init_gpgme(constants.PROTOCOL_OpenPGP)
+c = core.Context()
+c.set_textmode(True)
+c.set_armor(True)
+
+source = core.Data("Hallo Leute\n")
+sink = core.Data()
+
+c.op_sign(source, sink, constants.SIG_MODE_NORMAL)
+
+result = c.op_sign_result()
+check_result(result, constants.SIG_MODE_NORMAL)
+support.print_data(sink)
+
+# Now a detached signature.
+source.seek(0, os.SEEK_SET)
+sink = core.Data()
+
+c.op_sign(source, sink, constants.SIG_MODE_DETACH)
+
+result = c.op_sign_result()
+check_result(result, constants.SIG_MODE_DETACH)
+support.print_data(sink)
+
+# And finally a cleartext signature. */
+source.seek(0, os.SEEK_SET)
+sink = core.Data()
+
+c.op_sign(source, sink, constants.SIG_MODE_CLEAR)
+
+result = c.op_sign_result()
+check_result(result, constants.SIG_MODE_CLEAR)
+support.print_data(sink)
+
+# Idiomatic interface.
+with pyme.Context(armor=True, textmode=True) as c:
+ message = "Hallo Leute\n".encode()
+ signed, _ = c.sign(message)
+ assert len(signed) > 0
+ assert signed.find(b'BEGIN PGP MESSAGE') > 0, 'Message not found'
+
+ signed, _ = c.sign(message, mode=pyme.constants.SIG_MODE_DETACH)
+ assert len(signed) > 0
+ assert signed.find(b'BEGIN PGP SIGNATURE') > 0, 'Signature not found'
+
+ signed, _ = c.sign(message, mode=pyme.constants.SIG_MODE_CLEAR)
+ assert len(signed) > 0
+ assert signed.find(b'BEGIN PGP SIGNED MESSAGE') > 0, 'Message not found'
+ assert signed.find(message) > 0, 'Message content not found'
+ assert signed.find(b'BEGIN PGP SIGNATURE') > 0, 'Signature not found'
+
+with pyme.Context() as c:
+ message = "Hallo Leute\n".encode()
+
+ c.signers = [c.get_key(support.sign_only, True)]
+ c.sign(message)
+
+ c.signers = [c.get_key(support.encrypt_only, True)]
+ try:
+ c.sign(message)
+ except pyme.errors.InvalidSigners as e:
+ assert len(e.signers) == 1
+ assert support.encrypt_only.endswith(e.signers[0].fpr)
+ else:
+ assert False, "Expected an InvalidSigners error, got none"
diff --git a/lang/python/tests/t-signers.py b/lang/python/tests/t-signers.py
new file mode 100755
index 0000000..11403af
--- /dev/null
+++ b/lang/python/tests/t-signers.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python
+
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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
+
+import pyme
+from pyme import core, constants
+import support
+
+def fail(msg):
+ raise RuntimeError(msg)
+
+def check_result(r, typ):
+ if r.invalid_signers:
+ fail("Invalid signer found: {}".format(r.invalid_signers.fpr))
+
+ if len(r.signatures) != 2:
+ fail("Unexpected number of signatures created")
+
+ for signature in r.signatures:
+ if signature.type != typ:
+ fail("Wrong type of signature created")
+
+ if signature.pubkey_algo != constants.PK_DSA:
+ fail("Wrong pubkey algorithm reported: {}".format(
+ signature.pubkey_algo))
+
+ if signature.hash_algo != constants.MD_SHA1:
+ fail("Wrong hash algorithm reported: {}".format(
+ signature.hash_algo))
+
+ if signature.sig_class != 1:
+ fail("Wrong signature class reported: got {}, want {}".format(
+ signature.sig_class, 1))
+
+ if signature.fpr not in ("A0FF4590BB6122EDEF6E3C542D727CC768697734",
+ "23FD347A419429BACCD5E72D6BC4778054ACD246"):
+ fail("Wrong fingerprint reported: {}".format(signature.fpr))
+
+
+support.init_gpgme(constants.PROTOCOL_OpenPGP)
+c = core.Context()
+c.set_textmode(True)
+c.set_armor(True)
+
+keys = []
+c.op_keylist_start('', True)
+keys.append(c.op_keylist_next())
+keys.append(c.op_keylist_next())
+c.op_keylist_end()
+
+c.signers_add(keys[0])
+c.signers_add(keys[1])
+
+for mode in (constants.SIG_MODE_NORMAL, constants.SIG_MODE_DETACH,
+ constants.SIG_MODE_CLEAR):
+ source = core.Data("Hallo Leute\n")
+ sink = core.Data()
+
+ c.op_sign(source, sink, mode)
+
+ result = c.op_sign_result()
+ check_result(result, mode)
+ support.print_data(sink)
+
+# Idiomatic interface.
+with pyme.Context(armor=True, textmode=True, signers=keys) as c:
+ message = "Hallo Leute\n".encode()
+ signed, result = c.sign(message)
+ check_result(result, constants.SIG_MODE_NORMAL)
+ assert signed.find(b'BEGIN PGP MESSAGE') > 0, 'Message not found'
+
+ signed, result = c.sign(message, mode=constants.SIG_MODE_DETACH)
+ check_result(result, constants.SIG_MODE_DETACH)
+ assert signed.find(b'BEGIN PGP SIGNATURE') > 0, 'Signature not found'
+
+ signed, result = c.sign(message, mode=constants.SIG_MODE_CLEAR)
+ check_result(result, constants.SIG_MODE_CLEAR)
+ assert signed.find(b'BEGIN PGP SIGNED MESSAGE') > 0, 'Message not found'
+ assert signed.find(message) > 0, 'Message content not found'
+ assert signed.find(b'BEGIN PGP SIGNATURE') > 0, 'Signature not found'
diff --git a/lang/python/tests/t-trustlist.py b/lang/python/tests/t-trustlist.py
new file mode 100755
index 0000000..4253bd7
--- /dev/null
+++ b/lang/python/tests/t-trustlist.py
@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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 pyme import core, constants
+import support
+
+support.init_gpgme(constants.PROTOCOL_OpenPGP)
+c = core.Context()
+
+def dump_item(item):
+ print("l={} k={} t={} o={} v={} u={}".format(
+ item.level, item.keyid, item.type, item.owner_trust,
+ item.validity, item.name))
+
+c.op_trustlist_start("alice", 0)
+while True:
+ item = c.op_trustlist_next()
+ if not item:
+ break
+ dump_item(item)
+c.op_trustlist_end()
+
+for item in c.op_trustlist_all("alice", 0):
+ dump_item(item)
diff --git a/lang/python/tests/t-verify.py b/lang/python/tests/t-verify.py
new file mode 100755
index 0000000..39f6176
--- /dev/null
+++ b/lang/python/tests/t-verify.py
@@ -0,0 +1,195 @@
+#!/usr/bin/env python
+
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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
+
+import sys
+import os
+import pyme
+from pyme import core, constants, errors
+import support
+
+test_text1 = b"Just GNU it!\n"
+test_text1f= b"Just GNU it?\n"
+test_sig1 = b"""-----BEGIN PGP SIGNATURE-----
+
+iN0EABECAJ0FAjoS+i9FFIAAAAAAAwA5YmFyw7bDpMO8w58gZGFzIHdhcmVuIFVt
+bGF1dGUgdW5kIGpldHp0IGVpbiBwcm96ZW50JS1aZWljaGVuNRSAAAAAAAgAJGZv
+b2Jhci4xdGhpcyBpcyBhIG5vdGF0aW9uIGRhdGEgd2l0aCAyIGxpbmVzGhpodHRw
+Oi8vd3d3Lmd1Lm9yZy9wb2xpY3kvAAoJEC1yfMdoaXc0JBIAoIiLlUsvpMDOyGEc
+dADGKXF/Hcb+AKCJWPphZCphduxSvrzH0hgzHdeQaA==
+=nts1
+-----END PGP SIGNATURE-----
+"""
+
+test_sig2 = b"""-----BEGIN PGP MESSAGE-----
+
+owGbwMvMwCSoW1RzPCOz3IRxjXQSR0lqcYleSUWJTZOvjVdpcYmCu1+oQmaJIleH
+GwuDIBMDGysTSIqBi1MApi+nlGGuwDeHao53HBr+FoVGP3xX+kvuu9fCMJvl6IOf
+y1kvP4y+8D5a11ang0udywsA
+=Crq6
+-----END PGP MESSAGE-----
+"""
+
+# A message with a prepended but unsigned plaintext packet.
+double_plaintext_sig = b"""-----BEGIN PGP MESSAGE-----
+
+rDRiCmZvb2Jhci50eHRF4pxNVGhpcyBpcyBteSBzbmVha3kgcGxhaW50ZXh0IG1l
+c3NhZ2UKowGbwMvMwCSoW1RzPCOz3IRxTWISa6JebnG666MFD1wzSzJSixQ81XMV
+UlITUxTyixRyKxXKE0uSMxQyEosVikvyCwpSU/S4FNCArq6Ce1F+aXJGvoJvYlGF
+erFCTmJxiUJ5flFKMVeHGwuDIBMDGysTyA4GLk4BmO036xgWzMgzt9V85jCtfDFn
+UqVooWlGXHwNw/xg/fVzt9VNbtjtJ/fhUqYo0/LyCGEA
+=6+AK
+-----END PGP MESSAGE-----
+"""
+
+def check_result(result, summary, validity, fpr, status, notation):
+ assert len(result.signatures) == 1, "Unexpected number of signatures"
+ sig = result.signatures[0]
+ assert sig.summary == summary, \
+ "Unexpected signature summary: {}, want: {}".format(sig.summary,
+ summary)
+ assert sig.fpr == fpr
+ assert errors.GPGMEError(sig.status).getcode() == status
+
+ if notation:
+ expected_notations = {
+ "bar": (b"\xc3\xb6\xc3\xa4\xc3\xbc\xc3\x9f" +
+ b" das waren Umlaute und jetzt ein prozent%-Zeichen"
+ if sys.version_info[0] < 3 else
+ b"\xc3\xb6\xc3\xa4\xc3\xbc\xc3\x9f".decode() +
+ " das waren Umlaute und jetzt ein prozent%-Zeichen"),
+ "foobar.1": "this is a notation data with 2 lines",
+ None: "http://www.gu.org/policy/",
+ }
+ assert len(sig.notations) == len(expected_notations)
+
+ for r in sig.notations:
+ assert not 'name_len' in dir(r)
+ assert not 'value_len' in dir(r)
+ assert r.name in expected_notations
+ assert r.value == expected_notations[r.name], \
+ "Expected {!r}, got {!r}".format(expected_notations[r.name],
+ r.value)
+ expected_notations.pop(r.name)
+
+ assert len(expected_notations) == 0
+
+ assert not sig.wrong_key_usage
+ assert sig.validity == validity, \
+ "Unexpected signature validity: {}, want: {}".format(
+ sig.validity, validity)
+ assert errors.GPGMEError(sig.validity_reason).getcode() == errors.NO_ERROR
+
+
+support.init_gpgme(constants.PROTOCOL_OpenPGP)
+c = core.Context()
+c.set_armor(True)
+
+# Checking a valid message.
+text = core.Data(test_text1)
+sig = core.Data(test_sig1)
+c.op_verify(sig, text, None)
+result = c.op_verify_result()
+check_result(result, constants.SIGSUM_VALID | constants.SIGSUM_GREEN,
+ constants.VALIDITY_FULL,
+ "A0FF4590BB6122EDEF6E3C542D727CC768697734",
+ errors.NO_ERROR, True)
+
+
+# Checking a manipulated message.
+text = core.Data(test_text1f)
+sig.seek(0, os.SEEK_SET)
+c.op_verify(sig, text, None)
+result = c.op_verify_result()
+check_result(result, constants.SIGSUM_RED, constants.VALIDITY_UNKNOWN,
+ "2D727CC768697734", errors.BAD_SIGNATURE, False)
+
+# Checking a normal signature.
+text = core.Data()
+sig = core.Data(test_sig2)
+c.op_verify(sig, None, text)
+result = c.op_verify_result()
+check_result(result, constants.SIGSUM_VALID | constants.SIGSUM_GREEN,
+ constants.VALIDITY_FULL,
+ "A0FF4590BB6122EDEF6E3C542D727CC768697734",
+ errors.NO_ERROR, False)
+
+# Checking an invalid message.
+text = core.Data()
+sig = core.Data(double_plaintext_sig)
+try:
+ c.op_verify(sig, None, text)
+except Exception as e:
+ assert type(e) == errors.GPGMEError
+ assert e.getcode() == errors.BAD_DATA
+else:
+ assert False, "Expected an error but got none."
+
+
+# Idiomatic interface.
+with pyme.Context(armor=True) as c:
+ # Checking a valid message.
+ _, result = c.verify(test_text1, test_sig1)
+ check_result(result, constants.SIGSUM_VALID | constants.SIGSUM_GREEN,
+ constants.VALIDITY_FULL,
+ "A0FF4590BB6122EDEF6E3C542D727CC768697734",
+ errors.NO_ERROR, True)
+
+ # Checking a manipulated message.
+ try:
+ c.verify(test_text1f, test_sig1)
+ except errors.BadSignatures as e:
+ check_result(e.result, constants.SIGSUM_RED,
+ constants.VALIDITY_UNKNOWN,
+ "2D727CC768697734", errors.BAD_SIGNATURE, False)
+ else:
+ assert False, "Expected an error but got none."
+
+ # Checking a normal signature.
+ sig = core.Data(test_sig2)
+ data, result = c.verify(test_sig2)
+ check_result(result, constants.SIGSUM_VALID | constants.SIGSUM_GREEN,
+ constants.VALIDITY_FULL,
+ "A0FF4590BB6122EDEF6E3C542D727CC768697734",
+ errors.NO_ERROR, False)
+ assert data == test_text1
+
+ # Checking an invalid message.
+ try:
+ c.verify(double_plaintext_sig)
+ except errors.GPGMEError as e:
+ assert e.getcode() == errors.BAD_DATA
+ else:
+ assert False, "Expected an error but got none."
+
+ alpha = c.get_key("A0FF4590BB6122EDEF6E3C542D727CC768697734", False)
+ bob = c.get_key("D695676BDCEDCC2CDD6152BCFE180B1DA9E3B0B2", False)
+
+ # Checking a valid message.
+ c.verify(test_text1, test_sig1, verify=[alpha])
+
+ try:
+ c.verify(test_text1, test_sig1, verify=[alpha, bob])
+ except errors.MissingSignatures as e:
+ assert len(e.missing) == 1
+ assert e.missing[0] == bob
+ else:
+ assert False, "Expected an error, got none"
diff --git a/lang/python/tests/t-wait.py b/lang/python/tests/t-wait.py
new file mode 100755
index 0000000..b7d9a34
--- /dev/null
+++ b/lang/python/tests/t-wait.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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
+
+import time
+from pyme import core, constants, errors
+import support
+
+support.init_gpgme(constants.PROTOCOL_OpenPGP)
+c = core.Context()
+c.set_armor(True)
+
+# Checking a message without a signature.
+sig = core.Data("foo\n")
+text = core.Data()
+c.op_verify_start(sig, None, text)
+
+try:
+ while True:
+ err = c.wait(False)
+ if err:
+ break
+ time.sleep(0.1)
+except Exception as e:
+ assert e.getcode() == errors.NO_DATA
+else:
+ assert False, "Expected an error, got none"
diff --git a/lang/python/tests/t-wrapper.py b/lang/python/tests/t-wrapper.py
new file mode 100755
index 0000000..d260264
--- /dev/null
+++ b/lang/python/tests/t-wrapper.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+
+# 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 General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# GPGME is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 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 pyme import core
+
+d0 = core.Data()
+d0.seek # trigger on-demand-wrapping
+assert d0.seek == d0.seek, "Generated wrapper functions are not cached"
+assert hasattr(core.Data, 'seek'), "Generated wrapper functions are not shared"