summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog255
-rw-r--r--NEWS31
-rw-r--r--VERSION2
-rwxr-xr-xconfigure76
-rw-r--r--configure.ac48
-rw-r--r--gpgme.spec2
-rw-r--r--lang/cl/gpgme.asd2
-rw-r--r--lang/cpp/src/context.cpp5
-rw-r--r--lang/cpp/src/global.h3
-rw-r--r--lang/cpp/src/gpggencardkeyinteractor.cpp2
-rw-r--r--lang/python/Makefile.am7
-rw-r--r--lang/python/Makefile.in7
-rw-r--r--lang/python/doc/Makefile.am48
-rw-r--r--lang/python/doc/Makefile.in574
-rw-r--r--lang/python/doc/src/gpgme-python-howto.pdfbin52288 -> 0 bytes
-rw-r--r--lang/python/doc/src/gpgme-python-howto.tex3110
-rw-r--r--lang/python/doc/src/gpgme-python-howto.tex~3110
-rw-r--r--lang/python/doc/texinfo/gpgme-python-howto.aux0
-rw-r--r--lang/python/doc/texinfo/gpgme-python-howto.log519
-rw-r--r--lang/python/doc/texinfo/texput.log13
-rw-r--r--lang/python/examples/Makefile.am74
-rw-r--r--lang/python/examples/Makefile.in600
-rw-r--r--lang/python/src/Makefile.am47
-rw-r--r--lang/python/src/Makefile.in573
-rw-r--r--lang/python/src/core.py37
-rw-r--r--lang/python/src/errors.py6
-rw-r--r--lang/python/tests/Makefile.am5
-rw-r--r--lang/python/tests/Makefile.in59
-rwxr-xr-xlang/python/tests/t-decrypt-verify.py12
-rwxr-xr-xlang/python/tests/t-decrypt.py10
-rw-r--r--src/assuan-support.c4
-rw-r--r--src/cJSON.c48
-rw-r--r--src/data-compat.c2
-rw-r--r--src/data-fd.c2
-rw-r--r--src/data-mem.c2
-rw-r--r--src/data.c4
-rw-r--r--src/debug.c245
-rw-r--r--src/debug.h40
-rw-r--r--src/dirinfo.c22
-rw-r--r--src/engine-assuan.c2
-rw-r--r--src/engine-g13.c2
-rw-r--r--src/engine-gpg.c2
-rw-r--r--src/engine-gpgsm.c46
-rw-r--r--src/engine-uiserver.c2
-rw-r--r--src/engine.c1
-rw-r--r--src/posix-io.c144
-rw-r--r--src/posix-util.c9
-rw-r--r--src/sys-util.h13
-rw-r--r--src/version.c6
-rw-r--r--src/w32-glib-io.c21
-rw-r--r--src/w32-io.c53
-rw-r--r--src/w32-util.c143
-rw-r--r--src/wait.c15
-rw-r--r--tests/gpg/Makefile.am1
-rw-r--r--tests/gpg/Makefile.in1
-rw-r--r--tests/gpg/cipher-3.asc18
-rw-r--r--tests/gpg/cipher-no-sig.asc13
-rw-r--r--tests/run-import.c17
-rw-r--r--tests/run-threaded.c142
59 files changed, 3081 insertions, 7176 deletions
diff --git a/ChangeLog b/ChangeLog
index ae7ffe0..f5df68b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,258 @@
+2019-06-13 Werner Koch <wk@gnupg.org>
+
+ Release 1.13.1.
+ + commit ea11c2a13cd44caf0bab395bd5132bf232318ad8
+ * configure.ac: Bump LT versions to c=C33/A22/R1 cpp=C16/A10/R0
+ qt=C10/A3/R4.
+
+ python: Fix regression in t-decrypt-verify test.
+ + commit 7d0a979c07d2a32c1e39a9403f009cbe026f77ff
+ * lang/python/tests/t-decrypt-verify.py: Comment recent changes.
+
+ python: Set a default-key into gpg.conf for the tests.
+ + commit ad1395f210f3a3d5839f482f1933eac80a94d174
+ * lang/python/tests/Makefile.am (gpg.conf): Set a default key.
+
+ core: At debug levels up to 9 print only an ascii dump.
+ + commit 4f11210b21a1914a1daf67474e9b82084b2cac01
+ * src/debug.c (_gpgme_debug_buffer): Switch between two output
+ formats.
+
+2019-06-06 Werner Koch <wk@gnupg.org>
+
+ tests: Minor fix to run-threaded.c.
+ + commit f6fd90c99aea8b604d76ae49d29bc5269c236a98
+ * tests/run-threaded.c (random_data_close): Correct FD test.
+
+ core: Add commented debug helper to posix-io.c.
+ + commit f84abcad253ace782708c05760e52793814896f4
+ * src/posix-io.c (_gpgme_is_fd_valid): New out-commented function.
+
+ core: Fix error return value of _gpgme_run_io_cb.
+ + commit 6b9ff1ba391a4364b37cb116748194f3f33b4f12
+ * src/wait.c (_gpgme_run_io_cb): Fix return code.
+
+2019-06-06 Andre Heinecke <aheinecke@gnupg.org>
+
+ tests: Add option "allow-del" to run-threaded.
+ + commit e6f28273062ff26344163e219c1b784ae99de980
+ * tests/run-threaded.c (main): Handle allow-del.
+ (allow-del): New. Variable to allow deletion of keys.
+ (import): Delete key after import if allow-del is set.
+ (delete_impres): Delete keys from an import result.
+ (delete_fpr): Delete a key by fingerprint.
+
+2019-06-05 Andre Heinecke <aheinecke@gnupg.org>
+
+ tests: Implement import in run-threaded.
+ + commit 024a7f75d4b8e017b92a13552b23e7fb1d22a5ce
+ * tests/run-threaded.c (import): New.
+ (do_data_op): Call it.
+
+2019-06-05 Werner Koch <wk@gnupg.org>
+
+ core: Prettify _gpgme_io_select debug output again and fix TRACE_SYSRES.
+ + commit 0f68c9f16bdae7295cac4cbf3c9a197840989a85
+ * src/debug.c (_gpgme_debug): Take better care of NULL userinfo.
+ (_gpgme_debug_end): Rework.
+ (_trace_sysres): Print ERRNO and not the supplied RES.
+
+2019-06-05 Andre Heinecke <aheinecke@gnupg.org>
+
+ tests: Use synced output for error in run-threaded.
+ + commit 3a3648e3a56712261bda4fa866bd2419a740cec8
+ * tests/run-threaded.c (do_data_op): Use synced out macro.
+
+ tests: Avoid variable named the same as a macro.
+ + commit 3a11421d0f63b8cb5afa407cd65f763b11033241
+ * tests/run-threaded.c (decrypt, verify): Don't use a variable name
+ that is also a macro.
+
+ tests,w32: Fix thread creation in run-threaded.
+ + commit 9bbe15ebbc41533fa219d5b3017a26a75bc72731
+ * tests/run-threaded.c (create_thread): Check proper return
+ value and not return value of CloseHandle.
+
+2019-06-05 Werner Koch <wk@gnupg.org>
+
+ core: Improve the debug messages even more.
+ + commit 8f9f3224aac78ce9d54e19e73acf7ab659787168
+ * src/debug.c (_gpgme_debug): Add arg LINE. Chnage all callers.
+ (_gpgme_debug_begin): Remove.
+ * src/debug.h (TRACE_SEQ): Use the LINE arg of _gpgme_debug.
+
+ core: Avoid explicit locks in the debug code.
+ + commit 856d2e8d64f668855b1c22d3d38fe783904c6c48
+ * src/debug.c (debug_lock): Remove. Also remove all users.
+ (_gpgme_debug): Use gpgrt_bsprintf to prepare the output and finally
+ print using standard fprintf. Reformat to prefix to be narrower.
+
+2019-06-04 Werner Koch <wk@gnupg.org>
+
+ json: Print "nan", "-inf", "inf" if needed.
+ + commit f56c996318dfa1c9f0d10582b18c2fce10d24668
+ * src/cJSON.c (print_number): Print NaN and INF.
+
+ json: Improve handling of large exponents in the JSON parsor.
+ + commit fabe96126b4ed9c5e07b713813d7db26a02e5e5f
+ * src/cJSON.c: Include stdint.h.
+ (parse_number): Avoid overflob in SUBSCALE and cap integer values.
+
+ core: Implement recpstring option parsing for gpgsm.
+ + commit 1024884e07f750ce781fa74dffa62e126bdda622
+ * src/engine-gpg.c (append_args_from_recipients_string): Detect bad
+ options.
+ * src/engine-gpgsm.c (set_recipients_from_string): Implement option
+ parsing.
+
+ core: Make gpgme_op_encrypt_ext work for CMS.
+ + commit e9ca36f876e1066227668981f5a9e1a7f1031d9e
+ * src/engine-gpgsm.c (gpgsm_encrypt): Fix argument check.
+
+2019-06-04 NIIBE Yutaka <gniibe@fsij.org>
+
+ python: Fix typo in DecryptionError exception.
+ + commit 47135ffdb923de66bc275a37e31811ad22c73dd7
+ * lang/python/src/errors.py (DecryptionError): Rename from
+ DeryptionError.
+ (UnsupportedAlgorithm): Use DecryptionError.
+
+2019-06-03 Andre Heinecke <aheinecke@gnupg.org>
+
+ Add NEWS for 1.13.1.
+ + commit 49883023f661a18d73f9f2c7a3a98902af62ef6b
+ * NEWS: Add news for 1.13.1
+
+2019-05-06 Andre Heinecke <aheinecke@gnupg.org>
+
+ python: Make EXTRA_DIST files explicit.
+ + commit f9c923bb2d87711235312e8085964372d7480ce4
+ * configure.ac: Configure new Makefiles.
+ * lang/python/Makefile.am: Remove dirs from extra dist and use
+ subdirs.
+ * lang/python/examples/Makefile.am, lang/python/src/Makefile.am,
+ lang/python/doc/Makefile.am: New. Files that list EXTRA_DIST files.
+
+ Python, doc: Minor style improvement.
+ + commit 63055f13407760c877e5a3a94e564dfb3077dd47
+ * lang/python/src/core.py (Context): Retab and shorten
+ max line length.
+
+ Merge remote-tracking branch 'origin/dkg/fix-T4276'
+ + commit f303806a110a9813023a5fd9468a55ab0f7fb39d
+
+
+2019-05-03 Andre Heinecke <aheinecke@gnupg.org>
+
+ Always use maintainer mode -Wno cflags.
+ + commit 33b13d1c290d7ce35c636cce4265b512fb8e64c3
+ * configure.ac (CFLAGS): Move -Wno flags out of the maintainer mode.
+
+ cpp: Fix initialization warning.
+ + commit 0ed81498147723f0e9282df5ae08b8b8e40edd2f
+ * lanc/cpp/src/gpggencardkeyinteractor.cpp
+ (GpgGenCardKeyInteractor::Private): Fix initialization warning.
+
+2019-05-03 Daniel Kahn Gillmor <dkg@fifthhorseman.net>
+
+ python: stop raising BadSignatures from decrypt(verify=True)
+ + commit 4100794e305ba22241ea5a4f7b42bb5189fbd948
+ * src/core.py (decrypt): filter out signatures with errors from the
+ returned verify_result, but avoid raising BadSignatures
+ * tests/t-decrypt-verify.py: ensure that only a single signature is
+ returned when evaluating cipher-3.asc, since the other signature is
+ unknown.
+
+ python/tests: try to decrypt and verify new test data.
+ + commit bd2d282e572b5d02669238c9e087259b85638477
+ * lang/python/tests/t-decrypt.py: test decryption of cipher-3.asc and
+ cipher-no-sig.asc
+ * lang/python/tests/t-decrypt-verify.py: test decryption and
+ verification of cipher-3.asc and cipher-no-sig.asc
+
+ tests: add two new types of encrypted data.
+ + commit c5c3a9d10be415ea7bc0cd9730ad6085f16ee7a0
+ * tests/gpg/cipher-3.asc: add an encrypted file containing signatures
+ (one from a known key, and one from an unknown key)
+ * tests/gpg/cipher-no-sig.asc: add an encrypted file containing no
+ signatures at all
+
+ python: make it easier to run a limited number of tests.
+ + commit 30bd1c097544376f257d426d5feb4706fb5d3afd
+ * lang/python/tests/Makefile.am: prefer py_tests from the environment
+ if present.
+
+2019-05-02 Andre Heinecke <aheinecke@gnupg.org>
+
+ tests: Add cms mode to run-import.
+ + commit 4746c5c9e2dd9a3ee471a429c19bc1f7fd1d07db
+ * tests/run-import.c (show_usage): Add doc for cms / openpgp.
+ (main): Handle protocol.
+
+2019-04-24 Andre Heinecke <aheinecke@gnupg.org>
+
+ cpp: Add wrapper for gpgme_set_global_flag.
+ + commit 7981ec4147f3058d5b56905903456247993dc6f7
+ * lang/cpp/src/context.cpp (setGlobalFlag): New.
+ * lang/cpp/src/global.h (setGlobalFlag): Export it.
+
+2019-04-19 NIIBE Yutaka <gniibe@fsij.org>
+
+ core: Fix duplication of close_notify_handler for gpgsm.
+ + commit 7673ef7953482f42cab50dca1810e5c9d10f461e
+ * src/engine-gpgsm.c [!USE_DESCRIPTOR_PASSING] (gpgsm_new): Remove
+ last call to _gpgme_io_set_close_notify.
+
+ core: Fix error return.
+ + commit 814f6c8de8006830f19a029a879bd92f9e584789
+ * src/engine.c (_gpgme_set_engine_info): Add error return.
+
+2019-04-09 Andre Heinecke <aheinecke@gnupg.org>
+
+ core,w32: Fix minor potential memleak.
+ + commit 140d694e1fddf16fa3fd2371b9a852ebb14622c8
+ * src/w32-util.c (_gpgme_create_process_utf8): Free converted
+ startup info strings.
+
+ core,w32: Fix new w32-util functions.
+ + commit ecbba12b869106ba03e10b7b7dd80f74d086831b
+ * src/w32-util.c (_gpgme_access): Respect mode parameter.
+ (_gpgme_create_process_utf8): Convert startupinfo, too.
+
+ core,w32: Improve handling of Unicode paths.
+ + commit a82e3a0ae57a48ba173e282a050680751006c074
+ * src/dirinfo.c (get_gpgconf_item): Use _gpgme_access.
+ * src/posix-util.c (_gpgme_access): Add forward to normal access.
+ * src/sys-util.h (_gpgme_access): New for posix and w32.
+ * src/w32-io.c (_gpgme_io_spawn): Use _gpgme_crate_process_utf8.
+ * src/w32-util.c (utf8_to_wchar, utf8_to_wchar0): The usual w32 conv.
+ (find_program_in_dir): Use _gpgme_access.
+ (find_program_at_standard_place): Use wchar API and convert to UTF-8.
+ (_gpgme_access): Convert UTF-8 to wchar and use wchar API.
+ (_gpgme_create_process_utf8): Convert UTF-8 to wchar and use wchar API.
+
+ core,w32: Show w32-spawn warning only once.
+ + commit 937adfdcbb22f715c5a331e5e2d4546ac15a1b7e
+ * src/w32-io.c (_gpgme_io_spawn): Show MessageBox only once.
+
+2019-03-27 Andre Heinecke <aheinecke@gnupg.org>
+
+ core, w32: Fix format string errors on windows.
+ + commit 4a4680f8901ecdcb7e8d5ed55f48226ccfccd7c8
+ * src/debug.c (_gpgme_debug): Use gpgrt_vasprintf instead of
+ vfprintf to have a more portable format.
+
+ core: Fix assuan logger-fd hack for windows.
+ + commit 19a4c4daa2cfd075b181d5131a4b8b4d54714b8c
+ * src/assuan-support.c (my_spawn): Zero is a perfectly fine fd.
+
+2019-03-26 Andre Heinecke <aheinecke@gnupg.org>
+
+ core,w32,glib: Fix build of w32-glib-io.c.
+ + commit 213c4bc1eb1f45695cc3955cc722ebb363dcbdd0
+ * src/w32-glib-io.c (_gpgme_io_pipe, _gpgme_io_connect): Do not
+ use TRACE_SUC in a return statement.
+
2019-03-26 Werner Koch <wk@gnupg.org>
Release GPGME 1.13.0.
diff --git a/NEWS b/NEWS
index fed626a..9a7eac0 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,34 @@
+Noteworthy changes in version 1.13.1 (2019-06-13)
+-------------------------------------------------
+
+ * cpp: gpgme_set_global_flag is now wrapped. [#4471]
+
+ * w32: Improved handling of unicode install paths. [#4453]
+
+ * w32: The gpgme_io_spawn error message is now only shown once. [#4453]
+
+ * Fixed a crash introduced in 1.13.0 when working with S/MIME. [#4556]
+
+ * w32: Fixed format string errors introduced in 1.13.0 that could
+ cause crashes. [#4440]
+
+ * w32: Fixed an error in the new diagnostic gpgsm support introduced
+ in 1.13.0 that caused crashes in low fd scenarios. [#4439]
+
+ * python: Fixed a DecryptionError Exception. [#4478]
+
+ * python: No longer raises BadSignatures from decrypt(verify=True).
+ [#4276]
+
+ * Interface changes relative to the 1.13.0 release:
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ cpp: setGlobalFlag NEW.
+
+ [c=C33/A22/R1 cpp=C16/A10/R0 qt=C10/A3/R4]
+
+ Release-info: https://dev.gnupg.org/T4551
+
+
Noteworthy changes in version 1.13.0 (2019-03-26)
-------------------------------------------------
diff --git a/VERSION b/VERSION
index feaae22..b50dd27 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.13.0
+1.13.1
diff --git a/configure b/configure
index 0e3fb7c..e2318a3 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for gpgme 1.13.0.
+# Generated by GNU Autoconf 2.69 for gpgme 1.13.1.
#
# Report bugs to <https://bugs.gnupg.org>.
#
@@ -590,8 +590,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='gpgme'
PACKAGE_TARNAME='gpgme'
-PACKAGE_VERSION='1.13.0'
-PACKAGE_STRING='gpgme 1.13.0'
+PACKAGE_VERSION='1.13.1'
+PACKAGE_STRING='gpgme 1.13.1'
PACKAGE_BUGREPORT='https://bugs.gnupg.org'
PACKAGE_URL=''
@@ -1458,7 +1458,7 @@ if test "$ac_init_help" = "long"; then
# Omit some internal or obsolete options to make the list less imposing.
# This message is too long to be a string in the A/UX 3.1 sh.
cat <<_ACEOF
-\`configure' configures gpgme 1.13.0 to adapt to many kinds of systems.
+\`configure' configures gpgme 1.13.1 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1528,7 +1528,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of gpgme 1.13.0:";;
+ short | recursive ) echo "Configuration of gpgme 1.13.1:";;
esac
cat <<\_ACEOF
@@ -1678,7 +1678,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-gpgme configure 1.13.0
+gpgme configure 1.13.1
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -2508,7 +2508,7 @@ cat >config.log <<_ACEOF
This file contains any messages produced by compilers while
running configure, to aid debugging if configure makes a mistake.
-It was created by gpgme $as_me 1.13.0, which was
+It was created by gpgme $as_me 1.13.1, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -2874,18 +2874,18 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu
#
LIBGPGME_LT_CURRENT=33
LIBGPGME_LT_AGE=22
-LIBGPGME_LT_REVISION=0
+LIBGPGME_LT_REVISION=1
# If there is an ABI break in gpgmepp or qgpgme also bump the
# version in IMPORTED_LOCATION in the GpgmeppConfig-w32.cmake.in.in
-LIBGPGMEPP_LT_CURRENT=15
-LIBGPGMEPP_LT_AGE=9
+LIBGPGMEPP_LT_CURRENT=16
+LIBGPGMEPP_LT_AGE=10
LIBGPGMEPP_LT_REVISION=0
LIBQGPGME_LT_CURRENT=10
LIBQGPGME_LT_AGE=3
-LIBQGPGME_LT_REVISION=3
+LIBQGPGME_LT_REVISION=4
################################################
@@ -2911,7 +2911,7 @@ NEED_LIBASSUAN_VERSION=2.4.2
VERSION_MAJOR=1
VERSION_MINOR=13
-VERSION_MICRO=0
+VERSION_MICRO=1
ac_aux_dir=
for ac_dir in build-aux "$srcdir"/build-aux; do
@@ -3432,7 +3432,7 @@ fi
# Define the identity of the package.
PACKAGE='gpgme'
- VERSION='1.13.0'
+ VERSION='1.13.1'
cat >>confdefs.h <<_ACEOF
@@ -6493,7 +6493,7 @@ test -n "$GITLOG_TO_CHANGELOG" || GITLOG_TO_CHANGELOG="gitlog-to-changelog"
-VERSION_NUMBER=0x010d00
+VERSION_NUMBER=0x010d01
# We need to compile and run a program on the build machine. A
@@ -23884,7 +23884,7 @@ ENABLED_LANGUAGES=$enabled_languages
#
# Provide information about the build.
#
-BUILD_REVISION="1b5a6bf2"
+BUILD_REVISION="ea11c2a1"
cat >>confdefs.h <<_ACEOF
@@ -23893,7 +23893,7 @@ _ACEOF
BUILD_VERSION=`echo "$PACKAGE_VERSION" | sed 's/\([0-9.]*\).*/\1./'`
-BUILD_VERSION="${BUILD_VERSION}7002"
+BUILD_VERSION="${BUILD_VERSION}59921"
BUILD_FILEVERSION=`echo "${BUILD_VERSION}" | tr . ,`
@@ -24398,16 +24398,15 @@ fi
# Checks for compiler features.
if test "$GCC" = yes; then
CFLAGS="$CFLAGS -Wall -Wcast-align -Wshadow -Wstrict-prototypes"
- if test "$USE_MAINTAINER_MODE" = "yes"; then
- CFLAGS="$CFLAGS -Wformat -Wno-format-y2k -Wformat-security"
+ CFLAGS="$CFLAGS -Wno-format-y2k"
- # If -Wno-missing-field-initializers is supported we can enable a
- # a bunch of really useful warnings.
- { $as_echo "$as_me:${as_lineno-$LINENO}: checking if gcc supports -Wno-missing-field-initializers" >&5
+ # If -Wno-missing-field-initializers is supported we can expect a
+ # a larger set of warning options.
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking if gcc supports -Wno-missing-field-initializers" >&5
$as_echo_n "checking if gcc supports -Wno-missing-field-initializers... " >&6; }
- _gcc_cflags_save=$CFLAGS
- CFLAGS="-Wno-missing-field-initializers"
- cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+ _gcc_cflags_save=$CFLAGS
+ CFLAGS="-Wno-missing-field-initializers"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
int
@@ -24424,19 +24423,23 @@ else
_gcc_wopt=no
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
- { $as_echo "$as_me:${as_lineno-$LINENO}: result: $_gcc_wopt" >&5
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $_gcc_wopt" >&5
$as_echo "$_gcc_wopt" >&6; }
- CFLAGS=$_gcc_cflags_save;
+ CFLAGS=$_gcc_cflags_save;
+ if test x"$_gcc_wopt" = xyes ; then
+ CFLAGS="$CFLAGS -Wno-missing-field-initializers"
+ CFLAGS="$CFLAGS -Wno-sign-compare"
+ CFLAGS="$CFLAGS -Wno-format-zero-length"
+ CFLAGS="$CFLAGS -Wno-format-truncation"
+ CFLAGS="$CFLAGS -Wno-sizeof-pointer-div"
+ fi
+ if test "$USE_MAINTAINER_MODE" = "yes"; then
if test x"$_gcc_wopt" = xyes ; then
CFLAGS="$CFLAGS -W -Wextra -Wbad-function-cast"
CFLAGS="$CFLAGS -Wwrite-strings"
CFLAGS="$CFLAGS -Wdeclaration-after-statement"
- CFLAGS="$CFLAGS -Wno-missing-field-initializers"
- CFLAGS="$CFLAGS -Wno-sign-compare"
- CFLAGS="$CFLAGS -Wno-format-zero-length"
- CFLAGS="$CFLAGS -Wno-format-truncation"
- CFLAGS="$CFLAGS -Wno-sizeof-pointer-div"
fi
+ CFLAGS="$CFLAGS -Wformat -Wformat-security"
CXXFLAGS="$CXXFLAGS -Wall -Wextra -Wno-shadow"
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking if gcc supports -Wpointer-arith" >&5
@@ -25663,7 +25666,7 @@ ac_config_files="$ac_config_files lang/js/Makefile lang/js/src/Makefile lang/js/
ac_config_files="$ac_config_files lang/qt/doc/Makefile"
-ac_config_files="$ac_config_files lang/python/Makefile lang/python/version.py lang/python/tests/Makefile"
+ac_config_files="$ac_config_files lang/python/Makefile lang/python/version.py lang/python/tests/Makefile lang/python/src/Makefile lang/python/examples/Makefile lang/python/doc/Makefile"
ac_config_files="$ac_config_files lang/python/setup.py"
@@ -26265,7 +26268,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
# report actual input values of CONFIG_FILES etc. instead of their
# values after options handling.
ac_log="
-This file was extended by gpgme $as_me 1.13.0, which was
+This file was extended by gpgme $as_me 1.13.1, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -26331,7 +26334,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-gpgme config.status 1.13.0
+gpgme config.status 1.13.1
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
@@ -26965,6 +26968,9 @@ do
"lang/python/Makefile") CONFIG_FILES="$CONFIG_FILES lang/python/Makefile" ;;
"lang/python/version.py") CONFIG_FILES="$CONFIG_FILES lang/python/version.py" ;;
"lang/python/tests/Makefile") CONFIG_FILES="$CONFIG_FILES lang/python/tests/Makefile" ;;
+ "lang/python/src/Makefile") CONFIG_FILES="$CONFIG_FILES lang/python/src/Makefile" ;;
+ "lang/python/examples/Makefile") CONFIG_FILES="$CONFIG_FILES lang/python/examples/Makefile" ;;
+ "lang/python/doc/Makefile") CONFIG_FILES="$CONFIG_FILES lang/python/doc/Makefile" ;;
"lang/python/setup.py") CONFIG_FILES="$CONFIG_FILES lang/python/setup.py" ;;
*) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;;
@@ -28651,7 +28657,7 @@ fi
echo "
GPGME v${VERSION} has been configured as follows:
- Revision: 1b5a6bf2 (7002)
+ Revision: ea11c2a1 (59921)
Platform: $host
UI Server: $uiserver
diff --git a/configure.ac b/configure.ac
index 25184f4..7bc0604 100644
--- a/configure.ac
+++ b/configure.ac
@@ -30,7 +30,7 @@ min_automake_version="1.14"
m4_define([mym4_package],[gpgme])
m4_define([mym4_major], [1])
m4_define([mym4_minor], [13])
-m4_define([mym4_micro], [0])
+m4_define([mym4_micro], [1])
# Below is m4 magic to extract and compute the git revision number,
# the decimalized short revision number, a beta version string and a
@@ -53,18 +53,18 @@ AC_INIT([mym4_package],[mym4_version], [https://bugs.gnupg.org])
#
LIBGPGME_LT_CURRENT=33
LIBGPGME_LT_AGE=22
-LIBGPGME_LT_REVISION=0
+LIBGPGME_LT_REVISION=1
# If there is an ABI break in gpgmepp or qgpgme also bump the
# version in IMPORTED_LOCATION in the GpgmeppConfig-w32.cmake.in.in
-LIBGPGMEPP_LT_CURRENT=15
-LIBGPGMEPP_LT_AGE=9
+LIBGPGMEPP_LT_CURRENT=16
+LIBGPGMEPP_LT_AGE=10
LIBGPGMEPP_LT_REVISION=0
LIBQGPGME_LT_CURRENT=10
LIBQGPGME_LT_AGE=3
-LIBQGPGME_LT_REVISION=3
+LIBQGPGME_LT_REVISION=4
################################################
AC_SUBST(LIBGPGME_LT_CURRENT)
@@ -599,27 +599,30 @@ AM_SUBST_NOTMAKE(API__SSIZE_T)
# Checks for compiler features.
if test "$GCC" = yes; then
CFLAGS="$CFLAGS -Wall -Wcast-align -Wshadow -Wstrict-prototypes"
+ CFLAGS="$CFLAGS -Wno-format-y2k"
+
+ # If -Wno-missing-field-initializers is supported we can expect a
+ # a larger set of warning options.
+ AC_MSG_CHECKING([if gcc supports -Wno-missing-field-initializers])
+ _gcc_cflags_save=$CFLAGS
+ CFLAGS="-Wno-missing-field-initializers"
+ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_wopt=yes,_gcc_wopt=no)
+ AC_MSG_RESULT($_gcc_wopt)
+ CFLAGS=$_gcc_cflags_save;
+ if test x"$_gcc_wopt" = xyes ; then
+ CFLAGS="$CFLAGS -Wno-missing-field-initializers"
+ CFLAGS="$CFLAGS -Wno-sign-compare"
+ CFLAGS="$CFLAGS -Wno-format-zero-length"
+ CFLAGS="$CFLAGS -Wno-format-truncation"
+ CFLAGS="$CFLAGS -Wno-sizeof-pointer-div"
+ fi
if test "$USE_MAINTAINER_MODE" = "yes"; then
- CFLAGS="$CFLAGS -Wformat -Wno-format-y2k -Wformat-security"
-
- # If -Wno-missing-field-initializers is supported we can enable a
- # a bunch of really useful warnings.
- AC_MSG_CHECKING([if gcc supports -Wno-missing-field-initializers])
- _gcc_cflags_save=$CFLAGS
- CFLAGS="-Wno-missing-field-initializers"
- AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[])],_gcc_wopt=yes,_gcc_wopt=no)
- AC_MSG_RESULT($_gcc_wopt)
- CFLAGS=$_gcc_cflags_save;
if test x"$_gcc_wopt" = xyes ; then
CFLAGS="$CFLAGS -W -Wextra -Wbad-function-cast"
CFLAGS="$CFLAGS -Wwrite-strings"
CFLAGS="$CFLAGS -Wdeclaration-after-statement"
- CFLAGS="$CFLAGS -Wno-missing-field-initializers"
- CFLAGS="$CFLAGS -Wno-sign-compare"
- CFLAGS="$CFLAGS -Wno-format-zero-length"
- CFLAGS="$CFLAGS -Wno-format-truncation"
- CFLAGS="$CFLAGS -Wno-sizeof-pointer-div"
fi
+ CFLAGS="$CFLAGS -Wformat -Wformat-security"
CXXFLAGS="$CXXFLAGS -Wall -Wextra -Wno-shadow"
AC_MSG_CHECKING([if gcc supports -Wpointer-arith])
@@ -919,7 +922,10 @@ AC_CONFIG_FILES([lang/js/Makefile lang/js/src/Makefile
AC_CONFIG_FILES(lang/qt/doc/Makefile)
AC_CONFIG_FILES([lang/python/Makefile
lang/python/version.py
- lang/python/tests/Makefile])
+ lang/python/tests/Makefile
+ lang/python/src/Makefile
+ lang/python/examples/Makefile
+ lang/python/doc/Makefile])
AC_CONFIG_FILES([lang/python/setup.py], [chmod a+x lang/python/setup.py])
AC_OUTPUT
diff --git a/gpgme.spec b/gpgme.spec
index c1752e5..9ae2e39 100644
--- a/gpgme.spec
+++ b/gpgme.spec
@@ -1,7 +1,7 @@
# This is a template. The dist target uses it to create the real file.
Summary: GPGME - GnuPG Made Easy
Name: gpgme
-Version: 1.13.0
+Version: 1.13.1
Release: 1
URL: https://gnupg.org/gpgme.html
Source: https://www.gnupg.org/ftp/gcrypt/gpgme/%{name}-%{version}.tar.gz
diff --git a/lang/cl/gpgme.asd b/lang/cl/gpgme.asd
index b43eca7..43c4744 100644
--- a/lang/cl/gpgme.asd
+++ b/lang/cl/gpgme.asd
@@ -27,7 +27,7 @@
(defsystem gpgme
:description "GnuPG Made Easy."
:author "g10 Code GmbH"
- :version "1.13.0"
+ :version "1.13.1"
:licence "GPL"
:defsystem-depends-on ("cffi-grovel")
:depends-on ("cffi" "gpg-error" "trivial-garbage")
diff --git a/lang/cpp/src/context.cpp b/lang/cpp/src/context.cpp
index 444809a..c0a1dc2 100644
--- a/lang/cpp/src/context.cpp
+++ b/lang/cpp/src/context.cpp
@@ -1791,3 +1791,8 @@ bool GpgME::hasFeature(unsigned long features, unsigned long features2)
&& features2 == (features2 & supported_features2)
;
}
+
+int GpgME::setGlobalFlag(const char *name, const char *value)
+{
+ return gpgme_set_global_flag(name, value);
+}
diff --git a/lang/cpp/src/global.h b/lang/cpp/src/global.h
index 9760e21..d5c2e13 100644
--- a/lang/cpp/src/global.h
+++ b/lang/cpp/src/global.h
@@ -98,6 +98,9 @@ GPGMEPP_EXPORT const char *dirInfo(const char *what);
GPGMEPP_EXPORT Error checkEngine(Protocol proto);
GPGMEPP_EXPORT Error checkEngine(Engine engine);
+/* Wrapper for gpgme_set_global_flag */
+GPGMEPP_EXPORT int setGlobalFlag(const char *name, const char *value);
+
GPGMEPP_EXPORT GIOChannel *getGIOChannel(int fd);
GPGMEPP_EXPORT QIODevice *getQIODevice(int fd);
diff --git a/lang/cpp/src/gpggencardkeyinteractor.cpp b/lang/cpp/src/gpggencardkeyinteractor.cpp
index b109866..4d90aa0 100644
--- a/lang/cpp/src/gpggencardkeyinteractor.cpp
+++ b/lang/cpp/src/gpggencardkeyinteractor.cpp
@@ -41,8 +41,8 @@ public:
}
std::string name, email, backupFileName, expiry, serial, keysize;
- Algo algo;
bool backup;
+ Algo algo;
};
GpgGenCardKeyInteractor::~GpgGenCardKeyInteractor() {}
diff --git a/lang/python/Makefile.am b/lang/python/Makefile.am
index c888448..845b7b1 100644
--- a/lang/python/Makefile.am
+++ b/lang/python/Makefile.am
@@ -21,12 +21,9 @@ EXTRA_DIST = \
README \
MANIFEST.in \
gpgme.i \
- helpers.c helpers.h private.h \
- examples \
- doc \
- src
+ helpers.c helpers.h private.h
-SUBDIRS = . tests
+SUBDIRS = . tests examples doc src
.PHONY: prepare
prepare: copystamp
diff --git a/lang/python/Makefile.in b/lang/python/Makefile.in
index 3d83947..6bc5a15 100644
--- a/lang/python/Makefile.in
+++ b/lang/python/Makefile.in
@@ -411,12 +411,9 @@ EXTRA_DIST = \
README \
MANIFEST.in \
gpgme.i \
- helpers.c helpers.h private.h \
- examples \
- doc \
- src
+ helpers.c helpers.h private.h
-SUBDIRS = . tests
+SUBDIRS = . tests examples doc src
CLEANFILES = copystamp \
config.h \
data.h \
diff --git a/lang/python/doc/Makefile.am b/lang/python/doc/Makefile.am
new file mode 100644
index 0000000..3abc9e6
--- /dev/null
+++ b/lang/python/doc/Makefile.am
@@ -0,0 +1,48 @@
+# Makefile.am for the Python bindings.
+# Copyright (C) 2019 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 <https://gnu.org/licenses/>.
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+# Created by:
+# find . -type f -print | sed 's/^.\// /;$q;s/$/ \\/' | sort
+EXTRA_DIST = texinfo/what-was-new.texi \
+ meta/old-commits.log \
+ meta/TODO.org \
+ README \
+ rst/_build/README \
+ rst/conf.py \
+ rst/gpgme-python-howto.rst \
+ rst/index.rst \
+ rst/maintenance-mode.rst \
+ rst/short-history.rst \
+ rst/_static/README \
+ rst/_templates/README \
+ rst/what-is-new.rst \
+ rst/what-was-new.rst \
+ src/gpgme-python-howto \
+ src/index \
+ src/maintenance-mode \
+ src/short-history \
+ src/what-is-new \
+ src/what-was-new \
+ texinfo/gpgme-python-howto.texi \
+ texinfo/index.texi \
+ texinfo/maintenance-mode.texi \
+ texinfo/short-history.texi \
+ texinfo/texinfo.tex \
+ texinfo/what-is-new.texi \
+ texinfo/what-was-new.texi
diff --git a/lang/python/doc/Makefile.in b/lang/python/doc/Makefile.in
new file mode 100644
index 0000000..31f8187
--- /dev/null
+++ b/lang/python/doc/Makefile.in
@@ -0,0 +1,574 @@
+# Makefile.in generated by automake 1.15 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2014 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) 2019 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 <https://gnu.org/licenses/>.
+# SPDX-License-Identifier: LGPL-2.1-or-later
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+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/doc
+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/python.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)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/conf/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)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build-aux/mkinstalldirs README
+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@
+BUILD_VERSION = @BUILD_VERSION@
+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@
+GPGRT_CONFIG = @GPGRT_CONFIG@
+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@
+LDADD_FOR_TESTS_KLUDGE = @LDADD_FOR_TESTS_KLUDGE@
+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@
+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_MAJOR = @VERSION_MAJOR@
+VERSION_MICRO = @VERSION_MICRO@
+VERSION_MINOR = @VERSION_MINOR@
+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@
+
+# Created by:
+# find . -type f -print | sed 's/^.\// /;$q;s/$/ \\/' | sort
+EXTRA_DIST = texinfo/what-was-new.texi \
+ meta/old-commits.log \
+ meta/TODO.org \
+ README \
+ rst/_build/README \
+ rst/conf.py \
+ rst/gpgme-python-howto.rst \
+ rst/index.rst \
+ rst/maintenance-mode.rst \
+ rst/short-history.rst \
+ rst/_static/README \
+ rst/_templates/README \
+ rst/what-is-new.rst \
+ rst/what-was-new.rst \
+ src/gpgme-python-howto \
+ src/index \
+ src/maintenance-mode \
+ src/short-history \
+ src/what-is-new \
+ src/what-was-new \
+ texinfo/gpgme-python-howto.texi \
+ texinfo/index.texi \
+ texinfo/maintenance-mode.texi \
+ texinfo/short-history.texi \
+ texinfo/texinfo.tex \
+ texinfo/what-is-new.texi \
+ texinfo/what-was-new.texi
+
+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/doc/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu lang/python/doc/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:
+
+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 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 \
+ 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
+
+.PRECIOUS: Makefile
+
+
+# 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/doc/src/gpgme-python-howto.pdf b/lang/python/doc/src/gpgme-python-howto.pdf
deleted file mode 100644
index 4bc9cb0..0000000
--- a/lang/python/doc/src/gpgme-python-howto.pdf
+++ /dev/null
Binary files differ
diff --git a/lang/python/doc/src/gpgme-python-howto.tex b/lang/python/doc/src/gpgme-python-howto.tex
deleted file mode 100644
index 29b305a..0000000
--- a/lang/python/doc/src/gpgme-python-howto.tex
+++ /dev/null
@@ -1,3110 +0,0 @@
-% Created 2019-01-18 Fri 10:12
-% Intended LaTeX compiler: xelatex
-\documentclass[12pt]{article}
-\usepackage{graphicx}
-\usepackage{grffile}
-\usepackage{longtable}
-\usepackage{wrapfig}
-\usepackage{rotating}
-\usepackage[normalem]{ulem}
-\usepackage{amsmath}
-\usepackage{textcomp}
-\usepackage{amssymb}
-\usepackage{capt-of}
-\usepackage{hyperref}
-\usepackage{xltxtra}
-\usepackage[margin=1in]{geometry}
-\setmainfont[Ligatures={Common}]{Times New Roman}
-\author{Ben McGinnes <ben@gnupg.org>}
-\author{Ben McGinnes}
-\date{\today}
-\title{GNU Privacy Guard (GnuPG) Made Easy Python Bindings HOWTO (English)}
-\hypersetup{
- pdfauthor={Ben McGinnes},
- pdftitle={GNU Privacy Guard (GnuPG) Made Easy Python Bindings HOWTO (English)},
- pdfkeywords={},
- pdfsubject={},
- pdfcreator={Emacs 26.1 (Org mode 9.1.14)},
- pdflang={English}}
-\begin{document}
-
-\maketitle
-\tableofcontents
-
-
-
-\section{Introduction}
-\label{sec:org94d4d8e}
-\begin{center}
-\begin{tabular}{ll}
-Version: & 0.1.4\\
-GPGME Version: & 1.12.1\\
-Author: & Ben McGinnes <ben@gnupg.org>\\
-Author GPG Key: & DB4724E6FA4286C92B4E55C4321E4E2373590E5D\\
-Language: & Australian English, British English\\
-Language codes: & en-AU, en-GB, en\\
-\end{tabular}
-\end{center}
-
-This document provides basic instruction in how to use the GPGME
-Python bindings to programmatically leverage the GPGME library.
-
-
-\subsection{Python 2 versus Python 3}
-\label{sec:org64e998a}
-Though the GPGME Python bindings themselves provide support for both
-Python 2 and 3, the focus is unequivocally on Python 3 and
-specifically from Python 3.4 and above. As a consequence all the
-examples and instructions in this guide use Python 3 code.
-
-Much of it will work with Python 2, but much of it also deals with
-Python 3 byte literals, particularly when reading and writing data.
-Developers concentrating on Python 2.7, and possibly even 2.6, will
-need to make the appropriate modifications to support the older string
-and unicode types as opposed to bytes.
-
-There are multiple reasons for concentrating on Python 3; some of
-which relate to the immediate integration of these bindings, some of
-which relate to longer term plans for both GPGME and the python
-bindings and some of which relate to the impending EOL period for
-Python 2.7. Essentially, though, there is little value in tying the
-bindings to a version of the language which is a dead end and the
-advantages offered by Python 3 over Python 2 make handling the data
-types with which GPGME deals considerably easier.
-
-
-\subsection{Examples}
-\label{sec:orge70b980}
-All of the examples found in this document can be found as Python 3
-scripts in the \texttt{lang/python/examples/howto} directory.
-
-
-\subsection{Unofficial Drafts}
-\label{sec:orgc039694}
-In addition to shipping with each release of GPGME, there is a section
-on locations to read or download \hyperref[sec:org1cd0343]{draft editions} of this document from
-at the end of it. These are unofficial versions produced in between
-major releases.
-
-
-\subsection{What's New}
-\label{sec:org5a3566f}
-Full details of what is new are now available in the \href{what-is-new.org}{What's New} file
-and archives of the preceding \emph{What's New} sections are available in
-the \href{what-was-new}{What Was New} file.
-
-
-\subsubsection{New in GPGME 1·13·0}
-\label{sec:orgf12910d}
-See the \href{what-is-new\#new-stuff-1-13-0}{What's New} document for what is new in version 1.13.0.
-
-
-\subsubsection{New in GPGME 1·12·0}
-\label{sec:org5a31f34}
-See the \href{what-was-new\#new-stuff-1-12-0}{What Was New} document for what was new in version 1.12.0.
-
-
-\section{GPGME Concepts}
-\label{sec:org5ac85f0}
-\subsection{A C API}
-\label{sec:org6179bbb}
-Unlike many modern APIs with which programmers will be more familiar
-with these days, the GPGME API is a C API. The API is intended for
-use by C coders who would be able to access its features by including
-the \texttt{gpgme.h} header file with their own C source code and then access
-its functions just as they would any other C headers.
-
-This is a very effective method of gaining complete access to the API
-and in the most efficient manner possible. It does, however, have the
-drawback that it cannot be directly used by other languages without
-some means of providing an interface to those languages. This is
-where the need for bindings in various languages stems.
-
-
-\subsection{Python bindings}
-\label{sec:org9a68df9}
-The Python bindings for GPGME provide a higher level means of
-accessing the complete feature set of GPGME itself. It also provides
-a more pythonic means of calling these API functions.
-
-The bindings are generated dynamically with SWIG and the copy of
-\texttt{gpgme.h} generated when GPGME is compiled.
-
-This means that a version of the Python bindings is fundamentally tied
-to the exact same version of GPGME used to generate that copy of
-\texttt{gpgme.h}.
-
-
-\subsection{Difference between the Python bindings and other GnuPG Python packages}
-\label{sec:org780b78f}
-There have been numerous attempts to add GnuPG support to Python over
-the years. Some of the most well known are listed here, along with
-what differentiates them.
-
-
-\subsubsection{The python-gnupg package maintained by Vinay Sajip}
-\label{sec:orgb59b8cc}
-This is arguably the most popular means of integrating GPG with
-Python. The package utilises the \texttt{subprocess} module to implement
-wrappers for the \texttt{gpg} and \texttt{gpg2} executables normally invoked on the
-command line (\texttt{gpg.exe} and \texttt{gpg2.exe} on Windows).
-
-The popularity of this package stemmed from its ease of use and
-capability in providing the most commonly required features.
-
-Unfortunately it has been beset by a number of security issues in the
-past; most of which stemmed from using unsafe methods of accessing the
-command line via the \texttt{subprocess} calls. While some effort has been
-made over the last two to three years (as of 2018) to mitigate this,
-particularly by no longer providing shell access through those
-subprocess calls, the wrapper is still somewhat limited in the scope
-of its GnuPG features coverage.
-
-The python-gnupg package is available under the MIT license.
-
-
-\subsubsection{The gnupg package created and maintained by Isis Lovecruft}
-\label{sec:orgb7c1dae}
-In 2015 Isis Lovecruft from the Tor Project forked and then
-re-implemented the python-gnupg package as just gnupg. This new
-package also relied on subprocess to call the \texttt{gpg} or \texttt{gpg2}
-binaries, but did so somewhat more securely.
-
-The naming and version numbering selected for this package, however,
-resulted in conflicts with the original python-gnupg and since its
-functions were called in a different manner to python-gnupg, the
-release of this package also resulted in a great deal of consternation
-when people installed what they thought was an upgrade that
-subsequently broke the code relying on it.
-
-The gnupg package is available under the GNU General Public License
-version 3.0 (or any later version).
-
-
-\subsubsection{The PyME package maintained by Martin Albrecht}
-\label{sec:orgf9599d9}
-This package is the origin of these bindings, though they are somewhat
-different now. For details of when and how the PyME package was
-folded back into GPGME itself see the \href{short-history.org}{Short History} document.\footnote{\texttt{short-history} and/or \texttt{short-history.html}.}
-
-The PyME package was first released in 2002 and was also the first
-attempt to implement a low level binding to GPGME. In doing so it
-provided access to considerably more functionality than either the
-\texttt{python-gnupg} or \texttt{gnupg} packages.
-
-The PyME package is only available for Python 2.6 and 2.7.
-
-Porting the PyME package to Python 3.4 in 2015 is what resulted in it
-being folded into the GPGME project and the current bindings are the
-end result of that effort.
-
-The PyME package is available under the same dual licensing as GPGME
-itself: the GNU General Public License version 2.0 (or any later
-version) and the GNU Lesser General Public License version 2.1 (or any
-later version).
-
-
-\section{GPGME Python bindings installation}
-\label{sec:org9681d17}
-\subsection{No PyPI}
-\label{sec:org002a824}
-Most third-party Python packages and modules are available and
-distributed through the Python Package Installer, known as PyPI.
-
-Due to the nature of what these bindings are and how they work, it is
-infeasible to install the GPGME Python bindings in the same way.
-
-This is because the bindings use SWIG to dynamically generate C
-bindings against \texttt{gpgme.h} and \texttt{gpgme.h} is generated from
-\texttt{gpgme.h.in} at compile time when GPGME is built from source. Thus to
-include a package in PyPI which actually built correctly would require
-either statically built libraries for every architecture bundled with
-it or a full implementation of C for each architecture.
-
-See the additional notes regarding \hyperref[sec:org4315699]{CFFI and SWIG} at the end of this
-section for further details.
-
-
-\subsection{Requirements}
-\label{sec:org8e7dfcb}
-The GPGME Python bindings only have three requirements:
-
-\begin{enumerate}
-\item A suitable version of Python 2 or Python 3. With Python 2 that
-means CPython 2.7 and with Python 3 that means CPython 3.4 or
-higher.
-\item \href{https://www.swig.org}{SWIG}.
-\item GPGME itself. Which also means that all of GPGME's dependencies
-must be installed too.
-\end{enumerate}
-
-
-\subsubsection{Recommended Additions}
-\label{sec:orgda93e2c}
-Though none of the following are absolute requirements, they are all
-recommended for use with the Python bindings. In some cases these
-recommendations refer to which version(s) of CPython to use the
-bindings with, while others refer to third party modules which provide
-a significant advantage in some way.
-
-\begin{enumerate}
-\item If possible, use Python 3 instead of 2.
-\item Favour a more recent version of Python since even 3.4 is due to
-reach EOL soon. In production systems and services, Python 3.6
-should be robust enough to be relied on.
-\item If possible add the following Python modules which are not part of
-the standard library: \href{http://docs.python-requests.org/en/latest/index.html}{Requests}, \href{https://cython.org/}{Cython}, \href{https://pendulum.eustace.io/}{Pendulum} and \href{https://github.com/Selfnet/hkp4py}{hkp4py}.
-\end{enumerate}
-
-Chances are quite high that at least the first one and maybe two of
-those will already be installed.
-
-Note that, as with Cython, some of advanced use case scenarios will
-bring with them additional requirements. Most of these will be fairly
-well known and commonly installed ones, however, which are in many
-cases likely to have already been installed on many systems or be
-familiar to Python programmers.
-
-
-\subsection{Installation}
-\label{sec:org7ac02c3}
-Installing the Python bindings is effectively achieved by compiling
-and installing GPGME itself.
-
-Once SWIG is installed with Python and all the dependencies for GPGME
-are installed you only need to confirm that the version(s) of Python
-you want the bindings installed for are in your \texttt{\$PATH}.
-
-By default GPGME will attempt to install the bindings for the most
-recent or highest version number of Python 2 and Python 3 it detects
-in \texttt{\$PATH}. It specifically checks for the \texttt{python} and \texttt{python3}
-executables first and then checks for specific version numbers.
-
-For Python 2 it checks for these executables in this order: \texttt{python},
-\texttt{python2} and \texttt{python2.7}.
-
-For Python 3 it checks for these executables in this order: \texttt{python3},
- \texttt{python3.7}, \texttt{python3.6}, \texttt{python3.5} and \texttt{python3.4}.\footnote{With no issues reported specific to Python 3.7, the release of
-Python 3.7.1 at around the same time as GPGME 1.12.0 and the testing
-with Python 3.7.1rc1, there is no reason to delay moving 3.7 ahead of
-3.6 now. Production environments with more conservative requirements
-will always enforce their own policies anyway and installation to each
-supported minor release is quite possible too.}
-
-On systems where \texttt{python} is actually \texttt{python3} and not \texttt{python2} it
-may be possible that \texttt{python2} may be overlooked, but there have been
-no reports of that actually occurring as yet.
-
-In the three months or so since the release of Python 3.7.0 there has
-been extensive testing and work with these bindings with no issues
-specifically relating to the new version of Python or any of the new
-features of either the language or the bindings. This has also been
-the case with Python 3.7.1rc1. With that in mind and given the
-release of Python 3.7.1 is scheduled for around the same time as GPGME
-1.12.0, the order of preferred Python versions has been changed to
-move Python 3.7 ahead of Python 3.6.
-
-
-\subsubsection{Installing GPGME}
-\label{sec:org0d28316}
-See the GPGME \texttt{README} file for details of how to install GPGME from
-source.
-
-
-\subsection{Known Issues}
-\label{sec:org5f11db8}
-There are a few known issues with the current build process and the
-Python bindings. For the most part these are easily addressed should
-they be encountered.
-
-
-\subsubsection{Breaking Builds}
-\label{sec:orga10be64}
-Occasionally when installing GPGME with the Python bindings included
-it may be observed that the \texttt{make} portion of that process induces a
-large very number of warnings and, eventually errors which end that
-part of the build process. Yet following that with \texttt{make check} and
-\texttt{make install} appears to work seamlessly.
-
-The cause of this is related to the way SWIG needs to be called to
-dynamically generate the C bindings for GPGME in the first place. So
-the entire process will always produce \texttt{lang/python/python2-gpg/} and
-\texttt{lang/python/python3-gpg/} directories. These should contain the
-build output generated during compilation, including the complete
-bindings and module installed into \texttt{site-packages}.
-
-Occasionally the errors in the early part or some other conflict
-(e.g. not installing as \textbf{\emph{root}} or \textbf{\emph{su}}) may result in nothing
-being installed to the relevant \texttt{site-packages} directory and the
-build directory missing a lot of expected files. Even when this
-occurs, the solution is actually quite simple and will always work.
-
-That solution is simply to run the following commands as either the
-\textbf{root} user or prepended with \texttt{sudo -H}\footnote{Yes, even if you use virtualenv with everything you do in
-Python. If you want to install this module as just your user account
-then you will need to manually configure, compile and install the
-\emph{entire} GnuPG stack as that user as well. This includes libraries
-which are not often installed that way. It can be done and there are
-circumstances under which it is worthwhile, but generally only on
-POSIX systems which utilise single user mode (some even require it).} in the \texttt{lang/python/}
-directory:
-
-\begin{verbatim}
-/path/to/pythonX.Y setup.py build
-/path/to/pythonX.Y setup.py build
-/path/to/pythonX.Y setup.py install
-\end{verbatim}
-
-Yes, the build command does need to be run twice. Yes, you still need
-to run the potentially failing or incomplete steps during the
-\texttt{configure}, \texttt{make} and \texttt{make install} steps with installing GPGME.
-This is because those steps generate a lot of essential files needed,
-both by and in order to create, the bindings (including both the
-\texttt{setup.py} and \texttt{gpgme.h} files).
-
-
-\begin{enumerate}
-\item IMPORTANT Note
-\label{sec:org6926f46}
-If specifying a selected number of languages to create bindings for,
-try to leave Python last. Currently the majority of the other
-language bindings are also preceding Python of either version when
-listed alphabetically (not counting the Qt bindings).
-
-If Python is set to precede one of the other languages then it is
-possible that the errors described here may interrupt the build
-process before generating bindings for those other languages. In
-these cases it may be preferable to configure all preferred language
-bindings separately with alternative \texttt{configure} steps for GPGME using
-the \texttt{-{}-enable-languages=\$LANGUAGE} option.
-
-Alternatively \texttt{make} (or \texttt{gmake}, depending on your platform) may be
-run with the the \texttt{-k} option, which tells make to keep going even if
-errors are encountered. In that case the failure of one language's
-set of bindings to build should not hamper another language's bindings
-to build.
-\end{enumerate}
-
-
-\subsubsection{Reinstalling Responsibly}
-\label{sec:orgc1457e4}
-Regardless of whether you're installing for one version of Python or
-several, there will come a point where reinstallation is required.
-With most Python module installations, the installed files go into the
-relevant site-packages directory and are then forgotten about. Then
-the module is upgraded, the new files are copied over the old and
-that's the end of the matter.
-
-While the same is true of these bindings, there have been intermittent
-issues observed on some platforms which have benefited significantly
-from removing all the previous installations of the bindings before
-installing the updated versions.
-
-Removing the previous version(s) is simply a matter of changing to the
-relevant \texttt{site-packages} directory for the version of Python in
-question and removing the \texttt{gpg/} directory and any accompanying
-egg-info files for that module.
-
-In most cases this will require root or administration privileges on
-the system, but the same is true of installing the module in the first
-place.
-
-
-\subsubsection{Multiple installations}
-\label{sec:org3a1a2a2}
-For a variety of reasons it may be either necessary or just preferable
-to install the bindings to alternative installed Python versions which
-meet the requirements of these bindings.
-
-On POSIX systems this will generally be most simply achieved by
-running the manual installation commands (build, build, install) as
-described in the previous section for each Python installation the
-bindings need to be installed to.
-
-As per the SWIG documentation: the compilers, libraries and runtime
-used to build GPGME and the Python Bindings \textbf{must} match those used to
-compile Python itself, including the version number(s) (at least going
-by major version numbers and probably minor numbers too).
-
-On most POSIX systems, including OS X, this will very likely be the
-case in most, if not all, cases.
-
-Note that from GPGME \href{https://dev.gnupg.org/rMff6ff616aea6f59b7f2ce1176492850ecdf3851e}{1.12.1} the default installation installs to each
-version of Python it can find first. That is that it will currently
-install for the first copies of Python versions 2.7, 3.4, 3.5, 3.6,
-3.7 and 3.8 (dev branch) that it finds. Usually this will be in the
-same prefix as GPGME itself, but is dictated by the \texttt{\$PATH} when the
-installation is performed. The above instructions can still be
-performed on other python installations which the installer does not
-find, including alternative prefixes.
-
-
-
-\subsubsection{Won't Work With Windows}
-\label{sec:org06b5473}
-There are semi-regular reports of Windows users having considerable
-difficulty in installing and using the Python bindings at all. Very
-often, possibly even always, these reports come from Cygwin users
-and/or MinGW users and/or Msys2 users. Though not all of them have
-been confirmed, it appears that these reports have also come from
-people who installed Python using the Windows installer files from the
-\href{https://python.org}{Python website} (i.e. mostly MSI installers, sometimes self-extracting
-\texttt{.exe} files).
-
-The Windows versions of Python are not built using Cygwin, MinGW or
-Msys2; they're built using Microsoft Visual Studio. Furthermore the
-version used is \emph{considerably} more advanced than the version which
-MinGW obtained a small number of files from many years ago in order to
-be able to compile anything at all. Not only that, but there are
-changes to the version of Visual Studio between some micro releases,
-though that is is particularly the case with Python 2.7, since it has
-been kept around far longer than it should have been.
-
-There are two theoretical solutions to this issue:
-
-\begin{enumerate}
-\item Compile and install the GnuPG stack, including GPGME and the
-Python bindings using the same version of Microsoft Visual Studio
-used by the Python Foundation to compile the version of Python
-installed.
-
-If there are multiple versions of Python then this will need to be
-done with each different version of Visual Studio used for those
-versions of Python.
-
-\item Compile and install Python using the same tools used by choice,
-such as MinGW or Msys2.
-\end{enumerate}
-
-Do \textbf{not} use the official Windows installer for Python unless
-following the first method.
-
-In this type of situation it may even be for the best to accept that
-there are less limitations on permissive software than free software
-and simply opt to use a recent version of the Community Edition of
-Microsoft Visual Studio to compile and build all of it, no matter
-what.
-
-Investigations into the extent or the limitations of this issue are
-ongoing.
-
-The following table lists the version of Microsoft Visual Studio which
-needs to be used when compiling GPGME and the Python bindings with
-each version of the CPython binary released \href{https://www.python.org/downloads/windows/}{for Windows}:
-
-\begin{center}
-\begin{tabular}{rll}
-CPython & Microsoft product name & runtime filename\\
-2.7.6 & Visual Studio 2008 & MSVCR90.DLL\\
-3.4.0 & Visual Studio 2010 & MSVCR100.DLL\\
-3.5.0 & Visual Studio 2015 & \textbf{see below}\\
-3.6.0 & Visual Studio 2015 & \textbf{see below}\\
-3.7.0 & Visual Studio 2017* & \textbf{see below}\\
-\end{tabular}
-\end{center}
-
-It is important to note that MingW and Msys2 ship with the Visual C
-runtime from Microsoft Visual Studio 2005 and are thus \textbf{incompatible}
-with all the versions of CPython which can be used with the GPGME
-Python bindings.
-
-It is also important to note that from CPython 3.5 onwards, the Python
-Foundation has adopted the reworking of the Visual C runtime which was
-performed for Visual Studio 2015 and aimed at resolving many of these
-kinds of issues. Much greater detail on these issues and the correct
-file(s) to link to are available from Matthew Brett's invaluable page,
-\href{https://matthew-brett.github.io/pydagogue/python\_msvc.html}{Using Microsoft Visual C with Python}. It is also worth reading the
-Microsoft Developer Network blog post on \href{http://blogs.msdn.com/b/vcblog/archive/2015/03/03/introducing-the-universal-crt.aspx}{the universal CRT} and Steve
-Dower's blog posts on Python extensions (\href{http://stevedower.id.au/blog/building-for-python-3-5}{part 1} and \href{http://stevedower.id.au/blog/building-for-python-3-5-part-two}{part 2}).
-
-The second of those two posts by Steve Dower contains the details of
-specific configuration options required for compiling anything to be
-used with official CPython releases. In addition to those
-configuration and compiler settings to use, the versions of Visual
-Studio prior to Visual Studio 2015 did not support 64-bit systems by
-default. So compiling a 64-bit version of these bindings for a 64-bit
-version of CPython 2.7 or 3.4 requires additional work.
-
-In addition to the blog posts, the \href{https://wiki.python.org/moin/WindowsCompilers}{Windows compilers} wiki page on the
-CPython wiki is another essential reference on the relevant versions
-of Visual Studio to use and the degree of compatibility with CPython
-releases.
-
-Eventually someone will ask why there isn't an installable binary for
-Windows, which the GPGME of the licenses do not preclude as long as
-the source code is available in conjunction with such a release.
-
-The sheer number of versions of Visual Studio in conjunction with
-differing configuration options depending on the target Windows
-version and whether the architecture is 64-bit or 32-bit makes it
-difficult to provide a correct binary installer for Windows users. At
-the bare minimum doing so would require the GnuPG project compile ten
-different versions of the bindings with each release; both 32-bit and
-64-bit versions for CPython 2.7 and 3.4, with 64-bit versions for both
-x86-64 (i.e. Intel and AMD) and ARM architectures for CPython 3.5,
-3.6, 3.7 and later releases. That's the bare \textbf{minimum}, it'd probably
-be higher.
-
-Additionally, with only a binary installation used in conjunction with
-the CPython installer from \texttt{python.org} the advanced options available
-which utilise \hyperref[sec:orgeda6cec]{Cython} will not be able to be used at all. Cython
-depends on being able to compile the C code it generates and that too
-would need to utilise a matching runtime to both the installed version
-of CPython and these bindings in order to work with the bindings.
-
-Considering all of that, what do we recommend?
-
-\begin{enumerate}
-\item Use a recent version of CPython; at least 3.5, but ideally 3.6 or
-later.
-
-\item Use Visual Studio 2015 or the standalone build tools for Visual
-Studio 2017 (or later).
-
-\item Compile both CPython and GPGME with these bindings using the tools
-selected in step 2.
-
-\item Ignore MingW, Msys2 and the official CPython binary installers.
-
-\item Be thankful the answer to this question wasn't simply to say
-something like, “install Linux” or “install FreeBSD” (or even
-Apple's OS X).
-\end{enumerate}
-
-
-\subsubsection{CFFI is the Best™ and GPGME should use it instead of SWIG}
-\label{sec:org4315699}
-There are many reasons for favouring \href{https://cffi.readthedocs.io/en/latest/overview.html}{CFFI} and proponents of it are
-quite happy to repeat these things as if all it would take to switch
-from SWIG to CFFI is repeating that list as if it were a new concept.
-
-The fact is that there are things which Python's CFFI implementation
-cannot handle in the GPGME C code. Beyond that there are features of
-SWIG which are simply not available with CFFI at all. SWIG generates
-the bindings to Python using the \texttt{gpgme.h} file, but that file is not
-a single version shipped with each release, it too is generated when
-GPGME is compiled.
-
-CFFI is currently unable to adapt to such a potentially mutable
-codebase. If there were some means of applying SWIG's dynamic code
-generation to produce the Python/CFFI API modes of accessing the GPGME
-libraries (or the source source code directly), but such a thing does
-not exist yet either and it currently appears that work is needed in
-at least one of CFFI's dependencies before any of this can be
-addressed.
-
-So if you're a massive fan of CFFI; that's great, but if you want this
-project to switch to CFFI then rather than just insisting that it
-should, I'd suggest you volunteer to bring CFFI up to the level this
-project needs.
-
-If you're actually seriously considering doing so, then I'd suggest
-taking the \texttt{gpgme-tool.c} file in the GPGME \texttt{src/} directory and
-getting that to work with any of the CFFI API methods (not the ABI
-methods, they'll work with pretty much anything). When you start
-running into trouble with "ifdefs" then you'll know what sort of
-things are lacking. That doesn't even take into account the amount of
-work saved via SWIG's code generation techniques either.
-
-
-\subsubsection{Virtualised Environments}
-\label{sec:org16ddce9}
-It is fairly common practice amongst Python developers to, as much as
-possible, use packages like virtualenv to keep various things that are
-to be installed from interfering with each other. Given how much of
-the GPGME bindings is often at odds with the usual pythonic way of
-doing things, it stands to reason that this would be called into
-question too.
-
-As it happens the answer as to whether or not the bindings can be used
-with virtualenv, the answer is both yes and no.
-
-In general we recommend installing to the relevant path and matching
-prefix of GPGME itself. Which means that when GPGME, and ideally the
-rest of the GnuPG stack, is installed to a prefix like \texttt{/usr/local} or
-\texttt{/opt/local} then the bindings would need to be installed to the main
-Python installation and not a virtualised abstraction. Attempts to
-separate the two in the past have been known to cause weird and
-intermittent errors ranging from minor annoyances to complete failures
-in the build process.
-
-As a consequence we only recommend building with and installing to the
-main Python installations within the same prefix as GPGME is installed
-to or which are found by GPGME's configuration stage immediately prior
-to running the make commands. Which is exactly what the compiling and
-installing process of GPGME does by default.
-
-Once that is done, however, it appears that a copy of the compiled
-module may be installed into a virtualenv of the same major and minor
-version matching the build. Alternatively it is possible to utilise a
-\texttt{sites.pth} file in the \texttt{site-packages/} directory of a virtualenv
-installation, which links back to the system installations
-corresponding directory in order to import anything installed system
-wide. This may or may not be appropriate on a case by case basis.
-
-Though extensive testing of either of these options is not yet
-complete, preliminary testing of them indicates that both are viable
-as long as the main installation is complete. Which means that
-certain other options normally restricted to virtual environments are
-also available, including integration with pythonic test suites
-(e.g. \href{https://docs.pytest.org/en/latest/index.html}{pytest}) and other large projects.
-
-That said, it is worth reiterating the warning regarding non-standard
-installations. If one were to attempt to install the bindings only to
-a virtual environment without somehow also including the full GnuPG
-stack (or enough of it as to include GPGME) then it is highly likely
-that errors would be encountered at some point and more than a little
-likely that the build process itself would break.
-
-If a degree of separation from the main operating system is still
-required in spite of these warnings, then consider other forms of
-virtualisation. Either a virtual machine (e.g. \href{https://www.virtualbox.org/}{VirtualBox}), a
-hardware emulation layer (e.g. \href{https://www.qemu.org/}{QEMU}) or an application container
-(e.g. \href{https://www.docker.com/why-docker}{Docker}).
-
-Finally it should be noted that the limited tests conducted thus far
-have been using the \texttt{virtualenv} command in a new directory to create
-the virtual python environment. As opposed to the standard \texttt{python3
--m venv} and it is possible that this will make a difference depending
-on the system and version of Python in use. Another option is to run
-the command \texttt{python3 -m virtualenv /path/to/install/virtual/thingy}
-instead.
-
-
-\section{Fundamentals}
-\label{sec:orgabe2f65}
-Before we can get to the fun stuff, there are a few matters regarding
-GPGME's design which hold true whether you're dealing with the C code
-directly or these Python bindings.
-
-
-\subsection{No REST}
-\label{sec:org1784ffa}
-The first part of which is or will be fairly blatantly obvious upon
-viewing the first example, but it's worth reiterating anyway. That
-being that this API is \emph{\textbf{not}} a REST API. Nor indeed could it ever
-be one.
-
-Most, if not all, Python programmers (and not just Python programmers)
-know how easy it is to work with a RESTful API. In fact they've
-become so popular that many other APIs attempt to emulate REST-like
-behaviour as much as they are able. Right down to the use of JSON
-formatted output to facilitate the use of their API without having to
-retrain developers.
-
-This API does not do that. It would not be able to do that and also
-provide access to the entire C API on which it's built. It does,
-however, provide a very pythonic interface on top of the direct
-bindings and it's this pythonic layer that this HOWTO deals with.
-
-
-\subsection{Context}
-\label{sec:org0fda7e2}
-One of the reasons which prevents this API from being RESTful is that
-most operations require more than one instruction to the API to
-perform the task. Sure, there are certain functions which can be
-performed simultaneously, particularly if the result known or strongly
-anticipated (e.g. selecting and encrypting to a key known to be in the
-public keybox).
-
-There are many more, however, which cannot be manipulated so readily:
-they must be performed in a specific sequence and the result of one
-operation has a direct bearing on the outcome of subsequent
-operations. Not merely by generating an error either.
-
-When dealing with this type of persistent state on the web, full of
-both the RESTful and REST-like, it's most commonly referred to as a
-session. In GPGME, however, it is called a context and every
-operation type has one.
-
-
-\section{Working with keys}
-\label{sec:org3a0c959}
-\subsection{Key selection}
-\label{sec:orgf03fe33}
-Selecting keys to encrypt to or to sign with will be a common
-occurrence when working with GPGMe and the means available for doing
-so are quite simple.
-
-They do depend on utilising a Context; however once the data is
-recorded in another variable, that Context does not need to be the
-same one which subsequent operations are performed.
-
-The easiest way to select a specific key is by searching for that
-key's key ID or fingerprint, preferably the full fingerprint without
-any spaces in it. A long key ID will probably be okay, but is not
-advised and short key IDs are already a problem with some being
-generated to match specific patterns. It does not matter whether the
-pattern is upper or lower case.
-
-So this is the best method:
-
-\begin{verbatim}
-import gpg
-
-k = gpg.Context().keylist(pattern="258E88DCBD3CD44D8E7AB43F6ECB6AF0DEADBEEF")
-keys = list(k)
-\end{verbatim}
-
-This is passable and very likely to be common:
-
-\begin{verbatim}
-import gpg
-
-k = gpg.Context().keylist(pattern="0x6ECB6AF0DEADBEEF")
-keys = list(k)
-\end{verbatim}
-
-And this is a really bad idea:
-
-\begin{verbatim}
-import gpg
-
-k = gpg.Context().keylist(pattern="0xDEADBEEF")
-keys = list(k)
-\end{verbatim}
-
-Alternatively it may be that the intention is to create a list of keys
-which all match a particular search string. For instance all the
-addresses at a particular domain, like this:
-
-\begin{verbatim}
-import gpg
-
-ncsc = gpg.Context().keylist(pattern="ncsc.mil")
-nsa = list(ncsc)
-\end{verbatim}
-
-
-\subsubsection{Counting keys}
-\label{sec:orga4f8d07}
-Counting the number of keys in your public keybox (\texttt{pubring.kbx}), the
-format which has superseded the old keyring format (\texttt{pubring.gpg} and
-\texttt{secring.gpg}), or the number of secret keys is a very simple task.
-
-\begin{verbatim}
-import gpg
-
-c = gpg.Context()
-seckeys = c.keylist(pattern=None, secret=True)
-pubkeys = c.keylist(pattern=None, secret=False)
-
-seclist = list(seckeys)
-secnum = len(seclist)
-
-publist = list(pubkeys)
-pubnum = len(publist)
-
-print("""
- Number of secret keys: {0}
- Number of public keys: {1}
-""".format(secnum, pubnum))
-\end{verbatim}
-
-NOTE: The \hyperref[sec:orgeda6cec]{Cython} introduction in the \hyperref[sec:org261e415]{Advanced and Experimental}
-section uses this same key counting code with Cython to demonstrate
-some areas where Cython can improve performance even with the
-bindings. Users with large public keyrings or keyboxes, for instance,
-should consider these options if they are comfortable with using
-Cython.
-
-
-\subsection{Get key}
-\label{sec:org661cd0b}
-An alternative method of getting a single key via its fingerprint is
-available directly within a Context with \texttt{Context().get\_key}. This is
-the preferred method of selecting a key in order to modify it, sign or
-certify it and for obtaining relevant data about a single key as a
-part of other functions; when verifying a signature made by that key,
-for instance.
-
-By default this method will select public keys, but it can select
-secret keys as well.
-
-This first example demonstrates selecting the current key of Werner
-Koch, which is due to expire at the end of 2018:
-
-\begin{verbatim}
-import gpg
-
-fingerprint = "80615870F5BAD690333686D0F2AD85AC1E42B367"
-key = gpg.Context().get_key(fingerprint)
-\end{verbatim}
-
-Whereas this example demonstrates selecting the author's current key
-with the \texttt{secret} key word argument set to \texttt{True}:
-
-\begin{verbatim}
-import gpg
-
-fingerprint = "DB4724E6FA4286C92B4E55C4321E4E2373590E5D"
-key = gpg.Context().get_key(fingerprint, secret=True)
-\end{verbatim}
-
-It is, of course, quite possible to select expired, disabled and
-revoked keys with this function, but only to effectively display
-information about those keys.
-
-It is also possible to use both unicode or string literals and byte
-literals with the fingerprint when getting a key in this way.
-
-
-\subsection{Importing keys}
-\label{sec:org5a4abec}
-Importing keys is possible with the \texttt{key\_import()} method and takes
-one argument which is a bytes literal object containing either the
-binary or ASCII armoured key data for one or more keys.
-
-The following example retrieves one or more keys from the SKS
-keyservers via the web using the requests module. Since requests
-returns the content as a bytes literal object, we can then use that
-directly to import the resulting data into our keybox.
-
-\begin{verbatim}
-import gpg
-import os.path
-import requests
-
-c = gpg.Context()
-url = "https://sks-keyservers.net/pks/lookup"
-pattern = input("Enter the pattern to search for key or user IDs: ")
-payload = {"op": "get", "search": pattern}
-
-r = requests.get(url, verify=True, params=payload)
-result = c.key_import(r.content)
-
-if result is not None and hasattr(result, "considered") is False:
- print(result)
-elif result is not None and hasattr(result, "considered") is True:
- num_keys = len(result.imports)
- new_revs = result.new_revocations
- new_sigs = result.new_signatures
- new_subs = result.new_sub_keys
- new_uids = result.new_user_ids
- new_scrt = result.secret_imported
- nochange = result.unchanged
- print("""
- The total number of keys considered for import was: {0}
-
- Number of keys revoked: {1}
- Number of new signatures: {2}
- Number of new subkeys: {3}
- Number of new user IDs: {4}
- Number of new secret keys: {5}
- Number of unchanged keys: {6}
-
- The key IDs for all considered keys were:
-""".format(num_keys, new_revs, new_sigs, new_subs, new_uids, new_scrt,
- nochange))
- for i in range(num_keys):
- print("{0}\n".format(result.imports[i].fpr))
-else:
- pass
-\end{verbatim}
-
-NOTE: When searching for a key ID of any length or a fingerprint
-(without spaces), the SKS servers require the the leading \texttt{0x}
-indicative of hexadecimal be included. Also note that the old short
-key IDs (e.g. \texttt{0xDEADBEEF}) should no longer be used due to the
-relative ease by which such key IDs can be reproduced, as demonstrated
-by the Evil32 Project in 2014 (which was subsequently exploited in
-2016).
-
-Testing for whether a string in any given search is or may be a
-hexadecimal value which may be missing the leading \texttt{0x} is a simple
-matter of using a try/except statement which attempts to convert the
-string as hex to an integer and then back to hex; then using that to
-search with. Raising a ValueError simply results in treating the
-string as a string. This is the method and logic utilised in the
-\texttt{import-keys-hkp.py} script (see below).
-
-
-\subsubsection{Working with ProtonMail}
-\label{sec:org151cae2}
-Here is a variation on the example above which checks the constrained
-ProtonMail keyserver for ProtonMail public keys.
-
-\begin{verbatim}
-import gpg
-import requests
-import sys
-
-print("""
-This script searches the ProtonMail key server for the specified key and
-imports it.
-""")
-
-c = gpg.Context(armor=True)
-url = "https://api.protonmail.ch/pks/lookup"
-ksearch = []
-
-if len(sys.argv) >= 2:
- keyterm = sys.argv[1]
-else:
- keyterm = input("Enter the key ID, UID or search string: ")
-
-if keyterm.count("@") == 2 and keyterm.startswith("@") is True:
- ksearch.append(keyterm[1:])
- ksearch.append(keyterm[1:])
- ksearch.append(keyterm[1:])
-elif keyterm.count("@") == 1 and keyterm.startswith("@") is True:
- ksearch.append("{0}@protonmail.com".format(keyterm[1:]))
- ksearch.append("{0}@protonmail.ch".format(keyterm[1:]))
- ksearch.append("{0}@pm.me".format(keyterm[1:]))
-elif keyterm.count("@") == 0:
- ksearch.append("{0}@protonmail.com".format(keyterm))
- ksearch.append("{0}@protonmail.ch".format(keyterm))
- ksearch.append("{0}@pm.me".format(keyterm))
-elif keyterm.count("@") == 2 and keyterm.startswith("@") is False:
- uidlist = keyterm.split("@")
- for uid in uidlist:
- ksearch.append("{0}@protonmail.com".format(uid))
- ksearch.append("{0}@protonmail.ch".format(uid))
- ksearch.append("{0}@pm.me".format(uid))
-elif keyterm.count("@") > 2:
- uidlist = keyterm.split("@")
- for uid in uidlist:
- ksearch.append("{0}@protonmail.com".format(uid))
- ksearch.append("{0}@protonmail.ch".format(uid))
- ksearch.append("{0}@pm.me".format(uid))
-else:
- ksearch.append(keyterm)
-
-for k in ksearch:
- payload = {"op": "get", "search": k}
- try:
- r = requests.get(url, verify=True, params=payload)
- if r.ok is True:
- result = c.key_import(r.content)
- elif r.ok is False:
- result = r.content
- except Exception as e:
- result = None
-
- if result is not None and hasattr(result, "considered") is False:
- print("{0} for {1}".format(result.decode(), k))
- elif result is not None and hasattr(result, "considered") is True:
- num_keys = len(result.imports)
- new_revs = result.new_revocations
- new_sigs = result.new_signatures
- new_subs = result.new_sub_keys
- new_uids = result.new_user_ids
- new_scrt = result.secret_imported
- nochange = result.unchanged
- print("""
-The total number of keys considered for import was: {0}
-
-With UIDs wholely or partially matching the following string:
-
- {1}
-
- Number of keys revoked: {2}
- Number of new signatures: {3}
- Number of new subkeys: {4}
- Number of new user IDs: {5}
-Number of new secret keys: {6}
- Number of unchanged keys: {7}
-
-The key IDs for all considered keys were:
-""".format(num_keys, k, new_revs, new_sigs, new_subs, new_uids, new_scrt,
- nochange))
- for i in range(num_keys):
- print(result.imports[i].fpr)
- print("")
- elif result is None:
- print(e)
-\end{verbatim}
-
-Both the above example, \href{../examples/howto/pmkey-import.py}{pmkey-import.py}, and a version which prompts
-for an alternative GnuPG home directory, \href{../examples/howto/pmkey-import-alt.py}{pmkey-import-alt.py}, are
-available with the other examples and are executable scripts.
-
-Note that while the ProtonMail servers are based on the SKS servers,
-their server is related more to their API and is not feature complete
-by comparison to the servers in the SKS pool. One notable difference
-being that the ProtonMail server does not permit non ProtonMail users
-to update their own keys, which could be a vector for attacking
-ProtonMail users who may not receive a key's revocation if it had been
-compromised.
-
-
-\subsubsection{Importing with HKP for Python}
-\label{sec:org11e4a60}
-Performing the same tasks with the \href{https://github.com/Selfnet/hkp4py}{hkp4py module} (available via PyPI)
-is not too much different, but does provide a number of options of
-benefit to end users. Not least of which being the ability to perform
-some checks on a key before importing it or not. For instance it may
-be the policy of a site or project to only import keys which have not
-been revoked. The hkp4py module permits such checks prior to the
-importing of the keys found.
-
-\begin{verbatim}
-import gpg
-import hkp4py
-import sys
-
-c = gpg.Context()
-server = hkp4py.KeyServer("hkps://hkps.pool.sks-keyservers.net")
-results = []
-keys = []
-
-if len(sys.argv) > 2:
- pattern = " ".join(sys.argv[1:])
-elif len(sys.argv) == 2:
- pattern = sys.argv[1]
-else:
- pattern = input("Enter the pattern to search for keys or user IDs: ")
-
-
-if pattern is not None:
- try:
- key = server.search(hex(int(pattern, 16)))
- keyed = True
- except ValueError as ve:
- key = server.search(pattern)
- keyed = False
-
- if key is not None:
- keys.append(key[0])
- if keyed is True:
- try:
- fob = server.search(pattern)
- except:
- fob = None
- if fob is not None:
- keys.append(fob[0])
- else:
- pass
- else:
- pass
-
- for logrus in pattern.split():
- try:
- key = server.search(hex(int(logrus, 16)))
- hexed = True
- except ValueError as ve:
- key = server.search(logrus)
- hexed = False
-
- if key is not None:
- keys.append(key[0])
- if hexed is True:
- try:
- fob = server.search(logrus)
- except:
- fob = None
- if fob is not None:
- keys.append(fob[0])
- else:
- pass
- else:
- pass
-
-
-if len(keys) > 0:
- for key in keys:
- import_result = c.key_import(key.key_blob)
- results.append(import_result)
-
-for result in results:
- if result is not None and hasattr(result, "considered") is False:
- print(result)
- elif result is not None and hasattr(result, "considered") is True:
- num_keys = len(result.imports)
- new_revs = result.new_revocations
- new_sigs = result.new_signatures
- new_subs = result.new_sub_keys
- new_uids = result.new_user_ids
- new_scrt = result.secret_imported
- nochange = result.unchanged
- print("""
-The total number of keys considered for import was: {0}
-
- Number of keys revoked: {1}
- Number of new signatures: {2}
- Number of new subkeys: {3}
- Number of new user IDs: {4}
-Number of new secret keys: {5}
- Number of unchanged keys: {6}
-
-The key IDs for all considered keys were:
-""".format(num_keys, new_revs, new_sigs, new_subs, new_uids, new_scrt,
- nochange))
- for i in range(num_keys):
- print(result.imports[i].fpr)
- print("")
- else:
- pass
-\end{verbatim}
-
-Since the hkp4py module handles multiple keys just as effectively as
-one (\texttt{keys} is a list of responses per matching key), the example
-above is able to do a little bit more with the returned data before
-anything is actually imported.
-
-
-\subsubsection{Importing from ProtonMail with HKP for Python}
-\label{sec:orgce872d8}
-Though this can provide certain benefits even when working with
-ProtonMail, the scope is somewhat constrained there due to the
-limitations of the ProtonMail keyserver.
-
-For instance, searching the SKS keyserver pool for the term "gnupg"
-produces hundreds of results from any time the word appears in any
-part of a user ID. Performing the same search on the ProtonMail
-keyserver returns zero results, even though there are at least two
-test accounts which include it as part of the username.
-
-The cause of this discrepancy is the deliberate configuration of that
-server by ProtonMail to require an exact match of the full email
-address of the ProtonMail user whose key is being requested.
-Presumably this is intended to reduce breaches of privacy of their
-users as an email address must already be known before a key for that
-address can be obtained.
-
-
-\begin{enumerate}
-\item Import from ProtonMail via HKP for Python Example no. 1
-\label{sec:org4ffaa43}
-The following script is available with the rest of the examples under
-the somewhat less than original name, \texttt{pmkey-import-hkp.py}.
-
-\begin{verbatim}
-import gpg
-import hkp4py
-import os.path
-import sys
-
-print("""
-This script searches the ProtonMail key server for the specified key and
-imports it.
-
-Usage: pmkey-import-hkp.py [search strings]
-""")
-
-c = gpg.Context(armor=True)
-server = hkp4py.KeyServer("hkps://api.protonmail.ch")
-keyterms = []
-ksearch = []
-allkeys = []
-results = []
-paradox = []
-homeless = None
-
-if len(sys.argv) > 2:
- keyterms = sys.argv[1:]
-elif len(sys.argv) == 2:
- keyterm = sys.argv[1]
- keyterms.append(keyterm)
-else:
- key_term = input("Enter the key ID, UID or search string: ")
- keyterms = key_term.split()
-
-for keyterm in keyterms:
- if keyterm.count("@") == 2 and keyterm.startswith("@") is True:
- ksearch.append(keyterm[1:])
- ksearch.append(keyterm[1:])
- ksearch.append(keyterm[1:])
- elif keyterm.count("@") == 1 and keyterm.startswith("@") is True:
- ksearch.append("{0}@protonmail.com".format(keyterm[1:]))
- ksearch.append("{0}@protonmail.ch".format(keyterm[1:]))
- ksearch.append("{0}@pm.me".format(keyterm[1:]))
- elif keyterm.count("@") == 0:
- ksearch.append("{0}@protonmail.com".format(keyterm))
- ksearch.append("{0}@protonmail.ch".format(keyterm))
- ksearch.append("{0}@pm.me".format(keyterm))
- elif keyterm.count("@") == 2 and keyterm.startswith("@") is False:
- uidlist = keyterm.split("@")
- for uid in uidlist:
- ksearch.append("{0}@protonmail.com".format(uid))
- ksearch.append("{0}@protonmail.ch".format(uid))
- ksearch.append("{0}@pm.me".format(uid))
- elif keyterm.count("@") > 2:
- uidlist = keyterm.split("@")
- for uid in uidlist:
- ksearch.append("{0}@protonmail.com".format(uid))
- ksearch.append("{0}@protonmail.ch".format(uid))
- ksearch.append("{0}@pm.me".format(uid))
- else:
- ksearch.append(keyterm)
-
-for k in ksearch:
- print("Checking for key for: {0}".format(k))
- try:
- keys = server.search(k)
- if isinstance(keys, list) is True:
- for key in keys:
- allkeys.append(key)
- try:
- import_result = c.key_import(key.key_blob)
- except Exception as e:
- import_result = c.key_import(key.key)
- else:
- paradox.append(keys)
- import_result = None
- except Exception as e:
- import_result = None
- results.append(import_result)
-
-for result in results:
- if result is not None and hasattr(result, "considered") is False:
- print("{0} for {1}".format(result.decode(), k))
- elif result is not None and hasattr(result, "considered") is True:
- num_keys = len(result.imports)
- new_revs = result.new_revocations
- new_sigs = result.new_signatures
- new_subs = result.new_sub_keys
- new_uids = result.new_user_ids
- new_scrt = result.secret_imported
- nochange = result.unchanged
- print("""
-The total number of keys considered for import was: {0}
-
-With UIDs wholely or partially matching the following string:
-
- {1}
-
- Number of keys revoked: {2}
- Number of new signatures: {3}
- Number of new subkeys: {4}
- Number of new user IDs: {5}
-Number of new secret keys: {6}
- Number of unchanged keys: {7}
-
-The key IDs for all considered keys were:
-""".format(num_keys, k, new_revs, new_sigs, new_subs, new_uids, new_scrt,
- nochange))
- for i in range(num_keys):
- print(result.imports[i].fpr)
- print("")
- elif result is None:
- pass
-\end{verbatim}
-
-
-\item Import from ProtonMail via HKP for Python Example no. 2
-\label{sec:org62d6113}
-Like its counterpart above, this script can also be found with the
-rest of the examples, by the name pmkey-import-hkp-alt.py.
-
-With this script a modicum of effort has been made to treat anything
-passed as a \texttt{homedir} which either does not exist or which is not a
-directory, as also being a pssible user ID to check for. It's not
-guaranteed to pick up on all such cases, but it should cover most of
-them.
-
-\begin{verbatim}
-import gpg
-import hkp4py
-import os.path
-import sys
-
-print("""
-This script searches the ProtonMail key server for the specified key and
-imports it. Optionally enables specifying a different GnuPG home directory.
-
-Usage: pmkey-import-hkp.py [homedir] [search string]
- or: pmkey-import-hkp.py [search string]
-""")
-
-c = gpg.Context(armor=True)
-server = hkp4py.KeyServer("hkps://api.protonmail.ch")
-keyterms = []
-ksearch = []
-allkeys = []
-results = []
-paradox = []
-homeless = None
-
-if len(sys.argv) > 3:
- homedir = sys.argv[1]
- keyterms = sys.argv[2:]
-elif len(sys.argv) == 3:
- homedir = sys.argv[1]
- keyterm = sys.argv[2]
- keyterms.append(keyterm)
-elif len(sys.argv) == 2:
- homedir = ""
- keyterm = sys.argv[1]
- keyterms.append(keyterm)
-else:
- keyterm = input("Enter the key ID, UID or search string: ")
- homedir = input("Enter the GPG configuration directory path (optional): ")
- keyterms.append(keyterm)
-
-if len(homedir) == 0:
- homedir = None
- homeless = False
-
-if homedir is not None:
- if homedir.startswith("~"):
- if os.path.exists(os.path.expanduser(homedir)) is True:
- if os.path.isdir(os.path.expanduser(homedir)) is True:
- c.home_dir = os.path.realpath(os.path.expanduser(homedir))
- else:
- homeless = True
- else:
- homeless = True
- elif os.path.exists(os.path.realpath(homedir)) is True:
- if os.path.isdir(os.path.realpath(homedir)) is True:
- c.home_dir = os.path.realpath(homedir)
- else:
- homeless = True
- else:
- homeless = True
-
-# First check to see if the homedir really is a homedir and if not, treat it as
-# a search string.
-if homeless is True:
- keyterms.append(homedir)
- c.home_dir = None
-else:
- pass
-
-for keyterm in keyterms:
- if keyterm.count("@") == 2 and keyterm.startswith("@") is True:
- ksearch.append(keyterm[1:])
- ksearch.append(keyterm[1:])
- ksearch.append(keyterm[1:])
- elif keyterm.count("@") == 1 and keyterm.startswith("@") is True:
- ksearch.append("{0}@protonmail.com".format(keyterm[1:]))
- ksearch.append("{0}@protonmail.ch".format(keyterm[1:]))
- ksearch.append("{0}@pm.me".format(keyterm[1:]))
- elif keyterm.count("@") == 0:
- ksearch.append("{0}@protonmail.com".format(keyterm))
- ksearch.append("{0}@protonmail.ch".format(keyterm))
- ksearch.append("{0}@pm.me".format(keyterm))
- elif keyterm.count("@") == 2 and keyterm.startswith("@") is False:
- uidlist = keyterm.split("@")
- for uid in uidlist:
- ksearch.append("{0}@protonmail.com".format(uid))
- ksearch.append("{0}@protonmail.ch".format(uid))
- ksearch.append("{0}@pm.me".format(uid))
- elif keyterm.count("@") > 2:
- uidlist = keyterm.split("@")
- for uid in uidlist:
- ksearch.append("{0}@protonmail.com".format(uid))
- ksearch.append("{0}@protonmail.ch".format(uid))
- ksearch.append("{0}@pm.me".format(uid))
- else:
- ksearch.append(keyterm)
-
-for k in ksearch:
- print("Checking for key for: {0}".format(k))
- try:
- keys = server.search(k)
- if isinstance(keys, list) is True:
- for key in keys:
- allkeys.append(key)
- try:
- import_result = c.key_import(key.key_blob)
- except Exception as e:
- import_result = c.key_import(key.key)
- else:
- paradox.append(keys)
- import_result = None
- except Exception as e:
- import_result = None
- results.append(import_result)
-
-for result in results:
- if result is not None and hasattr(result, "considered") is False:
- print("{0} for {1}".format(result.decode(), k))
- elif result is not None and hasattr(result, "considered") is True:
- num_keys = len(result.imports)
- new_revs = result.new_revocations
- new_sigs = result.new_signatures
- new_subs = result.new_sub_keys
- new_uids = result.new_user_ids
- new_scrt = result.secret_imported
- nochange = result.unchanged
- print("""
-The total number of keys considered for import was: {0}
-
-With UIDs wholely or partially matching the following string:
-
- {1}
-
- Number of keys revoked: {2}
- Number of new signatures: {3}
- Number of new subkeys: {4}
- Number of new user IDs: {5}
-Number of new secret keys: {6}
- Number of unchanged keys: {7}
-
-The key IDs for all considered keys were:
-""".format(num_keys, k, new_revs, new_sigs, new_subs, new_uids, new_scrt,
- nochange))
- for i in range(num_keys):
- print(result.imports[i].fpr)
- print("")
- elif result is None:
- pass
-\end{verbatim}
-\end{enumerate}
-
-
-\subsection{Exporting keys}
-\label{sec:orgceb798d}
-Exporting keys remains a reasonably simple task, but has been
-separated into three different functions for the OpenPGP cryptographic
-engine. Two of those functions are for exporting public keys and the
-third is for exporting secret keys.
-
-
-\subsubsection{Exporting public keys}
-\label{sec:orga5e633a}
-There are two methods of exporting public keys, both of which are very
-similar to the other. The default method, \texttt{key\_export()}, will export
-a public key or keys matching a specified pattern as normal. The
-alternative, the \texttt{key\_export\_minimal()} method, will do the same thing
-except producing a minimised output with extra signatures and third
-party signatures or certifications removed.
-
-\begin{verbatim}
-import gpg
-import os.path
-import sys
-
-print("""
-This script exports one or more public keys.
-""")
-
-c = gpg.Context(armor=True)
-
-if len(sys.argv) >= 4:
- keyfile = sys.argv[1]
- logrus = sys.argv[2]
- homedir = sys.argv[3]
-elif len(sys.argv) == 3:
- keyfile = sys.argv[1]
- logrus = sys.argv[2]
- homedir = input("Enter the GPG configuration directory path (optional): ")
-elif len(sys.argv) == 2:
- keyfile = sys.argv[1]
- logrus = input("Enter the UID matching the key(s) to export: ")
- homedir = input("Enter the GPG configuration directory path (optional): ")
-else:
- keyfile = input("Enter the path and filename to save the secret key to: ")
- logrus = input("Enter the UID matching the key(s) to export: ")
- homedir = input("Enter the GPG configuration directory path (optional): ")
-
-if homedir.startswith("~"):
- if os.path.exists(os.path.expanduser(homedir)) is True:
- c.home_dir = os.path.expanduser(homedir)
- else:
- pass
-elif os.path.exists(homedir) is True:
- c.home_dir = homedir
-else:
- pass
-
-try:
- result = c.key_export(pattern=logrus)
-except:
- result = c.key_export(pattern=None)
-
-if result is not None:
- with open(keyfile, "wb") as f:
- f.write(result)
-else:
- pass
-\end{verbatim}
-
-It should be noted that the result will only return \texttt{None} when a
-search pattern has been entered, but has not matched any keys. When
-the search pattern itself is set to \texttt{None} this triggers the exporting
-of the entire public keybox.
-
-\begin{verbatim}
-import gpg
-import os.path
-import sys
-
-print("""
-This script exports one or more public keys in minimised form.
-""")
-
-c = gpg.Context(armor=True)
-
-if len(sys.argv) >= 4:
- keyfile = sys.argv[1]
- logrus = sys.argv[2]
- homedir = sys.argv[3]
-elif len(sys.argv) == 3:
- keyfile = sys.argv[1]
- logrus = sys.argv[2]
- homedir = input("Enter the GPG configuration directory path (optional): ")
-elif len(sys.argv) == 2:
- keyfile = sys.argv[1]
- logrus = input("Enter the UID matching the key(s) to export: ")
- homedir = input("Enter the GPG configuration directory path (optional): ")
-else:
- keyfile = input("Enter the path and filename to save the secret key to: ")
- logrus = input("Enter the UID matching the key(s) to export: ")
- homedir = input("Enter the GPG configuration directory path (optional): ")
-
-if homedir.startswith("~"):
- if os.path.exists(os.path.expanduser(homedir)) is True:
- c.home_dir = os.path.expanduser(homedir)
- else:
- pass
-elif os.path.exists(homedir) is True:
- c.home_dir = homedir
-else:
- pass
-
-try:
- result = c.key_export_minimal(pattern=logrus)
-except:
- result = c.key_export_minimal(pattern=None)
-
-if result is not None:
- with open(keyfile, "wb") as f:
- f.write(result)
-else:
- pass
-\end{verbatim}
-
-
-\subsubsection{Exporting secret keys}
-\label{sec:org732fce1}
-Exporting secret keys is, functionally, very similar to exporting
-public keys; save for the invocation of \texttt{pinentry} via \texttt{gpg-agent} in
-order to securely enter the key's passphrase and authorise the export.
-
-The following example exports the secret key to a file which is then
-set with the same permissions as the output files created by the
-command line secret key export options.
-
-\begin{verbatim}
-import gpg
-import os
-import os.path
-import sys
-
-print("""
-This script exports one or more secret keys.
-
-The gpg-agent and pinentry are invoked to authorise the export.
-""")
-
-c = gpg.Context(armor=True)
-
-if len(sys.argv) >= 4:
- keyfile = sys.argv[1]
- logrus = sys.argv[2]
- homedir = sys.argv[3]
-elif len(sys.argv) == 3:
- keyfile = sys.argv[1]
- logrus = sys.argv[2]
- homedir = input("Enter the GPG configuration directory path (optional): ")
-elif len(sys.argv) == 2:
- keyfile = sys.argv[1]
- logrus = input("Enter the UID matching the secret key(s) to export: ")
- homedir = input("Enter the GPG configuration directory path (optional): ")
-else:
- keyfile = input("Enter the path and filename to save the secret key to: ")
- logrus = input("Enter the UID matching the secret key(s) to export: ")
- homedir = input("Enter the GPG configuration directory path (optional): ")
-
-if len(homedir) == 0:
- homedir = None
-elif homedir.startswith("~"):
- userdir = os.path.expanduser(homedir)
- if os.path.exists(userdir) is True:
- homedir = os.path.realpath(userdir)
- else:
- homedir = None
-else:
- homedir = os.path.realpath(homedir)
-
-if os.path.exists(homedir) is False:
- homedir = None
-else:
- if os.path.isdir(homedir) is False:
- homedir = None
- else:
- pass
-
-if homedir is not None:
- c.home_dir = homedir
-else:
- pass
-
-try:
- result = c.key_export_secret(pattern=logrus)
-except:
- result = c.key_export_secret(pattern=None)
-
-if result is not None:
- with open(keyfile, "wb") as f:
- f.write(result)
- os.chmod(keyfile, 0o600)
-else:
- pass
-\end{verbatim}
-
-Alternatively the approach of the following script can be used. This
-longer example saves the exported secret key(s) in files in the GnuPG
-home directory, in addition to setting the file permissions as only
-readable and writable by the user. It also exports the secret key(s)
-twice in order to output both GPG binary (\texttt{.gpg}) and ASCII armoured
-(\texttt{.asc}) files.
-
-\begin{verbatim}
-import gpg
-import os
-import os.path
-import subprocess
-import sys
-
-print("""
-This script exports one or more secret keys as both ASCII armored and binary
-file formats, saved in files within the user's GPG home directory.
-
-The gpg-agent and pinentry are invoked to authorise the export.
-""")
-
-if sys.platform == "win32":
- gpgconfcmd = "gpgconf.exe --list-dirs homedir"
-else:
- gpgconfcmd = "gpgconf --list-dirs homedir"
-
-a = gpg.Context(armor=True)
-b = gpg.Context()
-c = gpg.Context()
-
-if len(sys.argv) >= 4:
- keyfile = sys.argv[1]
- logrus = sys.argv[2]
- homedir = sys.argv[3]
-elif len(sys.argv) == 3:
- keyfile = sys.argv[1]
- logrus = sys.argv[2]
- homedir = input("Enter the GPG configuration directory path (optional): ")
-elif len(sys.argv) == 2:
- keyfile = sys.argv[1]
- logrus = input("Enter the UID matching the secret key(s) to export: ")
- homedir = input("Enter the GPG configuration directory path (optional): ")
-else:
- keyfile = input("Enter the filename to save the secret key to: ")
- logrus = input("Enter the UID matching the secret key(s) to export: ")
- homedir = input("Enter the GPG configuration directory path (optional): ")
-
-if len(homedir) == 0:
- homedir = None
-elif homedir.startswith("~"):
- userdir = os.path.expanduser(homedir)
- if os.path.exists(userdir) is True:
- homedir = os.path.realpath(userdir)
- else:
- homedir = None
-else:
- homedir = os.path.realpath(homedir)
-
-if os.path.exists(homedir) is False:
- homedir = None
-else:
- if os.path.isdir(homedir) is False:
- homedir = None
- else:
- pass
-
-if homedir is not None:
- c.home_dir = homedir
-else:
- pass
-
-if c.home_dir is not None:
- if c.home_dir.endswith("/"):
- gpgfile = "{0}{1}.gpg".format(c.home_dir, keyfile)
- ascfile = "{0}{1}.asc".format(c.home_dir, keyfile)
- else:
- gpgfile = "{0}/{1}.gpg".format(c.home_dir, keyfile)
- ascfile = "{0}/{1}.asc".format(c.home_dir, keyfile)
-else:
- if os.path.exists(os.environ["GNUPGHOME"]) is True:
- hd = os.environ["GNUPGHOME"]
- else:
- try:
- hd = subprocess.getoutput(gpgconfcmd)
- except:
- process = subprocess.Popen(gpgconfcmd.split(),
- stdout=subprocess.PIPE)
- procom = process.communicate()
- if sys.version_info[0] == 2:
- hd = procom[0].strip()
- else:
- hd = procom[0].decode().strip()
- gpgfile = "{0}/{1}.gpg".format(hd, keyfile)
- ascfile = "{0}/{1}.asc".format(hd, keyfile)
-
-try:
- a_result = a.key_export_secret(pattern=logrus)
- b_result = b.key_export_secret(pattern=logrus)
-except:
- a_result = a.key_export_secret(pattern=None)
- b_result = b.key_export_secret(pattern=None)
-
-if a_result is not None:
- with open(ascfile, "wb") as f:
- f.write(a_result)
- os.chmod(ascfile, 0o600)
-else:
- pass
-
-if b_result is not None:
- with open(gpgfile, "wb") as f:
- f.write(b_result)
- os.chmod(gpgfile, 0o600)
-else:
- pass
-\end{verbatim}
-
-
-\subsubsection{Sending public keys to the SKS Keyservers}
-\label{sec:orgffa4d7d}
-As with the previous section on importing keys, the \texttt{hkp4py} module
-adds another option with exporting keys in order to send them to the
-public keyservers.
-
-The following example demonstrates how this may be done.
-
-\begin{verbatim}
-import gpg
-import hkp4py
-import os.path
-import sys
-
-print("""
-This script sends one or more public keys to the SKS keyservers and is
-essentially a slight variation on the export-key.py script.
-""")
-
-c = gpg.Context(armor=True)
-server = hkp4py.KeyServer("hkps://hkps.pool.sks-keyservers.net")
-
-if len(sys.argv) > 2:
- logrus = " ".join(sys.argv[1:])
-elif len(sys.argv) == 2:
- logrus = sys.argv[1]
-else:
- logrus = input("Enter the UID matching the key(s) to send: ")
-
-if len(logrus) > 0:
- try:
- export_result = c.key_export(pattern=logrus)
- except Exception as e:
- print(e)
- export_result = None
-else:
- export_result = c.key_export(pattern=None)
-
-if export_result is not None:
- try:
- try:
- send_result = server.add(export_result)
- except:
- send_result = server.add(export_result.decode())
- if send_result is not None:
- print(send_result)
- else:
- pass
- except Exception as e:
- print(e)
-else:
- pass
-\end{verbatim}
-
-An expanded version of this script with additional functions for
-specifying an alternative homedir location is in the examples
-directory as \texttt{send-key-to-keyserver.py}.
-
-The \texttt{hkp4py} module appears to handle both string and byte literal text
-data equally well, but the GPGME bindings deal primarily with byte
-literal data only and so this script sends in that format first, then
-tries the string literal form.
-
-
-\section{Basic Functions}
-\label{sec:org777ef00}
-The most frequently called features of any cryptographic library will
-be the most fundamental tasks for encryption software. In this
-section we will look at how to programmatically encrypt data, decrypt
-it, sign it and verify signatures.
-
-
-\subsection{Encryption}
-\label{sec:org793824e}
-Encrypting is very straight forward. In the first example below the
-message, \texttt{text}, is encrypted to a single recipient's key. In the
-second example the message will be encrypted to multiple recipients.
-
-
-\subsubsection{Encrypting to one key}
-\label{sec:org3aede2e}
-Once the the Context is set the main issues with encrypting data is
-essentially reduced to key selection and the keyword arguments
-specified in the \texttt{gpg.Context().encrypt()} method.
-
-Those keyword arguments are: \texttt{recipients}, a list of keys encrypted to
-(covered in greater detail in the following section); \texttt{sign}, whether
-or not to sign the plaintext data, see subsequent sections on signing
-and verifying signatures below (defaults to \texttt{True}); \texttt{sink}, to write
-results or partial results to a secure sink instead of returning it
-(defaults to \texttt{None}); \texttt{passphrase}, only used when utilising symmetric
-encryption (defaults to \texttt{None}); \texttt{always\_trust}, used to override the
-trust model settings for recipient keys (defaults to \texttt{False});
-\texttt{add\_encrypt\_to}, utilises any preconfigured \texttt{encrypt-to} or
-\texttt{default-key} settings in the user's \texttt{gpg.conf} file (defaults to
-\texttt{False}); \texttt{prepare}, prepare for encryption (defaults to \texttt{False});
-\texttt{expect\_sign}, prepare for signing (defaults to \texttt{False}); \texttt{compress},
-compresses the plaintext prior to encryption (defaults to \texttt{True}).
-
-\begin{verbatim}
-import gpg
-
-a_key = "0x12345678DEADBEEF"
-text = b"""Some text to test with.
-
-Since the text in this case must be bytes, it is most likely that
-the input form will be a separate file which is opened with "rb"
-as this is the simplest method of obtaining the correct data format.
-"""
-
-c = gpg.Context(armor=True)
-rkey = list(c.keylist(pattern=a_key, secret=False))
-ciphertext, result, sign_result = c.encrypt(text, recipients=rkey, sign=False)
-
-with open("secret_plans.txt.asc", "wb") as afile:
- afile.write(ciphertext)
-\end{verbatim}
-
-Though this is even more likely to be used like this; with the
-plaintext input read from a file, the recipient keys used for
-encryption regardless of key trust status and the encrypted output
-also encrypted to any preconfigured keys set in the \texttt{gpg.conf} file:
-
-\begin{verbatim}
-import gpg
-
-a_key = "0x12345678DEADBEEF"
-
-with open("secret_plans.txt", "rb") as afile:
- text = afile.read()
-
-c = gpg.Context(armor=True)
-rkey = list(c.keylist(pattern=a_key, secret=False))
-ciphertext, result, sign_result = c.encrypt(text, recipients=rkey, sign=True,
- always_trust=True,
- add_encrypt_to=True)
-
-with open("secret_plans.txt.asc", "wb") as afile:
- afile.write(ciphertext)
-\end{verbatim}
-
-If the \texttt{recipients} parameter is empty then the plaintext is encrypted
-symmetrically. If no \texttt{passphrase} is supplied as a parameter or via a
-callback registered with the \texttt{Context()} then an out-of-band prompt
-for the passphrase via pinentry will be invoked.
-
-
-\subsubsection{Encrypting to multiple keys}
-\label{sec:org076362a}
-Encrypting to multiple keys essentially just expands upon the key
-selection process and the recipients from the previous examples.
-
-The following example encrypts a message (\texttt{text}) to everyone with an
-email address on the \texttt{gnupg.org} domain,\footnote{You probably don't really want to do this. Searching the
-keyservers for "gnupg.org" produces over 400 results, the majority of
-which aren't actually at the gnupg.org domain, but just included a
-comment regarding the project in their key somewhere.} but does \emph{not} encrypt
-to a default key or other key which is configured to normally encrypt
-to.
-
-\begin{verbatim}
-import gpg
-
-text = b"""Oh look, another test message.
-
-The same rules apply as with the previous example and more likely
-than not, the message will actually be drawn from reading the
-contents of a file or, maybe, from entering data at an input()
-prompt.
-
-Since the text in this case must be bytes, it is most likely that
-the input form will be a separate file which is opened with "rb"
-as this is the simplest method of obtaining the correct data
-format.
-"""
-
-c = gpg.Context(armor=True)
-rpattern = list(c.keylist(pattern="@gnupg.org", secret=False))
-logrus = []
-
-for i in range(len(rpattern)):
- if rpattern[i].can_encrypt == 1:
- logrus.append(rpattern[i])
-
-ciphertext, result, sign_result = c.encrypt(text, recipients=logrus,
- sign=False, always_trust=True)
-
-with open("secret_plans.txt.asc", "wb") as afile:
- afile.write(ciphertext)
-\end{verbatim}
-
-All it would take to change the above example to sign the message
-and also encrypt the message to any configured default keys would
-be to change the \texttt{c.encrypt} line to this:
-
-\begin{verbatim}
-ciphertext, result, sign_result = c.encrypt(text, recipients=logrus,
- always_trust=True,
- add_encrypt_to=True)
-\end{verbatim}
-
-The only keyword arguments requiring modification are those for which
-the default values are changing. The default value of \texttt{sign} is
-\texttt{True}, the default of \texttt{always\_trust} is \texttt{False}, the default of
-\texttt{add\_encrypt\_to} is \texttt{False}.
-
-If \texttt{always\_trust} is not set to \texttt{True} and any of the recipient keys
-are not trusted (e.g. not signed or locally signed) then the
-encryption will raise an error. It is possible to mitigate this
-somewhat with something more like this:
-
-\begin{verbatim}
-import gpg
-
-with open("secret_plans.txt.asc", "rb") as afile:
- text = afile.read()
-
-c = gpg.Context(armor=True)
-rpattern = list(c.keylist(pattern="@gnupg.org", secret=False))
-logrus = []
-
-for i in range(len(rpattern)):
- if rpattern[i].can_encrypt == 1:
- logrus.append(rpattern[i])
-
- try:
- ciphertext, result, sign_result = c.encrypt(text, recipients=logrus,
- add_encrypt_to=True)
- except gpg.errors.InvalidRecipients as e:
- for i in range(len(e.recipients)):
- for n in range(len(logrus)):
- if logrus[n].fpr == e.recipients[i].fpr:
- logrus.remove(logrus[n])
- else:
- pass
- try:
- ciphertext, result, sign_result = c.encrypt(text,
- recipients=logrus,
- add_encrypt_to=True)
- with open("secret_plans.txt.asc", "wb") as afile:
- afile.write(ciphertext)
- except:
- pass
-\end{verbatim}
-
-This will attempt to encrypt to all the keys searched for, then remove
-invalid recipients if it fails and try again.
-
-
-\subsection{Decryption}
-\label{sec:orgabfeed8}
-Decrypting something encrypted to a key in one's secret keyring is
-fairly straight forward.
-
-In this example code, however, preconfiguring either \texttt{gpg.Context()}
-or \texttt{gpg.core.Context()} as \texttt{c} is unnecessary because there is no need
-to modify the Context prior to conducting the decryption and since the
-Context is only used once, setting it to \texttt{c} simply adds lines for no
-gain.
-
-\begin{verbatim}
-import gpg
-
-ciphertext = input("Enter path and filename of encrypted file: ")
-newfile = input("Enter path and filename of file to save decrypted data to: ")
-
-with open(ciphertext, "rb") as cfile:
- try:
- plaintext, result, verify_result = gpg.Context().decrypt(cfile)
- except gpg.errors.GPGMEError as e:
- plaintext = None
- print(e)
-
-if plaintext is not None:
- with open(newfile, "wb") as nfile:
- nfile.write(plaintext)
- else:
- pass
-\end{verbatim}
-
-The data available in \texttt{plaintext} in this example is the decrypted
-content as a byte object, the recipient key IDs and algorithms in
-\texttt{result} and the results of verifying any signatures of the data in
-\texttt{verify\_result}.
-
-If \texttt{gpg.Context().decrypt(cfile, verify=False)} is called instead,
-then \texttt{verify\_result} will be returned as \texttt{None} and the rest remains
-as described here.
-
-
-\subsection{Signing text and files}
-\label{sec:orge3099cc}
-The following sections demonstrate how to specify keys to sign with.
-
-
-\subsubsection{Signing key selection}
-\label{sec:org98a49d4}
-By default GPGME and the Python bindings will use the default key
-configured for the user invoking the GPGME API. If there is no
-default key specified and there is more than one secret key available
-it may be necessary to specify the key or keys with which to sign
-messages and files.
-
-\begin{verbatim}
-import gpg
-
-logrus = input("Enter the email address or string to match signing keys to: ")
-hancock = gpg.Context().keylist(pattern=logrus, secret=True)
-sig_src = list(hancock)
-\end{verbatim}
-
-The signing examples in the following sections include the explicitly
-designated \texttt{signers} parameter in two of the five examples; once where
-the resulting signature would be ASCII armoured and once where it
-would not be armoured.
-
-While it would be possible to enter a key ID or fingerprint here to
-match a specific key, it is not possible to enter two fingerprints and
-match two keys since the patten expects a string, bytes or None and
-not a list. A string with two fingerprints won't match any single
-key.
-
-
-\subsubsection{Normal or default signing messages or files}
-\label{sec:orgde09e64}
-The normal or default signing process is essentially the same as is
-most often invoked when also encrypting a message or file. So when
-the encryption component is not utilised, the result is to produce an
-encoded and signed output which may or may not be ASCII armoured and
-which may or may not also be compressed.
-
-By default compression will be used unless GnuPG detects that the
-plaintext is already compressed. ASCII armouring will be determined
-according to the value of \texttt{gpg.Context().armor}.
-
-The compression algorithm is selected in much the same way as the
-symmetric encryption algorithm or the hash digest algorithm is when
-multiple keys are involved; from the preferences saved into the key
-itself or by comparison with the preferences with all other keys
-involved.
-
-\begin{verbatim}
-import gpg
-
-text0 = """Declaration of ... something.
-
-"""
-text = text0.encode()
-
-c = gpg.Context(armor=True, signers=sig_src)
-signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.NORMAL)
-
-with open("/path/to/statement.txt.asc", "w") as afile:
- afile.write(signed_data.decode())
-\end{verbatim}
-
-Though everything in this example is accurate, it is more likely that
-reading the input data from another file and writing the result to a
-new file will be performed more like the way it is done in the next
-example. Even if the output format is ASCII armoured.
-
-\begin{verbatim}
-import gpg
-
-with open("/path/to/statement.txt", "rb") as tfile:
- text = tfile.read()
-
-c = gpg.Context()
-signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.NORMAL)
-
-with open("/path/to/statement.txt.sig", "wb") as afile:
- afile.write(signed_data)
-\end{verbatim}
-
-
-\subsubsection{Detached signing messages and files}
-\label{sec:orgbcaa288}
-Detached signatures will often be needed in programmatic uses of
-GPGME, either for signing files (e.g. tarballs of code releases) or as
-a component of message signing (e.g. PGP/MIME encoded email).
-
-\begin{verbatim}
-import gpg
-
-text0 = """Declaration of ... something.
-
-"""
-text = text0.encode()
-
-c = gpg.Context(armor=True)
-signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.DETACH)
-
-with open("/path/to/statement.txt.asc", "w") as afile:
- afile.write(signed_data.decode())
-\end{verbatim}
-
-As with normal signatures, detached signatures are best handled as
-byte literals, even when the output is ASCII armoured.
-
-\begin{verbatim}
-import gpg
-
-with open("/path/to/statement.txt", "rb") as tfile:
- text = tfile.read()
-
-c = gpg.Context(signers=sig_src)
-signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.DETACH)
-
-with open("/path/to/statement.txt.sig", "wb") as afile:
- afile.write(signed_data)
-\end{verbatim}
-
-
-\subsubsection{Clearsigning messages or text}
-\label{sec:orga3ad66e}
-Though PGP/in-line messages are no longer encouraged in favour of
-PGP/MIME, there is still sometimes value in utilising in-line
-signatures. This is where clear-signed messages or text is of value.
-
-\begin{verbatim}
-import gpg
-
-text0 = """Declaration of ... something.
-
-"""
-text = text0.encode()
-
-c = gpg.Context()
-signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.CLEAR)
-
-with open("/path/to/statement.txt.asc", "w") as afile:
- afile.write(signed_data.decode())
-\end{verbatim}
-
-In spite of the appearance of a clear-signed message, the data handled
-by GPGME in signing it must still be byte literals.
-
-\begin{verbatim}
-import gpg
-
-with open("/path/to/statement.txt", "rb") as tfile:
- text = tfile.read()
-
-c = gpg.Context()
-signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.CLEAR)
-
-with open("/path/to/statement.txt.asc", "wb") as afile:
- afile.write(signed_data)
-\end{verbatim}
-
-
-\subsection{Signature verification}
-\label{sec:orgdc5ac08}
-Essentially there are two principal methods of verification of a
-signature. The first of these is for use with the normal or default
-signing method and for clear-signed messages. The second is for use
-with files and data with detached signatures.
-
-The following example is intended for use with the default signing
-method where the file was not ASCII armoured:
-
-\begin{verbatim}
-import gpg
-import time
-
-filename = "statement.txt"
-gpg_file = "statement.txt.gpg"
-
-c = gpg.Context()
-
-try:
- data, result = c.verify(open(gpg_file))
- verified = True
-except gpg.errors.BadSignatures as e:
- verified = False
- print(e)
-
-if verified is True:
- for i in range(len(result.signatures)):
- sign = result.signatures[i]
- print("""Good signature from:
-{0}
-with key {1}
-made at {2}
-""".format(c.get_key(sign.fpr).uids[0].uid, sign.fpr,
- time.ctime(sign.timestamp)))
-else:
- pass
-\end{verbatim}
-
-Whereas this next example, which is almost identical would work with
-normal ASCII armoured files and with clear-signed files:
-
-\begin{verbatim}
-import gpg
-import time
-
-filename = "statement.txt"
-asc_file = "statement.txt.asc"
-
-c = gpg.Context()
-
-try:
- data, result = c.verify(open(asc_file))
- verified = True
-except gpg.errors.BadSignatures as e:
- verified = False
- print(e)
-
-if verified is True:
- for i in range(len(result.signatures)):
- sign = result.signatures[i]
- print("""Good signature from:
-{0}
-with key {1}
-made at {2}
-""".format(c.get_key(sign.fpr).uids[0].uid, sign.fpr,
- time.ctime(sign.timestamp)))
-else:
- pass
-\end{verbatim}
-
-In both of the previous examples it is also possible to compare the
-original data that was signed against the signed data in \texttt{data} to see
-if it matches with something like this:
-
-\begin{verbatim}
-with open(filename, "rb") as afile:
- text = afile.read()
-
-if text == data:
- print("Good signature.")
-else:
- pass
-\end{verbatim}
-
-The following two examples, however, deal with detached signatures.
-With his method of verification the data that was signed does not get
-returned since it is already being explicitly referenced in the first
-argument of \texttt{c.verify}. So \texttt{data} is \texttt{None} and only the information
-in \texttt{result} is available.
-
-\begin{verbatim}
-import gpg
-import time
-
-filename = "statement.txt"
-sig_file = "statement.txt.sig"
-
-c = gpg.Context()
-
-try:
- data, result = c.verify(open(filename), open(sig_file))
- verified = True
-except gpg.errors.BadSignatures as e:
- verified = False
- print(e)
-
-if verified is True:
- for i in range(len(result.signatures)):
- sign = result.signatures[i]
- print("""Good signature from:
-{0}
-with key {1}
-made at {2}
-""".format(c.get_key(sign.fpr).uids[0].uid, sign.fpr,
- time.ctime(sign.timestamp)))
-else:
- pass
-\end{verbatim}
-
-\begin{verbatim}
-import gpg
-import time
-
-filename = "statement.txt"
-asc_file = "statement.txt.asc"
-
-c = gpg.Context()
-
-try:
- data, result = c.verify(open(filename), open(asc_file))
- verified = True
-except gpg.errors.BadSignatures as e:
- verified = False
- print(e)
-
-if verified is True:
- for i in range(len(result.signatures)):
- sign = result.signatures[i]
- print("""Good signature from:
-{0}
-with key {1}
-made at {2}
-""".format(c.get_key(sign.fpr).uids[0].uid, sign.fpr,
- time.ctime(sign.timestamp)))
-else:
- pass
-\end{verbatim}
-
-
-\section{Creating keys and subkeys}
-\label{sec:orgddce733}
-The one thing, aside from GnuPG itself, that GPGME depends on, of
-course, is the keys themselves. So it is necessary to be able to
-generate them and modify them by adding subkeys, revoking or disabling
-them, sometimes deleting them and doing the same for user IDs.
-
-In the following examples a key will be created for the world's
-greatest secret agent, Danger Mouse. Since Danger Mouse is a secret
-agent he needs to be able to protect information to \texttt{SECRET} level
-clearance, so his keys will be 3072-bit keys.
-
-The pre-configured \texttt{gpg.conf} file which sets cipher, digest and other
-preferences contains the following configuration parameters:
-
-\begin{verbatim}
-expert
-allow-freeform-uid
-allow-secret-key-import
-trust-model tofu+pgp
-tofu-default-policy unknown
-enable-large-rsa
-enable-dsa2
-cert-digest-algo SHA512
-default-preference-list TWOFISH CAMELLIA256 AES256 CAMELLIA192 AES192 CAMELLIA128 AES BLOWFISH IDEA CAST5 3DES SHA512 SHA384 SHA256 SHA224 RIPEMD160 SHA1 ZLIB BZIP2 ZIP Uncompressed
-personal-cipher-preferences TWOFISH CAMELLIA256 AES256 CAMELLIA192 AES192 CAMELLIA128 AES BLOWFISH IDEA CAST5 3DES
-personal-digest-preferences SHA512 SHA384 SHA256 SHA224 RIPEMD160 SHA1
-personal-compress-preferences ZLIB BZIP2 ZIP Uncompressed
-\end{verbatim}
-
-
-\subsection{Primary key}
-\label{sec:org8aad39f}
-Generating a primary key uses the \texttt{create\_key} method in a Context.
-It contains multiple arguments and keyword arguments, including:
-\texttt{userid}, \texttt{algorithm}, \texttt{expires\_in}, \texttt{expires}, \texttt{sign}, \texttt{encrypt},
-\texttt{certify}, \texttt{authenticate}, \texttt{passphrase} and \texttt{force}. The defaults for
-all of those except \texttt{userid}, \texttt{algorithm}, \texttt{expires\_in}, \texttt{expires} and
-\texttt{passphrase} is \texttt{False}. The defaults for \texttt{algorithm} and
-\texttt{passphrase} is \texttt{None}. The default for \texttt{expires\_in} is \texttt{0}. The
-default for \texttt{expires} is \texttt{True}. There is no default for \texttt{userid}.
-
-If \texttt{passphrase} is left as \texttt{None} then the key will not be generated
-with a passphrase, if \texttt{passphrase} is set to a string then that will
-be the passphrase and if \texttt{passphrase} is set to \texttt{True} then gpg-agent
-will launch pinentry to prompt for a passphrase. For the sake of
-convenience, these examples will keep \texttt{passphrase} set to \texttt{None}.
-
-\begin{verbatim}
-import gpg
-
-c = gpg.Context()
-
-c.home_dir = "~/.gnupg-dm"
-userid = "Danger Mouse <dm@secret.example.net>"
-
-dmkey = c.create_key(userid, algorithm="rsa3072", expires_in=31536000,
- sign=True, certify=True)
-\end{verbatim}
-
-One thing to note here is the use of setting the \texttt{c.home\_dir}
-parameter. This enables generating the key or keys in a different
-location. In this case to keep the new key data created for this
-example in a separate location rather than adding it to existing and
-active key store data. As with the default directory, \texttt{\textasciitilde{}/.gnupg}, any
-temporary or separate directory needs the permissions set to only
-permit access by the directory owner. On posix systems this means
-setting the directory permissions to 700.
-
-The \texttt{temp-homedir-config.py} script in the HOWTO examples directory
-will create an alternative homedir with these configuration options
-already set and the correct directory and file permissions.
-
-The successful generation of the key can be confirmed via the returned
-\texttt{GenkeyResult} object, which includes the following data:
-
-\begin{verbatim}
-print("""
- Fingerprint: {0}
- Primary Key: {1}
- Public Key: {2}
- Secret Key: {3}
- Sub Key: {4}
-User IDs: {5}
-""".format(dmkey.fpr, dmkey.primary, dmkey.pubkey, dmkey.seckey, dmkey.sub,
- dmkey.uid))
-\end{verbatim}
-
-Alternatively the information can be confirmed using the command line
-program:
-
-\begin{verbatim}
-bash-4.4$ gpg --homedir ~/.gnupg-dm -K
-~/.gnupg-dm/pubring.kbx
-----------------------
-sec rsa3072 2018-03-15 [SC] [expires: 2019-03-15]
- 177B7C25DB99745EE2EE13ED026D2F19E99E63AA
-uid [ultimate] Danger Mouse <dm@secret.example.net>
-
-bash-4.4$
-\end{verbatim}
-
-As with generating keys manually, to preconfigure expanded preferences
-for the cipher, digest and compression algorithms, the \texttt{gpg.conf} file
-must contain those details in the home directory in which the new key
-is being generated. I used a cut down version of my own \texttt{gpg.conf}
-file in order to be able to generate this:
-
-\begin{verbatim}
-bash-4.4$ gpg --homedir ~/.gnupg-dm --edit-key 177B7C25DB99745EE2EE13ED026D2F19E99E63AA showpref quit
-Secret key is available.
-
-sec rsa3072/026D2F19E99E63AA
- created: 2018-03-15 expires: 2019-03-15 usage: SC
- trust: ultimate validity: ultimate
-[ultimate] (1). Danger Mouse <dm@secret.example.net>
-
-[ultimate] (1). Danger Mouse <dm@secret.example.net>
- Cipher: TWOFISH, CAMELLIA256, AES256, CAMELLIA192, AES192, CAMELLIA128, AES, BLOWFISH, IDEA, CAST5, 3DES
- Digest: SHA512, SHA384, SHA256, SHA224, RIPEMD160, SHA1
- Compression: ZLIB, BZIP2, ZIP, Uncompressed
- Features: MDC, Keyserver no-modify
-
-bash-4.4$
-\end{verbatim}
-
-
-\subsection{Subkeys}
-\label{sec:org1686912}
-Adding subkeys to a primary key is fairly similar to creating the
-primary key with the \texttt{create\_subkey} method. Most of the arguments
-are the same, but not quite all. Instead of the \texttt{userid} argument
-there is now a \texttt{key} argument for selecting which primary key to add
-the subkey to.
-
-In the following example an encryption subkey will be added to the
-primary key. Since Danger Mouse is a security conscious secret agent,
-this subkey will only be valid for about six months, half the length
-of the primary key.
-
-\begin{verbatim}
-import gpg
-
-c = gpg.Context()
-c.home_dir = "~/.gnupg-dm"
-
-key = c.get_key(dmkey.fpr, secret=True)
-dmsub = c.create_subkey(key, algorithm="rsa3072", expires_in=15768000,
- encrypt=True)
-\end{verbatim}
-
-As with the primary key, the results here can be checked with:
-
-\begin{verbatim}
-print("""
- Fingerprint: {0}
- Primary Key: {1}
- Public Key: {2}
- Secret Key: {3}
- Sub Key: {4}
-User IDs: {5}
-""".format(dmsub.fpr, dmsub.primary, dmsub.pubkey, dmsub.seckey, dmsub.sub,
- dmsub.uid))
-\end{verbatim}
-
-As well as on the command line with:
-
-\begin{verbatim}
-bash-4.4$ gpg --homedir ~/.gnupg-dm -K
-~/.gnupg-dm/pubring.kbx
-----------------------
-sec rsa3072 2018-03-15 [SC] [expires: 2019-03-15]
- 177B7C25DB99745EE2EE13ED026D2F19E99E63AA
-uid [ultimate] Danger Mouse <dm@secret.example.net>
-ssb rsa3072 2018-03-15 [E] [expires: 2018-09-13]
-
-bash-4.4$
-\end{verbatim}
-
-
-\subsection{User IDs}
-\label{sec:org2786b54}
-\subsubsection{Adding User IDs}
-\label{sec:org35fc1e8}
-By comparison to creating primary keys and subkeys, adding a new user
-ID to an existing key is much simpler. The method used to do this is
-\texttt{key\_add\_uid} and the only arguments it takes are for the \texttt{key} and
-the new \texttt{uid}.
-
-\begin{verbatim}
-import gpg
-
-c = gpg.Context()
-c.home_dir = "~/.gnupg-dm"
-
-dmfpr = "177B7C25DB99745EE2EE13ED026D2F19E99E63AA"
-key = c.get_key(dmfpr, secret=True)
-uid = "Danger Mouse <danger.mouse@secret.example.net>"
-
-c.key_add_uid(key, uid)
-\end{verbatim}
-
-Unsurprisingly the result of this is:
-
-\begin{verbatim}
-bash-4.4$ gpg --homedir ~/.gnupg-dm -K
-~/.gnupg-dm/pubring.kbx
-----------------------
-sec rsa3072 2018-03-15 [SC] [expires: 2019-03-15]
- 177B7C25DB99745EE2EE13ED026D2F19E99E63AA
-uid [ultimate] Danger Mouse <danger.mouse@secret.example.net>
-uid [ultimate] Danger Mouse <dm@secret.example.net>
-ssb rsa3072 2018-03-15 [E] [expires: 2018-09-13]
-
-bash-4.4$
-\end{verbatim}
-
-
-\subsubsection{Revoking User IDs}
-\label{sec:org45e3e8f}
-Revoking a user ID is a fairly similar process, except that it uses
-the \texttt{key\_revoke\_uid} method.
-
-\begin{verbatim}
-import gpg
-
-c = gpg.Context()
-c.home_dir = "~/.gnupg-dm"
-
-dmfpr = "177B7C25DB99745EE2EE13ED026D2F19E99E63AA"
-key = c.get_key(dmfpr, secret=True)
-uid = "Danger Mouse <danger.mouse@secret.example.net>"
-
-c.key_revoke_uid(key, uid)
-\end{verbatim}
-
-
-\subsection{Key certification}
-\label{sec:org14fd1c0}
-Since key certification is more frequently referred to as key signing,
-the method used to perform this function is \texttt{key\_sign}.
-
-The \texttt{key\_sign} method takes four arguments: \texttt{key}, \texttt{uids},
-\texttt{expires\_in} and \texttt{local}. The default value of \texttt{uids} is \texttt{None} and
-which results in all user IDs being selected. The default value of
-both \texttt{expires\_in} and \texttt{local} is \texttt{False}; which results in the
-signature never expiring and being able to be exported.
-
-The \texttt{key} is the key being signed rather than the key doing the
-signing. To change the key doing the signing refer to the signing key
-selection above for signing messages and files.
-
-If the \texttt{uids} value is not \texttt{None} then it must either be a string to
-match a single user ID or a list of strings to match multiple user
-IDs. In this case the matching of those strings must be precise and
-it is case sensitive.
-
-To sign Danger Mouse's key for just the initial user ID with a
-signature which will last a little over a month, do this:
-
-\begin{verbatim}
-import gpg
-
-c = gpg.Context()
-uid = "Danger Mouse <dm@secret.example.net>"
-
-dmfpr = "177B7C25DB99745EE2EE13ED026D2F19E99E63AA"
-key = c.get_key(dmfpr, secret=True)
-c.key_sign(key, uids=uid, expires_in=2764800)
-\end{verbatim}
-
-
-\subsubsection{Verifying key certifications}
-\label{sec:org1ab2f58}
-\begin{verbatim}
-import gpg
-import time
-
-c = gpg.Context()
-dmfpr = "177B7C25DB99745EE2EE13ED026D2F19E99E63AA"
-keys = list(c.keylist(pattern=dmuid, mode=gpg.constants.keylist.mode.SIGS))
-key = keys[0]
-
-for user in key.uids:
- for sig in user.signatures:
- print("0x{0}".format(sig.keyid), "", time.ctime(sig.timestamp), "",
- sig.uid)
-\end{verbatim}
-
-Which for Danger Mouse displays the following:
-
-\begin{verbatim}
-0x92E3F6115435C65A Thu Mar 15 13:17:44 2018 Danger Mouse <dm@secret.example.net>
-0x321E4E2373590E5D Mon Nov 26 12:46:05 2018 Ben McGinnes <ben@adversary.org>
-\end{verbatim}
-
-The two key signatures listed are for the self-certification of Danger
-Mouse's key made when the key was created in March, 2018; and the
-second is a signature made by the author and set to expire at the end
-of the year. Note that the second signature was made with the
-following code (including the preceding code to display the output of
-the certifications or key signatures):
-
-\begin{verbatim}
-import gpg
-import math
-import pendulum
-import time
-
-hd = "/home/dm/.gnupg"
-c = gpg.Context()
-d = gpg.Context(home_dir=hd)
-dmfpr = "177B7C25DB99745EE2EE13ED026D2F19E99E63AA"
-dmuid = "Danger Mouse <dm@secret.example.net>"
-dkeys = list(c.keylist(pattern=dmuid))
-dmkey = dkeys[0]
-
-c.key_import(d.key_export(pattern=None))
-
-tp = pendulum.period(pendulum.now(tz="local"), pendulum.datetime(2019, 1, 1))
-ts = tp.total_seconds()
-total_secs = math.ceil(ts)
-c.key_sign(dmkey, uids=dmuid, expires_in=total_secs)
-
-d.key_import(c.key_export(pattern=dmuid))
-keys = list(c.keylist(pattern=dmuid, mode=gpg.constants.keylist.mode.SIGS))
-key = keys[0]
-
-for user in key.uids:
- for sig in user.signatures:
- print("0x{0}".format(sig.keyid), "", time.ctime(sig.timestamp), "",
- sig.uid)
-\end{verbatim}
-
-Note that this final code block includes the use of a module which is
-\emph{not} part of Python's standard library, the \href{https://pendulum.eustace.io/}{pendulum module}. Unlike
-the standard datetime module, pendulum makes working with dates and
-times significantly easier in Python; just as the requests module
-makes working with HTTP and HTTPS easier than the builtin modules do.
-
-Though neither requests nor pendulum are required modules for using
-the GPGME Python bindings, they are both highly recommended more
-generally.
-
-
-\section{Advanced or Experimental Use Cases}
-\label{sec:org261e415}
-\subsection{C plus Python plus SWIG plus Cython}
-\label{sec:orgeda6cec}
-In spite of the apparent incongruence of using Python bindings to a C
-interface only to generate more C from the Python; it is in fact quite
-possible to use the GPGME bindings with \href{http://docs.cython.org/en/latest/index.html}{Cython}. Though in many cases
-the benefits may not be obvious since the most computationally
-intensive work never leaves the level of the C code with which GPGME
-itself is interacting with.
-
-Nevertheless, there are some situations where the benefits are
-demonstrable. One of the better and easier examples being the one of
-the early examples in this HOWTO, the \hyperref[sec:orga4f8d07]{key counting} code. Running that
-example as an executable Python script, \texttt{keycount.py} (available in
-the \texttt{examples/howto/} directory), will take a noticeable amount of time
-to run on most systems where the public keybox or keyring contains a
-few thousand public keys.
-
-Earlier in the evening, prior to starting this section, I ran that
-script on my laptop; as I tend to do periodically and timed it using
-\texttt{time} utility, with the following results:
-
-\begin{verbatim}
-bash-4.4$ time keycount.py
-
-Number of secret keys: 23
-Number of public keys: 12112
-
-
-real 11m52.945s
-user 0m0.913s
-sys 0m0.752s
-
-bash-4.4$
-\end{verbatim}
-
-Sometime after that I imported another key and followed it with a
-little test of Cython. This test was kept fairly basic, essentially
-lifting the material from the \href{http://docs.cython.org/en/latest/src/tutorial/cython\_tutorial.html}{Cython Basic Tutorial} to demonstrate
-compiling Python code to C. The first step was to take the example
-key counting code quoted previously, essentially from the importing of
-the \texttt{gpg} module to the end of the script:
-
-\begin{verbatim}
-import gpg
-
-c = gpg.Context()
-seckeys = c.keylist(pattern=None, secret=True)
-pubkeys = c.keylist(pattern=None, secret=False)
-
-seclist = list(seckeys)
-secnum = len(seclist)
-
-publist = list(pubkeys)
-pubnum = len(publist)
-
-print("""
- Number of secret keys: {0}
- Number of public keys: {1}
-
-""".format(secnum, pubnum))
-\end{verbatim}
-
-Save that into a file called \texttt{keycount.pyx} and then create a
-\texttt{setup.py} file which contains this:
-
-\begin{verbatim}
-from distutils.core import setup
-from Cython.Build import cythonize
-
-setup(
- ext_modules = cythonize("keycount.pyx")
-)
-\end{verbatim}
-
-Compile it:
-
-\begin{verbatim}
-bash-4.4$ python setup.py build_ext --inplace
-bash-4.4$
-\end{verbatim}
-
-Then run it in a similar manner to \texttt{keycount.py}:
-
-\begin{verbatim}
-bash-4.4$ time python3.7 -c "import keycount"
-
-Number of secret keys: 23
-Number of public keys: 12113
-
-
-real 6m47.905s
-user 0m0.785s
-sys 0m0.331s
-
-bash-4.4$
-\end{verbatim}
-
-Cython turned \texttt{keycount.pyx} into an 81KB \texttt{keycount.o} file in the
-\texttt{build/} directory, a 24KB \texttt{keycount.cpython-37m-darwin.so} file to be
-imported into Python 3.7 and a 113KB \texttt{keycount.c} generated C source
-code file of nearly three thousand lines. Quite a bit bigger than the
-314 bytes of the \texttt{keycount.pyx} file or the full 1,452 bytes of the
-full executable \texttt{keycount.py} example script.
-
-On the other hand it ran in nearly half the time; taking 6 minutes and
-47.905 seconds to run. As opposed to the 11 minutes and 52.945 seconds
-which the CPython script alone took.
-
-The \texttt{keycount.pyx} and \texttt{setup.py} files used to generate this example
-have been added to the \texttt{examples/howto/advanced/cython/} directory
-The example versions include some additional options to annotate the
-existing code and to detect Cython's use. The latter comes from the
-\href{http://docs.cython.org/en/latest/src/tutorial/pure.html\#magic-attributes-within-the-pxd}{Magic Attributes} section of the Cython documentation.
-
-
-\section{Miscellaneous extras and work-arounds}
-\label{sec:orgc2820d3}
-Most of the things in the following sections are here simply because
-there was no better place to put them, even though some are only
-peripherally related to the GPGME Python bindings. Some are also
-workarounds for functions not integrated with GPGME as yet. This is
-especially true of the first of these, dealing with \hyperref[sec:org8ea6368]{group lines}.
-
-
-\subsection{Group lines}
-\label{sec:org8ea6368}
-There is not yet an easy way to access groups configured in the
-gpg.conf file from within GPGME. As a consequence these central
-groupings of keys cannot be shared amongst multiple programs, such as
-MUAs readily.
-
-The following code, however, provides a work-around for obtaining this
-information in Python.
-
-\begin{verbatim}
-import subprocess
-import sys
-
-if sys.platform == "win32":
- gpgconfcmd = "gpgconf.exe --list-options gpg"
-else:
- gpgconfcmd = "gpgconf --list-options gpg"
-
-process = subprocess.Popen(gpgconfcmd.split(), stdout=subprocess.PIPE)
-procom = process.communicate()
-
-if sys.version_info[0] == 2:
- lines = procom[0].splitlines()
-else:
- lines = procom[0].decode().splitlines()
-
-for line in lines:
- if line.startswith("group") is True:
- break
-
-groups = line.split(":")[-1].replace('"', '').split(',')
-
-group_lines = []
-group_lists = []
-
-for i in range(len(groups)):
- group_lines.append(groups[i].split("="))
- group_lists.append(groups[i].split("="))
-
-for i in range(len(group_lists)):
- group_lists[i][1] = group_lists[i][1].split()
-\end{verbatim}
-
-The result of that code is that \texttt{group\_lines} is a list of lists where
-\texttt{group\_lines[i][0]} is the name of the group and \texttt{group\_lines[i][1]}
-is the key IDs of the group as a string.
-
-The \texttt{group\_lists} result is very similar in that it is a list of
-lists. The first part, \texttt{group\_lists[i][0]} matches
-\texttt{group\_lines[i][0]} as the name of the group, but \texttt{group\_lists[i][1]}
-is the key IDs of the group as a list.
-
-A demonstration of using the \texttt{groups.py} module is also available in
-the form of the executable \texttt{mutt-groups.py} script. This second
-script reads all the group entries in a user's \texttt{gpg.conf} file and
-converts them into crypt-hooks suitable for use with the Mutt and
-Neomutt mail clients.
-
-
-\subsection{Keyserver access for Python}
-\label{sec:orgba396cc}
-The \href{https://github.com/Selfnet/hkp4py}{hkp4py} module by Marcel Fest was originally a port of the old
-\href{https://github.com/dgladkov/python-hkp}{python-hkp} module from Python 2 to Python 3 and updated to use the
-\href{http://docs.python-requests.org/en/latest/index.html}{requests} module instead. It has since been modified to provide
-support for Python 2.7 as well and is available via PyPI.
-
-Since it rewrites the \texttt{hkp} protocol prefix as \texttt{http} and \texttt{hkps} as
-\texttt{https}, the module is able to be used even with servers which do not
-support the full scope of keyserver functions.\footnote{Such as with ProtonMail servers. This also means that
-restricted servers which only advertise either HTTP or HTTPS end
-points and not HKP or HKPS end points must still be identified as as
-HKP or HKPS within the Python Code. The \texttt{hkp4py} module will rewrite
-these appropriately when the connection is made to the server.} It also works quite
-readily when incorporated into a \hyperref[sec:orgeda6cec]{Cython} generated and compiled version
-of any code.
-
-
-\subsubsection{Key import format}
-\label{sec:orgfa04352}
-The hkp4py module returns key data via requests as string literals
-(\texttt{r.text}) instead of byte literals (\texttt{r.content}). This means that
-the retrurned key data must be encoded to UTF-8 when importing that
-key material using a \texttt{gpg.Context().key\_import()} method.
-
-For this reason an alternative method has been added to the \texttt{search}
-function of \texttt{hkp4py.KeyServer()} which returns the key in the correct
-format as expected by \texttt{key\_import}. When importing using this module,
-it is now possible to import with this:
-
-\begin{verbatim}
-for key in keys:
- if key.revoked is False:
- gpg.Context().key_import(key.key_blob)
- else:
- pass
-\end{verbatim}
-
-Without that recent addition it would have been necessary to encode
-the contents of each \texttt{hkp4py.KeyServer().search()[i].key} in
-\texttt{hkp4py.KeyServer().search()} before trying to import it.
-
-An example of this is included in the \hyperref[sec:org5a4abec]{Importing Keys} section of this
-HOWTO and the corresponding executable version of that example is
-available in the \texttt{lang/python/examples/howto} directory as normal; the
-executable version is the \texttt{import-keys-hkp.py} file.
-
-
-\subsection{GPGME version checking}
-\label{sec:org64c0457}
-For various reasons it may be necessary to check which version of
-GPGME the bindings have been built against; including whether a
-minimum required version of GPGME is in use.
-
-For the most part the \texttt{gpg.version.versionstr} and
-\texttt{gpg.version.versionlist} methods have been quite sufficient. The
-former returns the same string as \texttt{gpgme-config -{}-version}, while the
-latter returns the major, minor and patch values in a list.
-
-To check if the installed bindings have actually been built against
-the current installed libgpgme version, this check can be performed:
-
-\begin{verbatim}
-import gpg
-import subprocess
-import sys
-
-gpgme_version_call = subprocess.Popen(["gpgme-config", "--version"],
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
-gpgme_version_str = gpgme_version_call.communicate()
-
-if sys.version_info[0] == 2:
- gpgme_version = gpgme_version_str[0].strip()
-elif sys.version_info[0] >= 3:
- gpgme_version = gpgme_version_str[0].decode().strip()
-else:
- gpgme_version = None
-
-if gpgme_version is not None:
- if gpgme_version == gpg.version.versionstr:
- print("The GPGME Python bindings match libgpgme.")
- else:
- print("The GPGME Python bindings do NOT match libgpgme.")
-else:
- print("Upgrade Python and reinstall the GPGME Python bindings.")
-\end{verbatim}
-
-For many developers, however, the preferred checking means checking
-for a minimum version or point release. This is now readily available
-via the \texttt{gpg.version.versionintlist} method (added in version
-\texttt{1.12.1-beta79}). It is also now possible to easily check whether the
-installed GPGME Python bindings were built from a development or beta
-branch of the GPGME source code.
-
-The following code demonstrates how both of those methods may be used:
-
-\begin{verbatim}
-import gpg
-
-try:
- if gpg.version.is_beta is True:
- print("The installed GPGME Python bindings were built from beta code.")
- else:
- print("The installed GPGME Python bindings are a released version.")
-except Exception as e:
- print(e)
-
-try:
- if gpg.version.versionintlist[0] == 1:
- if gpg.version.versionintlist[1] == 12:
- if gpg.version.versionintlist[2] == 1:
- print("This is the minimum version for using versionintlist.")
- elif gpg.version.versionintlist[2] > 1:
- print("The versionintlist method is available.")
- else:
- pass
- elif gpg.version.versionintlist[1] > 12:
- print("The versionintlist method is available.")
- else:
- pass
- elif gpg.version.versionintlist[0] > 1:
- print("The versionintlist method is available.")
- else:
- pass
-except Exception as e:
- print(e)
-\end{verbatim}
-
-The points where \texttt{pass} is used in the above example will most likely
-also produce an \texttt{Exception} error since those results should only
-occur in versions which do not have the \texttt{gpgme.version.is\_beta} and
-\texttt{gpgme.version.versionintlist} methods available.
-
-
-\section{Copyright and Licensing}
-\label{sec:org7fd28fa}
-\subsection{Copyright}
-\label{sec:orga2eb61b}
-Copyright © The GnuPG Project, 2018.
-
-Copyright (C) The GnuPG Project, 2018.
-
-
-\subsection{Draft Editions of this HOWTO}
-\label{sec:org1cd0343}
-Draft editions of this HOWTO may be periodically available directly
-from the author at any of the following URLs:
-
-\begin{itemize}
-\item \href{https://files.au.adversary.org/crypto/gpgme-python-howto.html}{GPGME Python Bindings HOWTO draft (XHTML single file, AWS S3 SSL)}
-\item \href{http://files.au.adversary.org/crypto/gpgme-python-howto.html}{GPGME Python Bindings HOWTO draft (XHTML single file, AWS S3 no SSL)}
-\item \href{https://files.au.adversary.org/crypto/gpgme-python-howto-split/index.html}{GPGME Python Bindings HOWTO draft (XHTML multiple files, AWS S3 SSL)}
-\item \href{http://files.au.adversary.org/crypto/gpgme-python-howto/index.html}{GPGME Python Bindings HOWTO draft (XHTML multiple files, AWS S3 no SSL)}
-\end{itemize}
-
-All of these draft versions except for one have been generated from
-this document via GNU Emacs \href{https://orgmode.org/}{Org mode} and \href{https://www.gnu.org/software/texinfo/}{GNU Texinfo}. Though it is
-likely that the specific \href{https://files.au.adversary.org/crypto/gpgme-python-howto}{file} \href{http://files.au.adversary.org/crypto/gpgme-python-howto.org}{version} used will be on the same server
-with the generated output formats.
-
-The GNU Texinfo and reStructured Text versions ship with the software,
-while the GNU Emacs Info verseion is generated from the Texinfo
-version using GNU Texinfo or GNU Makeinfo. The Texinfo format is
-generated from the original Org mode source file in Org mode itself
-either within GNU Emacs or via the command line by invoking Emacs in
-batch mode:
-
-\begin{verbatim}
-emacs gpgme-python-howto.org --batch -f org-texinfo-export-to-texinfo --kill
-emacs gpgme-python-howto --batch -f org-texinfo-export-to-texinfo --kill
-\end{verbatim}
-
-The reStructuredText format is also generated from the Org-mode source
-file, except it is generated using \href{https://pandoc.org}{Pandoc} with either of the following
-commands:
-
-\begin{verbatim}
-pandoc -f org -t rst+smart -o gpgme-python-howto.rst gpgme-python-howto.org
-pandoc -f org -t rst+smart -o gpgme-python-howto.rst gpgme-python-howto
-\end{verbatim}
-
-In addition to these there is a significantly less frequently updated
-version as a HTML \href{https://files.au.adversary.org/crypto/gpgme-python/dita/webhelp/index.html}{WebHelp site} (AWS S3 SSL); generated from DITA XML
-source files, which can be found in \href{https://dev.gnupg.org/source/gpgme/browse/ben\%252Fhowto-dita/}{an alternative branch} of the GPGME
-git repository.
-
-Various generated output formats may occasionally be found in
-subdirectories of the \href{https://s3.amazonaws.com/files.au.adversary.org/crypto/gpgme-python}{gpgme-python} directory. In particular within
-the \href{https://s3.amazonaws.com/files.au.adversary.org/crypto/gpgme-python/dita}{DITA}, \href{https://s3.amazonaws.com/files.au.adversary.org/crypto/gpgme-python/rst}{reStructuredText} and \href{https://s3.amazonaws.com/files.au.adversary.org/crypto/gpgme-python/texinfo}{Texinfo} subdirectories. The \texttt{rst}
-directory contains output files generated with Sphix and may include a
-considerable number of its possible output formats.
-
-These draft editions are not official documents and the version of
-documentation in the master branch or which ships with released
-versions is the only official documentation. Nevertheless, these
-draft editions may occasionally be of use by providing more accessible
-web versions which are updated between releases. They are provided on
-the understanding that they may contain errors or may contain content
-subject to change prior to an official release.
-
-
-\subsection{License GPL compatible}
-\label{sec:org071f98e}
-This file is free software; as a special exception the author gives
-unlimited permission to copy and/or distribute it, with or without
-modifications, as long as this notice is preserved.
-
-This file 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.
-\end{document}
diff --git a/lang/python/doc/src/gpgme-python-howto.tex~ b/lang/python/doc/src/gpgme-python-howto.tex~
deleted file mode 100644
index ca89a4c..0000000
--- a/lang/python/doc/src/gpgme-python-howto.tex~
+++ /dev/null
@@ -1,3110 +0,0 @@
-% Created 2019-01-18 Fri 10:10
-% Intended LaTeX compiler: xelatex
-\documentclass[12pt]{article}
-\usepackage{graphicx}
-\usepackage{grffile}
-\usepackage{longtable}
-\usepackage{wrapfig}
-\usepackage{rotating}
-\usepackage[normalem]{ulem}
-\usepackage{amsmath}
-\usepackage{textcomp}
-\usepackage{amssymb}
-\usepackage{capt-of}
-\usepackage{hyperref}
-\usepackage{xltxtra}
-\usepackage[margin=1in]{geometry}
-\setmainfont[Ligatures={Common}]{Times New Roman}
-\author{Ben McGinnes <ben@gnupg.org>}
-\author{Ben McGinnes}
-\date{\today}
-\title{GNU Privacy Guard (GnuPG) Made Easy Python Bindings HOWTO (English)}
-\hypersetup{
- pdfauthor={Ben McGinnes},
- pdftitle={GNU Privacy Guard (GnuPG) Made Easy Python Bindings HOWTO (English)},
- pdfkeywords={},
- pdfsubject={},
- pdfcreator={Emacs 26.1 (Org mode 9.1.14)},
- pdflang={English}}
-\begin{document}
-
-\maketitle
-\tableofcontents
-
-
-
-\section{Introduction}
-\label{sec:org8e5fa02}
-\begin{center}
-\begin{tabular}{ll}
-Version: & 0.1.4\\
-GPGME Version: & 1.12.1\\
-Author: & Ben McGinnes <ben@gnupg.org>\\
-Author GPG Key: & DB4724E6FA4286C92B4E55C4321E4E2373590E5D\\
-Language: & Australian English, British English\\
-Language codes: & en-AU, en-GB, en\\
-\end{tabular}
-\end{center}
-
-This document provides basic instruction in how to use the GPGME
-Python bindings to programmatically leverage the GPGME library.
-
-
-\subsection{Python 2 versus Python 3}
-\label{sec:org517cbf4}
-Though the GPGME Python bindings themselves provide support for both
-Python 2 and 3, the focus is unequivocally on Python 3 and
-specifically from Python 3.4 and above. As a consequence all the
-examples and instructions in this guide use Python 3 code.
-
-Much of it will work with Python 2, but much of it also deals with
-Python 3 byte literals, particularly when reading and writing data.
-Developers concentrating on Python 2.7, and possibly even 2.6, will
-need to make the appropriate modifications to support the older string
-and unicode types as opposed to bytes.
-
-There are multiple reasons for concentrating on Python 3; some of
-which relate to the immediate integration of these bindings, some of
-which relate to longer term plans for both GPGME and the python
-bindings and some of which relate to the impending EOL period for
-Python 2.7. Essentially, though, there is little value in tying the
-bindings to a version of the language which is a dead end and the
-advantages offered by Python 3 over Python 2 make handling the data
-types with which GPGME deals considerably easier.
-
-
-\subsection{Examples}
-\label{sec:org2c08d53}
-All of the examples found in this document can be found as Python 3
-scripts in the \texttt{lang/python/examples/howto} directory.
-
-
-\subsection{Unofficial Drafts}
-\label{sec:orgceb2216}
-In addition to shipping with each release of GPGME, there is a section
-on locations to read or download \hyperref[sec:org080a94a]{draft editions} of this document from
-at the end of it. These are unofficial versions produced in between
-major releases.
-
-
-\subsection{What's New}
-\label{sec:orga79ddc2}
-Full details of what is new are now available in the \href{what-is-new.org}{What's New} file
-and archives of the preceding \emph{What's New} sections are available in
-the \href{what-was-new}{What Was New} file.
-
-
-\subsubsection{New in GPGME 1·13·0}
-\label{sec:org746b75d}
-See the \href{what-is-new\#new-stuff-1-13-0}{What's New} document for what is new in version 1.13.0.
-
-
-\subsubsection{New in GPGME 1·12·0}
-\label{sec:org7adcade}
-See the \href{what-was-new\#new-stuff-1-12-0}{What Was New} document for what was new in version 1.12.0.
-
-
-\section{GPGME Concepts}
-\label{sec:orgb257fc1}
-\subsection{A C API}
-\label{sec:org4fe1dfa}
-Unlike many modern APIs with which programmers will be more familiar
-with these days, the GPGME API is a C API. The API is intended for
-use by C coders who would be able to access its features by including
-the \texttt{gpgme.h} header file with their own C source code and then access
-its functions just as they would any other C headers.
-
-This is a very effective method of gaining complete access to the API
-and in the most efficient manner possible. It does, however, have the
-drawback that it cannot be directly used by other languages without
-some means of providing an interface to those languages. This is
-where the need for bindings in various languages stems.
-
-
-\subsection{Python bindings}
-\label{sec:org0e49e52}
-The Python bindings for GPGME provide a higher level means of
-accessing the complete feature set of GPGME itself. It also provides
-a more pythonic means of calling these API functions.
-
-The bindings are generated dynamically with SWIG and the copy of
-\texttt{gpgme.h} generated when GPGME is compiled.
-
-This means that a version of the Python bindings is fundamentally tied
-to the exact same version of GPGME used to generate that copy of
-\texttt{gpgme.h}.
-
-
-\subsection{Difference between the Python bindings and other GnuPG Python packages}
-\label{sec:org97acfd7}
-There have been numerous attempts to add GnuPG support to Python over
-the years. Some of the most well known are listed here, along with
-what differentiates them.
-
-
-\subsubsection{The python-gnupg package maintained by Vinay Sajip}
-\label{sec:org0136847}
-This is arguably the most popular means of integrating GPG with
-Python. The package utilises the \texttt{subprocess} module to implement
-wrappers for the \texttt{gpg} and \texttt{gpg2} executables normally invoked on the
-command line (\texttt{gpg.exe} and \texttt{gpg2.exe} on Windows).
-
-The popularity of this package stemmed from its ease of use and
-capability in providing the most commonly required features.
-
-Unfortunately it has been beset by a number of security issues in the
-past; most of which stemmed from using unsafe methods of accessing the
-command line via the \texttt{subprocess} calls. While some effort has been
-made over the last two to three years (as of 2018) to mitigate this,
-particularly by no longer providing shell access through those
-subprocess calls, the wrapper is still somewhat limited in the scope
-of its GnuPG features coverage.
-
-The python-gnupg package is available under the MIT license.
-
-
-\subsubsection{The gnupg package created and maintained by Isis Lovecruft}
-\label{sec:orgdc03987}
-In 2015 Isis Lovecruft from the Tor Project forked and then
-re-implemented the python-gnupg package as just gnupg. This new
-package also relied on subprocess to call the \texttt{gpg} or \texttt{gpg2}
-binaries, but did so somewhat more securely.
-
-The naming and version numbering selected for this package, however,
-resulted in conflicts with the original python-gnupg and since its
-functions were called in a different manner to python-gnupg, the
-release of this package also resulted in a great deal of consternation
-when people installed what they thought was an upgrade that
-subsequently broke the code relying on it.
-
-The gnupg package is available under the GNU General Public License
-version 3.0 (or any later version).
-
-
-\subsubsection{The PyME package maintained by Martin Albrecht}
-\label{sec:org715600b}
-This package is the origin of these bindings, though they are somewhat
-different now. For details of when and how the PyME package was
-folded back into GPGME itself see the \href{short-history.org}{Short History} document.\footnote{\texttt{short-history} and/or \texttt{short-history.html}.}
-
-The PyME package was first released in 2002 and was also the first
-attempt to implement a low level binding to GPGME. In doing so it
-provided access to considerably more functionality than either the
-\texttt{python-gnupg} or \texttt{gnupg} packages.
-
-The PyME package is only available for Python 2.6 and 2.7.
-
-Porting the PyME package to Python 3.4 in 2015 is what resulted in it
-being folded into the GPGME project and the current bindings are the
-end result of that effort.
-
-The PyME package is available under the same dual licensing as GPGME
-itself: the GNU General Public License version 2.0 (or any later
-version) and the GNU Lesser General Public License version 2.1 (or any
-later version).
-
-
-\section{GPGME Python bindings installation}
-\label{sec:org73d8d6f}
-\subsection{No PyPI}
-\label{sec:orgec9a038}
-Most third-party Python packages and modules are available and
-distributed through the Python Package Installer, known as PyPI.
-
-Due to the nature of what these bindings are and how they work, it is
-infeasible to install the GPGME Python bindings in the same way.
-
-This is because the bindings use SWIG to dynamically generate C
-bindings against \texttt{gpgme.h} and \texttt{gpgme.h} is generated from
-\texttt{gpgme.h.in} at compile time when GPGME is built from source. Thus to
-include a package in PyPI which actually built correctly would require
-either statically built libraries for every architecture bundled with
-it or a full implementation of C for each architecture.
-
-See the additional notes regarding \hyperref[sec:org645735a]{CFFI and SWIG} at the end of this
-section for further details.
-
-
-\subsection{Requirements}
-\label{sec:org2f7f06c}
-The GPGME Python bindings only have three requirements:
-
-\begin{enumerate}
-\item A suitable version of Python 2 or Python 3. With Python 2 that
-means CPython 2.7 and with Python 3 that means CPython 3.4 or
-higher.
-\item \href{https://www.swig.org}{SWIG}.
-\item GPGME itself. Which also means that all of GPGME's dependencies
-must be installed too.
-\end{enumerate}
-
-
-\subsubsection{Recommended Additions}
-\label{sec:org25bd7d4}
-Though none of the following are absolute requirements, they are all
-recommended for use with the Python bindings. In some cases these
-recommendations refer to which version(s) of CPython to use the
-bindings with, while others refer to third party modules which provide
-a significant advantage in some way.
-
-\begin{enumerate}
-\item If possible, use Python 3 instead of 2.
-\item Favour a more recent version of Python since even 3.4 is due to
-reach EOL soon. In production systems and services, Python 3.6
-should be robust enough to be relied on.
-\item If possible add the following Python modules which are not part of
-the standard library: \href{http://docs.python-requests.org/en/latest/index.html}{Requests}, \href{https://cython.org/}{Cython}, \href{https://pendulum.eustace.io/}{Pendulum} and \href{https://github.com/Selfnet/hkp4py}{hkp4py}.
-\end{enumerate}
-
-Chances are quite high that at least the first one and maybe two of
-those will already be installed.
-
-Note that, as with Cython, some of advanced use case scenarios will
-bring with them additional requirements. Most of these will be fairly
-well known and commonly installed ones, however, which are in many
-cases likely to have already been installed on many systems or be
-familiar to Python programmers.
-
-
-\subsection{Installation}
-\label{sec:orgf1c7587}
-Installing the Python bindings is effectively achieved by compiling
-and installing GPGME itself.
-
-Once SWIG is installed with Python and all the dependencies for GPGME
-are installed you only need to confirm that the version(s) of Python
-you want the bindings installed for are in your \texttt{\$PATH}.
-
-By default GPGME will attempt to install the bindings for the most
-recent or highest version number of Python 2 and Python 3 it detects
-in \texttt{\$PATH}. It specifically checks for the \texttt{python} and \texttt{python3}
-executables first and then checks for specific version numbers.
-
-For Python 2 it checks for these executables in this order: \texttt{python},
-\texttt{python2} and \texttt{python2.7}.
-
-For Python 3 it checks for these executables in this order: \texttt{python3},
- \texttt{python3.7}, \texttt{python3.6}, \texttt{python3.5} and \texttt{python3.4}.\footnote{With no issues reported specific to Python 3.7, the release of
-Python 3.7.1 at around the same time as GPGME 1.12.0 and the testing
-with Python 3.7.1rc1, there is no reason to delay moving 3.7 ahead of
-3.6 now. Production environments with more conservative requirements
-will always enforce their own policies anyway and installation to each
-supported minor release is quite possible too.}
-
-On systems where \texttt{python} is actually \texttt{python3} and not \texttt{python2} it
-may be possible that \texttt{python2} may be overlooked, but there have been
-no reports of that actually occurring as yet.
-
-In the three months or so since the release of Python 3.7.0 there has
-been extensive testing and work with these bindings with no issues
-specifically relating to the new version of Python or any of the new
-features of either the language or the bindings. This has also been
-the case with Python 3.7.1rc1. With that in mind and given the
-release of Python 3.7.1 is scheduled for around the same time as GPGME
-1.12.0, the order of preferred Python versions has been changed to
-move Python 3.7 ahead of Python 3.6.
-
-
-\subsubsection{Installing GPGME}
-\label{sec:org903bd67}
-See the GPGME \texttt{README} file for details of how to install GPGME from
-source.
-
-
-\subsection{Known Issues}
-\label{sec:org0d2a332}
-There are a few known issues with the current build process and the
-Python bindings. For the most part these are easily addressed should
-they be encountered.
-
-
-\subsubsection{Breaking Builds}
-\label{sec:orgbac75f3}
-Occasionally when installing GPGME with the Python bindings included
-it may be observed that the \texttt{make} portion of that process induces a
-large very number of warnings and, eventually errors which end that
-part of the build process. Yet following that with \texttt{make check} and
-\texttt{make install} appears to work seamlessly.
-
-The cause of this is related to the way SWIG needs to be called to
-dynamically generate the C bindings for GPGME in the first place. So
-the entire process will always produce \texttt{lang/python/python2-gpg/} and
-\texttt{lang/python/python3-gpg/} directories. These should contain the
-build output generated during compilation, including the complete
-bindings and module installed into \texttt{site-packages}.
-
-Occasionally the errors in the early part or some other conflict
-(e.g. not installing as \textbf{\emph{root}} or \textbf{\emph{su}}) may result in nothing
-being installed to the relevant \texttt{site-packages} directory and the
-build directory missing a lot of expected files. Even when this
-occurs, the solution is actually quite simple and will always work.
-
-That solution is simply to run the following commands as either the
-\textbf{root} user or prepended with \texttt{sudo -H}\footnote{Yes, even if you use virtualenv with everything you do in
-Python. If you want to install this module as just your user account
-then you will need to manually configure, compile and install the
-\emph{entire} GnuPG stack as that user as well. This includes libraries
-which are not often installed that way. It can be done and there are
-circumstances under which it is worthwhile, but generally only on
-POSIX systems which utilise single user mode (some even require it).} in the \texttt{lang/python/}
-directory:
-
-\begin{verbatim}
-/path/to/pythonX.Y setup.py build
-/path/to/pythonX.Y setup.py build
-/path/to/pythonX.Y setup.py install
-\end{verbatim}
-
-Yes, the build command does need to be run twice. Yes, you still need
-to run the potentially failing or incomplete steps during the
-\texttt{configure}, \texttt{make} and \texttt{make install} steps with installing GPGME.
-This is because those steps generate a lot of essential files needed,
-both by and in order to create, the bindings (including both the
-\texttt{setup.py} and \texttt{gpgme.h} files).
-
-
-\begin{enumerate}
-\item IMPORTANT Note
-\label{sec:org37f3d4a}
-If specifying a selected number of languages to create bindings for,
-try to leave Python last. Currently the majority of the other
-language bindings are also preceding Python of either version when
-listed alphabetically (not counting the Qt bindings).
-
-If Python is set to precede one of the other languages then it is
-possible that the errors described here may interrupt the build
-process before generating bindings for those other languages. In
-these cases it may be preferable to configure all preferred language
-bindings separately with alternative \texttt{configure} steps for GPGME using
-the \texttt{-{}-enable-languages=\$LANGUAGE} option.
-
-Alternatively \texttt{make} (or \texttt{gmake}, depending on your platform) may be
-run with the the \texttt{-k} option, which tells make to keep going even if
-errors are encountered. In that case the failure of one language's
-set of bindings to build should not hamper another language's bindings
-to build.
-\end{enumerate}
-
-
-\subsubsection{Reinstalling Responsibly}
-\label{sec:org21e4dec}
-Regardless of whether you're installing for one version of Python or
-several, there will come a point where reinstallation is required.
-With most Python module installations, the installed files go into the
-relevant site-packages directory and are then forgotten about. Then
-the module is upgraded, the new files are copied over the old and
-that's the end of the matter.
-
-While the same is true of these bindings, there have been intermittent
-issues observed on some platforms which have benefited significantly
-from removing all the previous installations of the bindings before
-installing the updated versions.
-
-Removing the previous version(s) is simply a matter of changing to the
-relevant \texttt{site-packages} directory for the version of Python in
-question and removing the \texttt{gpg/} directory and any accompanying
-egg-info files for that module.
-
-In most cases this will require root or administration privileges on
-the system, but the same is true of installing the module in the first
-place.
-
-
-\subsubsection{Multiple installations}
-\label{sec:org30b18bd}
-For a variety of reasons it may be either necessary or just preferable
-to install the bindings to alternative installed Python versions which
-meet the requirements of these bindings.
-
-On POSIX systems this will generally be most simply achieved by
-running the manual installation commands (build, build, install) as
-described in the previous section for each Python installation the
-bindings need to be installed to.
-
-As per the SWIG documentation: the compilers, libraries and runtime
-used to build GPGME and the Python Bindings \textbf{must} match those used to
-compile Python itself, including the version number(s) (at least going
-by major version numbers and probably minor numbers too).
-
-On most POSIX systems, including OS X, this will very likely be the
-case in most, if not all, cases.
-
-Note that from GPGME \href{https://dev.gnupg.org/rMff6ff616aea6f59b7f2ce1176492850ecdf3851e}{1.12.1} the default installation installs to each
-version of Python it can find first. That is that it will currently
-install for the first copies of Python versions 2.7, 3.4, 3.5, 3.6,
-3.7 and 3.8 (dev branch) that it finds. Usually this will be in the
-same prefix as GPGME itself, but is dictated by the \texttt{\$PATH} when the
-installation is performed. The above instructions can still be
-performed on other python installations which the installer does not
-find, including alternative prefixes.
-
-
-
-\subsubsection{Won't Work With Windows}
-\label{sec:org0ebae3a}
-There are semi-regular reports of Windows users having considerable
-difficulty in installing and using the Python bindings at all. Very
-often, possibly even always, these reports come from Cygwin users
-and/or MinGW users and/or Msys2 users. Though not all of them have
-been confirmed, it appears that these reports have also come from
-people who installed Python using the Windows installer files from the
-\href{https://python.org}{Python website} (i.e. mostly MSI installers, sometimes self-extracting
-\texttt{.exe} files).
-
-The Windows versions of Python are not built using Cygwin, MinGW or
-Msys2; they're built using Microsoft Visual Studio. Furthermore the
-version used is \emph{considerably} more advanced than the version which
-MinGW obtained a small number of files from many years ago in order to
-be able to compile anything at all. Not only that, but there are
-changes to the version of Visual Studio between some micro releases,
-though that is is particularly the case with Python 2.7, since it has
-been kept around far longer than it should have been.
-
-There are two theoretical solutions to this issue:
-
-\begin{enumerate}
-\item Compile and install the GnuPG stack, including GPGME and the
-Python bindings using the same version of Microsoft Visual Studio
-used by the Python Foundation to compile the version of Python
-installed.
-
-If there are multiple versions of Python then this will need to be
-done with each different version of Visual Studio used for those
-versions of Python.
-
-\item Compile and install Python using the same tools used by choice,
-such as MinGW or Msys2.
-\end{enumerate}
-
-Do \textbf{not} use the official Windows installer for Python unless
-following the first method.
-
-In this type of situation it may even be for the best to accept that
-there are less limitations on permissive software than free software
-and simply opt to use a recent version of the Community Edition of
-Microsoft Visual Studio to compile and build all of it, no matter
-what.
-
-Investigations into the extent or the limitations of this issue are
-ongoing.
-
-The following table lists the version of Microsoft Visual Studio which
-needs to be used when compiling GPGME and the Python bindings with
-each version of the CPython binary released \href{https://www.python.org/downloads/windows/}{for Windows}:
-
-\begin{center}
-\begin{tabular}{rll}
-CPython & Microsoft product name & runtime filename\\
-2.7.6 & Visual Studio 2008 & MSVCR90.DLL\\
-3.4.0 & Visual Studio 2010 & MSVCR100.DLL\\
-3.5.0 & Visual Studio 2015 & \textbf{see below}\\
-3.6.0 & Visual Studio 2015 & \textbf{see below}\\
-3.7.0 & Visual Studio 2017* & \textbf{see below}\\
-\end{tabular}
-\end{center}
-
-It is important to note that MingW and Msys2 ship with the Visual C
-runtime from Microsoft Visual Studio 2005 and are thus \textbf{incompatible}
-with all the versions of CPython which can be used with the GPGME
-Python bindings.
-
-It is also important to note that from CPython 3.5 onwards, the Python
-Foundation has adopted the reworking of the Visual C runtime which was
-performed for Visual Studio 2015 and aimed at resolving many of these
-kinds of issues. Much greater detail on these issues and the correct
-file(s) to link to are available from Matthew Brett's invaluable page,
-\href{https://matthew-brett.github.io/pydagogue/python\_msvc.html}{Using Microsoft Visual C with Python}. It is also worth reading the
-Microsoft Developer Network blog post on \href{http://blogs.msdn.com/b/vcblog/archive/2015/03/03/introducing-the-universal-crt.aspx}{the universal CRT} and Steve
-Dower's blog posts on Python extensions (\href{http://stevedower.id.au/blog/building-for-python-3-5}{part 1} and \href{http://stevedower.id.au/blog/building-for-python-3-5-part-two}{part 2}).
-
-The second of those two posts by Steve Dower contains the details of
-specific configuration options required for compiling anything to be
-used with official CPython releases. In addition to those
-configuration and compiler settings to use, the versions of Visual
-Studio prior to Visual Studio 2015 did not support 64-bit systems by
-default. So compiling a 64-bit version of these bindings for a 64-bit
-version of CPython 2.7 or 3.4 requires additional work.
-
-In addition to the blog posts, the \href{https://wiki.python.org/moin/WindowsCompilers}{Windows compilers} wiki page on the
-CPython wiki is another essential reference on the relevant versions
-of Visual Studio to use and the degree of compatibility with CPython
-releases.
-
-Eventually someone will ask why there isn't an installable binary for
-Windows, which the GPGME of the licenses do not preclude as long as
-the source code is available in conjunction with such a release.
-
-The sheer number of versions of Visual Studio in conjunction with
-differing configuration options depending on the target Windows
-version and whether the architecture is 64-bit or 32-bit makes it
-difficult to provide a correct binary installer for Windows users. At
-the bare minimum doing so would require the GnuPG project compile ten
-different versions of the bindings with each release; both 32-bit and
-64-bit versions for CPython 2.7 and 3.4, with 64-bit versions for both
-x86-64 (i.e. Intel and AMD) and ARM architectures for CPython 3.5,
-3.6, 3.7 and later releases. That's the bare \textbf{minimum}, it'd probably
-be higher.
-
-Additionally, with only a binary installation used in conjunction with
-the CPython installer from \texttt{python.org} the advanced options available
-which utilise \hyperref[sec:org3b53926]{Cython} will not be able to be used at all. Cython
-depends on being able to compile the C code it generates and that too
-would need to utilise a matching runtime to both the installed version
-of CPython and these bindings in order to work with the bindings.
-
-Considering all of that, what do we recommend?
-
-\begin{enumerate}
-\item Use a recent version of CPython; at least 3.5, but ideally 3.6 or
-later.
-
-\item Use Visual Studio 2015 or the standalone build tools for Visual
-Studio 2017 (or later).
-
-\item Compile both CPython and GPGME with these bindings using the tools
-selected in step 2.
-
-\item Ignore MingW, Msys2 and the official CPython binary installers.
-
-\item Be thankful the answer to this question wasn't simply to say
-something like, “install Linux” or “install FreeBSD” (or even
-Apple's OS X).
-\end{enumerate}
-
-
-\subsubsection{CFFI is the Best™ and GPGME should use it instead of SWIG}
-\label{sec:org645735a}
-There are many reasons for favouring \href{https://cffi.readthedocs.io/en/latest/overview.html}{CFFI} and proponents of it are
-quite happy to repeat these things as if all it would take to switch
-from SWIG to CFFI is repeating that list as if it were a new concept.
-
-The fact is that there are things which Python's CFFI implementation
-cannot handle in the GPGME C code. Beyond that there are features of
-SWIG which are simply not available with CFFI at all. SWIG generates
-the bindings to Python using the \texttt{gpgme.h} file, but that file is not
-a single version shipped with each release, it too is generated when
-GPGME is compiled.
-
-CFFI is currently unable to adapt to such a potentially mutable
-codebase. If there were some means of applying SWIG's dynamic code
-generation to produce the Python/CFFI API modes of accessing the GPGME
-libraries (or the source source code directly), but such a thing does
-not exist yet either and it currently appears that work is needed in
-at least one of CFFI's dependencies before any of this can be
-addressed.
-
-So if you're a massive fan of CFFI; that's great, but if you want this
-project to switch to CFFI then rather than just insisting that it
-should, I'd suggest you volunteer to bring CFFI up to the level this
-project needs.
-
-If you're actually seriously considering doing so, then I'd suggest
-taking the \texttt{gpgme-tool.c} file in the GPGME \texttt{src/} directory and
-getting that to work with any of the CFFI API methods (not the ABI
-methods, they'll work with pretty much anything). When you start
-running into trouble with "ifdefs" then you'll know what sort of
-things are lacking. That doesn't even take into account the amount of
-work saved via SWIG's code generation techniques either.
-
-
-\subsubsection{Virtualised Environments}
-\label{sec:orgb0d56f5}
-It is fairly common practice amongst Python developers to, as much as
-possible, use packages like virtualenv to keep various things that are
-to be installed from interfering with each other. Given how much of
-the GPGME bindings is often at odds with the usual pythonic way of
-doing things, it stands to reason that this would be called into
-question too.
-
-As it happens the answer as to whether or not the bindings can be used
-with virtualenv, the answer is both yes and no.
-
-In general we recommend installing to the relevant path and matching
-prefix of GPGME itself. Which means that when GPGME, and ideally the
-rest of the GnuPG stack, is installed to a prefix like \texttt{/usr/local} or
-\texttt{/opt/local} then the bindings would need to be installed to the main
-Python installation and not a virtualised abstraction. Attempts to
-separate the two in the past have been known to cause weird and
-intermittent errors ranging from minor annoyances to complete failures
-in the build process.
-
-As a consequence we only recommend building with and installing to the
-main Python installations within the same prefix as GPGME is installed
-to or which are found by GPGME's configuration stage immediately prior
-to running the make commands. Which is exactly what the compiling and
-installing process of GPGME does by default.
-
-Once that is done, however, it appears that a copy of the compiled
-module may be installed into a virtualenv of the same major and minor
-version matching the build. Alternatively it is possible to utilise a
-\texttt{sites.pth} file in the \texttt{site-packages/} directory of a virtualenv
-installation, which links back to the system installations
-corresponding directory in order to import anything installed system
-wide. This may or may not be appropriate on a case by case basis.
-
-Though extensive testing of either of these options is not yet
-complete, preliminary testing of them indicates that both are viable
-as long as the main installation is complete. Which means that
-certain other options normally restricted to virtual environments are
-also available, including integration with pythonic test suites
-(e.g. \href{https://docs.pytest.org/en/latest/index.html}{pytest}) and other large projects.
-
-That said, it is worth reiterating the warning regarding non-standard
-installations. If one were to attempt to install the bindings only to
-a virtual environment without somehow also including the full GnuPG
-stack (or enough of it as to include GPGME) then it is highly likely
-that errors would be encountered at some point and more than a little
-likely that the build process itself would break.
-
-If a degree of separation from the main operating system is still
-required in spite of these warnings, then consider other forms of
-virtualisation. Either a virtual machine (e.g. \href{https://www.virtualbox.org/}{VirtualBox}), a
-hardware emulation layer (e.g. \href{https://www.qemu.org/}{QEMU}) or an application container
-(e.g. \href{https://www.docker.com/why-docker}{Docker}).
-
-Finally it should be noted that the limited tests conducted thus far
-have been using the \texttt{virtualenv} command in a new directory to create
-the virtual python environment. As opposed to the standard \texttt{python3
--m venv} and it is possible that this will make a difference depending
-on the system and version of Python in use. Another option is to run
-the command \texttt{python3 -m virtualenv /path/to/install/virtual/thingy}
-instead.
-
-
-\section{Fundamentals}
-\label{sec:org832642e}
-Before we can get to the fun stuff, there are a few matters regarding
-GPGME's design which hold true whether you're dealing with the C code
-directly or these Python bindings.
-
-
-\subsection{No REST}
-\label{sec:org3d694ba}
-The first part of which is or will be fairly blatantly obvious upon
-viewing the first example, but it's worth reiterating anyway. That
-being that this API is \emph{\textbf{not}} a REST API. Nor indeed could it ever
-be one.
-
-Most, if not all, Python programmers (and not just Python programmers)
-know how easy it is to work with a RESTful API. In fact they've
-become so popular that many other APIs attempt to emulate REST-like
-behaviour as much as they are able. Right down to the use of JSON
-formatted output to facilitate the use of their API without having to
-retrain developers.
-
-This API does not do that. It would not be able to do that and also
-provide access to the entire C API on which it's built. It does,
-however, provide a very pythonic interface on top of the direct
-bindings and it's this pythonic layer that this HOWTO deals with.
-
-
-\subsection{Context}
-\label{sec:org10e4004}
-One of the reasons which prevents this API from being RESTful is that
-most operations require more than one instruction to the API to
-perform the task. Sure, there are certain functions which can be
-performed simultaneously, particularly if the result known or strongly
-anticipated (e.g. selecting and encrypting to a key known to be in the
-public keybox).
-
-There are many more, however, which cannot be manipulated so readily:
-they must be performed in a specific sequence and the result of one
-operation has a direct bearing on the outcome of subsequent
-operations. Not merely by generating an error either.
-
-When dealing with this type of persistent state on the web, full of
-both the RESTful and REST-like, it's most commonly referred to as a
-session. In GPGME, however, it is called a context and every
-operation type has one.
-
-
-\section{Working with keys}
-\label{sec:orgff114ca}
-\subsection{Key selection}
-\label{sec:org6589217}
-Selecting keys to encrypt to or to sign with will be a common
-occurrence when working with GPGMe and the means available for doing
-so are quite simple.
-
-They do depend on utilising a Context; however once the data is
-recorded in another variable, that Context does not need to be the
-same one which subsequent operations are performed.
-
-The easiest way to select a specific key is by searching for that
-key's key ID or fingerprint, preferably the full fingerprint without
-any spaces in it. A long key ID will probably be okay, but is not
-advised and short key IDs are already a problem with some being
-generated to match specific patterns. It does not matter whether the
-pattern is upper or lower case.
-
-So this is the best method:
-
-\begin{verbatim}
-import gpg
-
-k = gpg.Context().keylist(pattern="258E88DCBD3CD44D8E7AB43F6ECB6AF0DEADBEEF")
-keys = list(k)
-\end{verbatim}
-
-This is passable and very likely to be common:
-
-\begin{verbatim}
-import gpg
-
-k = gpg.Context().keylist(pattern="0x6ECB6AF0DEADBEEF")
-keys = list(k)
-\end{verbatim}
-
-And this is a really bad idea:
-
-\begin{verbatim}
-import gpg
-
-k = gpg.Context().keylist(pattern="0xDEADBEEF")
-keys = list(k)
-\end{verbatim}
-
-Alternatively it may be that the intention is to create a list of keys
-which all match a particular search string. For instance all the
-addresses at a particular domain, like this:
-
-\begin{verbatim}
-import gpg
-
-ncsc = gpg.Context().keylist(pattern="ncsc.mil")
-nsa = list(ncsc)
-\end{verbatim}
-
-
-\subsubsection{Counting keys}
-\label{sec:org63a3d62}
-Counting the number of keys in your public keybox (\texttt{pubring.kbx}), the
-format which has superseded the old keyring format (\texttt{pubring.gpg} and
-\texttt{secring.gpg}), or the number of secret keys is a very simple task.
-
-\begin{verbatim}
-import gpg
-
-c = gpg.Context()
-seckeys = c.keylist(pattern=None, secret=True)
-pubkeys = c.keylist(pattern=None, secret=False)
-
-seclist = list(seckeys)
-secnum = len(seclist)
-
-publist = list(pubkeys)
-pubnum = len(publist)
-
-print("""
- Number of secret keys: {0}
- Number of public keys: {1}
-""".format(secnum, pubnum))
-\end{verbatim}
-
-NOTE: The \hyperref[sec:org3b53926]{Cython} introduction in the \hyperref[sec:org944cc00]{Advanced and Experimental}
-section uses this same key counting code with Cython to demonstrate
-some areas where Cython can improve performance even with the
-bindings. Users with large public keyrings or keyboxes, for instance,
-should consider these options if they are comfortable with using
-Cython.
-
-
-\subsection{Get key}
-\label{sec:org26adb2e}
-An alternative method of getting a single key via its fingerprint is
-available directly within a Context with \texttt{Context().get\_key}. This is
-the preferred method of selecting a key in order to modify it, sign or
-certify it and for obtaining relevant data about a single key as a
-part of other functions; when verifying a signature made by that key,
-for instance.
-
-By default this method will select public keys, but it can select
-secret keys as well.
-
-This first example demonstrates selecting the current key of Werner
-Koch, which is due to expire at the end of 2018:
-
-\begin{verbatim}
-import gpg
-
-fingerprint = "80615870F5BAD690333686D0F2AD85AC1E42B367"
-key = gpg.Context().get_key(fingerprint)
-\end{verbatim}
-
-Whereas this example demonstrates selecting the author's current key
-with the \texttt{secret} key word argument set to \texttt{True}:
-
-\begin{verbatim}
-import gpg
-
-fingerprint = "DB4724E6FA4286C92B4E55C4321E4E2373590E5D"
-key = gpg.Context().get_key(fingerprint, secret=True)
-\end{verbatim}
-
-It is, of course, quite possible to select expired, disabled and
-revoked keys with this function, but only to effectively display
-information about those keys.
-
-It is also possible to use both unicode or string literals and byte
-literals with the fingerprint when getting a key in this way.
-
-
-\subsection{Importing keys}
-\label{sec:org1ad598a}
-Importing keys is possible with the \texttt{key\_import()} method and takes
-one argument which is a bytes literal object containing either the
-binary or ASCII armoured key data for one or more keys.
-
-The following example retrieves one or more keys from the SKS
-keyservers via the web using the requests module. Since requests
-returns the content as a bytes literal object, we can then use that
-directly to import the resulting data into our keybox.
-
-\begin{verbatim}
-import gpg
-import os.path
-import requests
-
-c = gpg.Context()
-url = "https://sks-keyservers.net/pks/lookup"
-pattern = input("Enter the pattern to search for key or user IDs: ")
-payload = {"op": "get", "search": pattern}
-
-r = requests.get(url, verify=True, params=payload)
-result = c.key_import(r.content)
-
-if result is not None and hasattr(result, "considered") is False:
- print(result)
-elif result is not None and hasattr(result, "considered") is True:
- num_keys = len(result.imports)
- new_revs = result.new_revocations
- new_sigs = result.new_signatures
- new_subs = result.new_sub_keys
- new_uids = result.new_user_ids
- new_scrt = result.secret_imported
- nochange = result.unchanged
- print("""
- The total number of keys considered for import was: {0}
-
- Number of keys revoked: {1}
- Number of new signatures: {2}
- Number of new subkeys: {3}
- Number of new user IDs: {4}
- Number of new secret keys: {5}
- Number of unchanged keys: {6}
-
- The key IDs for all considered keys were:
-""".format(num_keys, new_revs, new_sigs, new_subs, new_uids, new_scrt,
- nochange))
- for i in range(num_keys):
- print("{0}\n".format(result.imports[i].fpr))
-else:
- pass
-\end{verbatim}
-
-NOTE: When searching for a key ID of any length or a fingerprint
-(without spaces), the SKS servers require the the leading \texttt{0x}
-indicative of hexadecimal be included. Also note that the old short
-key IDs (e.g. \texttt{0xDEADBEEF}) should no longer be used due to the
-relative ease by which such key IDs can be reproduced, as demonstrated
-by the Evil32 Project in 2014 (which was subsequently exploited in
-2016).
-
-Testing for whether a string in any given search is or may be a
-hexadecimal value which may be missing the leading \texttt{0x} is a simple
-matter of using a try/except statement which attempts to convert the
-string as hex to an integer and then back to hex; then using that to
-search with. Raising a ValueError simply results in treating the
-string as a string. This is the method and logic utilised in the
-\texttt{import-keys-hkp.py} script (see below).
-
-
-\subsubsection{Working with ProtonMail}
-\label{sec:org27b0d6f}
-Here is a variation on the example above which checks the constrained
-ProtonMail keyserver for ProtonMail public keys.
-
-\begin{verbatim}
-import gpg
-import requests
-import sys
-
-print("""
-This script searches the ProtonMail key server for the specified key and
-imports it.
-""")
-
-c = gpg.Context(armor=True)
-url = "https://api.protonmail.ch/pks/lookup"
-ksearch = []
-
-if len(sys.argv) >= 2:
- keyterm = sys.argv[1]
-else:
- keyterm = input("Enter the key ID, UID or search string: ")
-
-if keyterm.count("@") == 2 and keyterm.startswith("@") is True:
- ksearch.append(keyterm[1:])
- ksearch.append(keyterm[1:])
- ksearch.append(keyterm[1:])
-elif keyterm.count("@") == 1 and keyterm.startswith("@") is True:
- ksearch.append("{0}@protonmail.com".format(keyterm[1:]))
- ksearch.append("{0}@protonmail.ch".format(keyterm[1:]))
- ksearch.append("{0}@pm.me".format(keyterm[1:]))
-elif keyterm.count("@") == 0:
- ksearch.append("{0}@protonmail.com".format(keyterm))
- ksearch.append("{0}@protonmail.ch".format(keyterm))
- ksearch.append("{0}@pm.me".format(keyterm))
-elif keyterm.count("@") == 2 and keyterm.startswith("@") is False:
- uidlist = keyterm.split("@")
- for uid in uidlist:
- ksearch.append("{0}@protonmail.com".format(uid))
- ksearch.append("{0}@protonmail.ch".format(uid))
- ksearch.append("{0}@pm.me".format(uid))
-elif keyterm.count("@") > 2:
- uidlist = keyterm.split("@")
- for uid in uidlist:
- ksearch.append("{0}@protonmail.com".format(uid))
- ksearch.append("{0}@protonmail.ch".format(uid))
- ksearch.append("{0}@pm.me".format(uid))
-else:
- ksearch.append(keyterm)
-
-for k in ksearch:
- payload = {"op": "get", "search": k}
- try:
- r = requests.get(url, verify=True, params=payload)
- if r.ok is True:
- result = c.key_import(r.content)
- elif r.ok is False:
- result = r.content
- except Exception as e:
- result = None
-
- if result is not None and hasattr(result, "considered") is False:
- print("{0} for {1}".format(result.decode(), k))
- elif result is not None and hasattr(result, "considered") is True:
- num_keys = len(result.imports)
- new_revs = result.new_revocations
- new_sigs = result.new_signatures
- new_subs = result.new_sub_keys
- new_uids = result.new_user_ids
- new_scrt = result.secret_imported
- nochange = result.unchanged
- print("""
-The total number of keys considered for import was: {0}
-
-With UIDs wholely or partially matching the following string:
-
- {1}
-
- Number of keys revoked: {2}
- Number of new signatures: {3}
- Number of new subkeys: {4}
- Number of new user IDs: {5}
-Number of new secret keys: {6}
- Number of unchanged keys: {7}
-
-The key IDs for all considered keys were:
-""".format(num_keys, k, new_revs, new_sigs, new_subs, new_uids, new_scrt,
- nochange))
- for i in range(num_keys):
- print(result.imports[i].fpr)
- print("")
- elif result is None:
- print(e)
-\end{verbatim}
-
-Both the above example, \href{../examples/howto/pmkey-import.py}{pmkey-import.py}, and a version which prompts
-for an alternative GnuPG home directory, \href{../examples/howto/pmkey-import-alt.py}{pmkey-import-alt.py}, are
-available with the other examples and are executable scripts.
-
-Note that while the ProtonMail servers are based on the SKS servers,
-their server is related more to their API and is not feature complete
-by comparison to the servers in the SKS pool. One notable difference
-being that the ProtonMail server does not permit non ProtonMail users
-to update their own keys, which could be a vector for attacking
-ProtonMail users who may not receive a key's revocation if it had been
-compromised.
-
-
-\subsubsection{Importing with HKP for Python}
-\label{sec:org592d7ab}
-Performing the same tasks with the \href{https://github.com/Selfnet/hkp4py}{hkp4py module} (available via PyPI)
-is not too much different, but does provide a number of options of
-benefit to end users. Not least of which being the ability to perform
-some checks on a key before importing it or not. For instance it may
-be the policy of a site or project to only import keys which have not
-been revoked. The hkp4py module permits such checks prior to the
-importing of the keys found.
-
-\begin{verbatim}
-import gpg
-import hkp4py
-import sys
-
-c = gpg.Context()
-server = hkp4py.KeyServer("hkps://hkps.pool.sks-keyservers.net")
-results = []
-keys = []
-
-if len(sys.argv) > 2:
- pattern = " ".join(sys.argv[1:])
-elif len(sys.argv) == 2:
- pattern = sys.argv[1]
-else:
- pattern = input("Enter the pattern to search for keys or user IDs: ")
-
-
-if pattern is not None:
- try:
- key = server.search(hex(int(pattern, 16)))
- keyed = True
- except ValueError as ve:
- key = server.search(pattern)
- keyed = False
-
- if key is not None:
- keys.append(key[0])
- if keyed is True:
- try:
- fob = server.search(pattern)
- except:
- fob = None
- if fob is not None:
- keys.append(fob[0])
- else:
- pass
- else:
- pass
-
- for logrus in pattern.split():
- try:
- key = server.search(hex(int(logrus, 16)))
- hexed = True
- except ValueError as ve:
- key = server.search(logrus)
- hexed = False
-
- if key is not None:
- keys.append(key[0])
- if hexed is True:
- try:
- fob = server.search(logrus)
- except:
- fob = None
- if fob is not None:
- keys.append(fob[0])
- else:
- pass
- else:
- pass
-
-
-if len(keys) > 0:
- for key in keys:
- import_result = c.key_import(key.key_blob)
- results.append(import_result)
-
-for result in results:
- if result is not None and hasattr(result, "considered") is False:
- print(result)
- elif result is not None and hasattr(result, "considered") is True:
- num_keys = len(result.imports)
- new_revs = result.new_revocations
- new_sigs = result.new_signatures
- new_subs = result.new_sub_keys
- new_uids = result.new_user_ids
- new_scrt = result.secret_imported
- nochange = result.unchanged
- print("""
-The total number of keys considered for import was: {0}
-
- Number of keys revoked: {1}
- Number of new signatures: {2}
- Number of new subkeys: {3}
- Number of new user IDs: {4}
-Number of new secret keys: {5}
- Number of unchanged keys: {6}
-
-The key IDs for all considered keys were:
-""".format(num_keys, new_revs, new_sigs, new_subs, new_uids, new_scrt,
- nochange))
- for i in range(num_keys):
- print(result.imports[i].fpr)
- print("")
- else:
- pass
-\end{verbatim}
-
-Since the hkp4py module handles multiple keys just as effectively as
-one (\texttt{keys} is a list of responses per matching key), the example
-above is able to do a little bit more with the returned data before
-anything is actually imported.
-
-
-\subsubsection{Importing from ProtonMail with HKP for Python}
-\label{sec:org3966a76}
-Though this can provide certain benefits even when working with
-ProtonMail, the scope is somewhat constrained there due to the
-limitations of the ProtonMail keyserver.
-
-For instance, searching the SKS keyserver pool for the term "gnupg"
-produces hundreds of results from any time the word appears in any
-part of a user ID. Performing the same search on the ProtonMail
-keyserver returns zero results, even though there are at least two
-test accounts which include it as part of the username.
-
-The cause of this discrepancy is the deliberate configuration of that
-server by ProtonMail to require an exact match of the full email
-address of the ProtonMail user whose key is being requested.
-Presumably this is intended to reduce breaches of privacy of their
-users as an email address must already be known before a key for that
-address can be obtained.
-
-
-\begin{enumerate}
-\item Import from ProtonMail via HKP for Python Example no. 1
-\label{sec:org6cd15c3}
-The following script is available with the rest of the examples under
-the somewhat less than original name, \texttt{pmkey-import-hkp.py}.
-
-\begin{verbatim}
-import gpg
-import hkp4py
-import os.path
-import sys
-
-print("""
-This script searches the ProtonMail key server for the specified key and
-imports it.
-
-Usage: pmkey-import-hkp.py [search strings]
-""")
-
-c = gpg.Context(armor=True)
-server = hkp4py.KeyServer("hkps://api.protonmail.ch")
-keyterms = []
-ksearch = []
-allkeys = []
-results = []
-paradox = []
-homeless = None
-
-if len(sys.argv) > 2:
- keyterms = sys.argv[1:]
-elif len(sys.argv) == 2:
- keyterm = sys.argv[1]
- keyterms.append(keyterm)
-else:
- key_term = input("Enter the key ID, UID or search string: ")
- keyterms = key_term.split()
-
-for keyterm in keyterms:
- if keyterm.count("@") == 2 and keyterm.startswith("@") is True:
- ksearch.append(keyterm[1:])
- ksearch.append(keyterm[1:])
- ksearch.append(keyterm[1:])
- elif keyterm.count("@") == 1 and keyterm.startswith("@") is True:
- ksearch.append("{0}@protonmail.com".format(keyterm[1:]))
- ksearch.append("{0}@protonmail.ch".format(keyterm[1:]))
- ksearch.append("{0}@pm.me".format(keyterm[1:]))
- elif keyterm.count("@") == 0:
- ksearch.append("{0}@protonmail.com".format(keyterm))
- ksearch.append("{0}@protonmail.ch".format(keyterm))
- ksearch.append("{0}@pm.me".format(keyterm))
- elif keyterm.count("@") == 2 and keyterm.startswith("@") is False:
- uidlist = keyterm.split("@")
- for uid in uidlist:
- ksearch.append("{0}@protonmail.com".format(uid))
- ksearch.append("{0}@protonmail.ch".format(uid))
- ksearch.append("{0}@pm.me".format(uid))
- elif keyterm.count("@") > 2:
- uidlist = keyterm.split("@")
- for uid in uidlist:
- ksearch.append("{0}@protonmail.com".format(uid))
- ksearch.append("{0}@protonmail.ch".format(uid))
- ksearch.append("{0}@pm.me".format(uid))
- else:
- ksearch.append(keyterm)
-
-for k in ksearch:
- print("Checking for key for: {0}".format(k))
- try:
- keys = server.search(k)
- if isinstance(keys, list) is True:
- for key in keys:
- allkeys.append(key)
- try:
- import_result = c.key_import(key.key_blob)
- except Exception as e:
- import_result = c.key_import(key.key)
- else:
- paradox.append(keys)
- import_result = None
- except Exception as e:
- import_result = None
- results.append(import_result)
-
-for result in results:
- if result is not None and hasattr(result, "considered") is False:
- print("{0} for {1}".format(result.decode(), k))
- elif result is not None and hasattr(result, "considered") is True:
- num_keys = len(result.imports)
- new_revs = result.new_revocations
- new_sigs = result.new_signatures
- new_subs = result.new_sub_keys
- new_uids = result.new_user_ids
- new_scrt = result.secret_imported
- nochange = result.unchanged
- print("""
-The total number of keys considered for import was: {0}
-
-With UIDs wholely or partially matching the following string:
-
- {1}
-
- Number of keys revoked: {2}
- Number of new signatures: {3}
- Number of new subkeys: {4}
- Number of new user IDs: {5}
-Number of new secret keys: {6}
- Number of unchanged keys: {7}
-
-The key IDs for all considered keys were:
-""".format(num_keys, k, new_revs, new_sigs, new_subs, new_uids, new_scrt,
- nochange))
- for i in range(num_keys):
- print(result.imports[i].fpr)
- print("")
- elif result is None:
- pass
-\end{verbatim}
-
-
-\item Import from ProtonMail via HKP for Python Example no. 2
-\label{sec:org3b674e5}
-Like its counterpart above, this script can also be found with the
-rest of the examples, by the name pmkey-import-hkp-alt.py.
-
-With this script a modicum of effort has been made to treat anything
-passed as a \texttt{homedir} which either does not exist or which is not a
-directory, as also being a pssible user ID to check for. It's not
-guaranteed to pick up on all such cases, but it should cover most of
-them.
-
-\begin{verbatim}
-import gpg
-import hkp4py
-import os.path
-import sys
-
-print("""
-This script searches the ProtonMail key server for the specified key and
-imports it. Optionally enables specifying a different GnuPG home directory.
-
-Usage: pmkey-import-hkp.py [homedir] [search string]
- or: pmkey-import-hkp.py [search string]
-""")
-
-c = gpg.Context(armor=True)
-server = hkp4py.KeyServer("hkps://api.protonmail.ch")
-keyterms = []
-ksearch = []
-allkeys = []
-results = []
-paradox = []
-homeless = None
-
-if len(sys.argv) > 3:
- homedir = sys.argv[1]
- keyterms = sys.argv[2:]
-elif len(sys.argv) == 3:
- homedir = sys.argv[1]
- keyterm = sys.argv[2]
- keyterms.append(keyterm)
-elif len(sys.argv) == 2:
- homedir = ""
- keyterm = sys.argv[1]
- keyterms.append(keyterm)
-else:
- keyterm = input("Enter the key ID, UID or search string: ")
- homedir = input("Enter the GPG configuration directory path (optional): ")
- keyterms.append(keyterm)
-
-if len(homedir) == 0:
- homedir = None
- homeless = False
-
-if homedir is not None:
- if homedir.startswith("~"):
- if os.path.exists(os.path.expanduser(homedir)) is True:
- if os.path.isdir(os.path.expanduser(homedir)) is True:
- c.home_dir = os.path.realpath(os.path.expanduser(homedir))
- else:
- homeless = True
- else:
- homeless = True
- elif os.path.exists(os.path.realpath(homedir)) is True:
- if os.path.isdir(os.path.realpath(homedir)) is True:
- c.home_dir = os.path.realpath(homedir)
- else:
- homeless = True
- else:
- homeless = True
-
-# First check to see if the homedir really is a homedir and if not, treat it as
-# a search string.
-if homeless is True:
- keyterms.append(homedir)
- c.home_dir = None
-else:
- pass
-
-for keyterm in keyterms:
- if keyterm.count("@") == 2 and keyterm.startswith("@") is True:
- ksearch.append(keyterm[1:])
- ksearch.append(keyterm[1:])
- ksearch.append(keyterm[1:])
- elif keyterm.count("@") == 1 and keyterm.startswith("@") is True:
- ksearch.append("{0}@protonmail.com".format(keyterm[1:]))
- ksearch.append("{0}@protonmail.ch".format(keyterm[1:]))
- ksearch.append("{0}@pm.me".format(keyterm[1:]))
- elif keyterm.count("@") == 0:
- ksearch.append("{0}@protonmail.com".format(keyterm))
- ksearch.append("{0}@protonmail.ch".format(keyterm))
- ksearch.append("{0}@pm.me".format(keyterm))
- elif keyterm.count("@") == 2 and keyterm.startswith("@") is False:
- uidlist = keyterm.split("@")
- for uid in uidlist:
- ksearch.append("{0}@protonmail.com".format(uid))
- ksearch.append("{0}@protonmail.ch".format(uid))
- ksearch.append("{0}@pm.me".format(uid))
- elif keyterm.count("@") > 2:
- uidlist = keyterm.split("@")
- for uid in uidlist:
- ksearch.append("{0}@protonmail.com".format(uid))
- ksearch.append("{0}@protonmail.ch".format(uid))
- ksearch.append("{0}@pm.me".format(uid))
- else:
- ksearch.append(keyterm)
-
-for k in ksearch:
- print("Checking for key for: {0}".format(k))
- try:
- keys = server.search(k)
- if isinstance(keys, list) is True:
- for key in keys:
- allkeys.append(key)
- try:
- import_result = c.key_import(key.key_blob)
- except Exception as e:
- import_result = c.key_import(key.key)
- else:
- paradox.append(keys)
- import_result = None
- except Exception as e:
- import_result = None
- results.append(import_result)
-
-for result in results:
- if result is not None and hasattr(result, "considered") is False:
- print("{0} for {1}".format(result.decode(), k))
- elif result is not None and hasattr(result, "considered") is True:
- num_keys = len(result.imports)
- new_revs = result.new_revocations
- new_sigs = result.new_signatures
- new_subs = result.new_sub_keys
- new_uids = result.new_user_ids
- new_scrt = result.secret_imported
- nochange = result.unchanged
- print("""
-The total number of keys considered for import was: {0}
-
-With UIDs wholely or partially matching the following string:
-
- {1}
-
- Number of keys revoked: {2}
- Number of new signatures: {3}
- Number of new subkeys: {4}
- Number of new user IDs: {5}
-Number of new secret keys: {6}
- Number of unchanged keys: {7}
-
-The key IDs for all considered keys were:
-""".format(num_keys, k, new_revs, new_sigs, new_subs, new_uids, new_scrt,
- nochange))
- for i in range(num_keys):
- print(result.imports[i].fpr)
- print("")
- elif result is None:
- pass
-\end{verbatim}
-\end{enumerate}
-
-
-\subsection{Exporting keys}
-\label{sec:org4596a77}
-Exporting keys remains a reasonably simple task, but has been
-separated into three different functions for the OpenPGP cryptographic
-engine. Two of those functions are for exporting public keys and the
-third is for exporting secret keys.
-
-
-\subsubsection{Exporting public keys}
-\label{sec:org873704f}
-There are two methods of exporting public keys, both of which are very
-similar to the other. The default method, \texttt{key\_export()}, will export
-a public key or keys matching a specified pattern as normal. The
-alternative, the \texttt{key\_export\_minimal()} method, will do the same thing
-except producing a minimised output with extra signatures and third
-party signatures or certifications removed.
-
-\begin{verbatim}
-import gpg
-import os.path
-import sys
-
-print("""
-This script exports one or more public keys.
-""")
-
-c = gpg.Context(armor=True)
-
-if len(sys.argv) >= 4:
- keyfile = sys.argv[1]
- logrus = sys.argv[2]
- homedir = sys.argv[3]
-elif len(sys.argv) == 3:
- keyfile = sys.argv[1]
- logrus = sys.argv[2]
- homedir = input("Enter the GPG configuration directory path (optional): ")
-elif len(sys.argv) == 2:
- keyfile = sys.argv[1]
- logrus = input("Enter the UID matching the key(s) to export: ")
- homedir = input("Enter the GPG configuration directory path (optional): ")
-else:
- keyfile = input("Enter the path and filename to save the secret key to: ")
- logrus = input("Enter the UID matching the key(s) to export: ")
- homedir = input("Enter the GPG configuration directory path (optional): ")
-
-if homedir.startswith("~"):
- if os.path.exists(os.path.expanduser(homedir)) is True:
- c.home_dir = os.path.expanduser(homedir)
- else:
- pass
-elif os.path.exists(homedir) is True:
- c.home_dir = homedir
-else:
- pass
-
-try:
- result = c.key_export(pattern=logrus)
-except:
- result = c.key_export(pattern=None)
-
-if result is not None:
- with open(keyfile, "wb") as f:
- f.write(result)
-else:
- pass
-\end{verbatim}
-
-It should be noted that the result will only return \texttt{None} when a
-search pattern has been entered, but has not matched any keys. When
-the search pattern itself is set to \texttt{None} this triggers the exporting
-of the entire public keybox.
-
-\begin{verbatim}
-import gpg
-import os.path
-import sys
-
-print("""
-This script exports one or more public keys in minimised form.
-""")
-
-c = gpg.Context(armor=True)
-
-if len(sys.argv) >= 4:
- keyfile = sys.argv[1]
- logrus = sys.argv[2]
- homedir = sys.argv[3]
-elif len(sys.argv) == 3:
- keyfile = sys.argv[1]
- logrus = sys.argv[2]
- homedir = input("Enter the GPG configuration directory path (optional): ")
-elif len(sys.argv) == 2:
- keyfile = sys.argv[1]
- logrus = input("Enter the UID matching the key(s) to export: ")
- homedir = input("Enter the GPG configuration directory path (optional): ")
-else:
- keyfile = input("Enter the path and filename to save the secret key to: ")
- logrus = input("Enter the UID matching the key(s) to export: ")
- homedir = input("Enter the GPG configuration directory path (optional): ")
-
-if homedir.startswith("~"):
- if os.path.exists(os.path.expanduser(homedir)) is True:
- c.home_dir = os.path.expanduser(homedir)
- else:
- pass
-elif os.path.exists(homedir) is True:
- c.home_dir = homedir
-else:
- pass
-
-try:
- result = c.key_export_minimal(pattern=logrus)
-except:
- result = c.key_export_minimal(pattern=None)
-
-if result is not None:
- with open(keyfile, "wb") as f:
- f.write(result)
-else:
- pass
-\end{verbatim}
-
-
-\subsubsection{Exporting secret keys}
-\label{sec:org6c28d3d}
-Exporting secret keys is, functionally, very similar to exporting
-public keys; save for the invocation of \texttt{pinentry} via \texttt{gpg-agent} in
-order to securely enter the key's passphrase and authorise the export.
-
-The following example exports the secret key to a file which is then
-set with the same permissions as the output files created by the
-command line secret key export options.
-
-\begin{verbatim}
-import gpg
-import os
-import os.path
-import sys
-
-print("""
-This script exports one or more secret keys.
-
-The gpg-agent and pinentry are invoked to authorise the export.
-""")
-
-c = gpg.Context(armor=True)
-
-if len(sys.argv) >= 4:
- keyfile = sys.argv[1]
- logrus = sys.argv[2]
- homedir = sys.argv[3]
-elif len(sys.argv) == 3:
- keyfile = sys.argv[1]
- logrus = sys.argv[2]
- homedir = input("Enter the GPG configuration directory path (optional): ")
-elif len(sys.argv) == 2:
- keyfile = sys.argv[1]
- logrus = input("Enter the UID matching the secret key(s) to export: ")
- homedir = input("Enter the GPG configuration directory path (optional): ")
-else:
- keyfile = input("Enter the path and filename to save the secret key to: ")
- logrus = input("Enter the UID matching the secret key(s) to export: ")
- homedir = input("Enter the GPG configuration directory path (optional): ")
-
-if len(homedir) == 0:
- homedir = None
-elif homedir.startswith("~"):
- userdir = os.path.expanduser(homedir)
- if os.path.exists(userdir) is True:
- homedir = os.path.realpath(userdir)
- else:
- homedir = None
-else:
- homedir = os.path.realpath(homedir)
-
-if os.path.exists(homedir) is False:
- homedir = None
-else:
- if os.path.isdir(homedir) is False:
- homedir = None
- else:
- pass
-
-if homedir is not None:
- c.home_dir = homedir
-else:
- pass
-
-try:
- result = c.key_export_secret(pattern=logrus)
-except:
- result = c.key_export_secret(pattern=None)
-
-if result is not None:
- with open(keyfile, "wb") as f:
- f.write(result)
- os.chmod(keyfile, 0o600)
-else:
- pass
-\end{verbatim}
-
-Alternatively the approach of the following script can be used. This
-longer example saves the exported secret key(s) in files in the GnuPG
-home directory, in addition to setting the file permissions as only
-readable and writable by the user. It also exports the secret key(s)
-twice in order to output both GPG binary (\texttt{.gpg}) and ASCII armoured
-(\texttt{.asc}) files.
-
-\begin{verbatim}
-import gpg
-import os
-import os.path
-import subprocess
-import sys
-
-print("""
-This script exports one or more secret keys as both ASCII armored and binary
-file formats, saved in files within the user's GPG home directory.
-
-The gpg-agent and pinentry are invoked to authorise the export.
-""")
-
-if sys.platform == "win32":
- gpgconfcmd = "gpgconf.exe --list-dirs homedir"
-else:
- gpgconfcmd = "gpgconf --list-dirs homedir"
-
-a = gpg.Context(armor=True)
-b = gpg.Context()
-c = gpg.Context()
-
-if len(sys.argv) >= 4:
- keyfile = sys.argv[1]
- logrus = sys.argv[2]
- homedir = sys.argv[3]
-elif len(sys.argv) == 3:
- keyfile = sys.argv[1]
- logrus = sys.argv[2]
- homedir = input("Enter the GPG configuration directory path (optional): ")
-elif len(sys.argv) == 2:
- keyfile = sys.argv[1]
- logrus = input("Enter the UID matching the secret key(s) to export: ")
- homedir = input("Enter the GPG configuration directory path (optional): ")
-else:
- keyfile = input("Enter the filename to save the secret key to: ")
- logrus = input("Enter the UID matching the secret key(s) to export: ")
- homedir = input("Enter the GPG configuration directory path (optional): ")
-
-if len(homedir) == 0:
- homedir = None
-elif homedir.startswith("~"):
- userdir = os.path.expanduser(homedir)
- if os.path.exists(userdir) is True:
- homedir = os.path.realpath(userdir)
- else:
- homedir = None
-else:
- homedir = os.path.realpath(homedir)
-
-if os.path.exists(homedir) is False:
- homedir = None
-else:
- if os.path.isdir(homedir) is False:
- homedir = None
- else:
- pass
-
-if homedir is not None:
- c.home_dir = homedir
-else:
- pass
-
-if c.home_dir is not None:
- if c.home_dir.endswith("/"):
- gpgfile = "{0}{1}.gpg".format(c.home_dir, keyfile)
- ascfile = "{0}{1}.asc".format(c.home_dir, keyfile)
- else:
- gpgfile = "{0}/{1}.gpg".format(c.home_dir, keyfile)
- ascfile = "{0}/{1}.asc".format(c.home_dir, keyfile)
-else:
- if os.path.exists(os.environ["GNUPGHOME"]) is True:
- hd = os.environ["GNUPGHOME"]
- else:
- try:
- hd = subprocess.getoutput(gpgconfcmd)
- except:
- process = subprocess.Popen(gpgconfcmd.split(),
- stdout=subprocess.PIPE)
- procom = process.communicate()
- if sys.version_info[0] == 2:
- hd = procom[0].strip()
- else:
- hd = procom[0].decode().strip()
- gpgfile = "{0}/{1}.gpg".format(hd, keyfile)
- ascfile = "{0}/{1}.asc".format(hd, keyfile)
-
-try:
- a_result = a.key_export_secret(pattern=logrus)
- b_result = b.key_export_secret(pattern=logrus)
-except:
- a_result = a.key_export_secret(pattern=None)
- b_result = b.key_export_secret(pattern=None)
-
-if a_result is not None:
- with open(ascfile, "wb") as f:
- f.write(a_result)
- os.chmod(ascfile, 0o600)
-else:
- pass
-
-if b_result is not None:
- with open(gpgfile, "wb") as f:
- f.write(b_result)
- os.chmod(gpgfile, 0o600)
-else:
- pass
-\end{verbatim}
-
-
-\subsubsection{Sending public keys to the SKS Keyservers}
-\label{sec:org82d4958}
-As with the previous section on importing keys, the \texttt{hkp4py} module
-adds another option with exporting keys in order to send them to the
-public keyservers.
-
-The following example demonstrates how this may be done.
-
-\begin{verbatim}
-import gpg
-import hkp4py
-import os.path
-import sys
-
-print("""
-This script sends one or more public keys to the SKS keyservers and is
-essentially a slight variation on the export-key.py script.
-""")
-
-c = gpg.Context(armor=True)
-server = hkp4py.KeyServer("hkps://hkps.pool.sks-keyservers.net")
-
-if len(sys.argv) > 2:
- logrus = " ".join(sys.argv[1:])
-elif len(sys.argv) == 2:
- logrus = sys.argv[1]
-else:
- logrus = input("Enter the UID matching the key(s) to send: ")
-
-if len(logrus) > 0:
- try:
- export_result = c.key_export(pattern=logrus)
- except Exception as e:
- print(e)
- export_result = None
-else:
- export_result = c.key_export(pattern=None)
-
-if export_result is not None:
- try:
- try:
- send_result = server.add(export_result)
- except:
- send_result = server.add(export_result.decode())
- if send_result is not None:
- print(send_result)
- else:
- pass
- except Exception as e:
- print(e)
-else:
- pass
-\end{verbatim}
-
-An expanded version of this script with additional functions for
-specifying an alternative homedir location is in the examples
-directory as \texttt{send-key-to-keyserver.py}.
-
-The \texttt{hkp4py} module appears to handle both string and byte literal text
-data equally well, but the GPGME bindings deal primarily with byte
-literal data only and so this script sends in that format first, then
-tries the string literal form.
-
-
-\section{Basic Functions}
-\label{sec:orgb40b488}
-The most frequently called features of any cryptographic library will
-be the most fundamental tasks for encryption software. In this
-section we will look at how to programmatically encrypt data, decrypt
-it, sign it and verify signatures.
-
-
-\subsection{Encryption}
-\label{sec:org3170365}
-Encrypting is very straight forward. In the first example below the
-message, \texttt{text}, is encrypted to a single recipient's key. In the
-second example the message will be encrypted to multiple recipients.
-
-
-\subsubsection{Encrypting to one key}
-\label{sec:org8098c64}
-Once the the Context is set the main issues with encrypting data is
-essentially reduced to key selection and the keyword arguments
-specified in the \texttt{gpg.Context().encrypt()} method.
-
-Those keyword arguments are: \texttt{recipients}, a list of keys encrypted to
-(covered in greater detail in the following section); \texttt{sign}, whether
-or not to sign the plaintext data, see subsequent sections on signing
-and verifying signatures below (defaults to \texttt{True}); \texttt{sink}, to write
-results or partial results to a secure sink instead of returning it
-(defaults to \texttt{None}); \texttt{passphrase}, only used when utilising symmetric
-encryption (defaults to \texttt{None}); \texttt{always\_trust}, used to override the
-trust model settings for recipient keys (defaults to \texttt{False});
-\texttt{add\_encrypt\_to}, utilises any preconfigured \texttt{encrypt-to} or
-\texttt{default-key} settings in the user's \texttt{gpg.conf} file (defaults to
-\texttt{False}); \texttt{prepare}, prepare for encryption (defaults to \texttt{False});
-\texttt{expect\_sign}, prepare for signing (defaults to \texttt{False}); \texttt{compress},
-compresses the plaintext prior to encryption (defaults to \texttt{True}).
-
-\begin{verbatim}
-import gpg
-
-a_key = "0x12345678DEADBEEF"
-text = b"""Some text to test with.
-
-Since the text in this case must be bytes, it is most likely that
-the input form will be a separate file which is opened with "rb"
-as this is the simplest method of obtaining the correct data format.
-"""
-
-c = gpg.Context(armor=True)
-rkey = list(c.keylist(pattern=a_key, secret=False))
-ciphertext, result, sign_result = c.encrypt(text, recipients=rkey, sign=False)
-
-with open("secret_plans.txt.asc", "wb") as afile:
- afile.write(ciphertext)
-\end{verbatim}
-
-Though this is even more likely to be used like this; with the
-plaintext input read from a file, the recipient keys used for
-encryption regardless of key trust status and the encrypted output
-also encrypted to any preconfigured keys set in the \texttt{gpg.conf} file:
-
-\begin{verbatim}
-import gpg
-
-a_key = "0x12345678DEADBEEF"
-
-with open("secret_plans.txt", "rb") as afile:
- text = afile.read()
-
-c = gpg.Context(armor=True)
-rkey = list(c.keylist(pattern=a_key, secret=False))
-ciphertext, result, sign_result = c.encrypt(text, recipients=rkey, sign=True,
- always_trust=True,
- add_encrypt_to=True)
-
-with open("secret_plans.txt.asc", "wb") as afile:
- afile.write(ciphertext)
-\end{verbatim}
-
-If the \texttt{recipients} parameter is empty then the plaintext is encrypted
-symmetrically. If no \texttt{passphrase} is supplied as a parameter or via a
-callback registered with the \texttt{Context()} then an out-of-band prompt
-for the passphrase via pinentry will be invoked.
-
-
-\subsubsection{Encrypting to multiple keys}
-\label{sec:orgdcf6e47}
-Encrypting to multiple keys essentially just expands upon the key
-selection process and the recipients from the previous examples.
-
-The following example encrypts a message (\texttt{text}) to everyone with an
-email address on the \texttt{gnupg.org} domain,\footnote{You probably don't really want to do this. Searching the
-keyservers for "gnupg.org" produces over 400 results, the majority of
-which aren't actually at the gnupg.org domain, but just included a
-comment regarding the project in their key somewhere.} but does \emph{not} encrypt
-to a default key or other key which is configured to normally encrypt
-to.
-
-\begin{verbatim}
-import gpg
-
-text = b"""Oh look, another test message.
-
-The same rules apply as with the previous example and more likely
-than not, the message will actually be drawn from reading the
-contents of a file or, maybe, from entering data at an input()
-prompt.
-
-Since the text in this case must be bytes, it is most likely that
-the input form will be a separate file which is opened with "rb"
-as this is the simplest method of obtaining the correct data
-format.
-"""
-
-c = gpg.Context(armor=True)
-rpattern = list(c.keylist(pattern="@gnupg.org", secret=False))
-logrus = []
-
-for i in range(len(rpattern)):
- if rpattern[i].can_encrypt == 1:
- logrus.append(rpattern[i])
-
-ciphertext, result, sign_result = c.encrypt(text, recipients=logrus,
- sign=False, always_trust=True)
-
-with open("secret_plans.txt.asc", "wb") as afile:
- afile.write(ciphertext)
-\end{verbatim}
-
-All it would take to change the above example to sign the message
-and also encrypt the message to any configured default keys would
-be to change the \texttt{c.encrypt} line to this:
-
-\begin{verbatim}
-ciphertext, result, sign_result = c.encrypt(text, recipients=logrus,
- always_trust=True,
- add_encrypt_to=True)
-\end{verbatim}
-
-The only keyword arguments requiring modification are those for which
-the default values are changing. The default value of \texttt{sign} is
-\texttt{True}, the default of \texttt{always\_trust} is \texttt{False}, the default of
-\texttt{add\_encrypt\_to} is \texttt{False}.
-
-If \texttt{always\_trust} is not set to \texttt{True} and any of the recipient keys
-are not trusted (e.g. not signed or locally signed) then the
-encryption will raise an error. It is possible to mitigate this
-somewhat with something more like this:
-
-\begin{verbatim}
-import gpg
-
-with open("secret_plans.txt.asc", "rb") as afile:
- text = afile.read()
-
-c = gpg.Context(armor=True)
-rpattern = list(c.keylist(pattern="@gnupg.org", secret=False))
-logrus = []
-
-for i in range(len(rpattern)):
- if rpattern[i].can_encrypt == 1:
- logrus.append(rpattern[i])
-
- try:
- ciphertext, result, sign_result = c.encrypt(text, recipients=logrus,
- add_encrypt_to=True)
- except gpg.errors.InvalidRecipients as e:
- for i in range(len(e.recipients)):
- for n in range(len(logrus)):
- if logrus[n].fpr == e.recipients[i].fpr:
- logrus.remove(logrus[n])
- else:
- pass
- try:
- ciphertext, result, sign_result = c.encrypt(text,
- recipients=logrus,
- add_encrypt_to=True)
- with open("secret_plans.txt.asc", "wb") as afile:
- afile.write(ciphertext)
- except:
- pass
-\end{verbatim}
-
-This will attempt to encrypt to all the keys searched for, then remove
-invalid recipients if it fails and try again.
-
-
-\subsection{Decryption}
-\label{sec:org1282b3d}
-Decrypting something encrypted to a key in one's secret keyring is
-fairly straight forward.
-
-In this example code, however, preconfiguring either \texttt{gpg.Context()}
-or \texttt{gpg.core.Context()} as \texttt{c} is unnecessary because there is no need
-to modify the Context prior to conducting the decryption and since the
-Context is only used once, setting it to \texttt{c} simply adds lines for no
-gain.
-
-\begin{verbatim}
-import gpg
-
-ciphertext = input("Enter path and filename of encrypted file: ")
-newfile = input("Enter path and filename of file to save decrypted data to: ")
-
-with open(ciphertext, "rb") as cfile:
- try:
- plaintext, result, verify_result = gpg.Context().decrypt(cfile)
- except gpg.errors.GPGMEError as e:
- plaintext = None
- print(e)
-
-if plaintext is not None:
- with open(newfile, "wb") as nfile:
- nfile.write(plaintext)
- else:
- pass
-\end{verbatim}
-
-The data available in \texttt{plaintext} in this example is the decrypted
-content as a byte object, the recipient key IDs and algorithms in
-\texttt{result} and the results of verifying any signatures of the data in
-\texttt{verify\_result}.
-
-If \texttt{gpg.Context().decrypt(cfile, verify=False)} is called instead,
-then \texttt{verify\_result} will be returned as \texttt{None} and the rest remains
-as described here.
-
-
-\subsection{Signing text and files}
-\label{sec:org59f1262}
-The following sections demonstrate how to specify keys to sign with.
-
-
-\subsubsection{Signing key selection}
-\label{sec:org909cb3c}
-By default GPGME and the Python bindings will use the default key
-configured for the user invoking the GPGME API. If there is no
-default key specified and there is more than one secret key available
-it may be necessary to specify the key or keys with which to sign
-messages and files.
-
-\begin{verbatim}
-import gpg
-
-logrus = input("Enter the email address or string to match signing keys to: ")
-hancock = gpg.Context().keylist(pattern=logrus, secret=True)
-sig_src = list(hancock)
-\end{verbatim}
-
-The signing examples in the following sections include the explicitly
-designated \texttt{signers} parameter in two of the five examples; once where
-the resulting signature would be ASCII armoured and once where it
-would not be armoured.
-
-While it would be possible to enter a key ID or fingerprint here to
-match a specific key, it is not possible to enter two fingerprints and
-match two keys since the patten expects a string, bytes or None and
-not a list. A string with two fingerprints won't match any single
-key.
-
-
-\subsubsection{Normal or default signing messages or files}
-\label{sec:org6ee259d}
-The normal or default signing process is essentially the same as is
-most often invoked when also encrypting a message or file. So when
-the encryption component is not utilised, the result is to produce an
-encoded and signed output which may or may not be ASCII armoured and
-which may or may not also be compressed.
-
-By default compression will be used unless GnuPG detects that the
-plaintext is already compressed. ASCII armouring will be determined
-according to the value of \texttt{gpg.Context().armor}.
-
-The compression algorithm is selected in much the same way as the
-symmetric encryption algorithm or the hash digest algorithm is when
-multiple keys are involved; from the preferences saved into the key
-itself or by comparison with the preferences with all other keys
-involved.
-
-\begin{verbatim}
-import gpg
-
-text0 = """Declaration of ... something.
-
-"""
-text = text0.encode()
-
-c = gpg.Context(armor=True, signers=sig_src)
-signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.NORMAL)
-
-with open("/path/to/statement.txt.asc", "w") as afile:
- afile.write(signed_data.decode())
-\end{verbatim}
-
-Though everything in this example is accurate, it is more likely that
-reading the input data from another file and writing the result to a
-new file will be performed more like the way it is done in the next
-example. Even if the output format is ASCII armoured.
-
-\begin{verbatim}
-import gpg
-
-with open("/path/to/statement.txt", "rb") as tfile:
- text = tfile.read()
-
-c = gpg.Context()
-signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.NORMAL)
-
-with open("/path/to/statement.txt.sig", "wb") as afile:
- afile.write(signed_data)
-\end{verbatim}
-
-
-\subsubsection{Detached signing messages and files}
-\label{sec:org51049d4}
-Detached signatures will often be needed in programmatic uses of
-GPGME, either for signing files (e.g. tarballs of code releases) or as
-a component of message signing (e.g. PGP/MIME encoded email).
-
-\begin{verbatim}
-import gpg
-
-text0 = """Declaration of ... something.
-
-"""
-text = text0.encode()
-
-c = gpg.Context(armor=True)
-signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.DETACH)
-
-with open("/path/to/statement.txt.asc", "w") as afile:
- afile.write(signed_data.decode())
-\end{verbatim}
-
-As with normal signatures, detached signatures are best handled as
-byte literals, even when the output is ASCII armoured.
-
-\begin{verbatim}
-import gpg
-
-with open("/path/to/statement.txt", "rb") as tfile:
- text = tfile.read()
-
-c = gpg.Context(signers=sig_src)
-signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.DETACH)
-
-with open("/path/to/statement.txt.sig", "wb") as afile:
- afile.write(signed_data)
-\end{verbatim}
-
-
-\subsubsection{Clearsigning messages or text}
-\label{sec:orgf528a20}
-Though PGP/in-line messages are no longer encouraged in favour of
-PGP/MIME, there is still sometimes value in utilising in-line
-signatures. This is where clear-signed messages or text is of value.
-
-\begin{verbatim}
-import gpg
-
-text0 = """Declaration of ... something.
-
-"""
-text = text0.encode()
-
-c = gpg.Context()
-signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.CLEAR)
-
-with open("/path/to/statement.txt.asc", "w") as afile:
- afile.write(signed_data.decode())
-\end{verbatim}
-
-In spite of the appearance of a clear-signed message, the data handled
-by GPGME in signing it must still be byte literals.
-
-\begin{verbatim}
-import gpg
-
-with open("/path/to/statement.txt", "rb") as tfile:
- text = tfile.read()
-
-c = gpg.Context()
-signed_data, result = c.sign(text, mode=gpg.constants.sig.mode.CLEAR)
-
-with open("/path/to/statement.txt.asc", "wb") as afile:
- afile.write(signed_data)
-\end{verbatim}
-
-
-\subsection{Signature verification}
-\label{sec:org6cd81af}
-Essentially there are two principal methods of verification of a
-signature. The first of these is for use with the normal or default
-signing method and for clear-signed messages. The second is for use
-with files and data with detached signatures.
-
-The following example is intended for use with the default signing
-method where the file was not ASCII armoured:
-
-\begin{verbatim}
-import gpg
-import time
-
-filename = "statement.txt"
-gpg_file = "statement.txt.gpg"
-
-c = gpg.Context()
-
-try:
- data, result = c.verify(open(gpg_file))
- verified = True
-except gpg.errors.BadSignatures as e:
- verified = False
- print(e)
-
-if verified is True:
- for i in range(len(result.signatures)):
- sign = result.signatures[i]
- print("""Good signature from:
-{0}
-with key {1}
-made at {2}
-""".format(c.get_key(sign.fpr).uids[0].uid, sign.fpr,
- time.ctime(sign.timestamp)))
-else:
- pass
-\end{verbatim}
-
-Whereas this next example, which is almost identical would work with
-normal ASCII armoured files and with clear-signed files:
-
-\begin{verbatim}
-import gpg
-import time
-
-filename = "statement.txt"
-asc_file = "statement.txt.asc"
-
-c = gpg.Context()
-
-try:
- data, result = c.verify(open(asc_file))
- verified = True
-except gpg.errors.BadSignatures as e:
- verified = False
- print(e)
-
-if verified is True:
- for i in range(len(result.signatures)):
- sign = result.signatures[i]
- print("""Good signature from:
-{0}
-with key {1}
-made at {2}
-""".format(c.get_key(sign.fpr).uids[0].uid, sign.fpr,
- time.ctime(sign.timestamp)))
-else:
- pass
-\end{verbatim}
-
-In both of the previous examples it is also possible to compare the
-original data that was signed against the signed data in \texttt{data} to see
-if it matches with something like this:
-
-\begin{verbatim}
-with open(filename, "rb") as afile:
- text = afile.read()
-
-if text == data:
- print("Good signature.")
-else:
- pass
-\end{verbatim}
-
-The following two examples, however, deal with detached signatures.
-With his method of verification the data that was signed does not get
-returned since it is already being explicitly referenced in the first
-argument of \texttt{c.verify}. So \texttt{data} is \texttt{None} and only the information
-in \texttt{result} is available.
-
-\begin{verbatim}
-import gpg
-import time
-
-filename = "statement.txt"
-sig_file = "statement.txt.sig"
-
-c = gpg.Context()
-
-try:
- data, result = c.verify(open(filename), open(sig_file))
- verified = True
-except gpg.errors.BadSignatures as e:
- verified = False
- print(e)
-
-if verified is True:
- for i in range(len(result.signatures)):
- sign = result.signatures[i]
- print("""Good signature from:
-{0}
-with key {1}
-made at {2}
-""".format(c.get_key(sign.fpr).uids[0].uid, sign.fpr,
- time.ctime(sign.timestamp)))
-else:
- pass
-\end{verbatim}
-
-\begin{verbatim}
-import gpg
-import time
-
-filename = "statement.txt"
-asc_file = "statement.txt.asc"
-
-c = gpg.Context()
-
-try:
- data, result = c.verify(open(filename), open(asc_file))
- verified = True
-except gpg.errors.BadSignatures as e:
- verified = False
- print(e)
-
-if verified is True:
- for i in range(len(result.signatures)):
- sign = result.signatures[i]
- print("""Good signature from:
-{0}
-with key {1}
-made at {2}
-""".format(c.get_key(sign.fpr).uids[0].uid, sign.fpr,
- time.ctime(sign.timestamp)))
-else:
- pass
-\end{verbatim}
-
-
-\section{Creating keys and subkeys}
-\label{sec:org754c6df}
-The one thing, aside from GnuPG itself, that GPGME depends on, of
-course, is the keys themselves. So it is necessary to be able to
-generate them and modify them by adding subkeys, revoking or disabling
-them, sometimes deleting them and doing the same for user IDs.
-
-In the following examples a key will be created for the world's
-greatest secret agent, Danger Mouse. Since Danger Mouse is a secret
-agent he needs to be able to protect information to \texttt{SECRET} level
-clearance, so his keys will be 3072-bit keys.
-
-The pre-configured \texttt{gpg.conf} file which sets cipher, digest and other
-preferences contains the following configuration parameters:
-
-\begin{verbatim}
-expert
-allow-freeform-uid
-allow-secret-key-import
-trust-model tofu+pgp
-tofu-default-policy unknown
-enable-large-rsa
-enable-dsa2
-cert-digest-algo SHA512
-default-preference-list TWOFISH CAMELLIA256 AES256 CAMELLIA192 AES192 CAMELLIA128 AES BLOWFISH IDEA CAST5 3DES SHA512 SHA384 SHA256 SHA224 RIPEMD160 SHA1 ZLIB BZIP2 ZIP Uncompressed
-personal-cipher-preferences TWOFISH CAMELLIA256 AES256 CAMELLIA192 AES192 CAMELLIA128 AES BLOWFISH IDEA CAST5 3DES
-personal-digest-preferences SHA512 SHA384 SHA256 SHA224 RIPEMD160 SHA1
-personal-compress-preferences ZLIB BZIP2 ZIP Uncompressed
-\end{verbatim}
-
-
-\subsection{Primary key}
-\label{sec:org0185c23}
-Generating a primary key uses the \texttt{create\_key} method in a Context.
-It contains multiple arguments and keyword arguments, including:
-\texttt{userid}, \texttt{algorithm}, \texttt{expires\_in}, \texttt{expires}, \texttt{sign}, \texttt{encrypt},
-\texttt{certify}, \texttt{authenticate}, \texttt{passphrase} and \texttt{force}. The defaults for
-all of those except \texttt{userid}, \texttt{algorithm}, \texttt{expires\_in}, \texttt{expires} and
-\texttt{passphrase} is \texttt{False}. The defaults for \texttt{algorithm} and
-\texttt{passphrase} is \texttt{None}. The default for \texttt{expires\_in} is \texttt{0}. The
-default for \texttt{expires} is \texttt{True}. There is no default for \texttt{userid}.
-
-If \texttt{passphrase} is left as \texttt{None} then the key will not be generated
-with a passphrase, if \texttt{passphrase} is set to a string then that will
-be the passphrase and if \texttt{passphrase} is set to \texttt{True} then gpg-agent
-will launch pinentry to prompt for a passphrase. For the sake of
-convenience, these examples will keep \texttt{passphrase} set to \texttt{None}.
-
-\begin{verbatim}
-import gpg
-
-c = gpg.Context()
-
-c.home_dir = "~/.gnupg-dm"
-userid = "Danger Mouse <dm@secret.example.net>"
-
-dmkey = c.create_key(userid, algorithm="rsa3072", expires_in=31536000,
- sign=True, certify=True)
-\end{verbatim}
-
-One thing to note here is the use of setting the \texttt{c.home\_dir}
-parameter. This enables generating the key or keys in a different
-location. In this case to keep the new key data created for this
-example in a separate location rather than adding it to existing and
-active key store data. As with the default directory, \texttt{\textasciitilde{}/.gnupg}, any
-temporary or separate directory needs the permissions set to only
-permit access by the directory owner. On posix systems this means
-setting the directory permissions to 700.
-
-The \texttt{temp-homedir-config.py} script in the HOWTO examples directory
-will create an alternative homedir with these configuration options
-already set and the correct directory and file permissions.
-
-The successful generation of the key can be confirmed via the returned
-\texttt{GenkeyResult} object, which includes the following data:
-
-\begin{verbatim}
-print("""
- Fingerprint: {0}
- Primary Key: {1}
- Public Key: {2}
- Secret Key: {3}
- Sub Key: {4}
-User IDs: {5}
-""".format(dmkey.fpr, dmkey.primary, dmkey.pubkey, dmkey.seckey, dmkey.sub,
- dmkey.uid))
-\end{verbatim}
-
-Alternatively the information can be confirmed using the command line
-program:
-
-\begin{verbatim}
-bash-4.4$ gpg --homedir ~/.gnupg-dm -K
-~/.gnupg-dm/pubring.kbx
-----------------------
-sec rsa3072 2018-03-15 [SC] [expires: 2019-03-15]
- 177B7C25DB99745EE2EE13ED026D2F19E99E63AA
-uid [ultimate] Danger Mouse <dm@secret.example.net>
-
-bash-4.4$
-\end{verbatim}
-
-As with generating keys manually, to preconfigure expanded preferences
-for the cipher, digest and compression algorithms, the \texttt{gpg.conf} file
-must contain those details in the home directory in which the new key
-is being generated. I used a cut down version of my own \texttt{gpg.conf}
-file in order to be able to generate this:
-
-\begin{verbatim}
-bash-4.4$ gpg --homedir ~/.gnupg-dm --edit-key 177B7C25DB99745EE2EE13ED026D2F19E99E63AA showpref quit
-Secret key is available.
-
-sec rsa3072/026D2F19E99E63AA
- created: 2018-03-15 expires: 2019-03-15 usage: SC
- trust: ultimate validity: ultimate
-[ultimate] (1). Danger Mouse <dm@secret.example.net>
-
-[ultimate] (1). Danger Mouse <dm@secret.example.net>
- Cipher: TWOFISH, CAMELLIA256, AES256, CAMELLIA192, AES192, CAMELLIA128, AES, BLOWFISH, IDEA, CAST5, 3DES
- Digest: SHA512, SHA384, SHA256, SHA224, RIPEMD160, SHA1
- Compression: ZLIB, BZIP2, ZIP, Uncompressed
- Features: MDC, Keyserver no-modify
-
-bash-4.4$
-\end{verbatim}
-
-
-\subsection{Subkeys}
-\label{sec:org5e2acbc}
-Adding subkeys to a primary key is fairly similar to creating the
-primary key with the \texttt{create\_subkey} method. Most of the arguments
-are the same, but not quite all. Instead of the \texttt{userid} argument
-there is now a \texttt{key} argument for selecting which primary key to add
-the subkey to.
-
-In the following example an encryption subkey will be added to the
-primary key. Since Danger Mouse is a security conscious secret agent,
-this subkey will only be valid for about six months, half the length
-of the primary key.
-
-\begin{verbatim}
-import gpg
-
-c = gpg.Context()
-c.home_dir = "~/.gnupg-dm"
-
-key = c.get_key(dmkey.fpr, secret=True)
-dmsub = c.create_subkey(key, algorithm="rsa3072", expires_in=15768000,
- encrypt=True)
-\end{verbatim}
-
-As with the primary key, the results here can be checked with:
-
-\begin{verbatim}
-print("""
- Fingerprint: {0}
- Primary Key: {1}
- Public Key: {2}
- Secret Key: {3}
- Sub Key: {4}
-User IDs: {5}
-""".format(dmsub.fpr, dmsub.primary, dmsub.pubkey, dmsub.seckey, dmsub.sub,
- dmsub.uid))
-\end{verbatim}
-
-As well as on the command line with:
-
-\begin{verbatim}
-bash-4.4$ gpg --homedir ~/.gnupg-dm -K
-~/.gnupg-dm/pubring.kbx
-----------------------
-sec rsa3072 2018-03-15 [SC] [expires: 2019-03-15]
- 177B7C25DB99745EE2EE13ED026D2F19E99E63AA
-uid [ultimate] Danger Mouse <dm@secret.example.net>
-ssb rsa3072 2018-03-15 [E] [expires: 2018-09-13]
-
-bash-4.4$
-\end{verbatim}
-
-
-\subsection{User IDs}
-\label{sec:org4869c58}
-\subsubsection{Adding User IDs}
-\label{sec:orgebb13ca}
-By comparison to creating primary keys and subkeys, adding a new user
-ID to an existing key is much simpler. The method used to do this is
-\texttt{key\_add\_uid} and the only arguments it takes are for the \texttt{key} and
-the new \texttt{uid}.
-
-\begin{verbatim}
-import gpg
-
-c = gpg.Context()
-c.home_dir = "~/.gnupg-dm"
-
-dmfpr = "177B7C25DB99745EE2EE13ED026D2F19E99E63AA"
-key = c.get_key(dmfpr, secret=True)
-uid = "Danger Mouse <danger.mouse@secret.example.net>"
-
-c.key_add_uid(key, uid)
-\end{verbatim}
-
-Unsurprisingly the result of this is:
-
-\begin{verbatim}
-bash-4.4$ gpg --homedir ~/.gnupg-dm -K
-~/.gnupg-dm/pubring.kbx
-----------------------
-sec rsa3072 2018-03-15 [SC] [expires: 2019-03-15]
- 177B7C25DB99745EE2EE13ED026D2F19E99E63AA
-uid [ultimate] Danger Mouse <danger.mouse@secret.example.net>
-uid [ultimate] Danger Mouse <dm@secret.example.net>
-ssb rsa3072 2018-03-15 [E] [expires: 2018-09-13]
-
-bash-4.4$
-\end{verbatim}
-
-
-\subsubsection{Revoking User IDs}
-\label{sec:org1683695}
-Revoking a user ID is a fairly similar process, except that it uses
-the \texttt{key\_revoke\_uid} method.
-
-\begin{verbatim}
-import gpg
-
-c = gpg.Context()
-c.home_dir = "~/.gnupg-dm"
-
-dmfpr = "177B7C25DB99745EE2EE13ED026D2F19E99E63AA"
-key = c.get_key(dmfpr, secret=True)
-uid = "Danger Mouse <danger.mouse@secret.example.net>"
-
-c.key_revoke_uid(key, uid)
-\end{verbatim}
-
-
-\subsection{Key certification}
-\label{sec:orgf431bf8}
-Since key certification is more frequently referred to as key signing,
-the method used to perform this function is \texttt{key\_sign}.
-
-The \texttt{key\_sign} method takes four arguments: \texttt{key}, \texttt{uids},
-\texttt{expires\_in} and \texttt{local}. The default value of \texttt{uids} is \texttt{None} and
-which results in all user IDs being selected. The default value of
-both \texttt{expires\_in} and \texttt{local} is \texttt{False}; which results in the
-signature never expiring and being able to be exported.
-
-The \texttt{key} is the key being signed rather than the key doing the
-signing. To change the key doing the signing refer to the signing key
-selection above for signing messages and files.
-
-If the \texttt{uids} value is not \texttt{None} then it must either be a string to
-match a single user ID or a list of strings to match multiple user
-IDs. In this case the matching of those strings must be precise and
-it is case sensitive.
-
-To sign Danger Mouse's key for just the initial user ID with a
-signature which will last a little over a month, do this:
-
-\begin{verbatim}
-import gpg
-
-c = gpg.Context()
-uid = "Danger Mouse <dm@secret.example.net>"
-
-dmfpr = "177B7C25DB99745EE2EE13ED026D2F19E99E63AA"
-key = c.get_key(dmfpr, secret=True)
-c.key_sign(key, uids=uid, expires_in=2764800)
-\end{verbatim}
-
-
-\subsubsection{Verifying key certifications}
-\label{sec:org87abdd3}
-\begin{verbatim}
-import gpg
-import time
-
-c = gpg.Context()
-dmfpr = "177B7C25DB99745EE2EE13ED026D2F19E99E63AA"
-keys = list(c.keylist(pattern=dmuid, mode=gpg.constants.keylist.mode.SIGS))
-key = keys[0]
-
-for user in key.uids:
- for sig in user.signatures:
- print("0x{0}".format(sig.keyid), "", time.ctime(sig.timestamp), "",
- sig.uid)
-\end{verbatim}
-
-Which for Danger Mouse displays the following:
-
-\begin{verbatim}
-0x92E3F6115435C65A Thu Mar 15 13:17:44 2018 Danger Mouse <dm@secret.example.net>
-0x321E4E2373590E5D Mon Nov 26 12:46:05 2018 Ben McGinnes <ben@adversary.org>
-\end{verbatim}
-
-The two key signatures listed are for the self-certification of Danger
-Mouse's key made when the key was created in March, 2018; and the
-second is a signature made by the author and set to expire at the end
-of the year. Note that the second signature was made with the
-following code (including the preceding code to display the output of
-the certifications or key signatures):
-
-\begin{verbatim}
-import gpg
-import math
-import pendulum
-import time
-
-hd = "/home/dm/.gnupg"
-c = gpg.Context()
-d = gpg.Context(home_dir=hd)
-dmfpr = "177B7C25DB99745EE2EE13ED026D2F19E99E63AA"
-dmuid = "Danger Mouse <dm@secret.example.net>"
-dkeys = list(c.keylist(pattern=dmuid))
-dmkey = dkeys[0]
-
-c.key_import(d.key_export(pattern=None))
-
-tp = pendulum.period(pendulum.now(tz="local"), pendulum.datetime(2019, 1, 1))
-ts = tp.total_seconds()
-total_secs = math.ceil(ts)
-c.key_sign(dmkey, uids=dmuid, expires_in=total_secs)
-
-d.key_import(c.key_export(pattern=dmuid))
-keys = list(c.keylist(pattern=dmuid, mode=gpg.constants.keylist.mode.SIGS))
-key = keys[0]
-
-for user in key.uids:
- for sig in user.signatures:
- print("0x{0}".format(sig.keyid), "", time.ctime(sig.timestamp), "",
- sig.uid)
-\end{verbatim}
-
-Note that this final code block includes the use of a module which is
-\emph{not} part of Python's standard library, the \href{https://pendulum.eustace.io/}{pendulum module}. Unlike
-the standard datetime module, pendulum makes working with dates and
-times significantly easier in Python; just as the requests module
-makes working with HTTP and HTTPS easier than the builtin modules do.
-
-Though neither requests nor pendulum are required modules for using
-the GPGME Python bindings, they are both highly recommended more
-generally.
-
-
-\section{Advanced or Experimental Use Cases}
-\label{sec:org944cc00}
-\subsection{C plus Python plus SWIG plus Cython}
-\label{sec:org3b53926}
-In spite of the apparent incongruence of using Python bindings to a C
-interface only to generate more C from the Python; it is in fact quite
-possible to use the GPGME bindings with \href{http://docs.cython.org/en/latest/index.html}{Cython}. Though in many cases
-the benefits may not be obvious since the most computationally
-intensive work never leaves the level of the C code with which GPGME
-itself is interacting with.
-
-Nevertheless, there are some situations where the benefits are
-demonstrable. One of the better and easier examples being the one of
-the early examples in this HOWTO, the \hyperref[sec:org63a3d62]{key counting} code. Running that
-example as an executable Python script, \texttt{keycount.py} (available in
-the \texttt{examples/howto/} directory), will take a noticeable amount of time
-to run on most systems where the public keybox or keyring contains a
-few thousand public keys.
-
-Earlier in the evening, prior to starting this section, I ran that
-script on my laptop; as I tend to do periodically and timed it using
-\texttt{time} utility, with the following results:
-
-\begin{verbatim}
-bash-4.4$ time keycount.py
-
-Number of secret keys: 23
-Number of public keys: 12112
-
-
-real 11m52.945s
-user 0m0.913s
-sys 0m0.752s
-
-bash-4.4$
-\end{verbatim}
-
-Sometime after that I imported another key and followed it with a
-little test of Cython. This test was kept fairly basic, essentially
-lifting the material from the \href{http://docs.cython.org/en/latest/src/tutorial/cython\_tutorial.html}{Cython Basic Tutorial} to demonstrate
-compiling Python code to C. The first step was to take the example
-key counting code quoted previously, essentially from the importing of
-the \texttt{gpg} module to the end of the script:
-
-\begin{verbatim}
-import gpg
-
-c = gpg.Context()
-seckeys = c.keylist(pattern=None, secret=True)
-pubkeys = c.keylist(pattern=None, secret=False)
-
-seclist = list(seckeys)
-secnum = len(seclist)
-
-publist = list(pubkeys)
-pubnum = len(publist)
-
-print("""
- Number of secret keys: {0}
- Number of public keys: {1}
-
-""".format(secnum, pubnum))
-\end{verbatim}
-
-Save that into a file called \texttt{keycount.pyx} and then create a
-\texttt{setup.py} file which contains this:
-
-\begin{verbatim}
-from distutils.core import setup
-from Cython.Build import cythonize
-
-setup(
- ext_modules = cythonize("keycount.pyx")
-)
-\end{verbatim}
-
-Compile it:
-
-\begin{verbatim}
-bash-4.4$ python setup.py build_ext --inplace
-bash-4.4$
-\end{verbatim}
-
-Then run it in a similar manner to \texttt{keycount.py}:
-
-\begin{verbatim}
-bash-4.4$ time python3.7 -c "import keycount"
-
-Number of secret keys: 23
-Number of public keys: 12113
-
-
-real 6m47.905s
-user 0m0.785s
-sys 0m0.331s
-
-bash-4.4$
-\end{verbatim}
-
-Cython turned \texttt{keycount.pyx} into an 81KB \texttt{keycount.o} file in the
-\texttt{build/} directory, a 24KB \texttt{keycount.cpython-37m-darwin.so} file to be
-imported into Python 3.7 and a 113KB \texttt{keycount.c} generated C source
-code file of nearly three thousand lines. Quite a bit bigger than the
-314 bytes of the \texttt{keycount.pyx} file or the full 1,452 bytes of the
-full executable \texttt{keycount.py} example script.
-
-On the other hand it ran in nearly half the time; taking 6 minutes and
-47.905 seconds to run. As opposed to the 11 minutes and 52.945 seconds
-which the CPython script alone took.
-
-The \texttt{keycount.pyx} and \texttt{setup.py} files used to generate this example
-have been added to the \texttt{examples/howto/advanced/cython/} directory
-The example versions include some additional options to annotate the
-existing code and to detect Cython's use. The latter comes from the
-\href{http://docs.cython.org/en/latest/src/tutorial/pure.html\#magic-attributes-within-the-pxd}{Magic Attributes} section of the Cython documentation.
-
-
-\section{Miscellaneous extras and work-arounds}
-\label{sec:orgd72f7de}
-Most of the things in the following sections are here simply because
-there was no better place to put them, even though some are only
-peripherally related to the GPGME Python bindings. Some are also
-workarounds for functions not integrated with GPGME as yet. This is
-especially true of the first of these, dealing with \hyperref[sec:org83f4d00]{group lines}.
-
-
-\subsection{Group lines}
-\label{sec:org83f4d00}
-There is not yet an easy way to access groups configured in the
-gpg.conf file from within GPGME. As a consequence these central
-groupings of keys cannot be shared amongst multiple programs, such as
-MUAs readily.
-
-The following code, however, provides a work-around for obtaining this
-information in Python.
-
-\begin{verbatim}
-import subprocess
-import sys
-
-if sys.platform == "win32":
- gpgconfcmd = "gpgconf.exe --list-options gpg"
-else:
- gpgconfcmd = "gpgconf --list-options gpg"
-
-process = subprocess.Popen(gpgconfcmd.split(), stdout=subprocess.PIPE)
-procom = process.communicate()
-
-if sys.version_info[0] == 2:
- lines = procom[0].splitlines()
-else:
- lines = procom[0].decode().splitlines()
-
-for line in lines:
- if line.startswith("group") is True:
- break
-
-groups = line.split(":")[-1].replace('"', '').split(',')
-
-group_lines = []
-group_lists = []
-
-for i in range(len(groups)):
- group_lines.append(groups[i].split("="))
- group_lists.append(groups[i].split("="))
-
-for i in range(len(group_lists)):
- group_lists[i][1] = group_lists[i][1].split()
-\end{verbatim}
-
-The result of that code is that \texttt{group\_lines} is a list of lists where
-\texttt{group\_lines[i][0]} is the name of the group and \texttt{group\_lines[i][1]}
-is the key IDs of the group as a string.
-
-The \texttt{group\_lists} result is very similar in that it is a list of
-lists. The first part, \texttt{group\_lists[i][0]} matches
-\texttt{group\_lines[i][0]} as the name of the group, but \texttt{group\_lists[i][1]}
-is the key IDs of the group as a list.
-
-A demonstration of using the \texttt{groups.py} module is also available in
-the form of the executable \texttt{mutt-groups.py} script. This second
-script reads all the group entries in a user's \texttt{gpg.conf} file and
-converts them into crypt-hooks suitable for use with the Mutt and
-Neomutt mail clients.
-
-
-\subsection{Keyserver access for Python}
-\label{sec:org5ad99aa}
-The \href{https://github.com/Selfnet/hkp4py}{hkp4py} module by Marcel Fest was originally a port of the old
-\href{https://github.com/dgladkov/python-hkp}{python-hkp} module from Python 2 to Python 3 and updated to use the
-\href{http://docs.python-requests.org/en/latest/index.html}{requests} module instead. It has since been modified to provide
-support for Python 2.7 as well and is available via PyPI.
-
-Since it rewrites the \texttt{hkp} protocol prefix as \texttt{http} and \texttt{hkps} as
-\texttt{https}, the module is able to be used even with servers which do not
-support the full scope of keyserver functions.\footnote{Such as with ProtonMail servers. This also means that
-restricted servers which only advertise either HTTP or HTTPS end
-points and not HKP or HKPS end points must still be identified as as
-HKP or HKPS within the Python Code. The \texttt{hkp4py} module will rewrite
-these appropriately when the connection is made to the server.} It also works quite
-readily when incorporated into a \hyperref[sec:org3b53926]{Cython} generated and compiled version
-of any code.
-
-
-\subsubsection{Key import format}
-\label{sec:org1e47c97}
-The hkp4py module returns key data via requests as string literals
-(\texttt{r.text}) instead of byte literals (\texttt{r.content}). This means that
-the retrurned key data must be encoded to UTF-8 when importing that
-key material using a \texttt{gpg.Context().key\_import()} method.
-
-For this reason an alternative method has been added to the \texttt{search}
-function of \texttt{hkp4py.KeyServer()} which returns the key in the correct
-format as expected by \texttt{key\_import}. When importing using this module,
-it is now possible to import with this:
-
-\begin{verbatim}
-for key in keys:
- if key.revoked is False:
- gpg.Context().key_import(key.key_blob)
- else:
- pass
-\end{verbatim}
-
-Without that recent addition it would have been necessary to encode
-the contents of each \texttt{hkp4py.KeyServer().search()[i].key} in
-\texttt{hkp4py.KeyServer().search()} before trying to import it.
-
-An example of this is included in the \hyperref[sec:org1ad598a]{Importing Keys} section of this
-HOWTO and the corresponding executable version of that example is
-available in the \texttt{lang/python/examples/howto} directory as normal; the
-executable version is the \texttt{import-keys-hkp.py} file.
-
-
-\subsection{GPGME version checking}
-\label{sec:org18b8ef8}
-For various reasons it may be necessary to check which version of
-GPGME the bindings have been built against; including whether a
-minimum required version of GPGME is in use.
-
-For the most part the \texttt{gpg.version.versionstr} and
-\texttt{gpg.version.versionlist} methods have been quite sufficient. The
-former returns the same string as \texttt{gpgme-config -{}-version}, while the
-latter returns the major, minor and patch values in a list.
-
-To check if the installed bindings have actually been built against
-the current installed libgpgme version, this check can be performed:
-
-\begin{verbatim}
-import gpg
-import subprocess
-import sys
-
-gpgme_version_call = subprocess.Popen(["gpgme-config", "--version"],
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
-gpgme_version_str = gpgme_version_call.communicate()
-
-if sys.version_info[0] == 2:
- gpgme_version = gpgme_version_str[0].strip()
-elif sys.version_info[0] >= 3:
- gpgme_version = gpgme_version_str[0].decode().strip()
-else:
- gpgme_version = None
-
-if gpgme_version is not None:
- if gpgme_version == gpg.version.versionstr:
- print("The GPGME Python bindings match libgpgme.")
- else:
- print("The GPGME Python bindings do NOT match libgpgme.")
-else:
- print("Upgrade Python and reinstall the GPGME Python bindings.")
-\end{verbatim}
-
-For many developers, however, the preferred checking means checking
-for a minimum version or point release. This is now readily available
-via the \texttt{gpg.version.versionintlist} method (added in version
-\texttt{1.12.1-beta79}). It is also now possible to easily check whether the
-installed GPGME Python bindings were built from a development or beta
-branch of the GPGME source code.
-
-The following code demonstrates how both of those methods may be used:
-
-\begin{verbatim}
-import gpg
-
-try:
- if gpg.version.is_beta is True:
- print("The installed GPGME Python bindings were built from beta code.")
- else:
- print("The installed GPGME Python bindings are a released version.")
-except Exception as e:
- print(e)
-
-try:
- if gpg.version.versionintlist[0] == 1:
- if gpg.version.versionintlist[1] == 12:
- if gpg.version.versionintlist[2] == 1:
- print("This is the minimum version for using versionintlist.")
- elif gpg.version.versionintlist[2] > 1:
- print("The versionintlist method is available.")
- else:
- pass
- elif gpg.version.versionintlist[1] > 12:
- print("The versionintlist method is available.")
- else:
- pass
- elif gpg.version.versionintlist[0] > 1:
- print("The versionintlist method is available.")
- else:
- pass
-except Exception as e:
- print(e)
-\end{verbatim}
-
-The points where \texttt{pass} is used in the above example will most likely
-also produce an \texttt{Exception} error since those results should only
-occur in versions which do not have the \texttt{gpgme.version.is\_beta} and
-\texttt{gpgme.version.versionintlist} methods available.
-
-
-\section{Copyright and Licensing}
-\label{sec:org9d52a23}
-\subsection{Copyright}
-\label{sec:org26ed04d}
-Copyright © The GnuPG Project, 2018.
-
-Copyright (C) The GnuPG Project, 2018.
-
-
-\subsection{Draft Editions of this HOWTO}
-\label{sec:org080a94a}
-Draft editions of this HOWTO may be periodically available directly
-from the author at any of the following URLs:
-
-\begin{itemize}
-\item \href{https://files.au.adversary.org/crypto/gpgme-python-howto.html}{GPGME Python Bindings HOWTO draft (XHTML single file, AWS S3 SSL)}
-\item \href{http://files.au.adversary.org/crypto/gpgme-python-howto.html}{GPGME Python Bindings HOWTO draft (XHTML single file, AWS S3 no SSL)}
-\item \href{https://files.au.adversary.org/crypto/gpgme-python-howto-split/index.html}{GPGME Python Bindings HOWTO draft (XHTML multiple files, AWS S3 SSL)}
-\item \href{http://files.au.adversary.org/crypto/gpgme-python-howto/index.html}{GPGME Python Bindings HOWTO draft (XHTML multiple files, AWS S3 no SSL)}
-\end{itemize}
-
-All of these draft versions except for one have been generated from
-this document via GNU Emacs \href{https://orgmode.org/}{Org mode} and \href{https://www.gnu.org/software/texinfo/}{GNU Texinfo}. Though it is
-likely that the specific \href{https://files.au.adversary.org/crypto/gpgme-python-howto}{file} \href{http://files.au.adversary.org/crypto/gpgme-python-howto.org}{version} used will be on the same server
-with the generated output formats.
-
-The GNU Texinfo and reStructured Text versions ship with the software,
-while the GNU Emacs Info verseion is generated from the Texinfo
-version using GNU Texinfo or GNU Makeinfo. The Texinfo format is
-generated from the original Org mode source file in Org mode itself
-either within GNU Emacs or via the command line by invoking Emacs in
-batch mode:
-
-\begin{verbatim}
-emacs gpgme-python-howto.org --batch -f org-texinfo-export-to-texinfo --kill
-emacs gpgme-python-howto --batch -f org-texinfo-export-to-texinfo --kill
-\end{verbatim}
-
-The reStructuredText format is also generated from the Org-mode source
-file, except it is generated using \href{https://pandoc.org}{Pandoc} with either of the following
-commands:
-
-\begin{verbatim}
-pandoc -f org -t rst+smart -o gpgme-python-howto.rst gpgme-python-howto.org
-pandoc -f org -t rst+smart -o gpgme-python-howto.rst gpgme-python-howto
-\end{verbatim}
-
-In addition to these there is a significantly less frequently updated
-version as a HTML \href{https://files.au.adversary.org/crypto/gpgme-python/dita/webhelp/index.html}{WebHelp site} (AWS S3 SSL); generated from DITA XML
-source files, which can be found in \href{https://dev.gnupg.org/source/gpgme/browse/ben\%252Fhowto-dita/}{an alternative branch} of the GPGME
-git repository.
-
-Various generated output formats may occasionally be found in
-subdirectories of the \href{https://s3.amazonaws.com/files.au.adversary.org/crypto/gpgme-python}{gpgme-python} directory. In particular within
-the \href{https://s3.amazonaws.com/files.au.adversary.org/crypto/gpgme-python/dita}{DITA}, \href{https://s3.amazonaws.com/files.au.adversary.org/crypto/gpgme-python/rst}{reStructuredText} and \href{https://s3.amazonaws.com/files.au.adversary.org/crypto/gpgme-python/texinfo}{Texinfo} subdirectories. The \texttt{rst}
-directory contains output files generated with Sphix and may include a
-considerable number of its possible output formats.
-
-These draft editions are not official documents and the version of
-documentation in the master branch or which ships with released
-versions is the only official documentation. Nevertheless, these
-draft editions may occasionally be of use by providing more accessible
-web versions which are updated between releases. They are provided on
-the understanding that they may contain errors or may contain content
-subject to change prior to an official release.
-
-
-\subsection{License GPL compatible}
-\label{sec:orge78f3b7}
-This file is free software; as a special exception the author gives
-unlimited permission to copy and/or distribute it, with or without
-modifications, as long as this notice is preserved.
-
-This file 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.
-\end{document}
diff --git a/lang/python/doc/texinfo/gpgme-python-howto.aux b/lang/python/doc/texinfo/gpgme-python-howto.aux
deleted file mode 100644
index e69de29..0000000
--- a/lang/python/doc/texinfo/gpgme-python-howto.aux
+++ /dev/null
diff --git a/lang/python/doc/texinfo/gpgme-python-howto.log b/lang/python/doc/texinfo/gpgme-python-howto.log
deleted file mode 100644
index 3414a0f..0000000
--- a/lang/python/doc/texinfo/gpgme-python-howto.log
+++ /dev/null
@@ -1,519 +0,0 @@
-This is pdfTeX, Version 3.14159265-2.6-1.40.17 (TeX Live 2016/Debian) (preloaded format=pdfetex 2018.3.2) 18 JAN 2019 10:08
-entering extended mode
- restricted \write18 enabled.
- file:line:error style messages enabled.
- %&-line parsing enabled.
-**\input ./gpgme-python-howto.texi
-(./gpgme-python-howto.texi
-(/home/wk/s/gpgme/lang/python/doc/texinfo/texinfo.tex
-Loading texinfo [version 2007-12-02.17]:
-\bindingoffset=\dimen16
-\normaloffset=\dimen17
-\pagewidth=\dimen18
-\pageheight=\dimen19
-\outerhsize=\dimen20
-\outervsize=\dimen21
-\cornerlong=\dimen22
-\cornerthick=\dimen23
-\topandbottommargin=\dimen24
-\headlinebox=\box16
-\footlinebox=\box17
-\margin=\insert252
-\EMsimple=\toks13
-\groupbox=\box18
-\groupinvalidhelp=\toks14
-\mil=\dimen25
-\exdentamount=\skip18
-\inmarginspacing=\skip19
- pdf,
-\tempnum=\count27
-\lnkcount=\count28
-\filename=\toks15
-\filenamelength=\count29
-\pgn=\count30
-\toksA=\toks16
-\toksB=\toks17
-\toksC=\toks18
-\toksD=\toks19
-\boxA=\box19
-\countA=\count31
-\nopdfimagehelp=\toks20
- fonts,
-\sffam=\fam8
-\textleading=\dimen26
-\fontdepth=\count32
- page headings,
-\titlepagetopglue=\skip20
-\titlepagebottomglue=\skip21
-\evenheadline=\toks21
-\oddheadline=\toks22
-\evenfootline=\toks23
-\oddfootline=\toks24
- tables,
-\tableindent=\dimen27
-\itemindent=\dimen28
-\itemmargin=\dimen29
-\itemmax=\dimen30
-\itemno=\count33
-\multitableparskip=\skip22
-\multitableparindent=\skip23
-\multitablecolspace=\dimen31
-\multitablelinespace=\skip24
-\colcount=\count34
-\everytab=\toks25
-
-conditionals,
-\doignorecount=\count35
- indexing,
-\whatsitskip=\skip25
-\whatsitpenalty=\count36
-\secondaryindent=\skip26
-\partialpage=\box20
-\doublecolumnhsize=\dimen32
- sectioning,
-\unnumberedno=\count37
-\chapno=\count38
-\secno=\count39
-\subsecno=\count40
-\subsubsecno=\count41
-\appendixno=\count42
-\absseclevel=\count43
-\secbase=\count44
-\chapheadingskip=\skip27
-\secheadingskip=\skip28
-\subsecheadingskip=\skip29
- toc,
-\tocfile=\write0
-\contentsrightmargin=\skip30
-\savepageno=\count45
-\lastnegativepageno=\count46
-\tocindent=\dimen33
- environments,
-\errorbox=\box21
-\lispnarrowing=\skip31
-\envskipamount=\skip32
-\circthick=\dimen34
-\cartouter=\dimen35
-\cartinner=\dimen36
-\normbskip=\skip33
-\normpskip=\skip34
-\normlskip=\skip35
-\lskip=\skip36
-\rskip=\skip37
-\tabw=\dimen37
- defuns,
-\defbodyindent=\skip38
-\defargsindent=\skip39
-\deflastargmargin=\skip40
-\defunpenalty=\count47
-\parencount=\count48
-\brackcount=\count49
- macros,
-\paramno=\count50
-\macname=\toks26
-
-cross references,
-\auxfile=\write1
-\savesfregister=\count51
- insertions,
-\footnoteno=\count52
-\SAVEfootins=\box22
-\SAVEmargin=\box23
-
-(/usr/share/texlive/texmf-dist/tex/generic/epsf/epsf.tex
-This is `epsf.tex' v2.7.4 <14 February 2011>
-\epsffilein=\read1
-\epsfframemargin=\dimen38
-\epsfframethickness=\dimen39
-\epsfrsize=\dimen40
-\epsftmp=\dimen41
-\epsftsize=\dimen42
-\epsfxsize=\dimen43
-\epsfysize=\dimen44
-\pspoints=\dimen45
-)
-\noepsfhelp=\toks27
- localization,
-\nolanghelp=\toks28
-\countUTFx=\count53
-\countUTFy=\count54
-\countUTFz=\count55
- formatting,
-\defaultparindent=\dimen46
- and turning on texinfo input format.)
-\openout1 = `gpgme-python-howto.aux'.
-
-@cpindfile=@write2
-@fnindfile=@write3
-@vrindfile=@write4
-@tpindfile=@write5
-@kyindfile=@write6
-@pgindfile=@write7
- defining Unicode char U+00A0 (decimal 160)
- defining Unicode char U+00A1 (decimal 161)
- defining Unicode char U+00A3 (decimal 163)
- defining Unicode char U+00A8 (decimal 168)
- defining Unicode char U+00A9 (decimal 169)
- defining Unicode char U+00AA (decimal 170)
- defining Unicode char U+00AB (decimal 171)
- defining Unicode char U+00AD (decimal 173)
- defining Unicode char U+00AE (decimal 174)
- defining Unicode char U+00AF (decimal 175)
- defining Unicode char U+00B0 (decimal 176)
- defining Unicode char U+00B4 (decimal 180)
- defining Unicode char U+00B8 (decimal 184)
- defining Unicode char U+00BA (decimal 186)
- defining Unicode char U+00BB (decimal 187)
- defining Unicode char U+00BF (decimal 191)
- defining Unicode char U+00C0 (decimal 192)
- defining Unicode char U+00C1 (decimal 193)
- defining Unicode char U+00C2 (decimal 194)
- defining Unicode char U+00C3 (decimal 195)
- defining Unicode char U+00C4 (decimal 196)
- defining Unicode char U+00C5 (decimal 197)
- defining Unicode char U+00C6 (decimal 198)
- defining Unicode char U+00C7 (decimal 199)
- defining Unicode char U+00C8 (decimal 200)
- defining Unicode char U+00C9 (decimal 201)
- defining Unicode char U+00CA (decimal 202)
- defining Unicode char U+00CB (decimal 203)
- defining Unicode char U+00CC (decimal 204)
- defining Unicode char U+00CD (decimal 205)
- defining Unicode char U+00CE (decimal 206)
- defining Unicode char U+00CF (decimal 207)
- defining Unicode char U+00D1 (decimal 209)
- defining Unicode char U+00D2 (decimal 210)
- defining Unicode char U+00D3 (decimal 211)
- defining Unicode char U+00D4 (decimal 212)
- defining Unicode char U+00D5 (decimal 213)
- defining Unicode char U+00D6 (decimal 214)
- defining Unicode char U+00D8 (decimal 216)
- defining Unicode char U+00D9 (decimal 217)
- defining Unicode char U+00DA (decimal 218)
- defining Unicode char U+00DB (decimal 219)
- defining Unicode char U+00DC (decimal 220)
- defining Unicode char U+00DD (decimal 221)
- defining Unicode char U+00DF (decimal 223)
- defining Unicode char U+00E0 (decimal 224)
- defining Unicode char U+00E1 (decimal 225)
- defining Unicode char U+00E2 (decimal 226)
- defining Unicode char U+00E3 (decimal 227)
- defining Unicode char U+00E4 (decimal 228)
- defining Unicode char U+00E5 (decimal 229)
- defining Unicode char U+00E6 (decimal 230)
- defining Unicode char U+00E7 (decimal 231)
- defining Unicode char U+00E8 (decimal 232)
- defining Unicode char U+00E9 (decimal 233)
- defining Unicode char U+00EA (decimal 234)
- defining Unicode char U+00EB (decimal 235)
- defining Unicode char U+00EC (decimal 236)
- defining Unicode char U+00ED (decimal 237)
- defining Unicode char U+00EE (decimal 238)
- defining Unicode char U+00EF (decimal 239)
- defining Unicode char U+00F1 (decimal 241)
- defining Unicode char U+00F2 (decimal 242)
- defining Unicode char U+00F3 (decimal 243)
- defining Unicode char U+00F4 (decimal 244)
- defining Unicode char U+00F5 (decimal 245)
- defining Unicode char U+00F6 (decimal 246)
- defining Unicode char U+00F8 (decimal 248)
- defining Unicode char U+00F9 (decimal 249)
- defining Unicode char U+00FA (decimal 250)
- defining Unicode char U+00FB (decimal 251)
- defining Unicode char U+00FC (decimal 252)
- defining Unicode char U+00FD (decimal 253)
- defining Unicode char U+00FF (decimal 255)
- defining Unicode char U+0100 (decimal 256)
- defining Unicode char U+0101 (decimal 257)
- defining Unicode char U+0102 (decimal 258)
- defining Unicode char U+0103 (decimal 259)
- defining Unicode char U+0106 (decimal 262)
- defining Unicode char U+0107 (decimal 263)
- defining Unicode char U+0108 (decimal 264)
- defining Unicode char U+0109 (decimal 265)
- defining Unicode char U+010A (decimal 266)
- defining Unicode char U+010B (decimal 267)
- defining Unicode char U+010C (decimal 268)
- defining Unicode char U+010D (decimal 269)
- defining Unicode char U+010E (decimal 270)
- defining Unicode char U+0112 (decimal 274)
- defining Unicode char U+0113 (decimal 275)
- defining Unicode char U+0114 (decimal 276)
- defining Unicode char U+0115 (decimal 277)
- defining Unicode char U+0116 (decimal 278)
- defining Unicode char U+0117 (decimal 279)
- defining Unicode char U+011A (decimal 282)
- defining Unicode char U+011B (decimal 283)
- defining Unicode char U+011C (decimal 284)
- defining Unicode char U+011D (decimal 285)
- defining Unicode char U+011E (decimal 286)
- defining Unicode char U+011F (decimal 287)
- defining Unicode char U+0120 (decimal 288)
- defining Unicode char U+0121 (decimal 289)
- defining Unicode char U+0124 (decimal 292)
- defining Unicode char U+0125 (decimal 293)
- defining Unicode char U+0128 (decimal 296)
- defining Unicode char U+0129 (decimal 297)
- defining Unicode char U+012A (decimal 298)
- defining Unicode char U+012B (decimal 299)
- defining Unicode char U+012C (decimal 300)
- defining Unicode char U+012D (decimal 301)
- defining Unicode char U+0130 (decimal 304)
- defining Unicode char U+0131 (decimal 305)
- defining Unicode char U+0132 (decimal 306)
- defining Unicode char U+0133 (decimal 307)
- defining Unicode char U+0134 (decimal 308)
- defining Unicode char U+0135 (decimal 309)
- defining Unicode char U+0139 (decimal 313)
- defining Unicode char U+013A (decimal 314)
- defining Unicode char U+0141 (decimal 321)
- defining Unicode char U+0142 (decimal 322)
- defining Unicode char U+0143 (decimal 323)
- defining Unicode char U+0144 (decimal 324)
- defining Unicode char U+0147 (decimal 327)
- defining Unicode char U+0148 (decimal 328)
- defining Unicode char U+014C (decimal 332)
- defining Unicode char U+014D (decimal 333)
- defining Unicode char U+014E (decimal 334)
- defining Unicode char U+014F (decimal 335)
- defining Unicode char U+0150 (decimal 336)
- defining Unicode char U+0151 (decimal 337)
- defining Unicode char U+0152 (decimal 338)
- defining Unicode char U+0153 (decimal 339)
- defining Unicode char U+0154 (decimal 340)
- defining Unicode char U+0155 (decimal 341)
- defining Unicode char U+0158 (decimal 344)
- defining Unicode char U+0159 (decimal 345)
- defining Unicode char U+015A (decimal 346)
- defining Unicode char U+015B (decimal 347)
- defining Unicode char U+015C (decimal 348)
- defining Unicode char U+015D (decimal 349)
- defining Unicode char U+015E (decimal 350)
- defining Unicode char U+015F (decimal 351)
- defining Unicode char U+0160 (decimal 352)
- defining Unicode char U+0161 (decimal 353)
- defining Unicode char U+0162 (decimal 354)
- defining Unicode char U+0163 (decimal 355)
- defining Unicode char U+0164 (decimal 356)
- defining Unicode char U+0168 (decimal 360)
- defining Unicode char U+0169 (decimal 361)
- defining Unicode char U+016A (decimal 362)
- defining Unicode char U+016B (decimal 363)
- defining Unicode char U+016C (decimal 364)
- defining Unicode char U+016D (decimal 365)
- defining Unicode char U+016E (decimal 366)
- defining Unicode char U+016F (decimal 367)
- defining Unicode char U+0170 (decimal 368)
- defining Unicode char U+0171 (decimal 369)
- defining Unicode char U+0174 (decimal 372)
- defining Unicode char U+0175 (decimal 373)
- defining Unicode char U+0176 (decimal 374)
- defining Unicode char U+0177 (decimal 375)
- defining Unicode char U+0178 (decimal 376)
- defining Unicode char U+0179 (decimal 377)
- defining Unicode char U+017A (decimal 378)
- defining Unicode char U+017B (decimal 379)
- defining Unicode char U+017C (decimal 380)
- defining Unicode char U+017D (decimal 381)
- defining Unicode char U+017E (decimal 382)
- defining Unicode char U+01C4 (decimal 452)
- defining Unicode char U+01C5 (decimal 453)
- defining Unicode char U+01C6 (decimal 454)
- defining Unicode char U+01C7 (decimal 455)
- defining Unicode char U+01C8 (decimal 456)
- defining Unicode char U+01C9 (decimal 457)
- defining Unicode char U+01CA (decimal 458)
- defining Unicode char U+01CB (decimal 459)
- defining Unicode char U+01CC (decimal 460)
- defining Unicode char U+01CD (decimal 461)
- defining Unicode char U+01CE (decimal 462)
- defining Unicode char U+01CF (decimal 463)
- defining Unicode char U+01D0 (decimal 464)
- defining Unicode char U+01D1 (decimal 465)
- defining Unicode char U+01D2 (decimal 466)
- defining Unicode char U+01D3 (decimal 467)
- defining Unicode char U+01D4 (decimal 468)
- defining Unicode char U+01E2 (decimal 482)
- defining Unicode char U+01E3 (decimal 483)
- defining Unicode char U+01E6 (decimal 486)
- defining Unicode char U+01E7 (decimal 487)
- defining Unicode char U+01E8 (decimal 488)
- defining Unicode char U+01E9 (decimal 489)
- defining Unicode char U+01F0 (decimal 496)
- defining Unicode char U+01F1 (decimal 497)
- defining Unicode char U+01F2 (decimal 498)
- defining Unicode char U+01F3 (decimal 499)
- defining Unicode char U+01F4 (decimal 500)
- defining Unicode char U+01F5 (decimal 501)
- defining Unicode char U+01F8 (decimal 504)
- defining Unicode char U+01F9 (decimal 505)
- defining Unicode char U+01FC (decimal 508)
- defining Unicode char U+01FD (decimal 509)
- defining Unicode char U+01FE (decimal 510)
- defining Unicode char U+01FF (decimal 511)
- defining Unicode char U+021E (decimal 542)
- defining Unicode char U+021F (decimal 543)
- defining Unicode char U+0226 (decimal 550)
- defining Unicode char U+0227 (decimal 551)
- defining Unicode char U+0228 (decimal 552)
- defining Unicode char U+0229 (decimal 553)
- defining Unicode char U+022E (decimal 558)
- defining Unicode char U+022F (decimal 559)
- defining Unicode char U+0232 (decimal 562)
- defining Unicode char U+0233 (decimal 563)
- defining Unicode char U+0237 (decimal 567)
- defining Unicode char U+1E02 (decimal 7682)
- defining Unicode char U+1E03 (decimal 7683)
- defining Unicode char U+1E04 (decimal 7684)
- defining Unicode char U+1E05 (decimal 7685)
- defining Unicode char U+1E06 (decimal 7686)
- defining Unicode char U+1E07 (decimal 7687)
- defining Unicode char U+1E0A (decimal 7690)
- defining Unicode char U+1E0B (decimal 7691)
- defining Unicode char U+1E0C (decimal 7692)
- defining Unicode char U+1E0D (decimal 7693)
- defining Unicode char U+1E0E (decimal 7694)
- defining Unicode char U+1E0F (decimal 7695)
- defining Unicode char U+1E1E (decimal 7710)
- defining Unicode char U+1E1F (decimal 7711)
- defining Unicode char U+1E20 (decimal 7712)
- defining Unicode char U+1E21 (decimal 7713)
- defining Unicode char U+1E22 (decimal 7714)
- defining Unicode char U+1E23 (decimal 7715)
- defining Unicode char U+1E24 (decimal 7716)
- defining Unicode char U+1E25 (decimal 7717)
- defining Unicode char U+1E26 (decimal 7718)
- defining Unicode char U+1E27 (decimal 7719)
- defining Unicode char U+1E30 (decimal 7728)
- defining Unicode char U+1E31 (decimal 7729)
- defining Unicode char U+1E32 (decimal 7730)
- defining Unicode char U+1E33 (decimal 7731)
- defining Unicode char U+1E34 (decimal 7732)
- defining Unicode char U+1E35 (decimal 7733)
- defining Unicode char U+1E36 (decimal 7734)
- defining Unicode char U+1E37 (decimal 7735)
- defining Unicode char U+1E3A (decimal 7738)
- defining Unicode char U+1E3B (decimal 7739)
- defining Unicode char U+1E3E (decimal 7742)
- defining Unicode char U+1E3F (decimal 7743)
- defining Unicode char U+1E40 (decimal 7744)
- defining Unicode char U+1E41 (decimal 7745)
- defining Unicode char U+1E42 (decimal 7746)
- defining Unicode char U+1E43 (decimal 7747)
- defining Unicode char U+1E44 (decimal 7748)
- defining Unicode char U+1E45 (decimal 7749)
- defining Unicode char U+1E46 (decimal 7750)
- defining Unicode char U+1E47 (decimal 7751)
- defining Unicode char U+1E48 (decimal 7752)
- defining Unicode char U+1E49 (decimal 7753)
- defining Unicode char U+1E54 (decimal 7764)
- defining Unicode char U+1E55 (decimal 7765)
- defining Unicode char U+1E56 (decimal 7766)
- defining Unicode char U+1E57 (decimal 7767)
- defining Unicode char U+1E58 (decimal 7768)
- defining Unicode char U+1E59 (decimal 7769)
- defining Unicode char U+1E5A (decimal 7770)
- defining Unicode char U+1E5B (decimal 7771)
- defining Unicode char U+1E5E (decimal 7774)
- defining Unicode char U+1E5F (decimal 7775)
- defining Unicode char U+1E60 (decimal 7776)
- defining Unicode char U+1E61 (decimal 7777)
- defining Unicode char U+1E62 (decimal 7778)
- defining Unicode char U+1E63 (decimal 7779)
- defining Unicode char U+1E6A (decimal 7786)
- defining Unicode char U+1E6B (decimal 7787)
- defining Unicode char U+1E6C (decimal 7788)
- defining Unicode char U+1E6D (decimal 7789)
- defining Unicode char U+1E6E (decimal 7790)
- defining Unicode char U+1E6F (decimal 7791)
- defining Unicode char U+1E7C (decimal 7804)
- defining Unicode char U+1E7D (decimal 7805)
- defining Unicode char U+1E7E (decimal 7806)
- defining Unicode char U+1E7F (decimal 7807)
- defining Unicode char U+1E80 (decimal 7808)
- defining Unicode char U+1E81 (decimal 7809)
- defining Unicode char U+1E82 (decimal 7810)
- defining Unicode char U+1E83 (decimal 7811)
- defining Unicode char U+1E84 (decimal 7812)
- defining Unicode char U+1E85 (decimal 7813)
- defining Unicode char U+1E86 (decimal 7814)
- defining Unicode char U+1E87 (decimal 7815)
- defining Unicode char U+1E88 (decimal 7816)
- defining Unicode char U+1E89 (decimal 7817)
- defining Unicode char U+1E8A (decimal 7818)
- defining Unicode char U+1E8B (decimal 7819)
- defining Unicode char U+1E8C (decimal 7820)
- defining Unicode char U+1E8D (decimal 7821)
- defining Unicode char U+1E8E (decimal 7822)
- defining Unicode char U+1E8F (decimal 7823)
- defining Unicode char U+1E90 (decimal 7824)
- defining Unicode char U+1E91 (decimal 7825)
- defining Unicode char U+1E92 (decimal 7826)
- defining Unicode char U+1E93 (decimal 7827)
- defining Unicode char U+1E94 (decimal 7828)
- defining Unicode char U+1E95 (decimal 7829)
- defining Unicode char U+1E96 (decimal 7830)
- defining Unicode char U+1E97 (decimal 7831)
- defining Unicode char U+1E98 (decimal 7832)
- defining Unicode char U+1E99 (decimal 7833)
- defining Unicode char U+1EA0 (decimal 7840)
- defining Unicode char U+1EA1 (decimal 7841)
- defining Unicode char U+1EB8 (decimal 7864)
- defining Unicode char U+1EB9 (decimal 7865)
- defining Unicode char U+1EBC (decimal 7868)
- defining Unicode char U+1EBD (decimal 7869)
- defining Unicode char U+1ECA (decimal 7882)
- defining Unicode char U+1ECB (decimal 7883)
- defining Unicode char U+1ECC (decimal 7884)
- defining Unicode char U+1ECD (decimal 7885)
- defining Unicode char U+1EE4 (decimal 7908)
- defining Unicode char U+1EE5 (decimal 7909)
- defining Unicode char U+1EF2 (decimal 7922)
- defining Unicode char U+1EF3 (decimal 7923)
- defining Unicode char U+1EF4 (decimal 7924)
- defining Unicode char U+1EF8 (decimal 7928)
- defining Unicode char U+1EF9 (decimal 7929)
- defining Unicode char U+2013 (decimal 8211)
- defining Unicode char U+2014 (decimal 8212)
- defining Unicode char U+2018 (decimal 8216)
- defining Unicode char U+2019 (decimal 8217)
- defining Unicode char U+201A (decimal 8218)
- defining Unicode char U+201C (decimal 8220)
- defining Unicode char U+201D (decimal 8221)
- defining Unicode char U+201E (decimal 8222)
- defining Unicode char U+2022 (decimal 8226)
- defining Unicode char U+2026 (decimal 8230)
- defining Unicode char U+2039 (decimal 8249)
- defining Unicode char U+203A (decimal 8250)
- defining Unicode char U+20AC (decimal 8364)
- defining Unicode char U+2192 (decimal 8594)
- defining Unicode char U+21D2 (decimal 8658)
- defining Unicode char U+2212 (decimal 8722)
- defining Unicode char U+2217 (decimal 8727)
- defining Unicode char U+2261 (decimal 8801)
-
-(/usr/share/texmf/tex/texinfo/txi-en.tex
-/usr/share/texmf/tex/texinfo/txi-en.tex:22: Undefined control sequence.
-l.22 \txisetlanguage
- {USenglish}{2}{3}
-?
-/usr/share/texmf/tex/texinfo/txi-en.tex:22: Interruption.
-<to be read again>
- {
-l.22 \txisetlanguage{
- USenglish}{2}{3}
-? m
-Type <return> to proceed, S to scroll future error messages,
-R to run without stopping, Q to run quietly,
-I to insert something, E to edit your file,
-H for help, X to quit.
-?
-/usr/share/texmf/tex/texinfo/txi-en.tex:22: Emergency stop.
-<to be read again>
- {
-l.22 \txisetlanguage{
- USenglish}{2}{3}
-End of file on the terminal!
-
-/usr/share/texmf/tex/texinfo/txi-en.tex:22: ==> Fatal error occurred, no outpu
-t PDF file produced!
diff --git a/lang/python/doc/texinfo/texput.log b/lang/python/doc/texinfo/texput.log
deleted file mode 100644
index af7e1d6..0000000
--- a/lang/python/doc/texinfo/texput.log
+++ /dev/null
@@ -1,13 +0,0 @@
-This is pdfTeX, Version 3.14159265-2.6-1.40.17 (TeX Live 2016/Debian) (preloaded format=pdfetex 2018.3.2) 18 JAN 2019 10:09
-entering extended mode
- restricted \write18 enabled.
- file:line:error style messages enabled.
- %&-line parsing enabled.
-**\input ./.
-
-! Emergency stop.
-<*> \input ./.
-
-End of file on the terminal!
-
-! ==> Fatal error occurred, no output PDF file produced!
diff --git a/lang/python/examples/Makefile.am b/lang/python/examples/Makefile.am
new file mode 100644
index 0000000..bd4319e
--- /dev/null
+++ b/lang/python/examples/Makefile.am
@@ -0,0 +1,74 @@
+# Makefile.am for the Python bindings.
+# Copyright (C) 2019 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 <https://gnu.org/licenses/>.
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+# Created by:
+# find . -type f -print | sed 's/^.\// /;$q;s/$/ \\/' | sort
+EXTRA_DIST = assuan.py \
+ decryption-filter.py \
+ delkey.py \
+ exportimport.py \
+ genkey.py \
+ howto/add-userid.py \
+ howto/advanced/cython/keycount.pyx \
+ howto/advanced/cython/requirements.txt \
+ howto/advanced/cython/setup.py \
+ howto/clear-sign-file.py \
+ howto/create-key.py \
+ howto/decrypt-file.py \
+ howto/detach-sign-file.py \
+ howto/encrypt-file.py \
+ howto/encrypt-sign-file.py \
+ howto/encrypt-to-group-gullible.py \
+ howto/encrypt-to-group.py \
+ howto/encrypt-to-group-trustno1.py \
+ howto/export-key.py \
+ howto/export-minimised-key.py \
+ howto/export-secret-key.py \
+ howto/export-secret-keys.py \
+ howto/groups.py \
+ howto/import-keybasekey.py \
+ howto/import-key.py \
+ howto/import-keys-hkp.py \
+ howto/import-keys.py \
+ howto/import-mailvelope-keys.py \
+ howto/keycount.py \
+ howto/local-sign-group.py \
+ howto/mutt-groups.py \
+ howto/pmkey-import-alt.py \
+ howto/pmkey-import-hkp-alt.py \
+ howto/pmkey-import-hkp.py \
+ howto/pmkey-import.py \
+ howto/post_installer.py \
+ howto/README.org \
+ howto/requirements.txt \
+ howto/revoke-userid.py \
+ howto/send-key-to-keyserver.py \
+ howto/sign-file.py \
+ howto/sign-key.py \
+ howto/symcrypt-file.py \
+ howto/temp-homedir-config.py \
+ howto/verify-signatures.py \
+ howto/verify-signed-file.py \
+ inter-edit.py \
+ low_level-encrypt_to_all.py \
+ sign.py \
+ signverify.py \
+ simple.py \
+ testCMSgetkey.py \
+ verifydetails.py
diff --git a/lang/python/examples/Makefile.in b/lang/python/examples/Makefile.in
new file mode 100644
index 0000000..7300016
--- /dev/null
+++ b/lang/python/examples/Makefile.in
@@ -0,0 +1,600 @@
+# Makefile.in generated by automake 1.15 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2014 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) 2019 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 <https://gnu.org/licenses/>.
+# SPDX-License-Identifier: LGPL-2.1-or-later
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+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/examples
+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/python.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)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/conf/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)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build-aux/mkinstalldirs
+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@
+BUILD_VERSION = @BUILD_VERSION@
+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@
+GPGRT_CONFIG = @GPGRT_CONFIG@
+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@
+LDADD_FOR_TESTS_KLUDGE = @LDADD_FOR_TESTS_KLUDGE@
+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@
+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_MAJOR = @VERSION_MAJOR@
+VERSION_MICRO = @VERSION_MICRO@
+VERSION_MINOR = @VERSION_MINOR@
+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@
+
+# Created by:
+# find . -type f -print | sed 's/^.\// /;$q;s/$/ \\/' | sort
+EXTRA_DIST = assuan.py \
+ decryption-filter.py \
+ delkey.py \
+ exportimport.py \
+ genkey.py \
+ howto/add-userid.py \
+ howto/advanced/cython/keycount.pyx \
+ howto/advanced/cython/requirements.txt \
+ howto/advanced/cython/setup.py \
+ howto/clear-sign-file.py \
+ howto/create-key.py \
+ howto/decrypt-file.py \
+ howto/detach-sign-file.py \
+ howto/encrypt-file.py \
+ howto/encrypt-sign-file.py \
+ howto/encrypt-to-group-gullible.py \
+ howto/encrypt-to-group.py \
+ howto/encrypt-to-group-trustno1.py \
+ howto/export-key.py \
+ howto/export-minimised-key.py \
+ howto/export-secret-key.py \
+ howto/export-secret-keys.py \
+ howto/groups.py \
+ howto/import-keybasekey.py \
+ howto/import-key.py \
+ howto/import-keys-hkp.py \
+ howto/import-keys.py \
+ howto/import-mailvelope-keys.py \
+ howto/keycount.py \
+ howto/local-sign-group.py \
+ howto/mutt-groups.py \
+ howto/pmkey-import-alt.py \
+ howto/pmkey-import-hkp-alt.py \
+ howto/pmkey-import-hkp.py \
+ howto/pmkey-import.py \
+ howto/post_installer.py \
+ howto/README.org \
+ howto/requirements.txt \
+ howto/revoke-userid.py \
+ howto/send-key-to-keyserver.py \
+ howto/sign-file.py \
+ howto/sign-key.py \
+ howto/symcrypt-file.py \
+ howto/temp-homedir-config.py \
+ howto/verify-signatures.py \
+ howto/verify-signed-file.py \
+ inter-edit.py \
+ low_level-encrypt_to_all.py \
+ sign.py \
+ signverify.py \
+ simple.py \
+ testCMSgetkey.py \
+ verifydetails.py
+
+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/examples/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu lang/python/examples/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:
+
+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 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 \
+ 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
+
+.PRECIOUS: Makefile
+
+
+# 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/src/Makefile.am b/lang/python/src/Makefile.am
new file mode 100644
index 0000000..dc7f768
--- /dev/null
+++ b/lang/python/src/Makefile.am
@@ -0,0 +1,47 @@
+# Makefile.am for the Python bindings.
+# Copyright (C) 2019 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 <https://gnu.org/licenses/>.
+# SPDX-License-Identifier: LGPL-2.1-or-later
+
+# Created by:
+# find . -type f -print | sed 's/^.\// /;$q;s/$/ \\/' | sort
+EXTRA_DIST = callbacks.py \
+ constants/create.py \
+ constants/data/encoding.py \
+ constants/data/__init__.py \
+ constants/event.py \
+ constants/import_type.py \
+ constants/__init__.py \
+ constants/keylist/__init__.py \
+ constants/keylist/mode.py \
+ constants/keysign.py \
+ constants/md.py \
+ constants/pk.py \
+ constants/protocol.py \
+ constants/sig/__init__.py \
+ constants/sig/mode.py \
+ constants/sig/notation.py \
+ constants/sigsum.py \
+ constants/status.py \
+ constants/tofu/__init__.py \
+ constants/tofu/policy.py \
+ constants/validity.py \
+ core.py \
+ errors.py \
+ __init__.py \
+ results.py \
+ util.py
diff --git a/lang/python/src/Makefile.in b/lang/python/src/Makefile.in
new file mode 100644
index 0000000..3a0f3ed
--- /dev/null
+++ b/lang/python/src/Makefile.in
@@ -0,0 +1,573 @@
+# Makefile.in generated by automake 1.15 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2014 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) 2019 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 <https://gnu.org/licenses/>.
+# SPDX-License-Identifier: LGPL-2.1-or-later
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+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/src
+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/python.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)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(SHELL) $(top_srcdir)/build-aux/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/conf/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)
+am__DIST_COMMON = $(srcdir)/Makefile.in \
+ $(top_srcdir)/build-aux/mkinstalldirs
+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@
+BUILD_VERSION = @BUILD_VERSION@
+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@
+GPGRT_CONFIG = @GPGRT_CONFIG@
+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@
+LDADD_FOR_TESTS_KLUDGE = @LDADD_FOR_TESTS_KLUDGE@
+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@
+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_MAJOR = @VERSION_MAJOR@
+VERSION_MICRO = @VERSION_MICRO@
+VERSION_MINOR = @VERSION_MINOR@
+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@
+
+# Created by:
+# find . -type f -print | sed 's/^.\// /;$q;s/$/ \\/' | sort
+EXTRA_DIST = callbacks.py \
+ constants/create.py \
+ constants/data/encoding.py \
+ constants/data/__init__.py \
+ constants/event.py \
+ constants/import_type.py \
+ constants/__init__.py \
+ constants/keylist/__init__.py \
+ constants/keylist/mode.py \
+ constants/keysign.py \
+ constants/md.py \
+ constants/pk.py \
+ constants/protocol.py \
+ constants/sig/__init__.py \
+ constants/sig/mode.py \
+ constants/sig/notation.py \
+ constants/sigsum.py \
+ constants/status.py \
+ constants/tofu/__init__.py \
+ constants/tofu/policy.py \
+ constants/validity.py \
+ core.py \
+ errors.py \
+ __init__.py \
+ results.py \
+ util.py
+
+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/src/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu lang/python/src/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:
+
+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 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 \
+ 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
+
+.PRECIOUS: Makefile
+
+
+# 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/src/core.py b/lang/python/src/core.py
index c096ee7..996c3b0 100644
--- a/lang/python/src/core.py
+++ b/lang/python/src/core.py
@@ -342,10 +342,12 @@ class Context(GpgmeWrapper):
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. Note: if
- VERIFY is an empty iterable, that is treated the same as
- passing verify=True (that is, do verify signatures, but no
- specific keys are required).
+ by all those keys, otherwise a MissingSignatures error is
+ raised. Note: if VERIFY is an empty iterable, that is treated
+ the same as passing verify=True (that is, verify signatures
+ and return data about any valid signatures found, but no
+ signatures are required and no MissingSignatures error will be
+ raised).
If the ciphertext is symmetrically encrypted using a
passphrase, that passphrase can be given as parameter, using a
@@ -353,21 +355,21 @@ class Context(GpgmeWrapper):
pinentry.
Keyword arguments:
- sink -- write result to sink instead of returning it
- passphrase -- for symmetric decryption
- verify -- check signatures (boolean or iterable of keys,
+ sink -- write result to sink instead of returning it
+ passphrase -- for symmetric decryption
+ verify -- check signatures (boolean or iterable of keys,
see above) (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)
+ plaintext -- the decrypted data (or None if sink is given)
+ result -- additional information about the decryption
+ verify_result -- additional information about the valid
+ signature(s) found
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
+ MissingSignatures -- if expected signatures are missing or bad
+ GPGMEError -- as signaled by the underlying library
"""
do_sig_verification = False
@@ -430,13 +432,8 @@ class Context(GpgmeWrapper):
results=results)
if do_sig_verification:
- # FIXME: should we really throw BadSignature, even if
- # we've encountered some good signatures? as above, once
- # we hit this error, there is no way to accept it and
- # continue to process the remaining signatures.
- if any(s.status != errors.NO_ERROR
- for s in verify_result.signatures):
- raise errors.BadSignatures(verify_result, results=results)
+ # filter out all invalid signatures
+ verify_result.signatures = list(filter(lambda s: s.status == errors.NO_ERROR, verify_result.signatures))
if required_keys is not None:
missing = []
for key in required_keys:
diff --git a/lang/python/src/errors.py b/lang/python/src/errors.py
index 9c7f037..5a58dea 100644
--- a/lang/python/src/errors.py
+++ b/lang/python/src/errors.py
@@ -163,13 +163,13 @@ class InvalidRecipients(EncryptionError):
for r in self.recipients)
-class DeryptionError(GpgError):
+class DecryptionError(GpgError):
pass
-class UnsupportedAlgorithm(DeryptionError):
+class UnsupportedAlgorithm(DecryptionError):
def __init__(self, algorithm, **kwargs):
- DeryptionError.__init__(self, **kwargs)
+ DecryptionError.__init__(self, **kwargs)
self.algorithm = algorithm
def __str__(self):
diff --git a/lang/python/tests/Makefile.am b/lang/python/tests/Makefile.am
index d5b6e00..026df03 100644
--- a/lang/python/tests/Makefile.am
+++ b/lang/python/tests/Makefile.am
@@ -28,7 +28,7 @@ TESTS_ENVIRONMENT = GNUPGHOME=$(GNUPGHOME) \
srcdir=$(srcdir) \
LD_LIBRARY_PATH="../../../src/.libs:$(LD_LIBRARY_PATH)"
-py_tests = t-wrapper.py \
+py_tests ?= t-wrapper.py \
t-callbacks.py \
t-data.py \
t-encrypt.py \
@@ -113,8 +113,7 @@ pubring-stamp: $(test_srcdir)/pubdemo.asc gpg-sample.stamp
gpg.conf:
# This is required for t-sig-notations.
echo no-force-v3-sigs > ./gpg.conf
- echo ignore-invalid-option agent-program >> ./gpg.conf
- echo "agent-program `which $(GPG_AGENT)`|--debug-quick-random" >> ./gpg.conf
+ echo default-key A0FF4590BB6122EDEF6E3C542D727CC768697734 >> ./gpg.conf
gpg-agent.conf:
# This is required for gpg2, which does not support command fd.
diff --git a/lang/python/tests/Makefile.in b/lang/python/tests/Makefile.in
index 3e1f9dd..e41c71f 100644
--- a/lang/python/tests/Makefile.in
+++ b/lang/python/tests/Makefile.in
@@ -355,34 +355,6 @@ TESTS_ENVIRONMENT = GNUPGHOME=$(GNUPGHOME) \
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-keylist-from-data.py \
- t-wait.py \
- t-encrypt-large.py \
- t-file-name.py \
- t-idiomatic.py \
- t-protocol-assuan.py \
- t-quick-key-creation.py \
- t-quick-subkey-creation.py \
- t-quick-key-manipulation.py \
- t-quick-key-signing.py
-
XTESTS = initial.py $(py_tests) final.py
EXTRA_DIST = support.py $(XTESTS) encrypt-only.asc sign-only.asc \
run-tests.py
@@ -599,6 +571,34 @@ uninstall-am:
.PRECIOUS: Makefile
+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-keylist-from-data.py \
+ t-wait.py \
+ t-encrypt-large.py \
+ t-file-name.py \
+ t-idiomatic.py \
+ t-protocol-assuan.py \
+ t-quick-key-creation.py \
+ t-quick-subkey-creation.py \
+ t-quick-key-manipulation.py \
+ t-quick-key-signing.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
@@ -636,8 +636,7 @@ pubring-stamp: $(test_srcdir)/pubdemo.asc gpg-sample.stamp
gpg.conf:
# This is required for t-sig-notations.
echo no-force-v3-sigs > ./gpg.conf
- echo ignore-invalid-option agent-program >> ./gpg.conf
- echo "agent-program `which $(GPG_AGENT)`|--debug-quick-random" >> ./gpg.conf
+ echo default-key A0FF4590BB6122EDEF6E3C542D727CC768697734 >> ./gpg.conf
gpg-agent.conf:
# This is required for gpg2, which does not support command fd.
diff --git a/lang/python/tests/t-decrypt-verify.py b/lang/python/tests/t-decrypt-verify.py
index a0049a0..5307da7 100755
--- a/lang/python/tests/t-decrypt-verify.py
+++ b/lang/python/tests/t-decrypt-verify.py
@@ -75,3 +75,15 @@ with gpg.Context() as c:
assert e.missing[0] == bob
else:
assert False, "Expected an error, got none"
+
+# plaintext, _, verify_result = c.decrypt(open(support.make_filename("cipher-no-sig.asc")))
+# assert len(plaintext) > 0
+# assert len(verify_result.signatures) == 0
+# assert plaintext.find(b'Viscosity Dispersal Thimble Saturday Flaxseed Deflected') >= 0, \
+# 'unsigned Plaintext was not found'
+#
+# plaintext, _, verify_result = c.decrypt(open(support.make_filename("cipher-3.asc")))
+# assert len(plaintext) > 0
+# assert len(verify_result.signatures) == 1
+# assert plaintext.find(b'Reenact Studied Thermos Bonehead Unclasp Opposing') >= 0, \
+# 'second Plaintext not found'
diff --git a/lang/python/tests/t-decrypt.py b/lang/python/tests/t-decrypt.py
index c72b51a..9900274 100755
--- a/lang/python/tests/t-decrypt.py
+++ b/lang/python/tests/t-decrypt.py
@@ -42,3 +42,13 @@ with gpg.Context() as c:
assert len(plaintext) > 0
assert plaintext.find(b'Wenn Sie dies lesen k') >= 0, \
'Plaintext not found'
+
+ plaintext, _, _ = c.decrypt(open(support.make_filename("cipher-3.asc")), verify=False)
+ assert len(plaintext) > 0
+ assert plaintext.find(b'Reenact Studied Thermos Bonehead Unclasp Opposing') >= 0, \
+ 'second Plaintext not found'
+
+ plaintext, _, _ = c.decrypt(open(support.make_filename("cipher-no-sig.asc")), verify=False)
+ assert len(plaintext) > 0
+ assert plaintext.find(b'Viscosity Dispersal Thimble Saturday Flaxseed Deflected') >= 0, \
+ 'third Plaintext was not found'
diff --git a/src/assuan-support.c b/src/assuan-support.c
index 925aeba..0ddf29b 100644
--- a/src/assuan-support.c
+++ b/src/assuan-support.c
@@ -54,7 +54,7 @@ _gpgme_assuan_log_cb (assuan_context_t ctx, void *hook,
if (msg == NULL)
return 1;
- _gpgme_debug (DEBUG_ASSUAN, -1, NULL, NULL, NULL, "%s", msg);
+ _gpgme_debug (NULL, DEBUG_ASSUAN, -1, NULL, NULL, NULL, "%s", msg);
return 0;
}
@@ -234,7 +234,7 @@ my_spawn (assuan_context_t ctx, pid_t *r_pid, const char *name,
break;
}
logger_fd = strtol (argv[loc], &tail, 10);
- if (tail == argv[loc] || logger_fd <= 0)
+ if (tail == argv[loc] || logger_fd < 0)
{
err = GPG_ERR_INV_ARG;
break;
diff --git a/src/cJSON.c b/src/cJSON.c
index 101d556..f233848 100644
--- a/src/cJSON.c
+++ b/src/cJSON.c
@@ -40,6 +40,7 @@
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
+#include <stdint.h>
#include <float.h>
#include <limits.h>
#include <ctype.h>
@@ -146,8 +147,13 @@ cJSON_Delete (cJSON * c)
static const char *
parse_number (cJSON * item, const char *num)
{
- double n = 0, sign = 1, scale = 0;
- int subscale = 0, signsubscale = 1;
+ int subscale = 0;
+ int signsubscale = 1;
+ double n = 0;
+ double sign = 1;
+ double scale = 0;
+ double dblmin = INT32_MIN;
+ double dblmax = INT32_MAX;
if (*num == '-')
sign = -1, num++; /* Has sign? */
@@ -169,17 +175,37 @@ parse_number (cJSON * item, const char *num)
num++;
if (*num == '+')
num++;
- else if (*num == '-')
- signsubscale = -1, num++; /* With sign? */
+ else if (*num == '-') /* With sign? */
+ signsubscale = -1, num++;
while (*num >= '0' && *num <= '9')
- subscale = (subscale * 10) + (*num++ - '0'); /* Number? */
+ {
+ if ((10 * (double)subscale) > dblmax)
+ break;
+ subscale = (subscale * 10) + (*num++ - '0');
+ }
}
/* number = +/- number.fraction * 10^+/- exponent */
n = sign * n * pow (10.0, (scale + subscale * signsubscale));
- item->valuedouble = n;
- item->valueint = (int) n;
+ /* For NAN set both parts to 0. For out of range values let the
+ * integer part be 0. */
+ if (isnan (n) || isinf (n))
+ {
+ item->valuedouble = 0;
+ item->valueint = 0;
+ }
+ else if (n > dblmax || n < dblmin)
+ {
+ item->valuedouble = n;
+ item->valueint = 0;
+ }
+ else
+ {
+ item->valuedouble = n;
+ item->valueint = (int)n;
+ }
+
item->type = cJSON_Number;
return num;
}
@@ -190,6 +216,8 @@ print_number (cJSON * item)
{
char *str;
double d = item->valuedouble;
+ int i;
+
if (fabs (((double) item->valueint) - d) <= DBL_EPSILON && d <= INT_MAX
&& d >= INT_MIN)
{
@@ -203,7 +231,11 @@ print_number (cJSON * item)
str = xtrymalloc (64); /* This is a nice tradeoff. */
if (str)
{
- if (fabs (floor (d) - d) <= DBL_EPSILON && fabs (d) < 1.0e60)
+ if (isnan (d))
+ strcpy (str, "nan");
+ else if ((i = isinf (d)))
+ strcpy (str, i > 0? "inf" : ":-inf");
+ else if (fabs (floor (d) - d) <= DBL_EPSILON && fabs (d) < 1.0e60)
sprintf (str, "%.0f", d);
else if (fabs (d) < 1.0e-6 || fabs (d) > 1.0e9)
sprintf (str, "%e", d);
diff --git a/src/data-compat.c b/src/data-compat.c
index 64ed2d2..4960bf4 100644
--- a/src/data-compat.c
+++ b/src/data-compat.c
@@ -160,7 +160,7 @@ gpgme_error_to_errno (gpgme_error_t err)
break;
}
}
- TRACE (DEBUG_DATA, "gpgme:gpgme_error_to_errno", 0,
+ TRACE (DEBUG_DATA, "gpgme:gpgme_error_to_errno", NULL,
"mapping %s <%s> to: %s", gpgme_strerror (err),
gpgme_strsource (err), strerror (res));
gpg_err_set_errno (res);
diff --git a/src/data-fd.c b/src/data-fd.c
index 5c68130..4bc8f61 100644
--- a/src/data-fd.c
+++ b/src/data-fd.c
@@ -75,7 +75,7 @@ gpgme_error_t
gpgme_data_new_from_fd (gpgme_data_t *r_dh, int fd)
{
gpgme_error_t err;
- TRACE_BEG (DEBUG_DATA, "gpgme_data_new_from_fd", r_dh, "fd=0x%x", fd);
+ TRACE_BEG (DEBUG_DATA, "gpgme_data_new_from_fd", r_dh, "fd=%d", fd);
err = _gpgme_data_new (r_dh, &fd_cbs);
if (err)
diff --git a/src/data-mem.c b/src/data-mem.c
index f51d2fd..539b453 100644
--- a/src/data-mem.c
+++ b/src/data-mem.c
@@ -296,7 +296,7 @@ gpgme_data_release_and_get_mem (gpgme_data_t dh, size_t *r_len)
void
gpgme_free (void *buffer)
{
- TRACE (DEBUG_DATA, "gpgme_free", buffer, "");
+ TRACE (DEBUG_DATA, "gpgme_free", NULL, "p=%p", buffer);
if (buffer)
free (buffer);
diff --git a/src/data.c b/src/data.c
index 44ef2d3..7059590 100644
--- a/src/data.c
+++ b/src/data.c
@@ -574,7 +574,7 @@ _gpgme_data_inbound_handler (void *opaque, int fd)
char *bufp = buffer;
gpgme_ssize_t buflen;
TRACE_BEG (DEBUG_CTX, "_gpgme_data_inbound_handler", dh,
- "fd=0x%x", fd);
+ "fd=%d", fd);
buflen = _gpgme_io_read (fd, buffer, BUFFER_SIZE);
if (buflen < 0)
@@ -605,7 +605,7 @@ _gpgme_data_outbound_handler (void *opaque, int fd)
gpgme_data_t dh = (gpgme_data_t) data->handler_value;
gpgme_ssize_t nwritten;
TRACE_BEG (DEBUG_CTX, "_gpgme_data_outbound_handler", dh,
- "fd=0x%x", fd);
+ "fd=%d", fd);
if (!dh->pending_len)
{
diff --git a/src/debug.c b/src/debug.c
index 81c2a90..d5d11bd 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -1,6 +1,6 @@
/* debug.c - helpful output in desperate situations
* Copyright (C) 2000 Werner Koch (dd9jn)
- * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009 g10 Code GmbH
+ * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, 2009, 2019 g10 Code GmbH
*
* This file is part of GPGME.
*
@@ -50,10 +50,6 @@
#include "debug.h"
-/* Lock to serialize initialization of the debug output subsystem and
- output of actual debug messages. */
-DEFINE_STATIC_LOCK (debug_lock);
-
/* The amount of detail requested by the user, per environment
variable GPGME_DEBUG. */
static int debug_level;
@@ -135,7 +131,6 @@ debug_init (void)
{
static int initialized;
- LOCK (debug_lock);
if (!initialized)
{
gpgme_error_t err;
@@ -152,10 +147,7 @@ debug_init (void)
{
err = _gpgme_getenv ("GPGME_DEBUG", &e);
if (err)
- {
- UNLOCK (debug_lock);
- return;
- }
+ return;
}
initialized = 1;
@@ -201,16 +193,15 @@ debug_init (void)
free (e);
}
}
- UNLOCK (debug_lock);
if (debug_level > 0)
{
- _gpgme_debug (DEBUG_INIT, -1, NULL, NULL, NULL,
+ _gpgme_debug (NULL, DEBUG_INIT, -1, NULL, NULL, NULL,
"gpgme_debug: level=%d\n", debug_level);
#ifdef HAVE_W32_SYSTEM
{
const char *name = _gpgme_get_inst_dir ();
- _gpgme_debug (DEBUG_INIT, -1, NULL, NULL, NULL,
+ _gpgme_debug (NULL, DEBUG_INIT, -1, NULL, NULL, NULL,
"gpgme_debug: gpgme='%s'\n", name? name: "?");
}
#endif
@@ -219,9 +210,9 @@ debug_init (void)
-/* This should be called as soon as the locks are initialized. It is
- required so that the assuan logging gets conncted to the gpgme log
- stream as early as possible. */
+/* This should be called as soon as possible. It is required so that
+ * the assuan logging gets connected to the gpgme log stream as early
+ * as possible. */
void
_gpgme_debug_subsystem_init (void)
{
@@ -240,6 +231,10 @@ _gpgme_debug_subsystem_init (void)
* 2 = debug a function (used by macro TRACE_LOG)
* 3 = leave a function (used by macro TRACE_SUC)
*
+ * If LINE is not NULL the output will be stored in that variabale but
+ * without a LF. _gpgme_debug_add can be used to add more and
+ * _gpgme_debug_end to finally output it.
+ *
* Returns: 0
*
* Note that we always return 0 because the old TRACE macro evaluated
@@ -249,96 +244,96 @@ _gpgme_debug_subsystem_init (void)
* value from the TRACE macros are actually used somewhere.
*/
int
-_gpgme_debug (int level, int mode, const char *func, const char *tagname,
+_gpgme_debug (void **line, int level, int mode,
+ const char *func, const char *tagname,
const char *tagvalue, const char *format, ...)
{
va_list arg_ptr;
int saved_errno;
int need_lf;
+ int indent;
+ char *prefix, *stdinfo, *userinfo;
+ const char *modestr;
+ int no_userinfo = 0;
if (debug_level < level)
return 0;
+#ifdef FRAME_NR
+ indent = frame_nr > 0? (2 * (frame_nr - 1)):0;
+#else
+ indent = 0;
+#endif
+
saved_errno = errno;
va_start (arg_ptr, format);
- LOCK (debug_lock);
{
struct tm *tp;
time_t atime = time (NULL);
tp = localtime (&atime);
- fprintf (errfp, "GPGME %04d-%02d-%02d %02d:%02d:%02d <0x%04llx> ",
- 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
- tp->tm_hour, tp->tm_min, tp->tm_sec,
- (unsigned long long) ath_self ());
+ prefix = gpgrt_bsprintf ("GPGME %04d%02d%02dT%02d%02d%02d %04llX %*s",
+ 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday,
+ tp->tm_hour, tp->tm_min, tp->tm_sec,
+ (unsigned long long) ath_self (),
+ indent < 40? indent : 40, "");
}
-#ifdef FRAME_NR
- {
- int indent;
-
- indent = frame_nr > 0? (2 * (frame_nr - 1)):0;
- fprintf (errfp, "%*s", indent < 40? indent : 40, "");
- }
-#endif
- need_lf = 0;
switch (mode)
{
- case -1: /* Do nothing. */
- break;
- case 0:
- fprintf (errfp, "%s: call: %s=%p ", func, tagname, tagvalue);
- break;
- case 1:
- fprintf (errfp, "%s: enter: %s=%p ", func, tagname, tagvalue);
- break;
- case 2:
- fprintf (errfp, "%s: check: %s=%p ", func, tagname, tagvalue);
- break;
- case 3:
- if (tagname)
- fprintf (errfp, "%s: leave: %s=%p ", func, tagname, tagvalue);
- else
- fprintf (errfp, "%s: leave: ", func);
- break;
- default:
- fprintf (errfp, "%s: ?(mode=%d): %s=%p ", func, mode, tagname, tagvalue);
- break;
+ case -1: modestr = NULL; break; /* Do nothing. */
+ case 0: modestr = "call"; break;
+ case 1: modestr = "enter"; break;
+ case 2: modestr = "check"; break;
+ case 3: modestr = "leave"; break;
+ default: modestr = "mode?"; break;
}
- need_lf = (mode != -1 && (!format || !*format));
-
- vfprintf (errfp, format, arg_ptr);
- va_end (arg_ptr);
- if (need_lf || (format && *format && format[strlen (format) - 1] != '\n'))
- putc ('\n', errfp);
- UNLOCK (debug_lock);
- fflush (errfp);
-
- gpg_err_set_errno (saved_errno);
- return 0;
-}
+ if (!modestr)
+ stdinfo = NULL;
+ else if (tagname && strcmp (tagname, XSTRINGIFY (NULL)))
+ stdinfo = gpgrt_bsprintf ("%s: %s: %s=%p ", func,modestr,tagname,tagvalue);
+ else
+ stdinfo = gpgrt_bsprintf ("%s: %s: ", func, modestr);
-/* Start a new debug line in *LINE, logged at level LEVEL or higher,
- and starting with the formatted string FORMAT. */
-void
-_gpgme_debug_begin (void **line, int level, const char *format, ...)
-{
- va_list arg_ptr;
- int res;
+ if (format && *format)
+ userinfo = gpgrt_vbsprintf (format, arg_ptr);
+ else
+ {
+ userinfo = NULL;
+ no_userinfo = 1;
+ }
+ va_end (arg_ptr);
- if (debug_level < level)
+ if (mode != -1 && (!format || !*format))
+ need_lf = 1;
+ else if (userinfo && *userinfo && userinfo[strlen (userinfo) - 1] != '\n')
+ need_lf = 1;
+ else
+ need_lf = 0;
+
+ if (line)
+ *line = gpgrt_bsprintf ("%s%s%s",
+ prefix? prefix : "GPGME out-of-core ",
+ !modestr? "" : stdinfo? stdinfo :
+ (!format || !*format)? "" :"out-of-core ",
+ userinfo? userinfo : "out-of-core");
+ else
{
- /* Disable logging of this line. */
- *line = NULL;
- return;
+ fprintf (errfp, "%s%s%s%s",
+ prefix? prefix : "GPGME out-of-core ",
+ !modestr? "" : stdinfo? stdinfo :
+ (!format || !*format)? "" :"out-of-core ",
+ userinfo? userinfo : no_userinfo? "" : "out-of-core",
+ need_lf? "\n":"");
+ fflush (errfp);
}
- va_start (arg_ptr, format);
- res = gpgrt_vasprintf ((char **) line, format, arg_ptr);
- va_end (arg_ptr);
- if (res < 0)
- *line = NULL;
+ gpgrt_free (userinfo);
+ gpgrt_free (stdinfo);
+ gpgrt_free (prefix);
+ gpg_err_set_errno (saved_errno);
+ return 0;
}
@@ -377,12 +372,16 @@ _gpgme_debug_add (void **line, const char *format, ...)
void
_gpgme_debug_end (void **line)
{
+ const char *string;
+
if (!*line)
return;
+ string = *line;
- /* The smallest possible level is 1, so force logging here by
- using that. */
- _gpgme_debug (1, -1, NULL, NULL, NULL, "%s", (char*)*line);
+ fprintf (errfp, "%s%s",
+ string,
+ (*string && string[strlen (string)-1] != '\n')? "\n":"");
+ fflush (errfp);
gpgrt_free (*line);
*line = NULL;
}
@@ -404,33 +403,65 @@ _gpgme_debug_buffer (int lvl, const char *const fmt,
if (!buffer)
return;
- while (idx < len)
+ if (lvl > 9)
{
- char str[51];
- char *strp = str;
- char *strp2 = &str[34];
+ while (idx < len)
+ {
+ char str[51];
+ char *strp = str;
+ char *strp2 = &str[34];
- for (j = 0; j < 16; j++)
- {
- unsigned char val;
- if (idx < len)
- {
- val = buffer[idx++];
- *(strp++) = TOHEX (val >> 4);
- *(strp++) = TOHEX (val % 16);
- *(strp2++) = isprint (val) ? val : '.';
- }
- else
- {
- *(strp++) = ' ';
- *(strp++) = ' ';
- }
- if (j == 7)
- *(strp++) = ' ';
- }
- *(strp++) = ' ';
- *(strp2) = '\0';
+ for (j = 0; j < 16; j++)
+ {
+ unsigned char val;
+ if (idx < len)
+ {
+ val = buffer[idx++];
+ *(strp++) = TOHEX (val >> 4);
+ *(strp++) = TOHEX (val % 16);
+ *(strp2++) = isprint (val)? val : '.';
+ }
+ else
+ {
+ *(strp++) = ' ';
+ *(strp++) = ' ';
+ }
+ if (j == 7)
+ *(strp++) = ' ';
+ }
+ *(strp++) = ' ';
+ *(strp2) = '\0';
- _gpgme_debug (lvl, -1, NULL, NULL, NULL, fmt, func, str);
+ _gpgme_debug (NULL, lvl, -1, NULL, NULL, NULL, fmt, func, str);
+ }
+ }
+ else
+ {
+ while (idx < len)
+ {
+ char str[48+4+1];
+ char *strp = str;
+
+ for (j = 0; j < 48; j++)
+ {
+ unsigned char val;
+ if (idx < len)
+ {
+ val = buffer[idx++];
+ if (val == '\n')
+ {
+ *strp++ = '<';
+ *strp++ = 'L';
+ *strp++ = 'F';
+ *strp++ = '>';
+ break;
+ }
+ *strp++ = (val > 31 && val < 127)? val : '.';
+ }
+ }
+ *strp = 0;
+
+ _gpgme_debug (NULL, lvl, -1, NULL, NULL, NULL, fmt, func, str);
+ }
}
}
diff --git a/src/debug.h b/src/debug.h
index 7ef8cf2..fa0bfc6 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -25,6 +25,7 @@
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
+#include <errno.h>
#include "gpgme.h" /* Required for gpgme_error stuff. */
@@ -68,15 +69,11 @@ int _gpgme_debug_set_debug_envvar (const char *value);
void _gpgme_debug_subsystem_init (void);
/* Log the formatted string FORMAT at debug level LEVEL or higher. */
-int _gpgme_debug (int level, int mode,
+int _gpgme_debug (void **line, int level, int mode,
const char *func, const char *tagname, const char *tagvalue,
- const char *format, ...) GPGRT_ATTR_PRINTF(6,7);
+ const char *format, ...) GPGRT_ATTR_PRINTF(7,8);
-/* Start a new debug line in *LINE, logged at level LEVEL or higher,
- and starting with the formatted string FORMAT. */
-void _gpgme_debug_begin (void **helper, int level, const char *format, ...);
-
/* Add the formatted string FORMAT to the debug line *LINE. */
void _gpgme_debug_add (void **helper, const char *format, ...);
@@ -94,7 +91,7 @@ int _gpgme_debug_frame_end (void);
static inline gpgme_error_t
_gpgme_trace_gpgme_error (gpgme_error_t err, const char *file, int line)
{
- _gpgme_debug (DEBUG_ENGINE, -1, NULL, NULL, NULL,
+ _gpgme_debug (NULL, DEBUG_ENGINE, -1, NULL, NULL, NULL,
"%s:%d: returning error: %s\n",
_gpgme_debug_srcname (file), line, gpgme_strerror (err));
return err;
@@ -116,13 +113,13 @@ _gpgme_trace_gpgme_error (gpgme_error_t err, const char *file, int line)
/* Note: We can't protect this with a do-while block. */
#define TRACE_BEG(lvl, name, tag, ...) \
_TRACE (lvl, name, tag); \
- _gpgme_debug (_gpgme_trace_level, 1, \
+ _gpgme_debug (NULL, _gpgme_trace_level, 1, \
_gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \
__VA_ARGS__)
#define TRACE(lvl, name, tag, ...) do { \
_gpgme_debug_frame_begin (); \
- _gpgme_debug (lvl, 0, name, STRINGIFY (tag), (void *)(uintptr_t)tag, \
+ _gpgme_debug (NULL, lvl, 0, name, STRINGIFY (tag), (void *)(uintptr_t)tag, \
__VA_ARGS__); \
_gpgme_debug_frame_end (); \
} while (0)
@@ -135,9 +132,9 @@ static inline gpg_error_t
_trace_err (gpg_error_t err, int lvl, const char *func, int line)
{
if (!err)
- _gpgme_debug (lvl, 3, func, NULL, NULL, "");
+ _gpgme_debug (NULL, lvl, 3, func, NULL, NULL, "");
else
- _gpgme_debug (lvl, -1, NULL, NULL, NULL,
+ _gpgme_debug (NULL, lvl, -1, NULL, NULL, NULL,
"%s:%d: error: %s <%s>\n",
func, line, gpgme_strerror (err), gpgme_strsource (err));
_gpgme_debug_frame_end ();
@@ -151,11 +148,11 @@ static inline int
_trace_sysres (int res, int lvl, const char *func, int line)
{
if (res >= 0)
- _gpgme_debug (lvl, 3, func, NULL, NULL, "result=%d", res);
+ _gpgme_debug (NULL, lvl, 3, func, NULL, NULL, "result=%d", res);
else
- _gpgme_debug (lvl, -1, NULL, NULL, NULL,
+ _gpgme_debug (NULL, lvl, -1, NULL, NULL, NULL,
"%s:%d: error: %s (%d)\n",
- func, line, strerror (res), res);
+ func, line, strerror (errno), errno);
_gpgme_debug_frame_end ();
return res;
}
@@ -167,9 +164,9 @@ static inline int
_trace_syserr (int rc, int lvl, const char *func, int line)
{
if (!rc)
- _gpgme_debug (lvl, 3, func, NULL, NULL, "result=0");
+ _gpgme_debug (NULL, lvl, 3, func, NULL, NULL, "result=0");
else
- _gpgme_debug (lvl, -1, NULL, NULL, NULL,
+ _gpgme_debug (NULL, lvl, -1, NULL, NULL, NULL,
"%s:%d: error: %s (%d)\n",
func, line, strerror (rc), rc);
_gpgme_debug_frame_end ();
@@ -177,13 +174,13 @@ _trace_syserr (int rc, int lvl, const char *func, int line)
}
#define TRACE_SUC(...) do { \
- _gpgme_debug (_gpgme_trace_level, 3, _gpgme_trace_func, NULL, NULL, \
+ _gpgme_debug (NULL, _gpgme_trace_level, 3, _gpgme_trace_func, NULL, NULL, \
__VA_ARGS__); \
_gpgme_debug_frame_end (); \
} while (0)
#define TRACE_LOG(...) do { \
- _gpgme_debug (_gpgme_trace_level, 2, \
+ _gpgme_debug (NULL, _gpgme_trace_level, 2, \
_gpgme_trace_func, _gpgme_trace_tagname, _gpgme_trace_tag, \
__VA_ARGS__); \
} while (0)
@@ -198,10 +195,9 @@ _trace_syserr (int rc, int lvl, const char *func, int line)
_gpgme_trace_func, buf, len); \
} while (0)
-#define TRACE_SEQ(hlp,fmt) do { \
- _gpgme_debug_begin (&(hlp), _gpgme_trace_level, \
- "%s: check: %s=%p, " fmt, _gpgme_trace_func, \
- _gpgme_trace_tagname, _gpgme_trace_tag); \
+#define TRACE_SEQ(hlp,...) do { \
+ _gpgme_debug (&(hlp), _gpgme_trace_level, 2, _gpgme_trace_func, \
+ _gpgme_trace_tagname, _gpgme_trace_tag, __VA_ARGS__); \
} while (0)
#define TRACE_ADD0(hlp,fmt) \
diff --git a/src/dirinfo.c b/src/dirinfo.c
index d481199..c4f0e4a 100644
--- a/src/dirinfo.c
+++ b/src/dirinfo.c
@@ -260,15 +260,15 @@ get_gpgconf_item (int what)
char *pgmname;
pgmname = dirinfo.disable_gpgconf? NULL : _gpgme_get_gpgconf_path ();
- if (pgmname && access (pgmname, F_OK))
+ if (pgmname && _gpgme_access (pgmname, F_OK))
{
- _gpgme_debug (DEBUG_INIT, -1, NULL, NULL, NULL,
+ _gpgme_debug (NULL, DEBUG_INIT, -1, NULL, NULL, NULL,
"gpgme-dinfo: gpgconf='%s' [not installed]\n", pgmname);
free (pgmname);
pgmname = NULL; /* Not available. */
}
else
- _gpgme_debug (DEBUG_INIT, -1, NULL, NULL, NULL,
+ _gpgme_debug (NULL, DEBUG_INIT, -1, NULL, NULL, NULL,
"gpgme-dinfo: gpgconf='%s'\n",
pgmname? pgmname : "[null]");
if (!pgmname)
@@ -295,35 +295,35 @@ get_gpgconf_item (int what)
allocated. */
dirinfo.valid = 1;
if (dirinfo.gpg_name)
- _gpgme_debug (DEBUG_INIT, -1, NULL, NULL, NULL,
+ _gpgme_debug (NULL, DEBUG_INIT, -1, NULL, NULL, NULL,
"gpgme-dinfo: gpg='%s'\n",
dirinfo.gpg_name);
if (dirinfo.g13_name)
- _gpgme_debug (DEBUG_INIT, -1, NULL, NULL, NULL,
+ _gpgme_debug (NULL, DEBUG_INIT, -1, NULL, NULL, NULL,
"gpgme-dinfo: g13='%s'\n",
dirinfo.g13_name);
if (dirinfo.gpgsm_name)
- _gpgme_debug (DEBUG_INIT, -1, NULL, NULL, NULL,
+ _gpgme_debug (NULL, DEBUG_INIT, -1, NULL, NULL, NULL,
"gpgme-dinfo: gpgsm='%s'\n",
dirinfo.gpgsm_name);
if (dirinfo.homedir)
- _gpgme_debug (DEBUG_INIT, -1, NULL, NULL, NULL,
+ _gpgme_debug (NULL, DEBUG_INIT, -1, NULL, NULL, NULL,
"gpgme-dinfo: homedir='%s'\n",
dirinfo.homedir);
if (dirinfo.agent_socket)
- _gpgme_debug (DEBUG_INIT, -1, NULL, NULL, NULL,
+ _gpgme_debug (NULL,DEBUG_INIT, -1, NULL, NULL, NULL,
"gpgme-dinfo: agent='%s'\n",
dirinfo.agent_socket);
if (dirinfo.agent_ssh_socket)
- _gpgme_debug (DEBUG_INIT, -1, NULL, NULL, NULL,
+ _gpgme_debug (NULL, DEBUG_INIT, -1, NULL, NULL, NULL,
"gpgme-dinfo: ssh='%s'\n",
dirinfo.agent_ssh_socket);
if (dirinfo.dirmngr_socket)
- _gpgme_debug (DEBUG_INIT, -1, NULL, NULL, NULL,
+ _gpgme_debug (NULL, DEBUG_INIT, -1, NULL, NULL, NULL,
"gpgme-dinfo: dirmngr='%s'\n",
dirinfo.dirmngr_socket);
if (dirinfo.uisrv_socket)
- _gpgme_debug (DEBUG_INIT, -1, NULL, NULL, NULL,
+ _gpgme_debug (NULL, DEBUG_INIT, -1, NULL, NULL, NULL,
"gpgme-dinfo: uisrv='%s'\n",
dirinfo.uisrv_socket);
}
diff --git a/src/engine-assuan.c b/src/engine-assuan.c
index 79e826e..497397d 100644
--- a/src/engine-assuan.c
+++ b/src/engine-assuan.c
@@ -658,7 +658,7 @@ add_io_cb (engine_llass_t llass, iocb_data_t *iocbd, gpgme_io_cb_t handler)
gpgme_error_t err;
TRACE_BEG (DEBUG_ENGINE, "engine-assuan:add_io_cb", llass,
- "fd %d, dir %d", iocbd->fd, iocbd->dir);
+ "fd=%d, dir %d", iocbd->fd, iocbd->dir);
err = (*llass->io_cbs.add) (llass->io_cbs.add_priv,
iocbd->fd, iocbd->dir,
handler, iocbd->data, &iocbd->tag);
diff --git a/src/engine-g13.c b/src/engine-g13.c
index edb8d54..19dd8f4 100644
--- a/src/engine-g13.c
+++ b/src/engine-g13.c
@@ -639,7 +639,7 @@ add_io_cb (engine_g13_t g13, iocb_data_t *iocbd, gpgme_io_cb_t handler)
gpgme_error_t err;
TRACE_BEG (DEBUG_ENGINE, "engine-g13:add_io_cb", g13,
- "fd %d, dir %d", iocbd->fd, iocbd->dir);
+ "fd=%d, dir %d", iocbd->fd, iocbd->dir);
err = (*g13->io_cbs.add) (g13->io_cbs.add_priv,
iocbd->fd, iocbd->dir,
handler, iocbd->data, &iocbd->tag);
diff --git a/src/engine-gpg.c b/src/engine-gpg.c
index 31d219a..dc2d945 100644
--- a/src/engine-gpg.c
+++ b/src/engine-gpg.c
@@ -2075,6 +2075,8 @@ append_args_from_recipients_string (engine_gpg_t gpg,
file = 0;
flags = orig_flags;
}
+ else if (!ignore && n > 2 && !memcmp (string, "--", 2))
+ err = gpg_error (GPG_ERR_UNKNOWN_OPTION);
else if (n) /* Not empty - use it. */
{
err = add_arg (gpg, file? (hidden? "-F":"-f") : (hidden? "-R":"-r"));
diff --git a/src/engine-gpgsm.c b/src/engine-gpgsm.c
index 396f19c..ae5d8ef 100644
--- a/src/engine-gpgsm.c
+++ b/src/engine-gpgsm.c
@@ -549,8 +549,6 @@ gpgsm_new (void **engine, const char *file_name, const char *home_dir,
|| _gpgme_io_set_close_notify (gpgsm->output_cb.fd,
close_notify_handler, gpgsm)
|| _gpgme_io_set_close_notify (gpgsm->message_cb.fd,
- close_notify_handler, gpgsm)
- || _gpgme_io_set_close_notify (gpgsm->diag_cb.fd,
close_notify_handler, gpgsm)))
{
err = gpg_error (GPG_ERR_GENERAL);
@@ -1129,7 +1127,7 @@ add_io_cb (engine_gpgsm_t gpgsm, iocb_data_t *iocbd, gpgme_io_cb_t handler)
gpgme_error_t err;
TRACE_BEG (DEBUG_ENGINE, "engine-gpgsm:add_io_cb", gpgsm,
- "fd %d, dir %d", iocbd->fd, iocbd->dir);
+ "fd=%d, dir %d", iocbd->fd, iocbd->dir);
err = (*gpgsm->io_cbs.add) (gpgsm->io_cbs.add_priv,
iocbd->fd, iocbd->dir,
handler, iocbd->data, &iocbd->tag);
@@ -1409,11 +1407,12 @@ set_recipients_from_string (engine_gpgsm_t gpgsm, const char *string)
{
gpg_error_t err = 0;
char *line = NULL;
- int no_pubkey = 0;
+ int ignore = 0;
+ int any = 0;
const char *s;
int n;
- for (;;)
+ do
{
while (*string == ' ' || *string == '\t')
string++;
@@ -1428,25 +1427,32 @@ set_recipients_from_string (engine_gpgsm_t gpgsm, const char *string)
while (n && (string[n-1] == ' ' || string[n-1] == '\t'))
n--;
- gpgrt_free (line);
- if (gpgrt_asprintf (&line, "RECIPIENT %.*s", n, string) < 0)
+ if (!ignore && n == 2 && !memcmp (string, "--", 2))
+ ignore = 1;
+ else if (!ignore && n > 2 && !memcmp (string, "--", 2))
+ err = gpg_error (GPG_ERR_UNKNOWN_OPTION);
+ else if (n) /* Not empty - use it. */
{
- err = gpg_error_from_syserror ();
- break;
+ gpgrt_free (line);
+ if (gpgrt_asprintf (&line, "RECIPIENT %.*s", n, string) < 0)
+ err = gpg_error_from_syserror ();
+ else
+ {
+ err = gpgsm_assuan_simple_command (gpgsm, line, gpgsm->status.fnc,
+ gpgsm->status.fnc_value);
+ if (!err)
+ any = 1;
+ }
}
- string += n + !!s;
-
- err = gpgsm_assuan_simple_command (gpgsm, line, gpgsm->status.fnc,
- gpgsm->status.fnc_value);
- /* Fixme: Improve error reporting. */
- if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
- no_pubkey++;
- else if (err)
- break;
+ string += n + !!s;
}
+ while (!err);
+
+ if (!err && !any)
+ err = gpg_error (GPG_ERR_MISSING_KEY);
gpgrt_free (line);
- return err? err : no_pubkey? gpg_error (GPG_ERR_NO_PUBKEY) : 0;
+ return err;
}
@@ -1460,7 +1466,7 @@ gpgsm_encrypt (void *engine, gpgme_key_t recp[], const char *recpstring,
if (!gpgsm)
return gpg_error (GPG_ERR_INV_VALUE);
- if (!recp)
+ if (!recp && !recpstring) /* Symmetric only */
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
if ((flags & GPGME_ENCRYPT_NO_ENCRYPT_TO))
diff --git a/src/engine-uiserver.c b/src/engine-uiserver.c
index 62c4e5b..cb8e155 100644
--- a/src/engine-uiserver.c
+++ b/src/engine-uiserver.c
@@ -879,7 +879,7 @@ add_io_cb (engine_uiserver_t uiserver, iocb_data_t *iocbd, gpgme_io_cb_t handler
gpgme_error_t err;
TRACE_BEG (DEBUG_ENGINE, "engine-uiserver:add_io_cb", uiserver,
- "fd %d, dir %d", iocbd->fd, iocbd->dir);
+ "fd=%d, dir %d", iocbd->fd, iocbd->dir);
err = (*uiserver->io_cbs.add) (uiserver->io_cbs.add_priv,
iocbd->fd, iocbd->dir,
handler, iocbd->data, &iocbd->tag);
diff --git a/src/engine.c b/src/engine.c
index b3df01a..05979c1 100644
--- a/src/engine.c
+++ b/src/engine.c
@@ -461,6 +461,7 @@ _gpgme_set_engine_info (gpgme_engine_info_t info, gpgme_protocol_t proto,
{
free (new_file_name);
free (new_home_dir);
+ return gpg_error_from_syserror ();
}
}
diff --git a/src/posix-io.c b/src/posix-io.c
index be08431..e712ef2 100644
--- a/src/posix-io.c
+++ b/src/posix-io.c
@@ -62,6 +62,70 @@
#include "debug.h"
+#ifdef USE_LINUX_GETDENTS
+/* This is not declared in public headers; getdents64(2) says that we must
+ * define it ourselves. */
+struct linux_dirent64
+{
+ ino64_t d_ino;
+ off64_t d_off;
+ unsigned short d_reclen;
+ unsigned char d_type;
+ char d_name[];
+};
+
+# define DIR_BUF_SIZE 1024
+#endif /*USE_LINUX_GETDENTS*/
+
+
+/* Return true if FD is valid file descriptor. */
+#if 0
+int
+_gpgme_is_fd_valid (int fd)
+{
+ int dir_fd;
+ char dir_buf[DIR_BUF_SIZE];
+ struct linux_dirent64 *dir_entry;
+ int r, pos, x;
+ const char *s;
+ int result = 0;
+
+ dir_fd = open ("/proc/self/fd", O_RDONLY | O_DIRECTORY);
+ if (dir_fd != -1)
+ {
+ for (;;)
+ {
+ r = syscall(SYS_getdents64, dir_fd, dir_buf, DIR_BUF_SIZE);
+ if (r == -1)
+ break; /* Ooops */
+ if (r == 0)
+ break;
+
+ for (pos = 0; pos < r; pos += dir_entry->d_reclen)
+ {
+ dir_entry = (struct linux_dirent64 *) (dir_buf + pos);
+ s = dir_entry->d_name;
+ if (*s < '0' || *s > '9')
+ continue;
+ /* atoi is not guaranteed to be async-signal-safe. */
+ for (x = 0; *s >= '0' && *s <= '9'; s++)
+ x = x * 10 + (*s - '0');
+ if (*s)
+ continue; /* Does not look like a file descriptor. */
+ if (x == fd)
+ {
+ result = 1;
+ goto leave;
+ }
+ }
+ }
+ leave:
+ close (dir_fd);
+ }
+ return result;
+}
+#endif /*0*/
+
void
_gpgme_io_subsystem_init (void)
@@ -109,8 +173,8 @@ int
_gpgme_io_read (int fd, void *buffer, size_t count)
{
int nread;
- TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_read", fd,
- "buffer=%p, count=%zu", buffer, count);
+ TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_read", NULL,
+ "fd=%d buffer=%p count=%zu", fd, buffer, count);
do
{
@@ -127,8 +191,8 @@ int
_gpgme_io_write (int fd, const void *buffer, size_t count)
{
int nwritten;
- TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_write", fd,
- "buffer=%p, count=%zu", buffer, count);
+ TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_write", NULL,
+ "fd=%d buffer=%p count=%zu", fd, buffer, count);
TRACE_LOGBUFX (buffer, count);
do
@@ -146,7 +210,7 @@ _gpgme_io_pipe (int filedes[2], int inherit_idx)
{
int saved_errno;
int err;
- TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_pipe", filedes,
+ TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_pipe", NULL,
"inherit_idx=%i (GPGME uses it for %s)",
inherit_idx, inherit_idx ? "reading" : "writing");
@@ -166,7 +230,7 @@ _gpgme_io_pipe (int filedes[2], int inherit_idx)
if (err)
return TRACE_SYSRES (err);
- TRACE_SUC ("read=0x%x, write=0x%x", filedes[0], filedes[1]);
+ TRACE_SUC ("read fd=%d write fd=%d", filedes[0], filedes[1]);
return 0;
}
@@ -179,7 +243,7 @@ _gpgme_io_close (int fd)
void *handler_value;
int idx;
- TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", fd, "");
+ TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_close", NULL, "fd=%d", fd);
if (fd == -1)
{
@@ -221,8 +285,8 @@ _gpgme_io_set_close_notify (int fd, _gpgme_close_notify_handler_t handler,
int res = 0;
int idx;
- TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_close_notify", fd,
- "close_handler=%p/%p", handler, value);
+ TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_close_notify", NULL,
+ "fd=%d close_handler=%p/%p", fd, handler, value);
assert (fd != -1);
@@ -272,7 +336,7 @@ _gpgme_io_set_nonblocking (int fd)
{
int flags;
int res;
- TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", fd, "");
+ TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_set_nonblocking", NULL, "fd=%d", fd);
flags = fcntl (fd, F_GETFL, 0);
if (flags == -1)
@@ -283,22 +347,6 @@ _gpgme_io_set_nonblocking (int fd)
}
-#ifdef USE_LINUX_GETDENTS
-/* This is not declared in public headers; getdents64(2) says that we must
- * define it ourselves. */
-struct linux_dirent64
-{
- ino64_t d_ino;
- off64_t d_off;
- unsigned short d_reclen;
- unsigned char d_type;
- char d_name[];
-};
-
-# define DIR_BUF_SIZE 1024
-#endif /*USE_LINUX_GETDENTS*/
-
-
static long int
get_max_fds (void)
{
@@ -428,7 +476,7 @@ get_max_fds (void)
}
#endif
- TRACE (DEBUG_SYSIO, "gpgme:max_fds", 0, "max fds=%ld (%s)", fds, source);
+ TRACE (DEBUG_SYSIO, "gpgme:max_fds", NULL, "max fds=%ld (%s)", fds, source);
return fds;
}
@@ -474,7 +522,7 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
int status;
int signo;
- TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_spawn", path,
+ TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_spawn", NULL,
"path=%s", path);
i = 0;
while (argv[i])
@@ -483,10 +531,12 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
i++;
}
for (i = 0; fd_list[i].fd != -1; i++)
- if (fd_list[i].dup_to == -1)
- TRACE_LOG ("fd[%i] = 0x%x", i, fd_list[i].fd);
- else
- TRACE_LOG ("fd[%i] = 0x%x -> 0x%x", i, fd_list[i].fd, fd_list[i].dup_to);
+ {
+ if (fd_list[i].dup_to == -1)
+ TRACE_LOG ("fd[%i] = 0x%x", i, fd_list[i].fd);
+ else
+ TRACE_LOG ("fd[%i] = 0x%x -> 0x%x", i,fd_list[i].fd,fd_list[i].dup_to);
+ }
pid = fork ();
if (pid == -1)
@@ -654,7 +704,7 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
/* Use a 1s timeout. */
struct timeval timeout = { 1, 0 };
void *dbg_help = NULL;
- TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_select", fds,
+ TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_select", NULL,
"nfds=%zu, nonblock=%u", nfds, nonblock);
FD_ZERO (&readfds);
@@ -682,8 +732,8 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
FD_SET (fds[i].fd, &readfds);
if (fds[i].fd > max_fd)
max_fd = fds[i].fd;
- TRACE_ADD1 (dbg_help, "r0x%x ", fds[i].fd);
- any = 1;
+ TRACE_ADD1 (dbg_help, "r=%d ", fds[i].fd);
+ any = 1;
}
else if (fds[i].for_write)
{
@@ -697,7 +747,7 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
FD_SET (fds[i].fd, &writefds);
if (fds[i].fd > max_fd)
max_fd = fds[i].fd;
- TRACE_ADD1 (dbg_help, "w0x%x ", fds[i].fd);
+ TRACE_ADD1 (dbg_help, "w=%d ", fds[i].fd);
any = 1;
}
fds[i].signaled = 0;
@@ -721,9 +771,9 @@ _gpgme_io_select (struct io_select_fd_s *fds, size_t nfds, int nonblock)
for (i = 0; i <= max_fd; i++)
{
if (FD_ISSET (i, &readfds))
- TRACE_ADD1 (dbg_help, "r0x%x ", i);
+ TRACE_ADD1 (dbg_help, "r=%d ", i);
if (FD_ISSET (i, &writefds))
- TRACE_ADD1 (dbg_help, "w0x%x ", i);
+ TRACE_ADD1 (dbg_help, "w=%d ", i);
}
TRACE_END (dbg_help, "]");
}
@@ -760,8 +810,8 @@ _gpgme_io_recvmsg (int fd, struct msghdr *msg, int flags)
int nread;
int saved_errno;
struct iovec *iov;
- TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_recvmsg", fd,
- "msg=%p, flags=%i", msg, flags);
+ TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_recvmsg", NULL,
+ "fd=%d msg=%p flags=%i", fd, msg, flags);
nread = 0;
iov = msg->msg_iov;
@@ -802,8 +852,8 @@ _gpgme_io_sendmsg (int fd, const struct msghdr *msg, int flags)
{
int nwritten;
struct iovec *iov;
- TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_sendmsg", fd,
- "msg=%p, flags=%i", msg, flags);
+ TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_sendmsg", NULL,
+ "fd=%d msg=%p flags=%i", fd, msg, flags);
nwritten = 0;
iov = msg->msg_iov;
@@ -841,7 +891,7 @@ _gpgme_io_dup (int fd)
new_fd = dup (fd);
while (new_fd == -1 && errno == EINTR);
- TRACE (DEBUG_SYSIO, "_gpgme_io_dup", fd, "new fd==%i", new_fd);
+ TRACE (DEBUG_SYSIO, "_gpgme_io_dup", NULL, "fd=%d -> fd=%d", fd, new_fd);
return new_fd;
}
@@ -852,8 +902,8 @@ _gpgme_io_socket (int domain, int type, int proto)
{
int res;
- TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_socket", domain,
- "type=%i, proto=%i", type, proto);
+ TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_socket", NULL,
+ "domain=%d type=%i proto=%i", domain, type, proto);
res = socket (domain, type, proto);
@@ -866,8 +916,8 @@ _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
{
int res;
- TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_connect", fd,
- "addr=%p, addrlen=%i", addr, addrlen);
+ TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_connect", NULL,
+ "fd=%d addr=%p addrlen=%i", fd, addr, addrlen);
do
res = ath_connect (fd, addr, addrlen);
diff --git a/src/posix-util.c b/src/posix-util.c
index 881856c..5c4f339 100644
--- a/src/posix-util.c
+++ b/src/posix-util.c
@@ -114,7 +114,7 @@ walk_path (const char *pgm)
path = s + 1;
}
- _gpgme_debug (DEBUG_ENGINE, -1, NULL, NULL, NULL,
+ _gpgme_debug (NULL, DEBUG_ENGINE, -1, NULL, NULL, NULL,
"gpgme-walk_path: '%s' not found in '%s'",
pgm, orig_path);
@@ -157,3 +157,10 @@ _gpgme_allow_set_foreground_window (pid_t pid)
(void)pid;
/* Not needed. */
}
+
+/* See w32-util.c */
+int
+_gpgme_access (const char *path, int mode)
+{
+ return access (path, mode);
+}
diff --git a/src/sys-util.h b/src/sys-util.h
index 6cb2224..e537613 100644
--- a/src/sys-util.h
+++ b/src/sys-util.h
@@ -28,9 +28,22 @@ int _gpgme_set_override_inst_dir (const char *dir);
char *_gpgme_get_gpg_path (void);
char *_gpgme_get_gpgconf_path (void);
+int _gpgme_access (const char *path_utf8, int mode);
+
#ifdef HAVE_W32_SYSTEM
const char *_gpgme_get_inst_dir (void);
void _gpgme_w32_cancel_synchronous_io (HANDLE thread);
+/* See CreateProcessA returns true on success */
+int _gpgme_create_process_utf8 (const char *application_name_utf8,
+ char *command_line_utf8,
+ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ BOOL bInheritHandles,
+ DWORD dwCreationFlags,
+ void *lpEnvironment,
+ char *working_directory_utf8,
+ LPSTARTUPINFOA lpStartupInfo,
+ LPPROCESS_INFORMATION lpProcessInformation);
#endif
#endif /* SYS_UTIL_H */
diff --git a/src/version.c b/src/version.c
index 3bf12e9..5beb63a 100644
--- a/src/version.c
+++ b/src/version.c
@@ -202,7 +202,7 @@ gpgme_check_version (const char *req_version)
before using the trace facility. If we won't the trace would
automagically initialize the debug system with out the locks
being initialized and missing the assuan log level setting. */
- TRACE (DEBUG_INIT, "gpgme_check_version", 0,
+ TRACE (DEBUG_INIT, "gpgme_check_version", NULL,
"req_version=%s, VERSION=%s",
req_version? req_version:"(null)", VERSION);
@@ -229,13 +229,13 @@ gpgme_check_version_internal (const char *req_version,
return result;
/* Catch-22, see above. */
- TRACE (DEBUG_INIT, "gpgme_check_version_internal", 0,
+ TRACE (DEBUG_INIT, "gpgme_check_version_internal", NULL,
"req_version=%s, offset_sig_validity=%zu",
req_version ? req_version : "(null)", offset_sig_validity);
if (offset_sig_validity != offsetof (struct _gpgme_signature, validity))
{
- TRACE (DEBUG_INIT, "gpgme_check_version_internal", 0,
+ TRACE (DEBUG_INIT, "gpgme_check_version_internal", NULL,
"offset_sig_validity mismatch: expected %i",
(int)offsetof (struct _gpgme_signature, validity));
_gpgme_selftest = GPG_ERR_SELFTEST_FAILED;
diff --git a/src/w32-glib-io.c b/src/w32-glib-io.c
index 8c8722c..09ffffa 100644
--- a/src/w32-glib-io.c
+++ b/src/w32-glib-io.c
@@ -324,7 +324,7 @@ _gpgme_io_write (int fd, const void *buffer, size_t count)
chan = find_channel (fd);
if (!chan)
{
- TRACE_LOG ("fd %d: no channel registered");
+ TRACE_LOG ("fd=%d: no channel registered");
errno = EINVAL;
return -1;
}
@@ -421,12 +421,13 @@ _gpgme_io_pipe (int filedes[2], int inherit_idx)
return TRACE_SYSRES (-1);
}
- return TRACE_SUC ("read=0x%x/%p, write=0x%x/%p, channel=%p",
- filedes[0],
- (HANDLE) _get_osfhandle (giochannel_table[filedes[0]].fd),
- filedes[1],
- (HANDLE) _get_osfhandle (giochannel_table[filedes[1]].fd),
- giochannel_table[1 - inherit_idx].chan);
+ TRACE_SUC ("read=0x%x/%p, write=0x%x/%p, channel=%p",
+ filedes[0],
+ (HANDLE) _get_osfhandle (giochannel_table[filedes[0]].fd),
+ filedes[1],
+ (HANDLE) _get_osfhandle (giochannel_table[filedes[1]].fd),
+ giochannel_table[1 - inherit_idx].chan);
+ return 0;
}
@@ -1066,7 +1067,7 @@ _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
return TRACE_SYSRES (-1);
}
- TRACE_LOG ("connect sockfd=0x%x", sockfd);
+ TRACE_LOG ("connect socket fd=%d", sockfd);
res = connect (sockfd, addr, addrlen);
/* FIXME: Error ignored here. */
@@ -1081,5 +1082,7 @@ _gpgme_io_connect (int fd, struct sockaddr *addr, int addrlen)
return TRACE_SYSRES (-1);
}
- return TRACE_SUC ("");
+ TRACE_SUC ("");
+
+ return 0;
}
diff --git a/src/w32-io.c b/src/w32-io.c
index 919ca6f..c5c21f5 100644
--- a/src/w32-io.c
+++ b/src/w32-io.c
@@ -1389,6 +1389,7 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
int tmp_fd;
char *tmp_name;
const char *spawnhelper;
+ static int spawn_warning_shown = 0;
TRACE_BEG (DEBUG_SYSIO, "_gpgme_io_spawn", path,
"path=%s", path);
@@ -1456,36 +1457,40 @@ _gpgme_io_spawn (const char *path, char *const argv[], unsigned int flags,
up their installation this should also be properly communicated
as otherwise calls to gnupg will result in unsupported protocol
errors that do not explain a lot. */
- char *msg;
- gpgrt_asprintf (&msg, "gpgme-w32spawn.exe was not found in the "
- "detected installation directory of GpgME"
- "\n\t\"%s\"\n\n"
- "Crypto operations will not work.\n\n"
- "If you see this it indicates a problem "
- "with your installation.\n"
- "Please report the problem to your "
- "distributor of GpgME.\n\n"
- "Developer's Note: The install dir can be "
- "manually set with: gpgme_set_global_flag",
- _gpgme_get_inst_dir ());
- MessageBoxA (NULL, msg, "GpgME not installed correctly", MB_OK);
- gpgrt_free (msg);
+ if (!spawn_warning_shown)
+ {
+ char *msg;
+ gpgrt_asprintf (&msg, "gpgme-w32spawn.exe was not found in the "
+ "detected installation directory of GpgME"
+ "\n\t\"%s\"\n\n"
+ "Crypto operations will not work.\n\n"
+ "If you see this it indicates a problem "
+ "with your installation.\n"
+ "Please report the problem to your "
+ "distributor of GpgME.\n\n"
+ "Developer's Note: The install dir can be "
+ "manually set with: gpgme_set_global_flag",
+ _gpgme_get_inst_dir ());
+ MessageBoxA (NULL, msg, "GpgME not installed correctly", MB_OK);
+ gpgrt_free (msg);
+ spawn_warning_shown = 1;
+ }
gpg_err_set_errno (EIO);
close (tmp_fd);
DeleteFileA (tmp_name);
free (tmp_name);
return TRACE_SYSRES (-1);
}
- if (!CreateProcessA (spawnhelper,
- arg_string,
- &sec_attr, /* process security attributes */
- &sec_attr, /* thread security attributes */
- FALSE, /* inherit handles */
- cr_flags, /* creation flags */
- NULL, /* environment */
- NULL, /* use current drive/directory */
- &si, /* startup information */
- &pi)) /* returns process information */
+ if (!_gpgme_create_process_utf8 (spawnhelper,
+ arg_string,
+ &sec_attr, /* process security attributes */
+ &sec_attr, /* thread security attributes */
+ FALSE, /* inherit handles */
+ cr_flags, /* creation flags */
+ NULL, /* environment */
+ NULL, /* use current drive/directory */
+ &si, /* startup information */
+ &pi)) /* returns process information */
{
int lasterr = (int)GetLastError ();
TRACE_LOG ("CreateProcess failed: ec=%d", lasterr);
diff --git a/src/w32-util.c b/src/w32-util.c
index 9802d9c..8207676 100644
--- a/src/w32-util.c
+++ b/src/w32-util.c
@@ -168,6 +168,48 @@ wchar_to_utf8 (const wchar_t *string)
}
+/* Return a malloced wide char string from an UTF-8 encoded input
+ string STRING. Caller must free this value. On failure returns
+ NULL; caller may use GetLastError to get the actual error number.
+ Calling this function with STRING set to NULL is not defined. */
+static wchar_t *
+utf8_to_wchar (const char *string)
+{
+ int n;
+ wchar_t *result;
+
+
+ n = MultiByteToWideChar (CP_UTF8, 0, string, -1, NULL, 0);
+ if (n < 0)
+ return NULL;
+
+ result = (wchar_t *) malloc ((n+1) * sizeof *result);
+ if (!result)
+ return NULL;
+
+ n = MultiByteToWideChar (CP_UTF8, 0, string, -1, result, n);
+ if (n < 0)
+ {
+ free (result);
+ return NULL;
+ }
+ return result;
+}
+
+
+/* Same as utf8_to_wchar but calling it with NULL returns
+ NULL. So a return value of NULL only indicates failure
+ if STRING is not set to NULL. */
+static wchar_t *
+utf8_to_wchar0 (const char *string)
+{
+ if (!string)
+ return NULL;
+
+ return utf8_to_wchar (string);
+}
+
+
/* Replace all forward slashes by backslashes. */
static void
replace_slashes (char *string)
@@ -220,19 +262,19 @@ _gpgme_allow_set_foreground_window (pid_t pid)
if (!pid || pid == (pid_t)(-1))
{
- TRACE (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0,
+ TRACE (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", NULL,
"no action for pid %d", (int)pid);
}
else if (func)
{
int rc = func (pid);
- TRACE (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0,
+ TRACE (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", NULL,
"called for pid %d; result=%d", (int)pid, rc);
}
else
{
- TRACE (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", 0,
+ TRACE (DEBUG_ENGINE, "gpgme:AllowSetForegroundWindow", NULL,
"function not available");
}
#endif /* HAVE_ALLOW_SET_FOREGROUND_WINDOW */
@@ -268,13 +310,13 @@ _gpgme_w32_cancel_synchronous_io (HANDLE thread)
{
if (!func (thread) && GetLastError() != ERROR_NOT_FOUND)
{
- TRACE (DEBUG_ENGINE, "gpgme:CancelSynchronousIo", 0,
+ TRACE (DEBUG_ENGINE, "gpgme:CancelSynchronousIo", NULL,
"called for thread %p: ec=%d", thread, GetLastError ());
}
}
else
{
- TRACE (DEBUG_ENGINE, "gpgme:CancelSynchronousIo", 0,
+ TRACE (DEBUG_ENGINE, "gpgme:CancelSynchronousIo", NULL,
"function not available");
}
}
@@ -395,7 +437,7 @@ find_program_in_dir (const char *dir, const char *name)
if (!result)
return NULL;
- if (access (result, F_OK))
+ if (_gpgme_access (result, F_OK))
{
free (result);
return NULL;
@@ -408,7 +450,7 @@ find_program_in_dir (const char *dir, const char *name)
static char *
find_program_at_standard_place (const char *name)
{
- char path[MAX_PATH];
+ wchar_t path[MAX_PATH];
char *result = NULL;
/* See https://wiki.tcl-lang.org/page/Getting+Windows+%22special+folders%22+with+Ffidl for details on compatibility.
@@ -416,20 +458,24 @@ find_program_at_standard_place (const char *name)
We First try the generic place and then fallback to the x86
(i.e. 32 bit) place. This will prefer a 64 bit of the program
over a 32 bit version on 64 bit Windows if installed. */
- if (SHGetSpecialFolderPathA (NULL, path, CSIDL_PROGRAM_FILES, 0))
+ if (SHGetSpecialFolderPathW (NULL, path, CSIDL_PROGRAM_FILES, 0))
{
- result = _gpgme_strconcat (path, "\\", name, NULL);
- if (result && access (result, F_OK))
+ char *utf8_path = wchar_to_utf8 (path);
+ result = _gpgme_strconcat (utf8_path, "\\", name, NULL);
+ free (utf8_path);
+ if (result && _gpgme_access (result, F_OK))
{
free (result);
result = NULL;
}
}
if (!result
- && SHGetSpecialFolderPathA (NULL, path, CSIDL_PROGRAM_FILESX86, 0))
+ && SHGetSpecialFolderPathW (NULL, path, CSIDL_PROGRAM_FILESX86, 0))
{
- result = _gpgme_strconcat (path, "\\", name, NULL);
- if (result && access (result, F_OK))
+ char *utf8_path = wchar_to_utf8 (path);
+ result = _gpgme_strconcat (utf8_path, "\\", name, NULL);
+ free (utf8_path);
+ if (result && _gpgme_access (result, F_OK))
{
free (result);
result = NULL;
@@ -532,7 +578,7 @@ _gpgme_get_gpg_path (void)
/* 4. Print a debug message if not found. */
if (!gpg)
- _gpgme_debug (DEBUG_ENGINE, -1, NULL, NULL, NULL,
+ _gpgme_debug (NULL, DEBUG_ENGINE, -1, NULL, NULL, NULL,
"_gpgme_get_gpg_path: '%s' not found", name);
return gpg;
@@ -608,7 +654,7 @@ _gpgme_get_gpgconf_path (void)
/* 5. Print a debug message if not found. */
if (!gpgconf)
- _gpgme_debug (DEBUG_ENGINE, -1, NULL, NULL, NULL,
+ _gpgme_debug (NULL, DEBUG_ENGINE, -1, NULL, NULL, NULL,
"_gpgme_get_gpgconf_path: '%s' not found",name);
return gpgconf;
@@ -781,6 +827,73 @@ _gpgme_mkstemp (int *fd, char **name)
}
+/* Like access but using windows _waccess */
+int
+_gpgme_access (const char *path, int mode)
+{
+ wchar_t *u16 = utf8_to_wchar0 (path);
+ int r = _waccess (u16, mode);
+
+ free(u16);
+ return r;
+}
+
+
+/* Like CreateProcessA but mapping the arguments to wchar API */
+int _gpgme_create_process_utf8 (const char *application_name_utf8,
+ char *command_line_utf8,
+ LPSECURITY_ATTRIBUTES lpProcessAttributes,
+ LPSECURITY_ATTRIBUTES lpThreadAttributes,
+ BOOL bInheritHandles,
+ DWORD dwCreationFlags,
+ void *lpEnvironment,
+ char *working_directory_utf8,
+ LPSTARTUPINFOA si,
+ LPPROCESS_INFORMATION lpProcessInformation)
+{
+ BOOL ret;
+ wchar_t *application_name = utf8_to_wchar0 (application_name_utf8);
+ wchar_t *command_line = utf8_to_wchar0 (command_line_utf8);
+ wchar_t *working_directory = utf8_to_wchar0 (working_directory_utf8);
+
+ STARTUPINFOW siw;
+ memset (&siw, 0, sizeof siw);
+ if (si)
+ {
+ siw.cb = sizeof (siw);
+ siw.dwFlags = si->dwFlags;
+ siw.wShowWindow = si->wShowWindow;
+ siw.hStdInput = si->hStdInput;
+ siw.hStdOutput = si->hStdOutput;
+ siw.hStdError = si->hStdError;
+ siw.dwX = si->dwX;
+ siw.dwY = si->dwY;
+ siw.dwXSize = si->dwXSize;
+ siw.dwYSize = si->dwYSize;
+ siw.dwXCountChars = si->dwXCountChars;
+ siw.dwYCountChars = si->dwYCountChars;
+ siw.dwFillAttribute = si->dwFillAttribute;
+ siw.lpDesktop = utf8_to_wchar0 (si->lpDesktop);
+ siw.lpTitle = utf8_to_wchar0 (si->lpTitle);
+ }
+
+ ret = CreateProcessW (application_name,
+ command_line,
+ lpProcessAttributes,
+ lpThreadAttributes,
+ bInheritHandles,
+ dwCreationFlags,
+ lpEnvironment,
+ working_directory,
+ si ? &siw : NULL,
+ lpProcessInformation);
+ free (siw.lpTitle);
+ free (siw.lpDesktop);
+ free (application_name);
+ free (command_line);
+ free (working_directory);
+ return ret;
+}
/* Entry point called by the DLL loader. */
#ifdef DLL_EXPORT
diff --git a/src/wait.c b/src/wait.c
index 1da9e93..c76f98f 100644
--- a/src/wait.c
+++ b/src/wait.c
@@ -137,7 +137,7 @@ _gpgme_add_io_cb (void *data, int fd, int dir, gpgme_io_cb_t fnc,
}
TRACE (DEBUG_CTX, "_gpgme_add_io_cb", ctx,
- "fd %d, dir=%d -> tag=%p", fd, dir, tag);
+ "fd=%d, dir=%d -> tag=%p", fd, dir, tag);
*r_tag = tag;
return 0;
@@ -179,7 +179,8 @@ _gpgme_remove_io_cb (void *data)
own event loops could compensate for that, but the external event
loops cannot. FIXME: We may still want to optimize this a bit when
we are called from our own event loops. So if CHECKED is 1, the
- check is skipped. */
+ check is skipped. FIXME: Give an example on how the status of other
+ fds can be influenced. */
gpgme_error_t
_gpgme_run_io_cb (struct io_select_fd_s *an_fds, int checked,
gpgme_error_t *op_err)
@@ -203,11 +204,13 @@ _gpgme_run_io_cb (struct io_select_fd_s *an_fds, int checked,
nr = _gpgme_io_select (&fds, 1, 1);
assert (nr <= 1);
if (nr < 0)
- return errno;
+ return gpg_error_from_syserror ();
else if (nr == 0)
- /* The status changed in the meantime, there is nothing left
- to do. */
- return 0;
+ {
+ /* The status changed in the meantime, there is nothing left
+ * to do. */
+ return 0;
+ }
}
TRACE (DEBUG_CTX, "_gpgme_run_io_cb", item, "handler (%p, %d)",
diff --git a/tests/gpg/Makefile.am b/tests/gpg/Makefile.am
index be84290..68222fa 100644
--- a/tests/gpg/Makefile.am
+++ b/tests/gpg/Makefile.am
@@ -59,6 +59,7 @@ private_keys = \
EXTRA_DIST = initial.test final.test \
pubdemo.asc secdemo.asc cipher-1.asc cipher-2.asc \
+ cipher-3.asc cipher-no-sig.asc \
geheim.txt pubkey-1.asc seckey-1.asc pinentry $(private_keys)
BUILT_SOURCES = gpg.conf gpg-agent.conf pubring-stamp \
diff --git a/tests/gpg/Makefile.in b/tests/gpg/Makefile.in
index fce0b85..5b1ec77 100644
--- a/tests/gpg/Makefile.in
+++ b/tests/gpg/Makefile.in
@@ -579,6 +579,7 @@ private_keys = \
EXTRA_DIST = initial.test final.test \
pubdemo.asc secdemo.asc cipher-1.asc cipher-2.asc \
+ cipher-3.asc cipher-no-sig.asc \
geheim.txt pubkey-1.asc seckey-1.asc pinentry $(private_keys)
BUILT_SOURCES = gpg.conf gpg-agent.conf pubring-stamp \
diff --git a/tests/gpg/cipher-3.asc b/tests/gpg/cipher-3.asc
new file mode 100644
index 0000000..10125b5
--- /dev/null
+++ b/tests/gpg/cipher-3.asc
@@ -0,0 +1,18 @@
+-----BEGIN PGP MESSAGE-----
+
+hQEOA++dwnahcsiBEAQApowkbz0idxMfLIT/a1G4QbZy77cWyaYOM/qh4lH4h7ra
+f19MZzbRViZYBtJeITszTFmulp31qrF++6gBy8tU7l8VfVfOtvwn1WlRvn2+bsns
+SicJAXtUiwIF8fbapSrkN6qpg2l+Fnx3SYV7+lh/7Fbf93DquwVEn+GwvgkyCQkD
+/j5UVU6PJ+k5E5JG7CMzLZHnpW1MtKUqKRG9sTW83SvvI5Wl9VMgjmdO6bZRPKPY
+Fr5udYJ1v4CIpVTb0O/TgAaiw7/Ak4SOg6jK0H0l1vuPPRjyzx43G4Y4lZ/EoCzT
+gU+0kiDD+O7xLDml5jPWTYov7LkhRMKI4Ugp37FzbF9p0sClAR1mY4WlAN23ku0W
+3bw54zSi7S6sh+i/uOmjZ+ziXr/S1pHFN2TNAkpH42o6VDhJ4vYk5KIB/mn/IoP1
+G57JUhLqpSHO7LF8X45eBLT3tKC3rIaILHXcXE5PeyMiOrl1snIClrpX+Qn6dRPX
+cYssX/0OQwE77bAla5QpZEy6YLOC4Ej4oTX5QU1rHNyLfvIDhwtphKMvbbssmsVI
+tAMvrGQudEwLSlvNlnJsI79jqmbi5W8PYr+ssvOvrd5hWoKti7EdglWddFNVVjZl
+ooJgztXUnPcufhaimQtNvX0Tj2hI8QwTxGPNO3aesPCQI0GbSUSqpvu6mlaQY2Qe
+NJNkfTH23fzoG61nFxIQ0HYbuBp0hJnGOXfHtMNxyjoAs1yEJbyQ20Lso/Vrsf3J
+sY7jkisFrURxTGQyBxxbnIO+nJPemOzV9Q/qo6Lj9yW0KagaffWogw/6X/xOvL4u
+aM8pI7NOi48b
+=mh2s
+-----END PGP MESSAGE-----
diff --git a/tests/gpg/cipher-no-sig.asc b/tests/gpg/cipher-no-sig.asc
new file mode 100644
index 0000000..47e399c
--- /dev/null
+++ b/tests/gpg/cipher-no-sig.asc
@@ -0,0 +1,13 @@
+-----BEGIN PGP MESSAGE-----
+
+hQEOA++dwnahcsiBEAP8CPkDWzZrcMJ1fyaPoQAhlx18p44+7vTF5zYCkMVT+i4E
+X/zcIreDYDpFNHzK2orNW/LGGNpMf3ZyDAYw7eDWgPwld2PJh2/ZfR/dXoGcAEd4
+vhbhA1D/ymQn+lxgSXNLMonxaoCqFK7cKStLCHfaPMbQ0S5GXU0IRwjfSn76oxwE
+AKzTa/54g8XNL9Dku9c/KATzopdDLzAzN6oBJlgRbjhc3T8mzTVJF91s+ow8dEC+
+0U2GBwkL5ZOKTFelSO3dBsH7RBNbYiJA+DbZ6nZia2KAtlLgyr4Z5vGh9bmltJl/
+1qdIdRF/La3wA/6bL10YMYF+7IJ9DiuDi/NRmSsO/ZWt0nMBXnzOeyLgxdbW1RS1
+NPWzKC/4RqvnWJmm7W3p0JG8v2ad+97O5KsOcqJnaKJOY0jKXeORrhih2DG32rpe
+4bhskRTIBJBaPtPnRGevlxardQi+Spbd75yqccCLjkQF0HhCLiHW9SJiLSMdnY1U
+U9th3ZR5
+=uvfB
+-----END PGP MESSAGE-----
diff --git a/tests/run-import.c b/tests/run-import.c
index f464eb1..36d9a0d 100644
--- a/tests/run-import.c
+++ b/tests/run-import.c
@@ -44,6 +44,8 @@ show_usage (int ex)
fputs ("usage: " PGM " [options] FILENAMEs\n\n"
"Options:\n"
" --verbose run in verbose mode\n"
+ " --openpgp use the OpenPGP protocol (default)\n"
+ " --cms use the CMS protocol\n"
" --url import from given URLs\n"
" -0 URLs are delimited by a nul\n"
, stderr);
@@ -60,6 +62,7 @@ main (int argc, char **argv)
int nul_mode = 0;
gpgme_import_result_t impres;
gpgme_data_t data;
+ gpgme_protocol_t protocol = GPGME_PROTOCOL_OpenPGP;
if (argc)
{ argc--; argv++; }
@@ -88,6 +91,16 @@ main (int argc, char **argv)
nul_mode = 1;
argc--; argv++;
}
+ else if (!strcmp (*argv, "--openpgp"))
+ {
+ protocol = GPGME_PROTOCOL_OpenPGP;
+ argc--; argv++;
+ }
+ else if (!strcmp (*argv, "--cms"))
+ {
+ protocol = GPGME_PROTOCOL_CMS;
+ argc--; argv++;
+ }
else if (!strncmp (*argv, "--", 2))
show_usage (1);
@@ -96,11 +109,11 @@ main (int argc, char **argv)
if (!argc)
show_usage (1);
- init_gpgme (GPGME_PROTOCOL_OpenPGP);
+ init_gpgme (protocol);
err = gpgme_new (&ctx);
fail_if_err (err);
- gpgme_set_protocol (ctx, GPGME_PROTOCOL_OpenPGP);
+ gpgme_set_protocol (ctx, protocol);
for (; argc; argc--, argv++)
{
diff --git a/tests/run-threaded.c b/tests/run-threaded.c
index 02524b2..1fe4f19 100644
--- a/tests/run-threaded.c
+++ b/tests/run-threaded.c
@@ -56,12 +56,14 @@ static int mem_only;
static void
create_thread (THREAD_RET (*func) (void *), void *arg)
{
- running_threads++;
- if (CloseHandle (CreateThread (NULL, 0, func, arg, 0, NULL)))
+ HANDLE hd = CreateThread (NULL, 0, func, arg, 0, NULL);
+ if (hd == INVALID_HANDLE_VALUE)
{
fprintf (stderr, "Failed to create thread!\n");
exit (1);
}
+ running_threads++;
+ CloseHandle (hd);
}
#else
@@ -124,6 +126,7 @@ show_usage (int ex)
"Options:\n"
" --verbose run in verbose mode\n"
" --no-list do not do keylistings\n"
+ " --allow-del allow to delete keys after import\n"
" --data-type mem function to use one of:\n"
" 1: fstream\n"
" 2: posix fd\n"
@@ -171,6 +174,8 @@ typedef struct keylist_args_s *keylist_args_t;
static volatile int keylists;
+static int allow_del;
+
static THREAD_RET
do_keylist (void *keylist_args)
{
@@ -279,6 +284,9 @@ random_data_new (const char *fname)
{
data_t ret = calloc (1, sizeof (struct data_s));
int data_rand;
+
+ ret->fd = -1;
+
if (data_type)
{
data_rand = data_type;
@@ -352,7 +360,8 @@ random_data_close (data_t data)
{
gpgme_data_release (data->dh);
}
- if (data->fd)
+
+ if (data->fd != -1)
{
close (data->fd);
}
@@ -376,7 +385,7 @@ verify (const char *fname, gpgme_protocol_t proto)
{
gpgme_ctx_t ctx;
gpgme_error_t err;
- gpgme_data_t out;
+ gpgme_data_t output;
char *msg;
size_t msg_len;
data_t data = random_data_new (fname);
@@ -384,7 +393,7 @@ verify (const char *fname, gpgme_protocol_t proto)
log ("Starting verify on: %s with protocol %s", fname,
proto == GPGME_PROTOCOL_CMS ? "CMS" : "OpenPGP");
- gpgme_data_new (&out);
+ gpgme_data_new (&output);
err = gpgme_new (&ctx);
fail_if_err (err);
@@ -392,13 +401,13 @@ verify (const char *fname, gpgme_protocol_t proto)
err = gpgme_set_protocol (ctx, proto);
fail_if_err (err);
- err = gpgme_op_verify (ctx, data->dh, NULL, out);
+ err = gpgme_op_verify (ctx, data->dh, NULL, output);
out ("Data: %p, %i %p %p %p", data->dh,
data->fd, data->file, data->stream,
data->mem);
fail_if_err (err);
- msg = gpgme_data_release_and_get_mem (out, &msg_len);
+ msg = gpgme_data_release_and_get_mem (output, &msg_len);
if (msg_len)
{
@@ -421,14 +430,14 @@ decrypt (const char *fname, gpgme_protocol_t proto)
{
gpgme_ctx_t ctx;
gpgme_error_t err;
- gpgme_data_t out;
+ gpgme_data_t output;
char *msg;
size_t msg_len;
data_t data = random_data_new (fname);
log ("Starting decrypt on: %s", fname);
- gpgme_data_new (&out);
+ gpgme_data_new (&output);
err = gpgme_new (&ctx);
fail_if_err (err);
@@ -436,12 +445,12 @@ decrypt (const char *fname, gpgme_protocol_t proto)
err = gpgme_set_protocol (ctx, proto);
fail_if_err (err);
- err = gpgme_op_decrypt (ctx, data->dh, out);
+ err = gpgme_op_decrypt (ctx, data->dh, output);
fail_if_err (err);
gpgme_release (ctx);
- msg = gpgme_data_release_and_get_mem (out, &msg_len);
+ msg = gpgme_data_release_and_get_mem (output, &msg_len);
if (msg_len)
{
@@ -451,6 +460,98 @@ decrypt (const char *fname, gpgme_protocol_t proto)
random_data_close (data);
}
+void
+delete_key (gpgme_key_t key)
+{
+ gpgme_ctx_t ctx;
+ gpgme_error_t err;
+
+ err = gpgme_new (&ctx);
+ fail_if_err (err);
+
+ gpgme_set_protocol (ctx, key->protocol);
+
+ err = gpgme_op_delete (ctx, key, 0);
+ fail_if_err (err);
+
+ gpgme_release (ctx);
+}
+
+/* Get the key for the fpr in protocol and call delete_key
+ on it. */
+void
+delete_fpr (const char *fpr, gpgme_protocol_t proto)
+{
+ gpgme_ctx_t ctx;
+ gpgme_error_t err;
+ gpgme_key_t key = NULL;
+
+ err = gpgme_new (&ctx);
+ fail_if_err (err);
+
+ gpgme_set_protocol (ctx, proto);
+
+ err = gpgme_get_key (ctx, fpr, &key, 0);
+ fail_if_err (err);
+
+ if (!key)
+ {
+ errpoint;
+ }
+ delete_key (key);
+
+ log ("deleted key %s", fpr);
+ gpgme_key_unref (key);
+ gpgme_release (ctx);
+}
+
+void
+delete_impres (gpgme_import_result_t r, gpgme_protocol_t proto)
+{
+ gpgme_import_status_t st;
+
+ if (!r)
+ {
+ errpoint;
+ }
+
+ for (st=r->imports; st; st = st->next)
+ {
+ delete_fpr (st->fpr, proto);
+ }
+}
+
+void
+import (const char *fname, gpgme_protocol_t proto)
+{
+ gpgme_ctx_t ctx;
+ gpgme_error_t err;
+ data_t data = random_data_new (fname);
+
+ log ("Starting import on: %s", fname);
+
+ err = gpgme_new (&ctx);
+ fail_if_err (err);
+
+ err = gpgme_set_protocol (ctx, proto);
+ fail_if_err (err);
+
+ gpgme_set_offline (ctx, 1);
+
+ err = gpgme_op_import (ctx, data->dh);
+ fail_if_err (err);
+
+ if (allow_del)
+ {
+ delete_impres (gpgme_op_import_result (ctx), proto);
+ }
+
+ gpgme_release (ctx);
+
+ log ("Import completed.");
+
+ random_data_close (data);
+}
static THREAD_RET
do_data_op (void *file_name)
@@ -500,10 +601,20 @@ do_data_op (void *file_name)
decrypt (fname, GPGME_PROTOCOL_CMS);
break;
}
+ case GPGME_DATA_TYPE_PGP_KEY:
+ {
+ import (fname, GPGME_PROTOCOL_OpenPGP);
+ break;
+ }
+ case GPGME_DATA_TYPE_X509_CERT:
+ {
+ import (fname, GPGME_PROTOCOL_CMS);
+ break;
+ }
default:
{
- fprintf (stderr, "Unhandled data type 0x%x for '%s'", type, fname);
- exit(1);
+ out ("Unhandled data type 0x%x for '%s'\n", type, fname);
+ errpoint;
}
}
@@ -569,6 +680,11 @@ main (int argc, char **argv)
no_list = 1;
argc--; argv++;
}
+ else if (!strcmp (*argv, "--allow-del"))
+ {
+ allow_del = 1;
+ argc--; argv++;
+ }
else if (!strcmp (*argv, "--mem-only"))
{
if (data_type)