diff --git a/doc/gpgme-python-howto.texi b/doc/gpgme-python-howto.texi index 2e13d872..69ae9599 100644 --- a/doc/gpgme-python-howto.texi +++ b/doc/gpgme-python-howto.texi @@ -64,6 +64,10 @@ GPGME Python bindings installation * Installation:: * Known Issues:: +Requirements + +* Recommended Additions:: + Installation * Installing GPGME:: @@ -71,6 +75,7 @@ Installation Known Issues * Breaking Builds:: +* Reinstalling Responsibly:: * Multiple installations:: * Won't Work With Windows:: * CFFI is the Bestâ„¢ and GPGME should use it instead of SWIG:: @@ -101,6 +106,7 @@ Exporting keys * Exporting public keys:: * Exporting secret keys:: +* Sending public keys to the SKS Keyservers:: Basic Functions @@ -445,7 +451,8 @@ The GPGME Python bindings only have three requirements: @enumerate @item A suitable version of Python 2 or Python 3. With Python 2 that -means Python 2.7 and with Python 3 that means Python 3.4 or higher. +means CPython 2.7 and with Python 3 that means CPython 3.4 or +higher. @item @uref{https://www.swig.org, SWIG}. @item @@ -453,6 +460,33 @@ GPGME itself. Which also means that all of GPGME's dependencies must be installed too. @end enumerate +@menu +* Recommended Additions:: +@end menu + +@node Recommended Additions +@subsection Recommended Additions + +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. + +@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: @uref{http://docs.python-requests.org/en/latest/index.html, Requests}, @uref{http://cython.org/, Cython} and @uref{https://github.com/Selfnet/hkp4py, hkp4py}. Chances are +quite high that at least the first one and maybe two of those will +already be installed. +@end enumerate + @node Installation @section Installation @@ -495,6 +529,7 @@ they be encountered. @menu * Breaking Builds:: +* Reinstalling Responsibly:: * Multiple installations:: * Won't Work With Windows:: * CFFI is the Bestâ„¢ and GPGME should use it instead of SWIG:: @@ -559,10 +594,34 @@ 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 -howto-export-public-keybindings separately with alternative @samp{configure} steps for GPGME using +bindings separately with alternative @samp{configure} steps for GPGME using the @samp{--enable-languages=$LANGUAGE} option. @end enumerate +@node Reinstalling Responsibly +@subsection Reinstalling Responsibly + +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 @samp{site-packages} directory for the version of Python in +question and removing the @samp{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. + @node Multiple installations @subsection Multiple installations @@ -1427,6 +1486,7 @@ third is for exporting secret keys. @menu * Exporting public keys:: * Exporting secret keys:: +* Sending public keys to the SKS Keyservers:: @end menu @node Exporting public keys @@ -1586,12 +1646,26 @@ else: logrus = input("Enter the UID matching the secret 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) +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 -elif os.path.exists(homedir) is True: + +if homedir is not None: c.home_dir = homedir else: pass @@ -1656,12 +1730,26 @@ else: logrus = input("Enter the UID matching the secret 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) +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 -elif os.path.exists(homedir) is True: + +if homedir is not None: c.home_dir = homedir else: pass @@ -1712,6 +1800,70 @@ else: pass @end example +@node Sending public keys to the SKS Keyservers +@subsection Sending public keys to the SKS Keyservers + +As with the previous section on importing keys, the @samp{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. + +@example +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 example + +An expanded version of this script with additional functions for +specifying an alternative homedir location is in the examples +directory as @samp{send-key-to-keyserver.py}. + +The @samp{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. + @node Basic Functions @chapter Basic Functions diff --git a/lang/python/docs/gpgme-python-howto.org b/lang/python/docs/gpgme-python-howto.org index 6c599cb0..90e133b1 100644 --- a/lang/python/docs/gpgme-python-howto.org +++ b/lang/python/docs/gpgme-python-howto.org @@ -293,12 +293,34 @@ section for further details. The GPGME Python bindings only have three requirements: 1. A suitable version of Python 2 or Python 3. With Python 2 that - means Python 2.7 and with Python 3 that means Python 3.4 or higher. + means CPython 2.7 and with Python 3 that means CPython 3.4 or + higher. 2. [[https://www.swig.org][SWIG]]. 3. GPGME itself. Which also means that all of GPGME's dependencies must be installed too. +*** Recommended Additions + :PROPERTIES: + :CUSTOM_ID: gpgme-python-recommendations + :END: + +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. + +1. If possible, use Python 3 instead of 2. +2. 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. +3. If possible add the following Python modules which are not part of + the standard library: [[http://docs.python-requests.org/en/latest/index.html][Requests]], [[http://cython.org/][Cython]] and [[https://github.com/Selfnet/hkp4py][hkp4py]]. Chances are + quite high that at least the first one and maybe two of those will + already be installed. + + ** Installation :PROPERTIES: :CUSTOM_ID: installation @@ -398,10 +420,37 @@ 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 -howto-export-public-keybindings separately with alternative =configure= steps for GPGME using +bindings separately with alternative =configure= steps for GPGME using the =--enable-languages=$LANGUAGE= option. +*** Reinstalling Responsibly + :PROPERTIES: + :CUSTOM_ID: snafu-lessons-for-the-lazy + :END: + +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 =site-packages= directory for the version of Python in +question and removing the =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. + + *** Multiple installations :PROPERTIES: :CUSTOM_ID: snafu-the-full-monty @@ -1446,12 +1495,26 @@ else: logrus = input("Enter the UID matching the secret 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) +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 -elif os.path.exists(homedir) is True: + +if homedir is not None: c.home_dir = homedir else: pass @@ -1516,12 +1579,26 @@ else: logrus = input("Enter the UID matching the secret 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) +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 -elif os.path.exists(homedir) is True: + +if homedir is not None: c.home_dir = homedir else: pass @@ -1573,6 +1650,73 @@ else: #+END_SRC +*** Sending public keys to the SKS Keyservers + :PROPERTIES: + :CUSTOM_ID: howto-send-public-key + :END: + +As with the previous section on importing keys, the =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_SRC python -i +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_SRC + +An expanded version of this script with additional functions for +specifying an alternative homedir location is in the examples +directory as =send-key-to-keyserver.py=. + +The =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. + + * Basic Functions :PROPERTIES: :CUSTOM_ID: howto-the-basics diff --git a/lang/python/examples/howto/export-key.py b/lang/python/examples/howto/export-key.py index 80768fe7..c17f2471 100755 --- a/lang/python/examples/howto/export-key.py +++ b/lang/python/examples/howto/export-key.py @@ -51,12 +51,26 @@ else: 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) +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 -elif os.path.exists(homedir) is True: + +if homedir is not None: c.home_dir = homedir else: pass diff --git a/lang/python/examples/howto/export-minimised-key.py b/lang/python/examples/howto/export-minimised-key.py index 9d5f8488..a5a453ce 100755 --- a/lang/python/examples/howto/export-minimised-key.py +++ b/lang/python/examples/howto/export-minimised-key.py @@ -51,12 +51,26 @@ else: 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) +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 -elif os.path.exists(homedir) is True: + +if homedir is not None: c.home_dir = homedir else: pass diff --git a/lang/python/examples/howto/export-secret-key.py b/lang/python/examples/howto/export-secret-key.py index ccc9f45d..e7d4e3af 100755 --- a/lang/python/examples/howto/export-secret-key.py +++ b/lang/python/examples/howto/export-secret-key.py @@ -54,12 +54,26 @@ else: logrus = input("Enter the UID matching the secret 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) +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 -elif os.path.exists(homedir) is True: + +if homedir is not None: c.home_dir = homedir else: pass diff --git a/lang/python/examples/howto/export-secret-keys.py b/lang/python/examples/howto/export-secret-keys.py index f2f1ccde..f0fddc6d 100755 --- a/lang/python/examples/howto/export-secret-keys.py +++ b/lang/python/examples/howto/export-secret-keys.py @@ -63,12 +63,26 @@ else: logrus = input("Enter the UID matching the secret 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) +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 -elif os.path.exists(homedir) is True: + +if homedir is not None: c.home_dir = homedir else: pass diff --git a/lang/python/examples/howto/send-key-to-keyserver.py b/lang/python/examples/howto/send-key-to-keyserver.py new file mode 100755 index 00000000..3541b198 --- /dev/null +++ b/lang/python/examples/howto/send-key-to-keyserver.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +from __future__ import absolute_import, division, unicode_literals + +import gpg +import hkp4py +import os.path +import sys + +# Copyright (C) 2018 Ben McGinnes +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU General Public License as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) any later +# version. +# +# This program is free software; you can redistribute it and/or modify it under +# the terms of the GNU Lesser General Public License as published by the Free +# Software Foundation; either version 2.1 of the License, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +# FOR A PARTICULAR PURPOSE. See the GNU General Public License and the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU General Public License and the GNU +# Lesser General Public along with this program; if not, see +# . + +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. + +The default is to send all keys if there is no pattern or search term. +""") + +c = gpg.Context(armor=True) +server = hkp4py.KeyServer("hkps://hkps.pool.sks-keyservers.net") + +if len(sys.argv) >= 3: + logrus = sys.argv[1] + homedir = sys.argv[2] +elif len(sys.argv) == 2: + logrus = sys.argv[1] + homedir = input("Enter the GPG configuration directory path (optional): ") +else: + logrus = input("Enter the UID matching the 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 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