Hello community,
here is the log from the commit of package python-gnupg for openSUSE:Factory checked in at 2015-05-10 10:46:22
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-gnupg (Old)
and /work/SRC/openSUSE:Factory/.python-gnupg.new (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-gnupg"
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-gnupg/python-gnupg.changes 2013-12-16 07:09:00.000000000 +0100
+++ /work/SRC/openSUSE:Factory/.python-gnupg.new/python-gnupg.changes 2015-05-10 10:46:24.000000000 +0200
@@ -1,0 +2,44 @@
+Wed May 6 12:08:06 UTC 2015 - benoit.monin@gmx.fr
+
+- update to version 0.3.7:
+ * Added an output keyword parameter to the sign and sign_file
+ methods, to allow writing the signature to a file
+ * Allowed specifying True for the sign keyword parameter, which
+ allows use of the default key for signing and avoids having to
+ specify a key id when it's desired to use the default
+ * Used a uniform approach with subprocess on Windows and POSIX:
+ shell=True is not used on either
+ * When signing/verifying, the status is updated to reflect any
+ expired or revoked keys or signatures
+ * Handled 'NOTATION_NAME' and 'NOTATION_DATA' during verification
+ * Fixed #1, #16, #18, #20: Quoting approach changed, since now
+ shell=False
+ * Fixed #14: Handled 'NEED_PASSPHRASE_PIN' message
+ * Fixed #8: Added a scan_keys method to allow scanning of keys
+ without the need to import into a keyring
+ * Fixed #5: Added '0x' prefix when searching for keys
+ * Fixed #4: Handled 'PROGRESS' message during encryption
+ * Fixed #3: Changed default encoding to Latin-1
+ * Fixed #2: Raised ValueError if no recipients were specified for
+ an asymmetric encryption request
+ * Handled 'UNEXPECTED' message during verification
+ * Replaced old range(len(X)) idiom with enumerate()
+ * Refactored ListKeys / SearchKeys classes to maximise use of
+ common functions
+ * Fixed GC94: Added export-minimal and armor options when
+ exporting keys
+- additional changes from version 0.3.6:
+ * Fixed GC82: Enabled fast random tests on gpg as well as gpg2
+ * Fixed GC85: Avoided deleting temporary file to preserve its
+ permissions
+ * Fixed GC87: Avoided writing passphrase to log
+ * Fixed GC95: Added verify_data() method to allow verification of
+ signatures in memory
+ * Fixed GC96: Regularised end-of-line characters
+ * Fixed GC98: Rectified problems with earlier fix for shell
+ injection
+- point the source URL to pypi
+- fix end of line of README.rst
+- rename LICENSE.txt and README.rst to follow upstream
+
+-------------------------------------------------------------------
Old:
----
python-gnupg-0.3.5.tar.gz
New:
----
python-gnupg-0.3.7.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-gnupg.spec ++++++
--- /var/tmp/diff_new_pack.Exeb3E/_old 2015-05-10 10:46:24.000000000 +0200
+++ /var/tmp/diff_new_pack.Exeb3E/_new 2015-05-10 10:46:24.000000000 +0200
@@ -1,7 +1,7 @@
#
# spec file for package python-gnupg
#
-# Copyright (c) 2013 SUSE LINUX Products GmbH, Nuernberg, Germany.
+# Copyright (c) 2015 SUSE LINUX GmbH, Nuernberg, Germany.
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
@@ -17,13 +17,13 @@
Name: python-gnupg
-Version: 0.3.5
+Version: 0.3.7
Release: 0
Url: http://code.google.com/p/python-gnupg/
Summary: A wrapper for the Gnu Privacy Guard (GPG or GnuPG)
License: BSD-3-Clause
Group: Development/Languages/Python
-Source: python-gnupg-%{version}.tar.gz
+Source: https://pypi.python.org/packages/source/p/python-gnupg/python-gnupg-%{version}.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-build
BuildRequires: python-devel
%if 0%{?suse_version} && 0%{?suse_version} <= 1110
@@ -39,6 +39,8 @@
%prep
%setup -q -n python-gnupg-%{version}
+# fix end of line
+sed -i 's/\r//' README.rst
%build
python setup.py build
@@ -48,7 +50,7 @@
%files
%defattr(-,root,root,-)
-%doc LICENSE README
+%doc LICENSE.txt README.rst
%{python_sitelib}/*
%changelog
++++++ python-gnupg-0.3.5.tar.gz -> python-gnupg-0.3.7.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-gnupg-0.3.5/LICENSE new/python-gnupg-0.3.7/LICENSE
--- old/python-gnupg-0.3.5/LICENSE 2013-02-08 15:09:00.000000000 +0100
+++ new/python-gnupg-0.3.7/LICENSE 1970-01-01 01:00:00.000000000 +0100
@@ -1,26 +0,0 @@
-Copyright (c) 2008-2013 by Vinay Sajip.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice,
- this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice,
- this list of conditions and the following disclaimer in the documentation
- and/or other materials provided with the distribution.
- * The name(s) of the copyright holder(s) may not be used to endorse or
- promote products derived from this software without specific prior
- written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) "AS IS" AND ANY EXPRESS OR
-IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
-EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
-INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
-OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-gnupg-0.3.5/LICENSE.txt new/python-gnupg-0.3.7/LICENSE.txt
--- old/python-gnupg-0.3.5/LICENSE.txt 1970-01-01 01:00:00.000000000 +0100
+++ new/python-gnupg-0.3.7/LICENSE.txt 2014-02-05 23:03:58.000000000 +0100
@@ -0,0 +1,26 @@
+Copyright (c) 2008-2014 by Vinay Sajip.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ * The name(s) of the copyright holder(s) may not be used to endorse or
+ promote products derived from this software without specific prior
+ written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) "AS IS" AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE COPYRIGHT HOLDER(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-gnupg-0.3.5/PKG-INFO new/python-gnupg-0.3.7/PKG-INFO
--- old/python-gnupg-0.3.5/PKG-INFO 2013-08-30 19:11:27.000000000 +0200
+++ new/python-gnupg-0.3.7/PKG-INFO 2014-12-07 20:45:57.000000000 +0100
@@ -1,12 +1,12 @@
Metadata-Version: 1.0
Name: python-gnupg
-Version: 0.3.5
+Version: 0.3.7
Summary: A wrapper for the Gnu Privacy Guard (GPG or GnuPG)
Home-page: http://packages.python.org/python-gnupg/index.html
Author: Vinay Sajip
Author-email: vinay_sajip@red-dove.com
-License: Copyright (C) 2008-2013 by Vinay Sajip. All Rights Reserved. See LICENSE for license.
-Download-URL: http://python-gnupg.googlecode.com/files/python-gnupg-0.3.5.tar.gz
+License: Copyright (C) 2008-2014 by Vinay Sajip. All Rights Reserved. See LICENSE.txt for license.
+Download-URL: https://pypi.python.org/packages/source/p/python-gnupg/python-gnupg-0.3.7.ta...
Description: This module allows easy access to GnuPG's key management, encryption and signature functionality from Python programs. It is intended for use with Python 2.4 or greater.
Platform: No particular restrictions
Classifier: Development Status :: 5 - Production/Stable
@@ -19,8 +19,8 @@
Classifier: Programming Language :: Python :: 2.5
Classifier: Programming Language :: Python :: 2.6
Classifier: Programming Language :: Python :: 2.7
-Classifier: Programming Language :: Python :: 3.0
-Classifier: Programming Language :: Python :: 3.1
Classifier: Programming Language :: Python :: 3.2
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Programming Language :: Python :: 3.4
Classifier: Operating System :: OS Independent
Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-gnupg-0.3.5/README new/python-gnupg-0.3.7/README
--- old/python-gnupg-0.3.5/README 2009-07-03 23:39:13.000000000 +0200
+++ new/python-gnupg-0.3.7/README 1970-01-01 01:00:00.000000000 +0100
@@ -1,5 +0,0 @@
-To install this package from a source distribution, do the following.
-
-1. Extract all the files in the distribution archive to some directory on your system.
-2. In that directory, run "python setup.py install".
-3. Optionally, run "python test_gnupg.py" to ensure that the package is working as expected.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-gnupg-0.3.5/README.rst new/python-gnupg-0.3.7/README.rst
--- old/python-gnupg-0.3.5/README.rst 1970-01-01 01:00:00.000000000 +0100
+++ new/python-gnupg-0.3.7/README.rst 2014-12-07 18:50:10.000000000 +0100
@@ -0,0 +1,276 @@
+What is it?
+===========
+
+The GNU Privacy Guard (gpg, or gpg.exe on Windows) is a command-line program
+which provides support for programmatic access via spawning a separate process
+to run it and then communicating with that process from your program.
+
+This project, ``python-gnupg``, implements a Python library which takes care
+of the internal details and allows its users to generate and manage keys,
+encrypt and decrypt data, and sign and verify messages.
+
+Installation
+============
+
+Installing from PyPI
+--------------------
+
+You can install this package from the Python Package Index (pyPI) by running::
+
+ pip install python-gnupg
+
+
+Installing from a source distribution archive
+---------------------------------------------
+To install this package from a source distribution archive, do the following:
+
+1. Extract all the files in the distribution archive to some directory on your
+ system.
+2. In that directory, run ``python setup.py install``.
+3. Optionally, run ``python test_gnupg.py`` to ensure that the package is
+ working as expected.
+
+Credits
+=======
+
+* The developers of the GNU Privacy Guard.
+* The original version of this module was developed by Andrew Kuchling.
+* It was improved by Richard Jones.
+* It was further improved by Steve Traugott.
+
+The present incarnation, based on the earlier versions, uses the ``subprocess``
+module and so works on Windows as well as Unix/Linux platforms. It's not,
+however, 100% backwards-compatible with earlier incarnations.
+
+Change log
+==========
+
+N.B: GCnn refers to an issue nn on Google Code.
+
+0.3.8 (future)
+--------------
+
+Released: Not yet
+
+0.3.7
+-----
+
+Released: 2014-12-07
+
+* Added an ``output`` keyword parameter to the ``sign`` and
+ ``sign_file`` methods, to allow writing the signature to a file.
+ Thanks to Jannis Leidel for the patch.
+
+* Allowed specifying ``True`` for the ``sign`` keyword parameter,
+ which allows use of the default key for signing and avoids having to
+ specify a key id when it's desired to use the default. Thanks to
+ Fabian Beutel for the patch.
+
+* Used a uniform approach with subprocess on Windows and POSIX: shell=True
+ is not used on either.
+
+* When signing/verifying, the status is updated to reflect any expired or
+ revoked keys or signatures.
+
+* Handled 'NOTATION_NAME' and 'NOTATION_DATA' during verification.
+
+* Fixed #1, #16, #18, #20: Quoting approach changed, since now shell=False.
+
+* Fixed #14: Handled 'NEED_PASSPHRASE_PIN' message.
+
+* Fixed #8: Added a scan_keys method to allow scanning of keys without the
+ need to import into a keyring. Thanks to Venzen Khaosan for the suggestion.
+
+* Fixed #5: Added '0x' prefix when searching for keys. Thanks to Aaron Toponce
+ for the report.
+
+* Fixed #4: Handled 'PROGRESS' message during encryption. Thanks to Daniel
+ Mills for the report.
+
+* Fixed #3: Changed default encoding to Latin-1.
+
+* Fixed #2: Raised ValueError if no recipients were specified
+ for an asymmetric encryption request.
+
+* Handled 'UNEXPECTED' message during verification. Thanks to
+ David Andersen for the patch.
+
+* Replaced old range(len(X)) idiom with enumerate().
+
+* Refactored ``ListKeys`` / ``SearchKeys`` classes to maximise use of common
+ functions.
+
+* Fixed GC94: Added ``export-minimal`` and ``armor`` options when exporting
+ keys. This addition was inadvertently left out of 0.3.6.
+
+0.3.6
+-----
+
+Released: 2014-02-05
+
+* Fixed GC82: Enabled fast random tests on gpg as well as gpg2.
+* Fixed GC85: Avoided deleting temporary file to preserve its permissions.
+* Fixed GC87: Avoided writing passphrase to log.
+* Fixed GC95: Added ``verify_data()`` method to allow verification of
+ signatures in memory.
+* Fixed GC96: Regularised end-of-line characters.
+* Fixed GC98: Rectified problems with earlier fix for shell injection.
+
+0.3.5
+-----
+
+Released: 2013-08-30
+
+* Added improved shell quoting to guard against shell injection.
+* Fixed GC76: Added ``search_keys()`` and ``send_keys()`` methods.
+* Fixed GC77: Allowed specifying a symmetric cipher algorithm.
+* Fixed GC78: Fell back to utf-8 encoding when no other could be determined.
+* Fixed GC79: Default key length is now 2048 bits.
+* Fixed GC80: Removed the Name-Comment default in key generation.
+
+0.3.4
+-----
+
+Released: 2013-06-05
+
+* Fixed GC65: Fixed encoding exception when getting version.
+* Fixed GC66: Now accepts sets and frozensets where appropriate.
+* Fixed GC67: Hash algorithm now captured in sign result.
+* Fixed GC68: Added support for ``--secret-keyring``.
+* Fixed GC70: Added support for multiple keyrings.
+
+0.3.3
+-----
+
+Released: 2013-03-11
+
+* Fixed GC57: Handled control characters in ``list_keys()``.
+* Fixed GC61: Enabled fast random for testing.
+* Fixed GC62: Handled ``KEYEXPIRED`` status.
+* Fixed GC63: Handled ``NO_SGNR`` status.
+
+0.3.2
+-----
+
+Released: 2013-01-17
+
+* Fixed GC56: Disallowed blank values in key generation.
+* Fixed GC57: Handled colons and other characters in ``list_keys()``.
+* Fixed GC59/GC60: Handled ``INV_SGNR`` status during verification and removed
+ calls requiring interactive password input from doctests.
+
+0.3.1
+-----
+
+Released: 2012-09-01
+
+* Fixed GC45: Allowed additional arguments to gpg executable.
+* Fixed GC50: Used latin-1 encoding in tests when it's known to be required.
+* Fixed GC51: Test now returns non-zero exit status on test failure.
+* Fixed GC53: Now handles ``INV_SGNR`` and ``KEY_NOT_CREATED`` statuses.
+* Fixed GC55: Verification and decryption now return trust level of signer in
+ integer and text form.
+
+0.3.0
+-----
+
+Released: 2012-05-12
+
+* Fixed GC49: Reinstated Yann Leboulanger's change to support subkeys
+ (accidentally left out in 0.2.7).
+
+0.2.9
+-----
+
+Released: 2012-03-29
+
+* Fixed GC36: Now handles ``CARDCTRL`` and ``POLICY_URL`` messages.
+* Fixed GC40: Now handles ``DECRYPTION_INFO``, ``DECRYPTION_FAILED`` and
+ ``DECRYPTION_OKAY`` messages.
+* The ``random_binary_data file`` is no longer shipped, but constructed by the
+ test suite if needed.
+
+0.2.8
+-----
+
+Released: 2011-09-02
+
+* Fixed GC29: Now handles ``IMPORT_RES`` while verifying.
+* Fixed GC30: Fixed an encoding problem.
+* Fixed GC33: Quoted arguments for added safety.
+
+0.2.7
+-----
+
+Released: 2011-04-10
+
+* Fixed GC24: License is clarified as BSD.
+* Fixed GC25: Incorporated Daniel Folkinshteyn's changes.
+* Fixed GC26: Incorporated Yann Leboulanger's subkey change.
+* Fixed GC27: Incorporated hysterix's support for symmetric encryption.
+* Did some internal cleanups of Unicode handling.
+
+0.2.6
+-----
+
+Released: 2011-01-25
+
+* Fixed GC14: Should be able to accept passphrases from GPG-Agent.
+* Fixed GC19: Should be able to create a detached signature.
+* Fixed GC21/GC23: Better handling of less common responses from GPG.
+
+0.2.5
+-----
+
+Released: 2010-10-13
+
+* Fixed GC11/GC16: Detached signatures can now be created.
+* Fixed GC3: Detached signatures can be verified.
+* Fixed GC12: Better support for RSA and IDEA.
+* Fixed GC15/GC17: Better support for non-ASCII input.
+
+0.2.4
+-----
+
+Released: 2010-03-01
+
+* Fixed GC9: Now allows encryption without armor and the ability to encrypt
+ and decrypt directly to/from files.
+
+0.2.3
+-----
+
+Released: 2010-01-07
+
+* Fixed GC7: Made sending data to process threaded and added a test case.
+ With a test data file used by the test case, the archive size has gone up
+ to 5MB (the size of the test file).
+
+0.2.2
+-----
+
+Released: 2009-10-06
+
+* Fixed GC5/GC6: Added ``--batch`` when specifying ``--passphrase-fd`` and
+ changed the name of the distribution file to add the ``python-`` prefix.
+
+0.2.1
+-----
+
+Released: 2009-08-07
+
+* Fixed GC2: Added ``handle_status()`` method to the ``ListKeys`` class.
+
+0.2.0
+-----
+
+Released: 2009-07-16
+
+* Various changes made to support Python 3.0.
+
+0.1.0
+-----
+
+Released: 2009-07-04
+
+* Initial release.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python-gnupg-0.3.5/gnupg.py new/python-gnupg-0.3.7/gnupg.py
--- old/python-gnupg-0.3.5/gnupg.py 2013-08-30 19:10:36.000000000 +0200
+++ new/python-gnupg-0.3.7/gnupg.py 2014-12-07 19:46:19.000000000 +0100
@@ -27,15 +27,14 @@
and so does not work on Windows). Renamed to gnupg.py to avoid confusion with
the previous versions.
-Modifications Copyright (C) 2008-2013 Vinay Sajip. All rights reserved.
+Modifications Copyright (C) 2008-2014 Vinay Sajip. All rights reserved.
A unittest harness (test_gnupg.py) has also been added.
"""
-import locale
-__version__ = "0.3.5"
+__version__ = "0.3.7"
__author__ = "Vinay Sajip"
-__date__ = "$30-Aug-2013 18:10:36$"
+__date__ = "$07-Dec-2014 18:46:17$"
try:
from io import StringIO
@@ -103,16 +102,19 @@
raise TypeError('Expected string type, got %s' % type(s))
if not s:
result = "''"
- elif len(s) >= 2 and (s[0], s[-1]) == ("'", "'"):
- result = '"%s"' % s.replace('"', r'\"')
elif not UNSAFE.search(s):
result = s
else:
- result = "'%s'" % s.replace("'", "'\"'\"'")
+ result = "'%s'" % s.replace("'", r"'\''")
return result
# end of sarge code
+# Now that we use shell=False, we shouldn't need to quote arguments.
+# Use no_quote instead of shell_quote to remind us of where quoting
+# was needed.
+def no_quote(s):
+ return s
def _copy_data(instream, outstream):
# Copy one stream to another
@@ -153,11 +155,19 @@
passphrase = '%s\n' % passphrase
passphrase = passphrase.encode(encoding)
stream.write(passphrase)
- logger.debug("Wrote passphrase: %r", passphrase)
+ logger.debug('Wrote passphrase')
def _is_sequence(instance):
return isinstance(instance, (list, tuple, set, frozenset))
+def _make_memory_stream(s):
+ try:
+ from io import BytesIO
+ rv = BytesIO(s)
+ except ImportError:
+ rv = StringIO(s)
+ return rv
+
def _make_binary_stream(s, encoding):
if _py3k:
if isinstance(s, str):
@@ -165,12 +175,7 @@
else:
if type(s) is not str:
s = s.encode(encoding)
- try:
- from io import BytesIO
- rv = BytesIO(s)
- except ImportError:
- rv = StringIO(s)
- return rv
+ return _make_memory_stream(s)
class Verify(object):
"Handle status messages for --verify"
@@ -216,7 +221,7 @@
"PLAINTEXT_LENGTH", "POLICY_URL", "DECRYPTION_INFO",
"DECRYPTION_OKAY", "INV_SGNR", "FILE_START", "FILE_ERROR",
"FILE_DONE", "PKA_TRUST_GOOD", "PKA_TRUST_BAD", "BADMDC",
- "GOODMDC", "NO_SGNR"):
+ "GOODMDC", "NO_SGNR", "NOTATION_NAME", "NOTATION_DATA"):
pass
elif key == "BADSIG":
self.valid = False
@@ -271,6 +276,10 @@
else:
self.key_status = 'signing key was revoked'
self.status = self.key_status
+ elif key == "UNEXPECTED":
+ self.valid = False
+ self.key_id = value
+ self.status = 'unexpected data'
else:
raise ValueError("Unknown status message: %r" % key)
@@ -339,8 +348,8 @@
'problem': reason, 'text': self.problem_reason[reason]})
elif key == "IMPORT_RES":
import_res = value.split()
- for i in range(len(self.counts)):
- setattr(self, self.counts[i], int(import_res[i]))
+ for i, count in enumerate(self.counts):
+ setattr(self, count, int(import_res[i]))
elif key == "KEYEXPIRED":
self.results.append({'fingerprint': None,
'problem': '0', 'text': 'Key expired'})
@@ -374,7 +383,46 @@
def handle_status(self, key, value):
logger.debug('SendResult: %s: %s', key, value)
-class ListKeys(list):
+class SearchKeys(list):
+ ''' Handle status messages for --search-keys.
+
+ Handle pub and uid (relating the latter to the former).
+
+ Don't care about the rest
+ '''
+
+ UID_INDEX = 1
+ FIELDS = 'type keyid algo length date expires'.split()
+
+ def __init__(self, gpg):
+ self.gpg = gpg
+ self.curkey = None
+ self.fingerprints = []
+ self.uids = []
+
+ def get_fields(self, args):
+ result = {}
+ for i, var in enumerate(self.FIELDS):
+ result[var] = args[i]
+ result['uids'] = []
+ return result
+
+ def pub(self, args):
+ self.curkey = curkey = self.get_fields(args)
+ self.append(curkey)
+
+ def uid(self, args):
+ uid = args[self.UID_INDEX]
+ uid = ESCAPE_PATTERN.sub(lambda m: chr(int(m.group(1), 16)), uid)
+ for k, v in BASIC_ESCAPES.items():
+ uid = uid.replace(k, v)
+ self.curkey['uids'].append(uid)
+ self.uids.append(uid)
+
+ def handle_status(self, key, value):
+ pass
+
+class ListKeys(SearchKeys):
''' Handle status messages for --list-keys.
Handle pub and uid (relating the latter to the former).
@@ -391,25 +439,17 @@
grp = reserved for gpgsm
rvk = revocation key
'''
- def __init__(self, gpg):
- self.gpg = gpg
- self.curkey = None
- self.fingerprints = []
- self.uids = []
+
+ UID_INDEX = 9
+ FIELDS = 'type trust length algo keyid date expires dummy ownertrust uid'.split()
def key(self, args):
- vars = ("""
- type trust length algo keyid date expires dummy ownertrust uid
- """).split()
- self.curkey = {}
- for i in range(len(vars)):
- self.curkey[vars[i]] = args[i]
- self.curkey['uids'] = []
- if self.curkey['uid']:
- self.curkey['uids'].append(self.curkey['uid'])
- del self.curkey['uid']
- self.curkey['subkeys'] = []
- self.append(self.curkey)
+ self.curkey = curkey = self.get_fields(args)
+ if curkey['uid']:
+ curkey['uids'].append(curkey['uid'])
+ del curkey['uid']
+ curkey['subkeys'] = []
+ self.append(curkey)
pub = sec = key
@@ -417,56 +457,34 @@
self.curkey['fingerprint'] = args[9]
self.fingerprints.append(args[9])
- def uid(self, args):
- uid = args[9]
- uid = ESCAPE_PATTERN.sub(lambda m: chr(int(m.group(1), 16)), uid)
- for k, v in BASIC_ESCAPES.items():
- uid = uid.replace(k, v)
- self.curkey['uids'].append(uid)
- self.uids.append(uid)
-
def sub(self, args):
subkey = [args[4], args[11]]
self.curkey['subkeys'].append(subkey)
- def handle_status(self, key, value):
- pass
-class SearchKeys(list):
- ''' Handle status messages for --search-keys.
+class ScanKeys(ListKeys):
+ ''' Handle status messages for --with-fingerprint.'''
- Handle pub and uid (relating the latter to the former).
+ def sub(self, args):
+ # --with-fingerprint --with-colons somehow outputs fewer colons,
+ # use the last value args[-1] instead of args[11]
+ subkey = [args[4], args[-1]]
+ self.curkey['subkeys'].append(subkey)
- Don't care about the rest
- '''
- def __init__(self, gpg):
- self.gpg = gpg
- self.curkey = None
- self.fingerprints = []
- self.uids = []
+class TextHandler(object):
+ def _as_text(self):
+ return self.data.decode(self.gpg.encoding, self.gpg.decode_errors)
- def pub(self, args):
- vars = ("""
- type keyid algo length date expires
- """).split()
- self.curkey = {}
- for i in range(len(vars)):
- self.curkey[vars[i]] = args[i]
- self.curkey['uids'] = []
- self.append(self.curkey)
+ if _py3k:
+ __str__ = _as_text
+ else:
+ __unicode__ = _as_text
- def uid(self, args):
- uid = args[1]
- uid = ESCAPE_PATTERN.sub(lambda m: chr(int(m.group(1), 16)), uid)
- for k, v in BASIC_ESCAPES.items():
- uid = uid.replace(k, v)
- self.curkey['uids'].append(uid)
- self.uids.append(uid)
+ def __str__(self):
+ return self.data
- def handle_status(self, key, value):
- pass
-class Crypt(Verify):
+class Crypt(Verify, TextHandler):
"Handle status messages for --encrypt and --decrypt"
def __init__(self, gpg):
Verify.__init__(self, gpg)
@@ -480,19 +498,16 @@
__bool__ = __nonzero__
- def __str__(self):
- return self.data.decode(self.gpg.encoding, self.gpg.decode_errors)
-
def handle_status(self, key, value):
if key in ("ENC_TO", "USERID_HINT", "GOODMDC", "END_DECRYPTION",
- "BEGIN_SIGNING", "NO_SECKEY", "ERROR", "NODATA",
+ "BEGIN_SIGNING", "NO_SECKEY", "ERROR", "NODATA", "PROGRESS",
"CARDCTRL", "BADMDC", "SC_OP_FAILURE", "SC_OP_SUCCESS"):
# in the case of ERROR, this is because a more specific error
# message will have come first
pass
elif key in ("NEED_PASSPHRASE", "BAD_PASSPHRASE", "GOOD_PASSPHRASE",
"MISSING_PASSPHRASE", "DECRYPTION_FAILED",
- "KEY_NOT_CREATED"):
+ "KEY_NOT_CREATED", "NEED_PASSPHRASE_PIN"):
self.status = key.replace("_", " ").lower()
elif key == "NEED_PASSPHRASE_SYM":
self.status = 'need symmetric passphrase'
@@ -569,7 +584,7 @@
__bool__ = __nonzero__
-class Sign(object):
+class Sign(TextHandler):
"Handle status messages for --sign"
def __init__(self, gpg):
self.gpg = gpg
@@ -582,15 +597,16 @@
__bool__ = __nonzero__
- def __str__(self):
- return self.data.decode(self.gpg.encoding, self.gpg.decode_errors)
-
def handle_status(self, key, value):
if key in ("USERID_HINT", "NEED_PASSPHRASE", "BAD_PASSPHRASE",
"GOOD_PASSPHRASE", "BEGIN_SIGNING", "CARDCTRL", "INV_SGNR",
- "KEYEXPIRED", "SIGEXPIRED", "KEYREVOKED", "NO_SGNR",
- "MISSING_PASSPHRASE", "SC_OP_FAILURE", "SC_OP_SUCCESS"):
+ "NO_SGNR", "MISSING_PASSPHRASE", "NEED_PASSPHRASE_PIN",
+ "SC_OP_FAILURE", "SC_OP_SUCCESS"):
pass
+ elif key in ("KEYEXPIRED", "SIGEXPIRED"):
+ self.status = 'key expired'
+ elif key == "KEYREVOKED":
+ self.status = 'key revoked'
elif key == "SIG_CREATED":
(self.type,
algo, self.hash_algo, cls,
@@ -599,7 +615,8 @@
else:
raise ValueError("Unknown status message: %r" % key)
-VERSION_RE = re.compile(r'gpg \(GnuPG\) (\d+(\.\d+)*)'.encode('utf-8'), re.I)
+VERSION_RE = re.compile(r'gpg \(GnuPG\) (\d+(\.\d+)*)'.encode('ascii'), re.I)
+HEX_DIGITS_RE = re.compile(r'[0-9a-f]+$', re.I)
class GPG(object):
@@ -612,6 +629,7 @@
'import': ImportResult,
'send': SendResult,
'list': ListKeys,
+ 'scan': ScanKeys,
'search': SearchKeys,
'sign': Sign,
'verify': Verify,
@@ -652,13 +670,11 @@
if isinstance(options, str):
options = [options]
self.options = options
- self.encoding = locale.getpreferredencoding()
- if self.encoding is None: # This happens on Jython!
- self.encoding = sys.stdin.encoding
- if self.encoding is None:
- logger.warning('No encoding found via locale.getpreferredencoding '
- 'or sys.stdin.encoding, defaulting to utf-8.')
- self.encoding = 'utf-8'
+ # Changed in 0.3.7 to use Latin-1 encoding rather than
+ # locale.getpreferredencoding falling back to sys.stdin.encoding
+ # falling back to utf-8, because gpg itself uses latin-1 as the default
+ # encoding.
+ self.encoding = 'latin-1'
if gnupghome and not os.path.isdir(self.gnupghome):
os.makedirs(self.gnupghome,0x1C0)
p = self._open_subprocess(["--version"])
@@ -671,7 +687,7 @@
if not m:
self.version = None
else:
- dot = '.'.encode('utf-8')
+ dot = '.'.encode('ascii')
self.version = tuple([int(s) for s in m.groups()[0].split(dot)])
def make_args(self, args, passphrase):
@@ -680,18 +696,18 @@
will be appended. The ``passphrase`` argument needs to be True if
a passphrase will be sent to GPG, else False.
"""
- cmd = [self.gpgbinary, '--status-fd 2 --no-tty']
+ cmd = [self.gpgbinary, '--status-fd', '2', '--no-tty']
if self.gnupghome:
- cmd.append('--homedir %s' % shell_quote(self.gnupghome))
+ cmd.extend(['--homedir', no_quote(self.gnupghome)])
if self.keyring:
cmd.append('--no-default-keyring')
for fn in self.keyring:
- cmd.append('--keyring %s' % shell_quote(fn))
+ cmd.extend(['--keyring', no_quote(fn)])
if self.secret_keyring:
for fn in self.secret_keyring:
- cmd.append('--secret-keyring %s' % shell_quote(fn))
+ cmd.extend(['--secret-keyring', no_quote(fn)])
if passphrase:
- cmd.append('--batch --passphrase-fd 0')
+ cmd.extend(['--batch', '--passphrase-fd', '0'])
if self.use_agent:
cmd.append('--use-agent')
if self.options:
@@ -702,11 +718,12 @@
def _open_subprocess(self, args, passphrase=False):
# Internal method: open a pipe to a GPG subprocess and return
# the file objects for communicating with it.
- cmd = ' '.join(self.make_args(args, passphrase))
+ cmd = self.make_args(args, passphrase)
if self.verbose:
- print(cmd)
+ pcmd = ' '.join(cmd)
+ print(pcmd)
logger.debug("%s", cmd)
- return Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)
+ return Popen(cmd, shell=False, stdin=PIPE, stdout=PIPE, stderr=PIPE)
def _read_response(self, stream, result):
# Internal method: reads all the stderr output from GPG, taking notice
@@ -808,8 +825,15 @@
f.close()
return result
+ def set_output_without_confirmation(self, args, output):
+ "If writing to a file which exists, avoid a confirmation message."
+ if os.path.exists(output):
+ # We need to avoid an overwrite confirmation message
+ args.extend(['--batch', '--yes'])
+ args.extend(['--output', output])
+
def sign_file(self, file, keyid=None, passphrase=None, clearsign=True,
- detach=False, binary=False):
+ detach=False, binary=False, output=None):
"""sign file"""
logger.debug("sign_file: %s", file)
if binary:
@@ -823,7 +847,10 @@
elif clearsign:
args.append("--clearsign")
if keyid:
- args.append('--default-key %s' % shell_quote(keyid))
+ args.extend(['--default-key', no_quote(keyid)])
+ if output: # write the output to a file with the specified name
+ self.set_output_without_confirmation(args, output)
+
result = self.result_map['sign'](self)
#We could use _handle_io here except for the fact that if the
#passphrase is bad, gpg bails and you can't write the message.
@@ -875,8 +902,8 @@
logger.debug('Wrote to temp file: %r', s)
os.write(fd, s)
os.close(fd)
- args.append(shell_quote(fn))
- args.append(shell_quote(data_filename))
+ args.append(no_quote(fn))
+ args.append(no_quote(data_filename))
try:
p = self._open_subprocess(args)
self._collect_output(p, result, stdin=p.stdin)
@@ -884,6 +911,15 @@
os.unlink(fn)
return result
+ def verify_data(self, sig_filename, data):
+ "Verify the signature in sig_filename against data in memory"
+ logger.debug('verify_data: %r, %r ...', sig_filename, data[:16])
+ result = self.result_map['verify'](self)
+ args = ['--verify', no_quote(sig_filename), '-']
+ stream = _make_memory_stream(data)
+ self._handle_io(args, stream, result, binary=True)
+ return result
+
#
# KEY MANAGEMENT
#
@@ -956,8 +992,8 @@
logger.debug('recv_keys: %r', keyids)
data = _make_binary_stream("", self.encoding)
#data = ""
- args = ['--keyserver', shell_quote(keyserver), '--recv-keys']
- args.extend([shell_quote(k) for k in keyids])
+ args = ['--keyserver', no_quote(keyserver), '--recv-keys']
+ args.extend([no_quote(k) for k in keyids])
self._handle_io(args, data, result, binary=True)
logger.debug('recv_keys result: %r', result.__dict__)
data.close()
@@ -973,8 +1009,8 @@
logger.debug('send_keys: %r', keyids)
data = _make_binary_stream('', self.encoding)
#data = ""
- args = ['--keyserver', shell_quote(keyserver), '--send-keys']
- args.extend([shell_quote(k) for k in keyids])
+ args = ['--keyserver', no_quote(keyserver), '--send-keys']
+ args.extend([no_quote(k) for k in keyids])
self._handle_io(args, data, result, binary=True)
logger.debug('send_keys result: %r', result.__dict__)
data.close()
@@ -985,25 +1021,31 @@
if secret:
which='secret-key'
if _is_sequence(fingerprints):
- fingerprints = ' '.join([shell_quote(s) for s in fingerprints])
+ fingerprints = [no_quote(s) for s in fingerprints]
else:
- fingerprints = shell_quote(fingerprints)
- args = ['--batch --delete-%s %s' % (which, fingerprints)]
+ fingerprints = [no_quote(fingerprints)]
+ args = ['--batch', '--delete-%s' % which]
+ args.extend(fingerprints)
result = self.result_map['delete'](self)
p = self._open_subprocess(args)
self._collect_output(p, result, stdin=p.stdin)
return result
- def export_keys(self, keyids, secret=False):
+ def export_keys(self, keyids, secret=False, armor=True, minimal=False):
"export the indicated keys. 'keyid' is anything gpg accepts"
which=''
if secret:
which='-secret-key'
if _is_sequence(keyids):
- keyids = ' '.join([shell_quote(k) for k in keyids])
+ keyids = [no_quote(k) for k in keyids]
else:
- keyids = shell_quote(keyids)
- args = ['--armor --export%s %s' % (which, keyids)]
+ keyids = [no_quote(keyids)]
+ args = ['--export%s' % which]
+ if armor:
+ args.append('--armor')
+ if minimal:
+ args.extend(['--export-options','export-minimal'])
+ args.extend(keyids)
p = self._open_subprocess(args)
# gpg --export produces no status-fd output; stdout will be
# empty in case of failure
@@ -1013,6 +1055,27 @@
logger.debug('export_keys result: %r', result.data)
return result.data.decode(self.encoding, self.decode_errors)
+ def _get_list_output(self, p, kind):
+ # Get the response information
+ result = self.result_map[kind](self)
+ self._collect_output(p, result, stdin=p.stdin)
+ lines = result.data.decode(self.encoding,
+ self.decode_errors).splitlines()
+ valid_keywords = 'pub uid sec fpr sub'.split()
+ for line in lines:
+ if self.verbose:
+ print(line)
+ logger.debug("line: %r", line.rstrip())
+ if not line:
+ break
+ L = line.strip().split(':')
+ if not L:
+ continue
+ keyword = L[0]
+ if keyword in valid_keywords:
+ getattr(result, keyword)(L)
+ return result
+
def list_keys(self, secret=False):
""" list the keys currently in the keyring
@@ -1033,32 +1096,23 @@
which='keys'
if secret:
which='secret-keys'
- args = "--list-%s --fixed-list-mode --fingerprint --with-colons" % (which,)
- args = [args]
+ args = ["--list-%s" % which, "--fixed-list-mode", "--fingerprint",
+ "--with-colons"]
p = self._open_subprocess(args)
+ return self._get_list_output(p, 'list')
- # there might be some status thingumy here I should handle... (amk)
- # ...nope, unless you care about expired sigs or keys (stevegt)
+ def scan_keys(self, filename):
+ """
+ List details of an ascii armored or binary key file
+ without first importing it to the local keyring.
- # Get the response information
- result = self.result_map['list'](self)
- self._collect_output(p, result, stdin=p.stdin)
- lines = result.data.decode(self.encoding,
- self.decode_errors).splitlines()
- valid_keywords = 'pub uid sec fpr sub'.split()
- for line in lines:
- if self.verbose:
- print(line)
- logger.debug("line: %r", line.rstrip())
- if not line:
- break
- L = line.strip().split(':')
- if not L:
- continue
- keyword = L[0]
- if keyword in valid_keywords:
- getattr(result, keyword)(L)
- return result
+ The function achieves this by running:
+ $ gpg --with-fingerprint --with-colons filename
+ """
+ args = ['--with-fingerprint', '--with-colons']
+ args.append(no_quote(filename))
+ p = self._open_subprocess(args)
+ return self._get_list_output(p, 'scan')
def search_keys(self, query, keyserver='pgp.mit.edu'):
""" search keyserver by query (using --search-keys option)
@@ -1068,16 +1122,18 @@
>>> gpg = GPG(gnupghome='keys')
>>> os.chmod('keys', 0x1C0)
>>> result = gpg.search_keys('