crypt: use AssuanClient.send_fds() to send file descriptors to gpgme-tool.
authorW. Trevor King <wking@tremily.us>
Tue, 9 Oct 2012 12:38:30 +0000 (08:38 -0400)
committerW. Trevor King <wking@tremily.us>
Tue, 9 Oct 2012 12:38:30 +0000 (08:38 -0400)
This requires Python 3.3, so I've updated the docs accordingly.  It
brings us up to date with pyassuan commit:

  commit 9133ecb527756f4fc8f3461002eecfc4c4f45aaa
  Author: W. Trevor King <wking@tremily.us>
  Date:   Mon Oct 8 14:47:35 2012 -0400

    client: add AssuanClient.send_fds() and .receive_fds().

README
bin/send-pgp-mime.py
pgp_mime/crypt.py
pgp_mime/key.py
setup.py

diff --git a/README b/README
index a122d478822a1ec130ea92a50f5109e8cc2ceacc..034c3c0dae1b89eac0168d969249fb4052ee9b8a 100644 (file)
--- a/README
+++ b/README
@@ -24,7 +24,7 @@ Dependencies
 ------------
 
 ``pgp-mime`` is a simple package with no external dependencies outside
-the Python 3 standard library.  There are a number of GnuPG_ wrappers
+the Python 3.3 standard library.  There are a number of GnuPG_ wrappers
 for python `out there`__, but none of them seem mature/stable enough
 to be worth installing.  Instead, we use the `pyassuan`_ module to
 talk to `gpgme-tool`_ over pipes or sockets.  If this isn't working
@@ -33,10 +33,11 @@ the cryptography.
 
 __ wrappers_
 
-It would be awkward to backport ``pgp-mime`` to Python 2.7, because we
-take advantage of the ``pass_fds`` argument to Popen_ to pass file
-descriptors to the child `gpgme-tool` processes.  Once we get to
-Python 3.3, we'll use sendmsg_ and recvmsg_ instead.
+It would be awkward to backport ``pgp-mime`` to earlier versions of
+Python, because versions before Python 3.3 lack sendmsg_ and recvmsg_,
+and Python 2.7 doesn't even have that pass_fds option for Popen.  This
+makes it much harder to pass file descriptors to the `gpgme-tool`
+process.
 
 Installing by hand
 ------------------
index 012f55e6615183c6b3f493214bb882889a0da4a8..01f063a07a6764e86f825b811e8712c364f2e34f 100755 (executable)
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python3.3
 # Copyright (C) 2012 W. Trevor King <wking@tremily.us>
 #
 # This file is part of pgp-mime.
index b98b323f781a09f5180e8ede758c46ab4d09a2c1..9c2a2d23e525d91d6f8b68db4b62925c0452fa97 100644 (file)
@@ -18,9 +18,6 @@ import codecs as _codecs
 import logging as _logging
 import os as _os
 import os.path as _os_path
-from _socket import socket as _Socket
-import socket as _socket
-import subprocess as _subprocess
 
 from pyassuan import client as _client
 from pyassuan import common as _common
@@ -29,41 +26,17 @@ from . import LOG as _LOG
 from . import signature as _signature
 
 
-def connect(client, filename, **kwargs):
-    filename = _os_path.expanduser(filename)
-    if False:
-        socket = _socket.socket(_socket.AF_UNIX, _socket.SOCK_STREAM)
-        socket.connect(filename)
-        client.input = socket.makefile('rb')
-        client.output = socket.makefile('wb')
-    else:
-        p = _subprocess.Popen(
-            filename, stdin=_subprocess.PIPE, stdout=_subprocess.PIPE,
-            close_fds=True, **kwargs)
-        client.input = p.stdout
-        client.output = p.stdin
-        socket = p
-    client.connect()
-    return socket
-
 def get_client(**kwargs):
     logger = _logging.getLogger('{}.{}'.format(_LOG.name, 'pyassuan'))
     client = _client.AssuanClient(
         name='pgp-mime', logger=logger, use_sublogger=False,
         close_on_disconnect=True)
-    socket = connect(client, '~/src/gpgme/build/src/gpgme-tool', **kwargs)
-    #socket = connect(client, '~/.assuan/S.gpgme-tool', **kwargs)
-    return (client, socket)
+    client.connect(socket_path='/tmp/gpgme-tool.sock')
+    return client
 
-def disconnect(client, socket):
+def disconnect(client):
     client.make_request(_common.Request('BYE'))
     client.disconnect()
-    if isinstance(socket, _Socket):
-        socket.shutdown(_socket.SHUT_RDWR)
-        socket.close()
-    else:
-        status = socket.wait()
-        assert status == 0, status
 
 def hello(client):
     responses,data = client.get_responses()  # get initial 'OK' from server
@@ -126,9 +99,7 @@ def sign_and_encrypt_bytes(data, signers=None, recipients=None,
     """
     input_read,input_write = _os.pipe()
     output_read,output_write = _os.pipe()
-    client,socket = get_client(pass_fds=(input_read, output_write))
-    _os.close(input_read)
-    _os.close(output_write)
+    client = get_client()
     try:
         hello(client)
         if signers:
@@ -137,10 +108,14 @@ def sign_and_encrypt_bytes(data, signers=None, recipients=None,
         if recipients:
             for recipient in recipients:
                 client.make_request(_common.Request('RECIPIENT', recipient))
-        client.make_request(
-            _common.Request('INPUT', 'FD={}'.format(input_read)))
-        client.make_request(
-            _common.Request('OUTPUT', 'FD={}'.format(output_write)))
+        client.send_fds([input_read])
+        client.make_request(_common.Request('INPUT', 'FD'))
+        _os.close(input_read)
+        input_read = -1
+        client.send_fds([output_write])
+        client.make_request(_common.Request('OUTPUT', 'FD'))
+        _os.close(output_write)
+        output_write = -1
         parameters = []
         if signers or allow_default_signer:
             if recipients:
@@ -161,8 +136,8 @@ def sign_and_encrypt_bytes(data, signers=None, recipients=None,
             _common.Request(command, ' '.join(parameters)))
         d = _read(output_read)
     finally:
-        disconnect(client, socket)
-        for fd in [input_write, output_read]:
+        disconnect(client)
+        for fd in [input_read, input_write, output_read, output_write]:
             if fd >= 0:
                 _os.close(fd)
     return d
@@ -191,23 +166,25 @@ def decrypt_bytes(data):
     """
     input_read,input_write = _os.pipe()
     output_read,output_write = _os.pipe()
-    client,socket = get_client(pass_fds=(input_read, output_write))
-    _os.close(input_read)
-    _os.close(output_write)
+    client = get_client()
     try:
         hello(client)
-        client.make_request(
-            _common.Request('INPUT', 'FD={}'.format(input_read)))
-        client.make_request(
-            _common.Request('OUTPUT', 'FD={}'.format(output_write)))
+        client.send_fds([input_read])
+        client.make_request(_common.Request('INPUT', 'FD'))
+        _os.close(input_read)
+        input_read = -1
+        client.send_fds([output_write])
+        client.make_request(_common.Request('OUTPUT', 'FD'))
+        _os.close(output_write)
+        output_write = -1
         _write(input_write, data)
         _os.close(input_write)
         input_write = -1
         client.make_request(_common.Request('DECRYPT'))
         d = _read(output_read)
     finally:
-        disconnect(client, socket)
-        for fd in [input_write, output_read]:
+        disconnect(client)
+        for fd in [input_read, input_write, output_read, output_write]:
             if fd >= 0:
                 _os.close(fd)
     return d
@@ -376,33 +353,31 @@ def verify_bytes(data, signature=None, always_trust=False):
       hash algorithm: SHA256
     """
     input_read,input_write = _os.pipe()
-    pass_fds = [input_read]
     if signature:
         message_read,message_write = _os.pipe()
-        output_read = -1
-        pass_fds.append(message_read)
+        output_read = output_write = -1
     else:
-        message_write = -1
+        message_read = message_write = -1
         output_read,output_write = _os.pipe()
-        pass_fds.append(output_write)
-    client,socket = get_client(pass_fds=pass_fds)
-    _os.close(input_read)
-    if signature:
-        _os.close(message_read)
-    else:
-        _os.close(output_write)
+    client = get_client()
     verified = None
     signatures = []
     try:
         hello(client)
-        client.make_request(
-            _common.Request('INPUT', 'FD={}'.format(input_read)))
+        client.send_fds([input_read])
+        client.make_request(_common.Request('INPUT', 'FD'))
+        _os.close(input_read)
+        input_read = -1
         if signature:
-            client.make_request(
-                _common.Request('MESSAGE', 'FD={}'.format(message_read)))
+            client.send_fds([message_read])
+            client.make_request(_common.Request('MESSAGE', 'FD'))
+            _os.close(message_read)
+            message_read = -1
         else:
-            client.make_request(
-                _common.Request('OUTPUT', 'FD={}'.format(output_write)))
+            client.send_fds([output_write])
+            client.make_request(_common.Request('OUTPUT', 'FD'))
+            _os.close(output_write)
+            output_write = -1
         if signature:
             _write(input_write, signature)
             _os.close(input_write)
@@ -428,8 +403,9 @@ def verify_bytes(data, signature=None, always_trust=False):
             elif signature.pka_trust != 'good':
                 verified = False
     finally:
-        disconnect(client, socket)
-        for fd in [input_write, message_write, output_read]:
+        disconnect(client)
+        for fd in [input_read, input_write, message_read, message_write,
+                   output_read, output_write]:
             if fd >= 0:
                 _os.close(fd)
     return (plain, verified, signatures)
index 3a5ab48545345cd391c5f1cde4c0fdac794ac09b..00ba12784f11f8248b52c28c60dcb5fd74188778 100644 (file)
@@ -144,7 +144,7 @@ def lookup_keys(patterns=None):
     [..., <Key 4332B6E3>, ...]
     """
     _LOG.debug('lookup key: {}'.format(patterns))
-    client,socket = _crypt.get_client()
+    client = _crypt.get_client()
     parameters = []
     if patterns:
         args = [' '.join(patterns)]
@@ -154,7 +154,7 @@ def lookup_keys(patterns=None):
         _crypt.hello(client)
         rs,result = client.make_request(_common.Request('KEYLIST', *args))
     finally:
-        _crypt.disconnect(client, socket)
+        _crypt.disconnect(client)
     tag_mapping = {
         }
     tree = _etree.fromstring(result.replace(b'\x00', b''))
index 97f63e40c2ab19048f6e46e3c0334728432082c5..9fa096a73ece2887cd760added46fa90e433a75c 100644 (file)
--- a/setup.py
+++ b/setup.py
@@ -41,7 +41,6 @@ _setup(
         'Operating System :: OS Independent',
         'License :: OSI Approved :: GNU General Public License (GPL)',
         'Programming Language :: Python :: 3',
-        'Programming Language :: Python :: 3.2',
         'Programming Language :: Python :: 3.3',
         'Topic :: Communications :: Email',
         'Topic :: Security :: Cryptography',