You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by hw...@apache.org on 2012/05/16 22:32:54 UTC

svn commit: r1339349 [36/37] - in /subversion/branches/fix-rdump-editor: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ build/win32/ contrib/client-side/emacs/ contrib/client-side/vim/ contrib/server-side/ notes/ notes/api-errat...

Modified: subversion/branches/fix-rdump-editor/tools/dev/merge-graph.py
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/tools/dev/merge-graph.py?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/tools/dev/merge-graph.py (original)
+++ subversion/branches/fix-rdump-editor/tools/dev/merge-graph.py Wed May 16 20:32:43 2012
@@ -19,25 +19,40 @@
 #    under the License.
 # ====================================================================
 
-args_message = 'GRAPH_CONFIG_FILE...'
+args_message = '[-f png|svg|gif|dia... [-f ...]] GRAPH_CONFIG_FILE...'
 help_message = """Produce pretty graphs representing branches and merging.
-For each config file specified, construct a graph and write it as a PNG file."""
+For each config file specified, construct a graph and write it as a PNG file
+(or other graphical file formats)."""
 
 import sys
+import getopt
 from mergegraph import MergeDot
 
 
 # If run as a program, process each input filename as a graph config file.
 if __name__ == '__main__':
+  optlist, args = getopt.getopt(sys.argv[1:], 'f:', ['format'])
+
   prog_name = sys.argv[0]
-  if len(sys.argv) == 1:
+  if not args:
     usage = '%s: usage: "%s %s"' % (prog_name, prog_name, args_message)
     print >> sys.stderr, usage
     sys.exit(1)
 
-  for config_filename in sys.argv[1:]:
-    print prog_name + ": reading '" + config_filename + "',",
-    graph = MergeDot(config_filename, rankdir='LR', dpi='72')
-    print "writing '" + graph.filename + "'"
-    graph.write_png(graph.filename)
+  formats = []
+
+  for opt, opt_arg in optlist:
+    if opt == '-f':
+      formats.append(opt_arg)
 
+  if not formats:
+    formats.append('png')
+
+  for config_filename in args:
+    print "%s: reading '%s'," % (prog_name, config_filename),
+    graph = MergeDot(config_filename, rankdir='LR', dpi='72')
+    for format in formats:
+      filename = '%s.%s' % (graph.basename, format)
+      print "writing '%s'" % filename,
+      graph.save(format=format, filename=filename)
+    print

Modified: subversion/branches/fix-rdump-editor/tools/dev/mergegraph/mergegraph.py
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/tools/dev/mergegraph/mergegraph.py?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/tools/dev/mergegraph/mergegraph.py (original)
+++ subversion/branches/fix-rdump-editor/tools/dev/mergegraph/mergegraph.py Wed May 16 20:32:43 2012
@@ -228,11 +228,11 @@ class MergeDot(MergeGraph, pydot.Dot):
     """Initialize a MergeDot graph's input data from a config file."""
     import ConfigParser
     if config_filename.endswith('.txt'):
-      default_filename = config_filename[:-4] + '.png'
+      default_basename = config_filename[:-4]
     else:
-      default_filename = config_filename + '.png'
+      default_basename = config_filename
 
-    config = ConfigParser.SafeConfigParser({ 'filename': default_filename,
+    config = ConfigParser.SafeConfigParser({ 'basename': default_basename,
                                              'title': None,
                                              'merges': '[]',
                                              'annotations': '[]' })
@@ -240,7 +240,7 @@ class MergeDot(MergeGraph, pydot.Dot):
     if len(files_read) == 0:
       print >> sys.stderr, 'graph: unable to read graph config from "' + config_filename + '"'
       sys.exit(1)
-    graph.filename = config.get('graph', 'filename')
+    graph.basename = config.get('graph', 'basename')
     graph.title = config.get('graph', 'title')
     graph.branches = eval(config.get('graph', 'branches'))
     graph.changes = eval(config.get('graph', 'changes'))
@@ -294,3 +294,11 @@ class MergeDot(MergeGraph, pydot.Dot):
     if graph.title:
       graph.add_node(Node('title', shape='plaintext', label='"' + graph.title + '"'))
 
+  def save(graph, format='png', filename=None):
+    """Save this merge graph to the given file format. If filename is None,
+       construct a filename from the basename of the original file (as passed
+       to the constructor and then stored in graph.basename) and the suffix
+       according to the given format."""
+    if not filename:
+      filename = graph.basename + '.' + format
+    pydot.Dot.write(graph, filename, format=format)

Modified: subversion/branches/fix-rdump-editor/tools/dev/svnraisetreeconflict/main.c
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/tools/dev/svnraisetreeconflict/main.c?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/tools/dev/svnraisetreeconflict/main.c (original)
+++ subversion/branches/fix-rdump-editor/tools/dev/svnraisetreeconflict/main.c Wed May 16 20:32:43 2012
@@ -316,7 +316,6 @@ check_lib_versions(void)
 int
 main(int argc, const char *argv[])
 {
-  apr_allocator_t *allocator;
   apr_pool_t *pool;
   svn_error_t *err;
   apr_getopt_t *os;
@@ -336,13 +335,7 @@ main(int argc, const char *argv[])
   /* Create our top-level pool.  Use a separate mutexless allocator,
    * given this application is single threaded.
    */
-  if (apr_allocator_create(&allocator))
-    return EXIT_FAILURE;
-
-  apr_allocator_max_free_set(allocator, SVN_ALLOCATOR_RECOMMENDED_MAX_FREE);
-
-  pool = svn_pool_create_ex(NULL, allocator);
-  apr_allocator_owner_set(allocator, pool);
+  pool = apr_allocator_owner_get(svn_pool_create_allocator(FALSE));
 
   /* Check library versions */
   err = check_lib_versions();

Modified: subversion/branches/fix-rdump-editor/tools/dev/unix-build/Makefile.svn
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/tools/dev/unix-build/Makefile.svn?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/tools/dev/unix-build/Makefile.svn (original)
+++ subversion/branches/fix-rdump-editor/tools/dev/unix-build/Makefile.svn Wed May 16 20:32:43 2012
@@ -30,6 +30,7 @@
 #   |______________________________________________________________|
 
 ENABLE_PERL_BINDINGS ?= yes
+THREADING ?= yes
 ENABLE_JAVA_BINDINGS ?= no # they don't build with thread-less APR...
 USE_APR_ICONV ?= no # set to yes to use APR iconv instead of GNU iconv
 PARALLEL ?= 1
@@ -60,20 +61,20 @@ OBJDIR		= $(PWD)/objdir
 
 BDB_MAJOR_VER	= 4.7
 BDB_VER		= $(BDB_MAJOR_VER).25
-APR_VER		= 1.4.5
+APR_VER		= 1.4.6
 APR_ICONV_VER	= 1.2.1
-GNU_ICONV_VER	= 1.13.1
-APR_UTIL_VER	= 1.3.12
-HTTPD_VER	= 2.2.19
+GNU_ICONV_VER	= 1.14
+APR_UTIL_VER	= 1.4.1
+HTTPD_VER	= 2.2.22
 NEON_VER	= 0.29.6
-SERF_VER	= 1.0.0
+SERF_VER	= 1.0.3
 SERF_OLD_VER	= 0.3.1
-CYRUS_SASL_VER	= 2.1.23
-SQLITE_VER	= 3070603
+CYRUS_SASL_VER	= 2.1.25
+SQLITE_VER	= 3071100
 LIBMAGIC_VER	= 5.11
-RUBY_VER	= 1.8.7-p334
+RUBY_VER	= 1.8.7-p358
 BZ2_VER	= 1.0.6
-PYTHON_VER	= 2.7.2
+PYTHON_VER	= 2.7.3
 
 BDB_DIST	= db-$(BDB_VER).tar.gz
 APR_ICONV_DIST	= apr-iconv-$(APR_ICONV_VER).tar.gz
@@ -117,7 +118,7 @@ CYRUS_SASL_URL	= ftp://ftp.andrew.cmu.ed
 LIBMAGIC_URL	= ftp://ftp.astron.com/pub/file/$(LIBMAGIC_DIST)
 RUBY_URL	= http://ftp.ruby-lang.org/pub/ruby/1.8/$(RUBY_DIST)
 BZ2_URL		= http://bzip.org/$(BZ2_VER)/$(BZ2_DIST)
-PYTHON_URL	= http://python.org/ftp/python/2.7.2/$(PYTHON_DIST)
+PYTHON_URL	= http://python.org/ftp/python/$(PYTHON_VER)/$(PYTHON_DIST)
 
 BDB_SRCDIR	= $(SRCDIR)/db-$(BDB_VER)
 APR_SRCDIR	= $(SRCDIR)/apr-$(APR_VER)
@@ -292,7 +293,7 @@ $(APR_OBJDIR)/.retrieved:
 	fi
 	touch $@
 
-ifdef THREADING
+ifeq ($(THREADING),yes)
 THREADS_FLAG=--enable-threads
 else
 THREADS_FLAG=--disable-threads
@@ -304,11 +305,6 @@ endif
 
 # configure apr
 $(APR_OBJDIR)/.configured: $(APR_OBJDIR)/.retrieved
-	cp $(APR_SRCDIR)/build/apr_hints.m4 \
-		$(APR_SRCDIR)/build/apr_hints.m4.orig
-	cat $(APR_SRCDIR)/build/apr_hints.m4.orig \
-		| sed -e '/^.*APR_ADDTO(CPPFLAGS, \[-D_POSIX_THREADS\]).*$$/d' \
-			> $(APR_SRCDIR)/build/apr_hints.m4
 	cd $(APR_SRCDIR) && ./buildconf
 	cd $(APR_OBJDIR) \
 		&& env CFLAGS="-O0 -g $(PROFILE_CFLAGS)" GREP="`which grep`" \
@@ -779,7 +775,7 @@ $(SQLITE_OBJDIR)/.retrieved: $(DISTDIR)/
 	tar -C $(SRCDIR) -zxf $(DISTDIR)/$(SQLITE_DIST)
 	touch $@
 
-ifdef THREADING
+ifeq ($(THREADING),yes)
 THREADSAFE_FLAG=--enable-threadsafe
 else
 THREADSAFE_FLAG=--disable-threadsafe

Modified: subversion/branches/fix-rdump-editor/tools/dev/unix-build/README
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/tools/dev/unix-build/README?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/tools/dev/unix-build/README (original)
+++ subversion/branches/fix-rdump-editor/tools/dev/unix-build/README Wed May 16 20:32:43 2012
@@ -22,12 +22,24 @@ repository.
 
 Usage
 =====
-First, choose a directory $(SVN_DEV) to set up the environment. Note
-that this directory cannot be changed later because the script
-hardcodes build and link paths relative to the current working
-directory.
+First, choose a directory $(SVN_DEV) to set up the environment.
+For example, $(SVN_DEV) could be the directory "~/svn".
+Note that this directory cannot be changed later because the script
+hardcodes build and link paths relative to the current working directory.
+
+  $ mkdir $(SVN_DEV)
+
+Now change into this directory and make the Makefile available in it:
+
+  $ cd $(SVN_DEV)
+  $ svn checkout https://svn.apache.org/repos/asf/subversion/trunk/tools/dev/unix-build
+  $ ln -s unix-build/Makefile.svn Makefile
+
+To fetch and build trunk, simply don't pass anything, just run 'make':
+
+  $ cd $(SVN_DEV)
+  $ make
 
-To fetch and build trunk, simply don't pass anything.
 Pass the branch you want to build in BRANCH, e.g.
 	$ make BRANCH="1.5.x"
 You can also pass a tag to build: 
@@ -39,19 +51,37 @@ than one working copy of the same branch
 When the script has finished fetching and building, it uses
 $(SVN_DEV)/prefix to install Subversion libraries and
 binaries. $(SVN_DEV)/prefix/svn-trunk (or whatever you choose to
-build) will contain the latest Subversion binaries: you should add
-$(SVN_DEV)/prefix/svn-trunk/bin to your $PATH to use them. The
-Makefile in $(SVN_DEV)/svn-trunk is configured to build with sane
+build) will contain the latest Subversion binaries. You can add
+$(SVN_DEV)/prefix/svn-trunk/bin to your $PATH to use them:
+
+  $ export PATH="$(SVN_DEV)/prefix/svn-trunk/bin:$PATH"
+
+The Makefile in $(SVN_DEV)/svn-trunk is configured to build with sane
 options: while developing Subversion, simply `svn up` to pull the
 latest changes, `make` and `make install` to install the binaries in
-$(SVN_DEV)/prefix/svn-trunk.
+$(SVN_DEV)/prefix/svn-trunk. This usually works fine. If not, you may
+need to use the 'svn-reset' target and recompile everything.
+
+If at any point, you want to recompile any of the packages with the
+default configuration in Makefile.svn, use the *-clean and *-reset
+target in Makefile.svn before trying to rebuild again. For example:
+
+  $ make svn-clean
+  $ make svn-reset
+  $ make
+
+Or, if you want to recompile svn and all dependencies:
+
+  $ make clean
+  $ make reset
+  $ make
+
+If you want to remove everything including the installed binaries effectively
+returning to the starting point, use the "nuke" target (BE CAREFUL, this will
+remove the 'svn' binary compiled from trunk which you might need to manage
+existing working copies):
 
-If at any point, you want to re-configure any of the packages to the
-default configuration in Makefile.svn, just run the "<PACKAGE>-reset"
-target in Makefile.svn before trying to rebuild again. If, in the
-extreme case, you want to remove everything including the installed
-binaries effectively returning to the starting point, use the "nuke"
-target.
+  $ make nuke
 
 Extended usage
 ==============

Modified: subversion/branches/fix-rdump-editor/tools/dev/which-error.py
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/tools/dev/which-error.py?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/tools/dev/which-error.py (original)
+++ subversion/branches/fix-rdump-editor/tools/dev/which-error.py Wed May 16 20:32:43 2012
@@ -29,6 +29,7 @@
 # $LastChangedRevision$
 #
 
+import errno
 import sys
 import os.path
 import re
@@ -68,6 +69,13 @@ codes.  This can be done in variety of w
 
 def get_errors():
   errs = {}
+  ## errno values.
+  errs.update(errno.errorcode)
+  ## APR-defined errors, from apr_errno.h.
+  for line in open(os.path.join(os.path.dirname(sys.argv[0]), 'aprerr.txt')):
+    key, _, val = line.split()
+    errs[int(val)] = key
+  ## Subversion errors, from svn_error_codes.h.
   for key in vars(core):
     if key.find('SVN_ERR_') == 0:
       try:

Modified: subversion/branches/fix-rdump-editor/tools/dev/windows-build/Makefile
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/tools/dev/windows-build/Makefile?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/tools/dev/windows-build/Makefile (original)
+++ subversion/branches/fix-rdump-editor/tools/dev/windows-build/Makefile Wed May 16 20:32:43 2012
@@ -128,7 +128,7 @@ all2: targetdir
 package:
 	test -d $(SVNDIR)\$(CONFIG)\Subversion\tests\cmdline || mkdir $(SVNDIR)\$(CONFIG)\Subversion\tests\cmdline
 	test -d $(TARGETDIR)\bin || mkdir $(TARGETDIR)\bin
-	for %%i in (svn svnadmin svndumpfilter svnlook svnserve svnsync svnversion svnrdump) do @$(CP) $(CONFIG)\subversion\%%i\%%i.exe $(TARGETDIR)\bin
+	for %%i in (svn svnadmin svndumpfilter svnlook svnserve svnsync svnversion svnrdump svnmucc) do @$(CP) $(CONFIG)\subversion\%%i\%%i.exe $(TARGETDIR)\bin
 	for %%i in (diff diff3 diff4) do @if exist $(CONFIG)\tools\diff\%%i.exe $(CP) $(CONFIG)\tools\diff\%%i.exe $(TARGETDIR)\bin
 	$(CP) $(APRDIR)\$(CONFIG)/*.dll $(TARGETDIR)\bin
 	$(CP) $(APRUTILDIR)\$(CONFIG)/*.dll $(TARGETDIR)\bin

Modified: subversion/branches/fix-rdump-editor/tools/dist/_gnupg.py
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/tools/dist/_gnupg.py?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/tools/dist/_gnupg.py (original)
+++ subversion/branches/fix-rdump-editor/tools/dist/_gnupg.py Wed May 16 20:32:43 2012
@@ -1,3 +1,29 @@
+# Copyright (c) 2008-2011 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.
+
 """ A wrapper for the 'gpg' command::
 
 Portions of this module are derived from A.M. Kuchling's well-designed
@@ -34,7 +60,7 @@ A unittest harness (test_gnupg.py) has a
 import locale
 
 __author__ = "Vinay Sajip"
-__date__  = "$10-Apr-2011 11:40:48$"
+__date__  = "$02-Sep-2011 13:18:12$"
 
 try:
     from io import StringIO
@@ -125,859 +151,885 @@ def _make_binary_stream(s, encoding):
         rv = StringIO(s)
     return rv
 
-class GPG(object):
-    "Encapsulate access to the gpg executable"
-    def __init__(self, gpgbinary='gpg', gnupghome=None, verbose=False,
-                 use_agent=False, keyring=None):
-        """Initialize a GPG process wrapper.  Options are:
-
-        gpgbinary -- full pathname for GPG binary.
+class Verify(object):
+    "Handle status messages for --verify"
 
-        gnupghome -- full pathname to where we can find the public and
-        private keyrings.  Default is whatever gpg defaults to.
-        keyring -- name of alternative keyring file to use. If specified,
-        the default keyring is not used.
-        """
-        self.gpgbinary = gpgbinary
-        self.gnupghome = gnupghome
-        self.keyring = keyring
-        self.verbose = verbose
-        self.use_agent = use_agent
-        self.encoding = locale.getpreferredencoding()
-        if self.encoding is None: # This happens on Jython!
-            self.encoding = sys.stdin.encoding
-        if gnupghome and not os.path.isdir(self.gnupghome):
-            os.makedirs(self.gnupghome,0x1C0)
-        p = self._open_subprocess(["--version"])
-        result = Verify() # any result will do for this
-        self._collect_output(p, result, stdin=p.stdin)
-        if p.returncode != 0:
-            raise ValueError("Error invoking gpg: %s: %s" % (p.returncode,
-                                                             result.stderr))
+    def __init__(self, gpg):
+        self.gpg = gpg
+        self.valid = False
+        self.fingerprint = self.creation_date = self.timestamp = None
+        self.signature_id = self.key_id = None
+        self.username = None
 
-    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 = [self.gpgbinary, '--status-fd 2 --no-tty']
-        if self.gnupghome:
-            cmd.append('--homedir "%s" ' % self.gnupghome)
-        if self.keyring:
-            cmd.append('--no-default-keyring --keyring "%s" ' % self.keyring)
-        if passphrase:
-            cmd.append('--batch --passphrase-fd 0')
-        if self.use_agent:
-            cmd.append('--use-agent')
-        cmd.extend(args)
-        cmd = ' '.join(cmd)
-        if self.verbose:
-            print(cmd)
-        logger.debug("%s", cmd)
-        return Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)
+    def __nonzero__(self):
+        return self.valid
 
-    def _read_response(self, stream, result):
-        # Internal method: reads all the stderr output from GPG, taking notice
-        # only of lines that begin with the magic [GNUPG:] prefix.
-        #
-        # Calls methods on the response object for each valid token found,
-        # with the arg being the remainder of the status line.
-        lines = []
-        while True:
-            line = stream.readline()
-            if len(line) == 0:
-                break
-            lines.append(line)
-            line = line.rstrip()
-            if self.verbose:
-                print(line)
-            logger.debug("%s", line)
-            if line[0:9] == '[GNUPG:] ':
-                # Chop off the prefix
-                line = line[9:]
-                L = line.split(None, 1)
-                keyword = L[0]
-                if len(L) > 1:
-                    value = L[1]
-                else:
-                    value = ""
-                result.handle_status(keyword, value)
-        result.stderr = ''.join(lines)
+    __bool__ = __nonzero__
 
-    def _read_data(self, stream, result):
-        # Read the contents of the file from GPG's stdout
-        chunks = []
-        while True:
-            data = stream.read(1024)
-            if len(data) == 0:
-                break
-            logger.debug("chunk: %r" % data[:256])
-            chunks.append(data)
-        if _py3k:
-            # Join using b'' or '', as appropriate
-            result.data = type(data)().join(chunks)
+    def handle_status(self, key, value):
+        if key in ("TRUST_UNDEFINED", "TRUST_NEVER", "TRUST_MARGINAL",
+                   "TRUST_FULLY", "TRUST_ULTIMATE", "RSA_OR_IDEA", "NODATA",
+                   "IMPORT_RES", "PLAINTEXT", "PLAINTEXT_LENGTH"):
+            pass
+        elif key == "BADSIG":
+            self.valid = False
+            self.status = 'signature bad'
+            self.key_id, self.username = value.split(None, 1)
+        elif key == "GOODSIG":
+            self.valid = True
+            self.status = 'signature good'
+            self.key_id, self.username = value.split(None, 1)
+        elif key == "VALIDSIG":
+            (self.fingerprint,
+             self.creation_date,
+             self.sig_timestamp,
+             self.expire_timestamp) = value.split()[:4]
+            # may be different if signature is made with a subkey
+            self.pubkey_fingerprint = value.split()[-1]
+            self.status = 'signature valid'
+        elif key == "SIG_ID":
+            (self.signature_id,
+             self.creation_date, self.timestamp) = value.split()
+        elif key == "ERRSIG":
+            self.valid = False
+            (self.key_id,
+             algo, hash_algo,
+             cls,
+             self.timestamp) = value.split()[:5]
+            self.status = 'signature error'
+        elif key == "NO_PUBKEY":
+            self.valid = False
+            self.key_id = value
+            self.status = 'no public key'
+        elif key in ("KEYEXPIRED", "SIGEXPIRED"):
+            # these are useless in verify, since they are spit out for any
+            # pub/subkeys on the key, not just the one doing the signing.
+            # if we want to check for signatures with expired key,
+            # the relevant flag is EXPKEYSIG.
+            pass
+        elif key in ("EXPKEYSIG", "REVKEYSIG"):
+            # signed with expired or revoked key
+            self.valid = False
+            self.key_id = value.split()[0]
+            self.status = (('%s %s') % (key[:3], key[3:])).lower()
         else:
-            result.data = ''.join(chunks)
-
-    def _collect_output(self, process, result, writer=None, stdin=None):
-        """
-        Drain the subprocesses output streams, writing the collected output
-        to the result. If a writer thread (writing to the subprocess) is given,
-        make sure it's joined before returning. If a stdin stream is given,
-        close it before returning.
-        """
-        stderr = codecs.getreader(self.encoding)(process.stderr)
-        rr = threading.Thread(target=self._read_response, args=(stderr, result))
-        rr.setDaemon(True)
-        logger.debug('stderr reader: %r', rr)
-        rr.start()
+            raise ValueError("Unknown status message: %r" % key)
 
-        stdout = process.stdout
-        dr = threading.Thread(target=self._read_data, args=(stdout, result))
-        dr.setDaemon(True)
-        logger.debug('stdout reader: %r', dr)
-        dr.start()
+class ImportResult(object):
+    "Handle status messages for --import"
 
-        dr.join()
-        rr.join()
-        if writer is not None:
-            writer.join()
-        process.wait()
-        if stdin is not None:
-            try:
-                stdin.close()
-            except IOError:
-                pass
-        stderr.close()
-        stdout.close()
+    counts = '''count no_user_id imported imported_rsa unchanged
+            n_uids n_subk n_sigs n_revoc sec_read sec_imported
+            sec_dups not_imported'''.split()
+    def __init__(self, gpg):
+        self.gpg = gpg
+        self.imported = []
+        self.results = []
+        self.fingerprints = []
+        for result in self.counts:
+            setattr(self, result, None)
 
-    def _handle_io(self, args, file, result, passphrase=None, binary=False):
-        "Handle a call to GPG - pass input data, collect output data"
-        # Handle a basic data call - pass data to GPG, handle the output
-        # including status information. Garbage In, Garbage Out :)
-        p = self._open_subprocess(args, passphrase is not None)
-        if not binary:
-            stdin = codecs.getwriter(self.encoding)(p.stdin)
-        else:
-            stdin = p.stdin
-        if passphrase:
-            _write_passphrase(stdin, passphrase, self.encoding)
-        writer = _threaded_copy_data(file, stdin)
-        self._collect_output(p, result, writer, stdin)
-        return result
+    def __nonzero__(self):
+        if self.not_imported: return False
+        if not self.fingerprints: return False
+        return True
 
-    #
-    # SIGNATURE METHODS
-    #
-    def sign(self, message, **kwargs):
-        """sign message"""
-        f = _make_binary_stream(message, self.encoding)
-        result = self.sign_file(f, **kwargs)
-        f.close()
-        return result
+    __bool__ = __nonzero__
 
-    def sign_file(self, file, keyid=None, passphrase=None, clearsign=True,
-                  detach=False, binary=False):
-        """sign file"""
-        logger.debug("sign_file: %s", file)
-        if binary:
-            args = ['-s']
-        else:
-            args = ['-sa']
-        # You can't specify detach-sign and clearsign together: gpg ignores
-        # the detach-sign in that case.
-        if detach:
-            args.append("--detach-sign")
-        elif clearsign:
-            args.append("--clearsign")
-        if keyid:
-            args.append("--default-key %s" % keyid)
-        result = Sign(self.encoding)
-        #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.
-        p = self._open_subprocess(args, passphrase is not None)
-        try:
-            stdin = p.stdin
-            if passphrase:
-                _write_passphrase(stdin, passphrase, self.encoding)
-            writer = _threaded_copy_data(file, stdin)
-        except IOError:
-            logging.exception("error writing message")
-            writer = None
-        self._collect_output(p, result, writer, stdin)
-        return result
+    ok_reason = {
+        '0': 'Not actually changed',
+        '1': 'Entirely new key',
+        '2': 'New user IDs',
+        '4': 'New signatures',
+        '8': 'New subkeys',
+        '16': 'Contains private key',
+    }
 
-    def verify(self, data):
-        """Verify the signature on the contents of the string 'data'
+    problem_reason = {
+        '0': 'No specific reason given',
+        '1': 'Invalid Certificate',
+        '2': 'Issuer Certificate missing',
+        '3': 'Certificate Chain too long',
+        '4': 'Error storing certificate',
+    }
 
-        >>> gpg = GPG(gnupghome="keys")
-        >>> input = gpg.gen_key_input(Passphrase='foo')
-        >>> key = gpg.gen_key(input)
-        >>> assert key
-        >>> sig = gpg.sign('hello',keyid=key.fingerprint,passphrase='bar')
-        >>> assert not sig
-        >>> sig = gpg.sign('hello',keyid=key.fingerprint,passphrase='foo')
-        >>> assert sig
-        >>> verify = gpg.verify(sig.data)
-        >>> assert verify
-
-        """
-        f = _make_binary_stream(data, self.encoding)
-        result = self.verify_file(f)
-        f.close()
-        return result
-
-    def verify_file(self, file, data_filename=None):
-        "Verify the signature on the contents of the file-like object 'file'"
-        logger.debug('verify_file: %r, %r', file, data_filename)
-        result = Verify()
-        args = ['--verify']
-        if data_filename is None:
-            self._handle_io(args, file, result, binary=True)
-        else:
-            logger.debug('Handling detached verification')
-            import tempfile
-            fd, fn = tempfile.mkstemp(prefix='pygpg')
-            s = file.read()
-            file.close()
-            logger.debug('Wrote to temp file: %r', s)
-            os.write(fd, s)
-            os.close(fd)
-            args.append(fn)
-            args.append(data_filename)
+    def handle_status(self, key, value):
+        if key == "IMPORTED":
+            # this duplicates info we already see in import_ok & import_problem
+            pass
+        elif key == "NODATA":
+            self.results.append({'fingerprint': None,
+                'problem': '0', 'text': 'No valid data found'})
+        elif key == "IMPORT_OK":
+            reason, fingerprint = value.split()
+            reasons = []
+            for code, text in list(self.ok_reason.items()):
+                if int(reason) | int(code) == int(reason):
+                    reasons.append(text)
+            reasontext = '\n'.join(reasons) + "\n"
+            self.results.append({'fingerprint': fingerprint,
+                'ok': reason, 'text': reasontext})
+            self.fingerprints.append(fingerprint)
+        elif key == "IMPORT_PROBLEM":
             try:
-                p = self._open_subprocess(args)
-                self._collect_output(p, result, stdin=p.stdin)
-            finally:
-                os.unlink(fn)
-        return result
-
-    #
-    # KEY MANAGEMENT
-    #
-
-    def import_keys(self, key_data):
-        """ import the key_data into our keyring
+                reason, fingerprint = value.split()
+            except:
+                reason = value
+                fingerprint = '<unknown>'
+            self.results.append({'fingerprint': fingerprint,
+                '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]))
+        elif key == "KEYEXPIRED":
+            self.results.append({'fingerprint': None,
+                'problem': '0', 'text': 'Key expired'})
+        elif key == "SIGEXPIRED":
+            self.results.append({'fingerprint': None,
+                'problem': '0', 'text': 'Signature expired'})
+        else:
+            raise ValueError("Unknown status message: %r" % key)
 
-        >>> import shutil
-        >>> shutil.rmtree("keys")
-        >>> gpg = GPG(gnupghome="keys")
-        >>> input = gpg.gen_key_input()
-        >>> result = gpg.gen_key(input)
-        >>> print1 = result.fingerprint
-        >>> result = gpg.gen_key(input)
-        >>> print2 = result.fingerprint
-        >>> pubkey1 = gpg.export_keys(print1)
-        >>> seckey1 = gpg.export_keys(print1,secret=True)
-        >>> seckeys = gpg.list_keys(secret=True)
-        >>> pubkeys = gpg.list_keys()
-        >>> assert print1 in seckeys.fingerprints
-        >>> assert print1 in pubkeys.fingerprints
-        >>> str(gpg.delete_keys(print1))
-        'Must delete secret key first'
-        >>> str(gpg.delete_keys(print1,secret=True))
-        'ok'
-        >>> str(gpg.delete_keys(print1))
-        'ok'
-        >>> str(gpg.delete_keys("nosuchkey"))
-        'No such key'
-        >>> seckeys = gpg.list_keys(secret=True)
-        >>> pubkeys = gpg.list_keys()
-        >>> assert not print1 in seckeys.fingerprints
-        >>> assert not print1 in pubkeys.fingerprints
-        >>> result = gpg.import_keys('foo')
-        >>> assert not result
-        >>> result = gpg.import_keys(pubkey1)
-        >>> pubkeys = gpg.list_keys()
-        >>> seckeys = gpg.list_keys(secret=True)
-        >>> assert not print1 in seckeys.fingerprints
-        >>> assert print1 in pubkeys.fingerprints
-        >>> result = gpg.import_keys(seckey1)
-        >>> assert result
-        >>> seckeys = gpg.list_keys(secret=True)
-        >>> pubkeys = gpg.list_keys()
-        >>> assert print1 in seckeys.fingerprints
-        >>> assert print1 in pubkeys.fingerprints
-        >>> assert print2 in pubkeys.fingerprints
+    def summary(self):
+        l = []
+        l.append('%d imported'%self.imported)
+        if self.not_imported:
+            l.append('%d not imported'%self.not_imported)
+        return ', '.join(l)
 
-        """
-        result = ImportResult()
-        logger.debug('import_keys: %r', key_data[:256])
-        data = _make_binary_stream(key_data, self.encoding)
-        self._handle_io(['--import'], data, result, binary=True)
-        logger.debug('import_keys result: %r', result.__dict__)
-        data.close()
-        return result
+class ListKeys(list):
+    ''' Handle status messages for --list-keys.
 
-    def recv_keys(self, keyserver, *keyids):
-        """Import a key from a keyserver
+        Handle pub and uid (relating the latter to the former).
 
-        >>> import shutil
-        >>> shutil.rmtree("keys")
-        >>> gpg = GPG(gnupghome="keys")
-        >>> result = gpg.recv_keys('pgp.mit.edu', '3FF0DB166A7476EA')
-        >>> assert result
+        Don't care about (info from src/DETAILS):
 
-        """
-        result = ImportResult()
-        logger.debug('recv_keys: %r', keyids)
-        data = _make_binary_stream("", self.encoding)
-        #data = ""
-        args = ['--keyserver', keyserver, '--recv-keys']
-        args.extend(keyids)
-        self._handle_io(args, data, result, binary=True)
-        logger.debug('recv_keys result: %r', result.__dict__)
-        data.close()
-        return result
+        crt = X.509 certificate
+        crs = X.509 certificate and private key available
+        sub = subkey (secondary key)
+        ssb = secret subkey (secondary key)
+        uat = user attribute (same as user id except for field 10).
+        sig = signature
+        rev = revocation signature
+        pkd = public key data (special field format, see below)
+        grp = reserved for gpgsm
+        rvk = revocation key
+    '''
+    def __init__(self, gpg):
+        self.gpg = gpg
+        self.curkey = None
+        self.fingerprints = []
+        self.uids = []
 
-    def delete_keys(self, fingerprints, secret=False):
-        which='key'
-        if secret:
-            which='secret-key'
-        if _is_sequence(fingerprints):
-            fingerprints = ' '.join(fingerprints)
-        args = ["--batch --delete-%s %s" % (which, fingerprints)]
-        result = DeleteResult()
-        p = self._open_subprocess(args)
-        self._collect_output(p, result, stdin=p.stdin)
-        return result
+    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.append(self.curkey)
 
-    def export_keys(self, keyids, secret=False):
-        "export the indicated keys. 'keyid' is anything gpg accepts"
-        which=''
-        if secret:
-            which='-secret-key'
-        if _is_sequence(keyids):
-            keyids = ' '.join(keyids)
-        args = ["--armor --export%s %s" % (which, keyids)]
-        p = self._open_subprocess(args)
-        # gpg --export produces no status-fd output; stdout will be
-        # empty in case of failure
-        #stdout, stderr = p.communicate()
-        result = DeleteResult() # any result will do
-        self._collect_output(p, result, stdin=p.stdin)
-        logger.debug('export_keys result: %r', result.data)
-        return result.data.decode(self.encoding)
+    pub = sec = key
 
-    def list_keys(self, secret=False):
-        """ list the keys currently in the keyring
+    def fpr(self, args):
+        self.curkey['fingerprint'] = args[9]
+        self.fingerprints.append(args[9])
 
-        >>> import shutil
-        >>> shutil.rmtree("keys")
-        >>> gpg = GPG(gnupghome="keys")
-        >>> input = gpg.gen_key_input()
-        >>> result = gpg.gen_key(input)
-        >>> print1 = result.fingerprint
-        >>> result = gpg.gen_key(input)
-        >>> print2 = result.fingerprint
-        >>> pubkeys = gpg.list_keys()
-        >>> assert print1 in pubkeys.fingerprints
-        >>> assert print2 in pubkeys.fingerprints
+    def uid(self, args):
+        self.curkey['uids'].append(args[9])
+        self.uids.append(args[9])
 
-        """
+    def handle_status(self, key, value):
+        pass
 
-        which='keys'
-        if secret:
-            which='secret-keys'
-        args = "--list-%s --fixed-list-mode --fingerprint --with-colons" % (which,)
-        args = [args]
-        p = self._open_subprocess(args)
+class Crypt(Verify):
+    "Handle status messages for --encrypt and --decrypt"
+    def __init__(self, gpg):
+        Verify.__init__(self, gpg)
+        self.data = ''
+        self.ok = False
+        self.status = ''
 
-        # there might be some status thingumy here I should handle... (amk)
-        # ...nope, unless you care about expired sigs or keys (stevegt)
+    def __nonzero__(self):
+        if self.ok: return True
+        return False
 
-        # Get the response information
-        result = ListKeys()
-        self._collect_output(p, result, stdin=p.stdin)
-        lines = result.data.decode(self.encoding).splitlines()
-        valid_keywords = 'pub uid sec fpr'.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
+    __bool__ = __nonzero__
 
-    def gen_key(self, input):
-        """Generate a key; you might use gen_key_input() to create the
-        control input.
+    def __str__(self):
+        return self.data.decode(self.gpg.encoding, self.gpg.decode_errors)
 
-        >>> gpg = GPG(gnupghome="keys")
-        >>> input = gpg.gen_key_input()
-        >>> result = gpg.gen_key(input)
-        >>> assert result
-        >>> result = gpg.gen_key('foo')
-        >>> assert not result
-
-        """
-        args = ["--gen-key --batch"]
-        result = GenKey()
-        f = _make_binary_stream(input, self.encoding)
-        self._handle_io(args, f, result, binary=True)
-        f.close()
-        return result
+    def handle_status(self, key, value):
+        if key in ("ENC_TO", "USERID_HINT", "GOODMDC", "END_DECRYPTION",
+                   "BEGIN_SIGNING", "NO_SECKEY", "ERROR", "NODATA"):
+            # 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"):
+            self.status = key.replace("_", " ").lower()
+        elif key == "NEED_PASSPHRASE_SYM":
+            self.status = 'need symmetric passphrase'
+        elif key == "BEGIN_DECRYPTION":
+            self.status = 'decryption incomplete'
+        elif key == "BEGIN_ENCRYPTION":
+            self.status = 'encryption incomplete'
+        elif key == "DECRYPTION_OKAY":
+            self.status = 'decryption ok'
+            self.ok = True
+        elif key == "END_ENCRYPTION":
+            self.status = 'encryption ok'
+            self.ok = True
+        elif key == "INV_RECP":
+            self.status = 'invalid recipient'
+        elif key == "KEYEXPIRED":
+            self.status = 'key expired'
+        elif key == "SIG_CREATED":
+            self.status = 'sig created'
+        elif key == "SIGEXPIRED":
+            self.status = 'sig expired'
+        else:
+            Verify.handle_status(self, key, value)
 
-    def gen_key_input(self, **kwargs):
-        """
-        Generate --gen-key input per gpg doc/DETAILS
-        """
-        parms = {}
-        for key, val in list(kwargs.items()):
-            key = key.replace('_','-').title()
-            parms[key] = val
-        parms.setdefault('Key-Type','RSA')
-        parms.setdefault('Key-Length',1024)
-        parms.setdefault('Name-Real', "Autogenerated Key")
-        parms.setdefault('Name-Comment', "Generated by gnupg.py")
-        try:
-            logname = os.environ['LOGNAME']
-        except KeyError:
-            logname = os.environ['USERNAME']
-        hostname = socket.gethostname()
-        parms.setdefault('Name-Email', "%s@%s" % (logname.replace(' ', '_'),
-                                                  hostname))
-        out = "Key-Type: %s\n" % parms.pop('Key-Type')
-        for key, val in list(parms.items()):
-            out += "%s: %s\n" % (key, val)
-        out += "%commit\n"
-        return out
+class GenKey(object):
+    "Handle status messages for --gen-key"
+    def __init__(self, gpg):
+        self.gpg = gpg
+        self.type = None
+        self.fingerprint = None
 
-        # Key-Type: RSA
-        # Key-Length: 1024
-        # Name-Real: ISdlink Server on %s
-        # Name-Comment: Created by %s
-        # Name-Email: isdlink@%s
-        # Expire-Date: 0
-        # %commit
-        #
-        #
-        # Key-Type: DSA
-        # Key-Length: 1024
-        # Subkey-Type: ELG-E
-        # Subkey-Length: 1024
-        # Name-Real: Joe Tester
-        # Name-Comment: with stupid passphrase
-        # Name-Email: joe@foo.bar
-        # Expire-Date: 0
-        # Passphrase: abc
-        # %pubring foo.pub
-        # %secring foo.sec
-        # %commit
+    def __nonzero__(self):
+        if self.fingerprint: return True
+        return False
 
-    #
-    # ENCRYPTION
-    #
-    def encrypt_file(self, file, recipients, sign=None,
-            always_trust=False, passphrase=None,
-            armor=True, output=None, symmetric=False):
-        "Encrypt the message read from the file-like object 'file'"
-        args = ['--encrypt']
-        if symmetric:
-            args = ['--symmetric']
-        else:
-            args = ['--encrypt']
-            if not _is_sequence(recipients):
-                recipients = (recipients,)
-            for recipient in recipients:
-                args.append('--recipient %s' % recipient)
-        if armor:   # create ascii-armored output - set to False for binary output
-            args.append('--armor')
-        if output:  # write the output to a file with the specified name
-            if os.path.exists(output):
-                os.remove(output) # to avoid overwrite confirmation message
-            args.append('--output %s' % output)
-        if sign:
-            args.append("--sign --default-key %s" % sign)
-        if always_trust:
-            args.append("--always-trust")
-        result = Crypt(self.encoding)
-        self._handle_io(args, file, result, passphrase=passphrase, binary=True)
-        logger.debug('encrypt result: %r', result.data)
-        return result
+    __bool__ = __nonzero__
 
-    def encrypt(self, data, recipients, **kwargs):
-        """Encrypt the message contained in the string 'data'
+    def __str__(self):
+        return self.fingerprint or ''
 
-        >>> import shutil
-        >>> if os.path.exists("keys"):
-        ...     shutil.rmtree("keys")
-        >>> gpg = GPG(gnupghome="keys")
-        >>> input = gpg.gen_key_input(passphrase='foo')
-        >>> result = gpg.gen_key(input)
-        >>> print1 = result.fingerprint
-        >>> input = gpg.gen_key_input()
-        >>> result = gpg.gen_key(input)
-        >>> print2 = result.fingerprint
-        >>> result = gpg.encrypt("hello",print2)
-        >>> message = str(result)
-        >>> assert message != 'hello'
-        >>> result = gpg.decrypt(message)
-        >>> assert result
-        >>> str(result)
-        'hello'
-        >>> result = gpg.encrypt("hello again",print1)
-        >>> message = str(result)
-        >>> result = gpg.decrypt(message)
-        >>> result.status == 'need passphrase'
-        True
-        >>> result = gpg.decrypt(message,passphrase='bar')
-        >>> result.status in ('decryption failed', 'bad passphrase')
-        True
-        >>> assert not result
-        >>> result = gpg.decrypt(message,passphrase='foo')
-        >>> result.status == 'decryption ok'
-        True
-        >>> str(result)
-        'hello again'
-        >>> result = gpg.encrypt("signed hello",print2,sign=print1)
-        >>> result.status == 'need passphrase'
-        True
-        >>> result = gpg.encrypt("signed hello",print2,sign=print1,passphrase='foo')
-        >>> result.status == 'encryption ok'
-        True
-        >>> message = str(result)
-        >>> result = gpg.decrypt(message)
-        >>> result.status == 'decryption ok'
-        True
-        >>> assert result.fingerprint == print1
+    def handle_status(self, key, value):
+        if key in ("PROGRESS", "GOOD_PASSPHRASE", "NODATA"):
+            pass
+        elif key == "KEY_CREATED":
+            (self.type,self.fingerprint) = value.split()
+        else:
+            raise ValueError("Unknown status message: %r" % key)
 
-        """
-        data = _make_binary_stream(data, self.encoding)
-        result = self.encrypt_file(data, recipients, **kwargs)
-        data.close()
-        return result
+class DeleteResult(object):
+    "Handle status messages for --delete-key and --delete-secret-key"
+    def __init__(self, gpg):
+        self.gpg = gpg
+        self.status = 'ok'
 
-    def decrypt(self, message, **kwargs):
-        data = _make_binary_stream(message, self.encoding)
-        result = self.decrypt_file(data, **kwargs)
-        data.close()
-        return result
+    def __str__(self):
+        return self.status
 
-    def decrypt_file(self, file, always_trust=False, passphrase=None,
-                     output=None):
-        args = ["--decrypt"]
-        if output:  # write the output to a file with the specified name
-            if os.path.exists(output):
-                os.remove(output) # to avoid overwrite confirmation message
-            args.append('--output %s' % output)
-        if always_trust:
-            args.append("--always-trust")
-        result = Crypt(self.encoding)
-        self._handle_io(args, file, result, passphrase, binary=True)
-        logger.debug('decrypt result: %r', result.data)
-        return result
+    problem_reason = {
+        '1': 'No such key',
+        '2': 'Must delete secret key first',
+        '3': 'Ambigious specification',
+    }
 
-class Verify(object):
-    "Handle status messages for --verify"
+    def handle_status(self, key, value):
+        if key == "DELETE_PROBLEM":
+            self.status = self.problem_reason.get(value,
+                                                  "Unknown error: %r" % value)
+        else:
+            raise ValueError("Unknown status message: %r" % key)
 
-    def __init__(self):
-        self.valid = False
-        self.fingerprint = self.creation_date = self.timestamp = None
-        self.signature_id = self.key_id = None
-        self.username = None
+class Sign(object):
+    "Handle status messages for --sign"
+    def __init__(self, gpg):
+        self.gpg = gpg
+        self.type = None
+        self.fingerprint = None
 
     def __nonzero__(self):
-        return self.valid
+        return self.fingerprint is not None
 
     __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 ("TRUST_UNDEFINED", "TRUST_NEVER", "TRUST_MARGINAL",
-                   "TRUST_FULLY", "TRUST_ULTIMATE", "RSA_OR_IDEA", "NODATA"):
-            pass
-        elif key in ("PLAINTEXT", "PLAINTEXT_LENGTH"):
+        if key in ("USERID_HINT", "NEED_PASSPHRASE", "BAD_PASSPHRASE",
+                   "GOOD_PASSPHRASE", "BEGIN_SIGNING"):
             pass
-        elif key == "BADSIG":
-            self.valid = False
-            self.key_id, self.username = value.split(None, 1)
-        elif key == "GOODSIG":
-            self.valid = True
-            self.key_id, self.username = value.split(None, 1)
-        elif key == "VALIDSIG":
-            (self.fingerprint,
-             self.creation_date,
-             self.sig_timestamp,
-             self.expire_timestamp) = value.split()[:4]
-            # may be different if signature is made with a subkey
-            self.pubkey_fingerprint = value.split()[-1]
-        elif key == "SIG_ID":
-            (self.signature_id,
-             self.creation_date, self.timestamp) = value.split()
-        elif key == "ERRSIG":
-            self.valid = False
-            (self.key_id,
-             algo, hash_algo,
-             cls,
-             self.timestamp) = value.split()[:5]
-        elif key == "NO_PUBKEY":
-            self.valid = False
-            self.key_id = value
-        elif key in ("KEYEXPIRED", "SIGEXPIRED"):
-            # these are useless in verify, since they are spit out for any
-            # pub/subkeys on the key, not just the one doing the signing.
-            # if we want to check for signatures with expired key,
-            # the relevant flag is EXPKEYSIG.
-            pass
-        elif key in ("EXPKEYSIG", "REVKEYSIG"):
-            # signed with expired or revoked key
-            self.valid = False
-            self.key_id = value.split()[0]
+        elif key == "SIG_CREATED":
+            (self.type,
+             algo, hashalgo, cls,
+             self.timestamp, self.fingerprint
+             ) = value.split()
         else:
             raise ValueError("Unknown status message: %r" % key)
 
-class ImportResult(object):
-    "Handle status messages for --import"
-
-    counts = '''count no_user_id imported imported_rsa unchanged
-            n_uids n_subk n_sigs n_revoc sec_read sec_imported
-            sec_dups not_imported'''.split()
-    def __init__(self):
-        self.imported = []
-        self.results = []
-        self.fingerprints = []
-        for result in self.counts:
-            setattr(self, result, None)
-
-    def __nonzero__(self):
-        if self.not_imported: return False
-        if not self.fingerprints: return False
-        return True
 
-    __bool__ = __nonzero__
+class GPG(object):
 
-    ok_reason = {
-        '0': 'Not actually changed',
-        '1': 'Entirely new key',
-        '2': 'New user IDs',
-        '4': 'New signatures',
-        '8': 'New subkeys',
-        '16': 'Contains private key',
-    }
+    decode_errors = 'strict'
 
-    problem_reason = {
-        '0': 'No specific reason given',
-        '1': 'Invalid Certificate',
-        '2': 'Issuer Certificate missing',
-        '3': 'Certificate Chain too long',
-        '4': 'Error storing certificate',
+    result_map = {
+        'crypt': Crypt,
+        'delete': DeleteResult,
+        'generate': GenKey,
+        'import': ImportResult,
+        'list': ListKeys,
+        'sign': Sign,
+        'verify': Verify,
     }
 
-    def handle_status(self, key, value):
-        if key == "IMPORTED":
-            # this duplicates info we already see in import_ok & import_problem
-            pass
-        elif key == "NODATA":
-            self.results.append({'fingerprint': None,
-                'problem': '0', 'text': 'No valid data found'})
-        elif key == "IMPORT_OK":
-            reason, fingerprint = value.split()
-            reasons = []
-            for code, text in list(self.ok_reason.items()):
-                if int(reason) | int(code) == int(reason):
-                    reasons.append(text)
-            reasontext = '\n'.join(reasons) + "\n"
-            self.results.append({'fingerprint': fingerprint,
-                'ok': reason, 'text': reasontext})
-            self.fingerprints.append(fingerprint)
-        elif key == "IMPORT_PROBLEM":
-            try:
-                reason, fingerprint = value.split()
-            except:
-                reason = value
-                fingerprint = '<unknown>'
-            self.results.append({'fingerprint': fingerprint,
-                '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]))
-        elif key == "KEYEXPIRED":
-            self.results.append({'fingerprint': None,
-                'problem': '0', 'text': 'Key expired'})
-        elif key == "SIGEXPIRED":
-            self.results.append({'fingerprint': None,
-                'problem': '0', 'text': 'Signature expired'})
-        else:
-            raise ValueError("Unknown status message: %r" % key)
-
-    def summary(self):
-        l = []
-        l.append('%d imported'%self.imported)
-        if self.not_imported:
-            l.append('%d not imported'%self.not_imported)
-        return ', '.join(l)
+    "Encapsulate access to the gpg executable"
+    def __init__(self, gpgbinary='gpg', gnupghome=None, verbose=False,
+                 use_agent=False, keyring=None):
+        """Initialize a GPG process wrapper.  Options are:
 
-class ListKeys(list):
-    ''' Handle status messages for --list-keys.
+        gpgbinary -- full pathname for GPG binary.
 
-        Handle pub and uid (relating the latter to the former).
+        gnupghome -- full pathname to where we can find the public and
+        private keyrings.  Default is whatever gpg defaults to.
+        keyring -- name of alternative keyring file to use. If specified,
+        the default keyring is not used.
+        """
+        self.gpgbinary = gpgbinary
+        self.gnupghome = gnupghome
+        self.keyring = keyring
+        self.verbose = verbose
+        self.use_agent = use_agent
+        self.encoding = locale.getpreferredencoding()
+        if self.encoding is None: # This happens on Jython!
+            self.encoding = sys.stdin.encoding
+        if gnupghome and not os.path.isdir(self.gnupghome):
+            os.makedirs(self.gnupghome,0x1C0)
+        p = self._open_subprocess(["--version"])
+        result = self.result_map['verify'](self) # any result will do for this
+        self._collect_output(p, result, stdin=p.stdin)
+        if p.returncode != 0:
+            raise ValueError("Error invoking gpg: %s: %s" % (p.returncode,
+                                                             result.stderr))
 
-        Don't care about (info from src/DETAILS):
+    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 = [self.gpgbinary, '--status-fd 2 --no-tty']
+        if self.gnupghome:
+            cmd.append('--homedir "%s" ' % self.gnupghome)
+        if self.keyring:
+            cmd.append('--no-default-keyring --keyring "%s" ' % self.keyring)
+        if passphrase:
+            cmd.append('--batch --passphrase-fd 0')
+        if self.use_agent:
+            cmd.append('--use-agent')
+        cmd.extend(args)
+        cmd = ' '.join(cmd)
+        if self.verbose:
+            print(cmd)
+        logger.debug("%s", cmd)
+        return Popen(cmd, shell=True, stdin=PIPE, stdout=PIPE, stderr=PIPE)
 
-        crt = X.509 certificate
-        crs = X.509 certificate and private key available
-        sub = subkey (secondary key)
-        ssb = secret subkey (secondary key)
-        uat = user attribute (same as user id except for field 10).
-        sig = signature
-        rev = revocation signature
-        pkd = public key data (special field format, see below)
-        grp = reserved for gpgsm
-        rvk = revocation key
-    '''
-    def __init__(self):
-        self.curkey = None
-        self.fingerprints = []
-        self.uids = []
+    def _read_response(self, stream, result):
+        # Internal method: reads all the stderr output from GPG, taking notice
+        # only of lines that begin with the magic [GNUPG:] prefix.
+        #
+        # Calls methods on the response object for each valid token found,
+        # with the arg being the remainder of the status line.
+        lines = []
+        while True:
+            line = stream.readline()
+            if len(line) == 0:
+                break
+            lines.append(line)
+            line = line.rstrip()
+            if self.verbose:
+                print(line)
+            logger.debug("%s", line)
+            if line[0:9] == '[GNUPG:] ':
+                # Chop off the prefix
+                line = line[9:]
+                L = line.split(None, 1)
+                keyword = L[0]
+                if len(L) > 1:
+                    value = L[1]
+                else:
+                    value = ""
+                result.handle_status(keyword, value)
+        result.stderr = ''.join(lines)
 
-    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.append(self.curkey)
+    def _read_data(self, stream, result):
+        # Read the contents of the file from GPG's stdout
+        chunks = []
+        while True:
+            data = stream.read(1024)
+            if len(data) == 0:
+                break
+            logger.debug("chunk: %r" % data[:256])
+            chunks.append(data)
+        if _py3k:
+            # Join using b'' or '', as appropriate
+            result.data = type(data)().join(chunks)
+        else:
+            result.data = ''.join(chunks)
 
-    pub = sec = key
+    def _collect_output(self, process, result, writer=None, stdin=None):
+        """
+        Drain the subprocesses output streams, writing the collected output
+        to the result. If a writer thread (writing to the subprocess) is given,
+        make sure it's joined before returning. If a stdin stream is given,
+        close it before returning.
+        """
+        stderr = codecs.getreader(self.encoding)(process.stderr)
+        rr = threading.Thread(target=self._read_response, args=(stderr, result))
+        rr.setDaemon(True)
+        logger.debug('stderr reader: %r', rr)
+        rr.start()
 
-    def fpr(self, args):
-        self.curkey['fingerprint'] = args[9]
-        self.fingerprints.append(args[9])
+        stdout = process.stdout
+        dr = threading.Thread(target=self._read_data, args=(stdout, result))
+        dr.setDaemon(True)
+        logger.debug('stdout reader: %r', dr)
+        dr.start()
 
-    def uid(self, args):
-        self.curkey['uids'].append(args[9])
-        self.uids.append(args[9])
+        dr.join()
+        rr.join()
+        if writer is not None:
+            writer.join()
+        process.wait()
+        if stdin is not None:
+            try:
+                stdin.close()
+            except IOError:
+                pass
+        stderr.close()
+        stdout.close()
 
-    def handle_status(self, key, value):
-        pass
+    def _handle_io(self, args, file, result, passphrase=None, binary=False):
+        "Handle a call to GPG - pass input data, collect output data"
+        # Handle a basic data call - pass data to GPG, handle the output
+        # including status information. Garbage In, Garbage Out :)
+        p = self._open_subprocess(args, passphrase is not None)
+        if not binary:
+            stdin = codecs.getwriter(self.encoding)(p.stdin)
+        else:
+            stdin = p.stdin
+        if passphrase:
+            _write_passphrase(stdin, passphrase, self.encoding)
+        writer = _threaded_copy_data(file, stdin)
+        self._collect_output(p, result, writer, stdin)
+        return result
 
-class Crypt(Verify):
-    "Handle status messages for --encrypt and --decrypt"
-    def __init__(self, encoding):
-        Verify.__init__(self)
-        self.data = ''
-        self.ok = False
-        self.status = ''
-        self.encoding = encoding
+    #
+    # SIGNATURE METHODS
+    #
+    def sign(self, message, **kwargs):
+        """sign message"""
+        f = _make_binary_stream(message, self.encoding)
+        result = self.sign_file(f, **kwargs)
+        f.close()
+        return result
 
-    def __nonzero__(self):
-        if self.ok: return True
-        return False
+    def sign_file(self, file, keyid=None, passphrase=None, clearsign=True,
+                  detach=False, binary=False):
+        """sign file"""
+        logger.debug("sign_file: %s", file)
+        if binary:
+            args = ['-s']
+        else:
+            args = ['-sa']
+        # You can't specify detach-sign and clearsign together: gpg ignores
+        # the detach-sign in that case.
+        if detach:
+            args.append("--detach-sign")
+        elif clearsign:
+            args.append("--clearsign")
+        if keyid:
+            args.append('--default-key "%s"' % keyid)
+        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.
+        p = self._open_subprocess(args, passphrase is not None)
+        try:
+            stdin = p.stdin
+            if passphrase:
+                _write_passphrase(stdin, passphrase, self.encoding)
+            writer = _threaded_copy_data(file, stdin)
+        except IOError:
+            logging.exception("error writing message")
+            writer = None
+        self._collect_output(p, result, writer, stdin)
+        return result
 
-    __bool__ = __nonzero__
+    def verify(self, data):
+        """Verify the signature on the contents of the string 'data'
 
-    def __str__(self):
-        return self.data.decode(self.encoding)
+        >>> gpg = GPG(gnupghome="keys")
+        >>> input = gpg.gen_key_input(Passphrase='foo')
+        >>> key = gpg.gen_key(input)
+        >>> assert key
+        >>> sig = gpg.sign('hello',keyid=key.fingerprint,passphrase='bar')
+        >>> assert not sig
+        >>> sig = gpg.sign('hello',keyid=key.fingerprint,passphrase='foo')
+        >>> assert sig
+        >>> verify = gpg.verify(sig.data)
+        >>> assert verify
 
-    def handle_status(self, key, value):
-        if key in ("ENC_TO", "USERID_HINT", "GOODMDC", "END_DECRYPTION",
-                   "BEGIN_SIGNING", "NO_SECKEY", "ERROR", "NODATA"):
-            # 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"):
-            self.status = key.replace("_", " ").lower()
-        elif key == "NEED_PASSPHRASE_SYM":
-            self.status = 'need symmetric passphrase'
-        elif key == "BEGIN_DECRYPTION":
-            self.status = 'decryption incomplete'
-        elif key == "BEGIN_ENCRYPTION":
-            self.status = 'encryption incomplete'
-        elif key == "DECRYPTION_OKAY":
-            self.status = 'decryption ok'
-            self.ok = True
-        elif key == "END_ENCRYPTION":
-            self.status = 'encryption ok'
-            self.ok = True
-        elif key == "INV_RECP":
-            self.status = 'invalid recipient'
-        elif key == "KEYEXPIRED":
-            self.status = 'key expired'
-        elif key == "SIG_CREATED":
-            self.status = 'sig created'
-        elif key == "SIGEXPIRED":
-            self.status = 'sig expired'
+        """
+        f = _make_binary_stream(data, self.encoding)
+        result = self.verify_file(f)
+        f.close()
+        return result
+
+    def verify_file(self, file, data_filename=None):
+        "Verify the signature on the contents of the file-like object 'file'"
+        logger.debug('verify_file: %r, %r', file, data_filename)
+        result = self.result_map['verify'](self)
+        args = ['--verify']
+        if data_filename is None:
+            self._handle_io(args, file, result, binary=True)
         else:
-            Verify.handle_status(self, key, value)
+            logger.debug('Handling detached verification')
+            import tempfile
+            fd, fn = tempfile.mkstemp(prefix='pygpg')
+            s = file.read()
+            file.close()
+            logger.debug('Wrote to temp file: %r', s)
+            os.write(fd, s)
+            os.close(fd)
+            args.append(fn)
+            args.append('"%s"' % data_filename)
+            try:
+                p = self._open_subprocess(args)
+                self._collect_output(p, result, stdin=p.stdin)
+            finally:
+                os.unlink(fn)
+        return result
 
-class GenKey(object):
-    "Handle status messages for --gen-key"
-    def __init__(self):
-        self.type = None
-        self.fingerprint = None
+    #
+    # KEY MANAGEMENT
+    #
 
-    def __nonzero__(self):
-        if self.fingerprint: return True
-        return False
+    def import_keys(self, key_data):
+        """ import the key_data into our keyring
 
-    __bool__ = __nonzero__
+        >>> import shutil
+        >>> shutil.rmtree("keys")
+        >>> gpg = GPG(gnupghome="keys")
+        >>> input = gpg.gen_key_input()
+        >>> result = gpg.gen_key(input)
+        >>> print1 = result.fingerprint
+        >>> result = gpg.gen_key(input)
+        >>> print2 = result.fingerprint
+        >>> pubkey1 = gpg.export_keys(print1)
+        >>> seckey1 = gpg.export_keys(print1,secret=True)
+        >>> seckeys = gpg.list_keys(secret=True)
+        >>> pubkeys = gpg.list_keys()
+        >>> assert print1 in seckeys.fingerprints
+        >>> assert print1 in pubkeys.fingerprints
+        >>> str(gpg.delete_keys(print1))
+        'Must delete secret key first'
+        >>> str(gpg.delete_keys(print1,secret=True))
+        'ok'
+        >>> str(gpg.delete_keys(print1))
+        'ok'
+        >>> str(gpg.delete_keys("nosuchkey"))
+        'No such key'
+        >>> seckeys = gpg.list_keys(secret=True)
+        >>> pubkeys = gpg.list_keys()
+        >>> assert not print1 in seckeys.fingerprints
+        >>> assert not print1 in pubkeys.fingerprints
+        >>> result = gpg.import_keys('foo')
+        >>> assert not result
+        >>> result = gpg.import_keys(pubkey1)
+        >>> pubkeys = gpg.list_keys()
+        >>> seckeys = gpg.list_keys(secret=True)
+        >>> assert not print1 in seckeys.fingerprints
+        >>> assert print1 in pubkeys.fingerprints
+        >>> result = gpg.import_keys(seckey1)
+        >>> assert result
+        >>> seckeys = gpg.list_keys(secret=True)
+        >>> pubkeys = gpg.list_keys()
+        >>> assert print1 in seckeys.fingerprints
+        >>> assert print1 in pubkeys.fingerprints
+        >>> assert print2 in pubkeys.fingerprints
 
-    def __str__(self):
-        return self.fingerprint or ''
+        """
+        result = self.result_map['import'](self)
+        logger.debug('import_keys: %r', key_data[:256])
+        data = _make_binary_stream(key_data, self.encoding)
+        self._handle_io(['--import'], data, result, binary=True)
+        logger.debug('import_keys result: %r', result.__dict__)
+        data.close()
+        return result
 
-    def handle_status(self, key, value):
-        if key in ("PROGRESS", "GOOD_PASSPHRASE", "NODATA"):
-            pass
-        elif key == "KEY_CREATED":
-            (self.type,self.fingerprint) = value.split()
-        else:
-            raise ValueError("Unknown status message: %r" % key)
+    def recv_keys(self, keyserver, *keyids):
+        """Import a key from a keyserver
 
-class DeleteResult(object):
-    "Handle status messages for --delete-key and --delete-secret-key"
-    def __init__(self):
-        self.status = 'ok'
+        >>> import shutil
+        >>> shutil.rmtree("keys")
+        >>> gpg = GPG(gnupghome="keys")
+        >>> result = gpg.recv_keys('pgp.mit.edu', '3FF0DB166A7476EA')
+        >>> assert result
 
-    def __str__(self):
-        return self.status
+        """
+        result = self.result_map['import'](self)
+        logger.debug('recv_keys: %r', keyids)
+        data = _make_binary_stream("", self.encoding)
+        #data = ""
+        args = ['--keyserver', keyserver, '--recv-keys']
+        args.extend(keyids)
+        self._handle_io(args, data, result, binary=True)
+        logger.debug('recv_keys result: %r', result.__dict__)
+        data.close()
+        return result
 
-    problem_reason = {
-        '1': 'No such key',
-        '2': 'Must delete secret key first',
-        '3': 'Ambigious specification',
-    }
+    def delete_keys(self, fingerprints, secret=False):
+        which='key'
+        if secret:
+            which='secret-key'
+        if _is_sequence(fingerprints):
+            fingerprints = ' '.join(fingerprints)
+        args = ['--batch --delete-%s "%s"' % (which, fingerprints)]
+        result = self.result_map['delete'](self)
+        p = self._open_subprocess(args)
+        self._collect_output(p, result, stdin=p.stdin)
+        return result
 
-    def handle_status(self, key, value):
-        if key == "DELETE_PROBLEM":
-            self.status = self.problem_reason.get(value,
-                                                  "Unknown error: %r" % value)
-        else:
-            raise ValueError("Unknown status message: %r" % key)
+    def export_keys(self, keyids, secret=False):
+        "export the indicated keys. 'keyid' is anything gpg accepts"
+        which=''
+        if secret:
+            which='-secret-key'
+        if _is_sequence(keyids):
+            keyids = ' '.join(['"%s"' % k for k in keyids])
+        args = ["--armor --export%s %s" % (which, keyids)]
+        p = self._open_subprocess(args)
+        # gpg --export produces no status-fd output; stdout will be
+        # empty in case of failure
+        #stdout, stderr = p.communicate()
+        result = self.result_map['delete'](self) # any result will do
+        self._collect_output(p, result, stdin=p.stdin)
+        logger.debug('export_keys result: %r', result.data)
+        return result.data.decode(self.encoding, self.decode_errors)
 
-class Sign(object):
-    "Handle status messages for --sign"
-    def __init__(self, encoding):
-        self.type = None
-        self.fingerprint = None
-        self.encoding = encoding
+    def list_keys(self, secret=False):
+        """ list the keys currently in the keyring
 
-    def __nonzero__(self):
-        return self.fingerprint is not None
+        >>> import shutil
+        >>> shutil.rmtree("keys")
+        >>> gpg = GPG(gnupghome="keys")
+        >>> input = gpg.gen_key_input()
+        >>> result = gpg.gen_key(input)
+        >>> print1 = result.fingerprint
+        >>> result = gpg.gen_key(input)
+        >>> print2 = result.fingerprint
+        >>> pubkeys = gpg.list_keys()
+        >>> assert print1 in pubkeys.fingerprints
+        >>> assert print2 in pubkeys.fingerprints
 
-    __bool__ = __nonzero__
+        """
 
-    def __str__(self):
-        return self.data.decode(self.encoding)
+        which='keys'
+        if secret:
+            which='secret-keys'
+        args = "--list-%s --fixed-list-mode --fingerprint --with-colons" % (which,)
+        args = [args]
+        p = self._open_subprocess(args)
 
-    def handle_status(self, key, value):
-        if key in ("USERID_HINT", "NEED_PASSPHRASE", "BAD_PASSPHRASE",
-                   "GOOD_PASSPHRASE", "BEGIN_SIGNING"):
-            pass
-        elif key == "SIG_CREATED":
-            (self.type,
-             algo, hashalgo, cls,
-             self.timestamp, self.fingerprint
-             ) = value.split()
+        # there might be some status thingumy here I should handle... (amk)
+        # ...nope, unless you care about expired sigs or keys (stevegt)
+
+        # 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'.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 gen_key(self, input):
+        """Generate a key; you might use gen_key_input() to create the
+        control input.
+
+        >>> gpg = GPG(gnupghome="keys")
+        >>> input = gpg.gen_key_input()
+        >>> result = gpg.gen_key(input)
+        >>> assert result
+        >>> result = gpg.gen_key('foo')
+        >>> assert not result
+
+        """
+        args = ["--gen-key --batch"]
+        result = self.result_map['generate'](self)
+        f = _make_binary_stream(input, self.encoding)
+        self._handle_io(args, f, result, binary=True)
+        f.close()
+        return result
+
+    def gen_key_input(self, **kwargs):
+        """
+        Generate --gen-key input per gpg doc/DETAILS
+        """
+        parms = {}
+        for key, val in list(kwargs.items()):
+            key = key.replace('_','-').title()
+            parms[key] = val
+        parms.setdefault('Key-Type','RSA')
+        parms.setdefault('Key-Length',1024)
+        parms.setdefault('Name-Real', "Autogenerated Key")
+        parms.setdefault('Name-Comment', "Generated by gnupg.py")
+        try:
+            logname = os.environ['LOGNAME']
+        except KeyError:
+            logname = os.environ['USERNAME']
+        hostname = socket.gethostname()
+        parms.setdefault('Name-Email', "%s@%s" % (logname.replace(' ', '_'),
+                                                  hostname))
+        out = "Key-Type: %s\n" % parms.pop('Key-Type')
+        for key, val in list(parms.items()):
+            out += "%s: %s\n" % (key, val)
+        out += "%commit\n"
+        return out
+
+        # Key-Type: RSA
+        # Key-Length: 1024
+        # Name-Real: ISdlink Server on %s
+        # Name-Comment: Created by %s
+        # Name-Email: isdlink@%s
+        # Expire-Date: 0
+        # %commit
+        #
+        #
+        # Key-Type: DSA
+        # Key-Length: 1024
+        # Subkey-Type: ELG-E
+        # Subkey-Length: 1024
+        # Name-Real: Joe Tester
+        # Name-Comment: with stupid passphrase
+        # Name-Email: joe@foo.bar
+        # Expire-Date: 0
+        # Passphrase: abc
+        # %pubring foo.pub
+        # %secring foo.sec
+        # %commit
+
+    #
+    # ENCRYPTION
+    #
+    def encrypt_file(self, file, recipients, sign=None,
+            always_trust=False, passphrase=None,
+            armor=True, output=None, symmetric=False):
+        "Encrypt the message read from the file-like object 'file'"
+        args = ['--encrypt']
+        if symmetric:
+            args = ['--symmetric']
         else:
-            raise ValueError("Unknown status message: %r" % key)
+            args = ['--encrypt']
+            if not _is_sequence(recipients):
+                recipients = (recipients,)
+            for recipient in recipients:
+                args.append('--recipient "%s"' % recipient)
+        if armor:   # create ascii-armored output - set to False for binary output
+            args.append('--armor')
+        if output:  # write the output to a file with the specified name
+            if os.path.exists(output):
+                os.remove(output) # to avoid overwrite confirmation message
+            args.append('--output "%s"' % output)
+        if sign:
+            args.append('--sign --default-key "%s"' % sign)
+        if always_trust:
+            args.append("--always-trust")
+        result = self.result_map['crypt'](self)
+        self._handle_io(args, file, result, passphrase=passphrase, binary=True)
+        logger.debug('encrypt result: %r', result.data)
+        return result
+
+    def encrypt(self, data, recipients, **kwargs):
+        """Encrypt the message contained in the string 'data'
+
+        >>> import shutil
+        >>> if os.path.exists("keys"):
+        ...     shutil.rmtree("keys")
+        >>> gpg = GPG(gnupghome="keys")
+        >>> input = gpg.gen_key_input(passphrase='foo')
+        >>> result = gpg.gen_key(input)
+        >>> print1 = result.fingerprint
+        >>> input = gpg.gen_key_input()
+        >>> result = gpg.gen_key(input)
+        >>> print2 = result.fingerprint
+        >>> result = gpg.encrypt("hello",print2)
+        >>> message = str(result)
+        >>> assert message != 'hello'
+        >>> result = gpg.decrypt(message)
+        >>> assert result
+        >>> str(result)
+        'hello'
+        >>> result = gpg.encrypt("hello again",print1)
+        >>> message = str(result)
+        >>> result = gpg.decrypt(message)
+        >>> result.status == 'need passphrase'
+        True
+        >>> result = gpg.decrypt(message,passphrase='bar')
+        >>> result.status in ('decryption failed', 'bad passphrase')
+        True
+        >>> assert not result
+        >>> result = gpg.decrypt(message,passphrase='foo')
+        >>> result.status == 'decryption ok'
+        True
+        >>> str(result)
+        'hello again'
+        >>> result = gpg.encrypt("signed hello",print2,sign=print1)
+        >>> result.status == 'need passphrase'
+        True
+        >>> result = gpg.encrypt("signed hello",print2,sign=print1,passphrase='foo')
+        >>> result.status == 'encryption ok'
+        True
+        >>> message = str(result)
+        >>> result = gpg.decrypt(message)
+        >>> result.status == 'decryption ok'
+        True
+        >>> assert result.fingerprint == print1
+
+        """
+        data = _make_binary_stream(data, self.encoding)
+        result = self.encrypt_file(data, recipients, **kwargs)
+        data.close()
+        return result
+
+    def decrypt(self, message, **kwargs):
+        data = _make_binary_stream(message, self.encoding)
+        result = self.decrypt_file(data, **kwargs)
+        data.close()
+        return result
+
+    def decrypt_file(self, file, always_trust=False, passphrase=None,
+                     output=None):
+        args = ["--decrypt"]
+        if output:  # write the output to a file with the specified name
+            if os.path.exists(output):
+                os.remove(output) # to avoid overwrite confirmation message
+            args.append('--output "%s"' % output)
+        if always_trust:
+            args.append("--always-trust")
+        result = self.result_map['crypt'](self)
+        self._handle_io(args, file, result, passphrase, binary=True)
+        logger.debug('decrypt result: %r', result.data)
+        return result
+

Modified: subversion/branches/fix-rdump-editor/tools/dist/dist.sh
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/tools/dist/dist.sh?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/tools/dist/dist.sh (original)
+++ subversion/branches/fix-rdump-editor/tools/dist/dist.sh Wed May 16 20:32:43 2012
@@ -224,18 +224,33 @@ echo "Exporting $REPOS_PATH r$REVISION i
 
 rm -f "$DISTPATH/STATUS"
 
+ver_major=`echo $VERSION | cut -d '.' -f 1`
+ver_minor=`echo $VERSION | cut -d '.' -f 2`
+ver_patch=`echo $VERSION | cut -d '.' -f 3`
+
 # Remove contrib/ from our distribution tarball.  Some of it is of
 # unknown license, and usefulness.
 # (See http://svn.haxx.se/dev/archive-2009-04/0166.shtml for discussion.)
-rm -rf "$DISTPATH/contrib"
+if [ "$ver_major" -eq "1" -a "$ver_minor" -ge "7" ]; then
+  rm -rf "$DISTPATH/contrib"
+fi
 
 # Remove notes/ from our distribution tarball.  It's large, but largely
 # blue-sky and out-of-date, and of questionable use to end users.
-rm -rf "$DISTPATH/notes"
+if [ "$ver_major" -eq "1" -a "$ver_minor" -ge "7" ]; then
+  rm -rf "$DISTPATH/notes"
+fi
 
 # Remove packages/ from the tarball.
 # (See http://svn.haxx.se/dev/archive-2009-12/0205.shtml)
-rm -rf "$DISTPATH/packages"
+if [ "$ver_major" -eq "1" -a "$ver_minor" -ge "7" ]; then
+  rm -rf "$DISTPATH/packages"
+fi
+
+# Remove www/ from the tarball for 1.6.x and earlier releases
+if [ "$ver_major" -eq "1" -a "$ver_minor" -le "6" ]; then
+  rm -rf "$DISTPATH/www"
+fi
 
 # Check for a recent enough Python
 # Instead of attempting to deal with various line ending issues, just export
@@ -260,10 +275,6 @@ find "$DISTPATH" -name config.nice -prin
 # on end-user's systems, when they should just be compiled by the
 # Release Manager and left at that.
 
-ver_major=`echo $VERSION | cut -d '.' -f 1`
-ver_minor=`echo $VERSION | cut -d '.' -f 2`
-ver_patch=`echo $VERSION | cut -d '.' -f 3`
-
 vsn_file="$DISTPATH/subversion/include/svn_version.h"
 if [ "$VERSION" != "trunk" ] && [ "$VERSION" != "nightly" ]; then
   sed \
@@ -363,6 +374,10 @@ sign_file()
   fi
 }
 
+# allow md5sum and sha1sum tool names to be overridden
+[ -n "$MD5SUM" ] || MD5SUM=md5sum
+[ -n "$SHA1SUM" ] || SHA1SUM=sha1sum
+
 echo ""
 echo "Done:"
 if [ -z "$ZIP" ]; then
@@ -370,23 +385,23 @@ if [ -z "$ZIP" ]; then
   sign_file $DISTNAME.tar.gz $DISTNAME.tar.bz2
   echo ""
   echo "md5sums:"
-  md5sum "$DISTNAME.tar.bz2" "$DISTNAME.tar.gz"
-  type sha1sum > /dev/null 2>&1
+  $MD5SUM "$DISTNAME.tar.bz2" "$DISTNAME.tar.gz"
+  type $SHA1SUM > /dev/null 2>&1
   if [ $? -eq 0 ]; then
     echo ""
     echo "sha1sums:"
-    sha1sum "$DISTNAME.tar.bz2" "$DISTNAME.tar.gz"
+    $SHA1SUM "$DISTNAME.tar.bz2" "$DISTNAME.tar.gz"
   fi
 else
   ls -l "$DISTNAME.zip"
   sign_file $DISTNAME.zip
   echo ""
   echo "md5sum:"
-  md5sum "$DISTNAME.zip"
-  type sha1sum > /dev/null 2>&1
+  $MD5SUM "$DISTNAME.zip"
+  type $SHA1SUM > /dev/null 2>&1
   if [ $? -eq 0 ]; then
     echo ""
     echo "sha1sum:"
-    sha1sum "$DISTNAME.zip"
+    $SHA1SUM "$DISTNAME.zip"
   fi
 fi

Propchange: subversion/branches/fix-rdump-editor/tools/dist/make-deps-tarball.sh
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: subversion/branches/fix-rdump-editor/tools/dist/make-deps-tarball.sh
------------------------------------------------------------------------------
    svn:executable = *

Propchange: subversion/branches/fix-rdump-editor/tools/dist/make-deps-tarball.sh
------------------------------------------------------------------------------
--- svn:mergeinfo (added)
+++ svn:mergeinfo Wed May 16 20:32:43 2012
@@ -0,0 +1,37 @@
+/subversion/branches/1.5.x-r30215/tools/dist/construct-rolling-environment.sh:870312
+/subversion/branches/bdb-reverse-deltas/tools/dist/construct-rolling-environment.sh:872050-872529
+/subversion/branches/diff-callbacks3/tools/dist/construct-rolling-environment.sh:870059-870761
+/subversion/branches/dont-save-plaintext-passwords-by-default/tools/dist/construct-rolling-environment.sh:870728-871118
+/subversion/branches/double-delete/tools/dist/construct-rolling-environment.sh:870511-872970
+/subversion/branches/explore-wc/tools/dist/construct-rolling-environment.sh:875486,875493,875497,875507,875511,875514,875559,875580-875581,875584,875587,875611,875627,875647,875667-875668,875711-875712,875733-875734,875736,875744-875748,875751,875758,875782,875795-875796,875830,875836,875838,875842,875852,875855,875864,875870,875873,875880,875885-875888,875890,875897-875898,875905,875907-875909,875935,875943-875944,875946,875979,875982-875983,875985-875986,875990,875997
+/subversion/branches/file-externals/tools/dist/construct-rolling-environment.sh:871779-873302
+/subversion/branches/fs-rep-sharing/tools/dist/construct-rolling-environment.sh:869036-873803
+/subversion/branches/fsfs-pack/tools/dist/construct-rolling-environment.sh:873717-874575
+/subversion/branches/gnome-keyring/tools/dist/construct-rolling-environment.sh:870558-871410
+/subversion/branches/http-protocol-v2/tools/dist/construct-rolling-environment.sh:874395-876041
+/subversion/branches/in-memory-cache/tools/dist/construct-rolling-environment.sh:869829-871452
+/subversion/branches/issue-2843-dev/tools/dist/construct-rolling-environment.sh:871432-874179
+/subversion/branches/issue-3000/tools/dist/construct-rolling-environment.sh:871713,871716-871719,871721-871726,871728,871734
+/subversion/branches/issue-3067-deleted-subtrees/tools/dist/construct-rolling-environment.sh:873375-874084
+/subversion/branches/issue-3148-dev/tools/dist/construct-rolling-environment.sh:875193-875204
+/subversion/branches/issue-3220-dev/tools/dist/construct-rolling-environment.sh:872210-872226
+/subversion/branches/issue-3242-dev/tools/dist/construct-rolling-environment.sh:879653-896436
+/subversion/branches/issue-3334-dirs/tools/dist/construct-rolling-environment.sh:875156-875867
+/subversion/branches/kwallet/tools/dist/construct-rolling-environment.sh:870785-871314
+/subversion/branches/log-g-performance/tools/dist/construct-rolling-environment.sh:870941-871032
+/subversion/branches/merge-skips-obstructions/tools/dist/construct-rolling-environment.sh:874525-874615
+/subversion/branches/ra_serf-digest-authn/tools/dist/construct-rolling-environment.sh:875693-876404
+/subversion/branches/reintegrate-improvements/tools/dist/construct-rolling-environment.sh:873853-874164
+/subversion/branches/subtree-mergeinfo/tools/dist/construct-rolling-environment.sh:876734-878766
+/subversion/branches/svn-mergeinfo-enhancements/tools/dist/construct-rolling-environment.sh:870119-870195,870197-870288
+/subversion/branches/svn-patch-improvements/tools/dist/construct-rolling-environment.sh:918519-934609
+/subversion/branches/svnpatch-diff/tools/dist/construct-rolling-environment.sh:865738-876477
+/subversion/branches/svnraisetc/tools/dist/construct-rolling-environment.sh:874709-875149
+/subversion/branches/svnserve-logging/tools/dist/construct-rolling-environment.sh:869828-870893
+/subversion/branches/tc-issue-3334/tools/dist/construct-rolling-environment.sh:874697-874773
+/subversion/branches/tc-merge-notify/tools/dist/construct-rolling-environment.sh:874017-874062
+/subversion/branches/tc-resolve/tools/dist/construct-rolling-environment.sh:874191-874239
+/subversion/branches/tc_url_rev/tools/dist/construct-rolling-environment.sh:874351-874483
+/subversion/branches/tree-conflicts/tools/dist/construct-rolling-environment.sh:868291-873154
+/subversion/branches/tree-conflicts-notify/tools/dist/construct-rolling-environment.sh:873926-874008
+/subversion/trunk/tools/dist/make-deps-tarball.sh:1304315-1339328

Modified: subversion/branches/fix-rdump-editor/tools/dist/nightly.sh
URL: http://svn.apache.org/viewvc/subversion/branches/fix-rdump-editor/tools/dist/nightly.sh?rev=1339349&r1=1339348&r2=1339349&view=diff
==============================================================================
--- subversion/branches/fix-rdump-editor/tools/dist/nightly.sh (original)
+++ subversion/branches/fix-rdump-editor/tools/dist/nightly.sh Wed May 16 20:32:43 2012
@@ -63,7 +63,7 @@ $svn export -r $head $repo/trunk/build/g
 # Create the environment
 cd roll
 echo '----------------building environment------------------'
-../release.py --base-dir ${abscwd}/roll build-env
+../release.py --base-dir ${abscwd}/roll build-env trunk-nightly
 
 # Roll the tarballs
 echo '-------------------rolling tarball--------------------'
@@ -72,8 +72,8 @@ cd ..
 
 # Create the information page
 echo '-------------------moving results---------------------'
-./release.py --base-dir ${abscwd}/roll post-candidates trunk-nightly $head \
-    --target $target
+# ./release.py --base-dir ${abscwd}/roll post-candidates trunk-nightly $head \
+#     --target $target
 if [ ! -d "$target/dist" ]; then mkdir "$target/dist"; fi
 if [ -d "$target/dist/r$head" ]; then rm -r "$target/dist/r$head"; fi
 mv roll/deploy $target/dist/r$head