adding GnuPGInterface.py
This commit is contained in:
parent
e3a6e53112
commit
646191d40b
|
@ -0,0 +1,649 @@
|
|||
"""Interface to GNU Privacy Guard (GnuPG)
|
||||
|
||||
GnuPGInterface is a Python module to interface with GnuPG.
|
||||
It concentrates on interacting with GnuPG via filehandles,
|
||||
providing access to control GnuPG via versatile and extensible means.
|
||||
|
||||
This module is based on GnuPG::Interface, a Perl module by the same author.
|
||||
|
||||
Normally, using this module will involve creating a
|
||||
GnuPG object, setting some options in it's 'options' data member
|
||||
(which is of type Options), creating some pipes
|
||||
to talk with GnuPG, and then calling the run() method, which will
|
||||
connect those pipes to the GnuPG process. run() returns a
|
||||
Process object, which contains the filehandles to talk to GnuPG with.
|
||||
|
||||
Example code:
|
||||
|
||||
>>> import GnuPGInterface
|
||||
>>>
|
||||
>>> plaintext = "Three blind mice"
|
||||
>>> passphrase = "This is the passphrase"
|
||||
>>>
|
||||
>>> gnupg = GnuPGInterface.GnuPG()
|
||||
>>> gnupg.options.armor = 1
|
||||
>>> gnupg.options.meta_interactive = 0
|
||||
>>> gnupg.options.extra_args.append('--no-secmem-warning')
|
||||
>>>
|
||||
>>> # Normally we might specify something in
|
||||
>>> # gnupg.options.recipients, like
|
||||
>>> # gnupg.options.recipients = [ '0xABCD1234', 'bob@foo.bar' ]
|
||||
>>> # but since we're doing symmetric-only encryption, it's not needed.
|
||||
>>> # If you are doing standard, public-key encryption, using
|
||||
>>> # --encrypt, you will need to specify recipients before
|
||||
>>> # calling gnupg.run()
|
||||
>>>
|
||||
>>> # First we'll encrypt the test_text input symmetrically
|
||||
>>> p1 = gnupg.run(['--symmetric'],
|
||||
... create_fhs=['stdin', 'stdout', 'passphrase'])
|
||||
>>>
|
||||
>>> p1.handles['passphrase'].write(passphrase)
|
||||
>>> p1.handles['passphrase'].close()
|
||||
>>>
|
||||
>>> p1.handles['stdin'].write(plaintext)
|
||||
>>> p1.handles['stdin'].close()
|
||||
>>>
|
||||
>>> ciphertext = p1.handles['stdout'].read()
|
||||
>>> p1.handles['stdout'].close()
|
||||
>>>
|
||||
>>> # process cleanup
|
||||
>>> p1.wait()
|
||||
>>>
|
||||
>>> # Now we'll decrypt what we just encrypted it,
|
||||
>>> # using the convience method to get the
|
||||
>>> # passphrase to GnuPG
|
||||
>>> gnupg.passphrase = passphrase
|
||||
>>>
|
||||
>>> p2 = gnupg.run(['--decrypt'], create_fhs=['stdin', 'stdout'])
|
||||
>>>
|
||||
>>> p2.handles['stdin'].write(ciphertext)
|
||||
>>> p2.handles['stdin'].close()
|
||||
>>>
|
||||
>>> decrypted_plaintext = p2.handles['stdout'].read()
|
||||
>>> p2.handles['stdout'].close()
|
||||
>>>
|
||||
>>> # process cleanup
|
||||
>>> p2.wait()
|
||||
>>>
|
||||
>>> # Our decrypted plaintext:
|
||||
>>> decrypted_plaintext
|
||||
'Three blind mice'
|
||||
>>>
|
||||
>>> # ...and see it's the same as what we orignally encrypted
|
||||
>>> assert decrypted_plaintext == plaintext, \
|
||||
"GnuPG decrypted output does not match original input"
|
||||
>>>
|
||||
>>>
|
||||
>>> ##################################################
|
||||
>>> # Now let's trying using run()'s attach_fhs paramter
|
||||
>>>
|
||||
>>> # we're assuming we're running on a unix...
|
||||
>>> input = open('/etc/motd')
|
||||
>>>
|
||||
>>> p1 = gnupg.run(['--symmetric'], create_fhs=['stdout'],
|
||||
... attach_fhs={'stdin': input})
|
||||
>>>
|
||||
>>> # GnuPG will read the stdin from /etc/motd
|
||||
>>> ciphertext = p1.handles['stdout'].read()
|
||||
>>>
|
||||
>>> # process cleanup
|
||||
>>> p1.wait()
|
||||
>>>
|
||||
>>> # Now let's run the output through GnuPG
|
||||
>>> # We'll write the output to a temporary file,
|
||||
>>> import tempfile
|
||||
>>> temp = tempfile.TemporaryFile()
|
||||
>>>
|
||||
>>> p2 = gnupg.run(['--decrypt'], create_fhs=['stdin'],
|
||||
... attach_fhs={'stdout': temp})
|
||||
>>>
|
||||
>>> # give GnuPG our encrypted stuff from the first run
|
||||
>>> p2.handles['stdin'].write(ciphertext)
|
||||
>>> p2.handles['stdin'].close()
|
||||
>>>
|
||||
>>> # process cleanup
|
||||
>>> p2.wait()
|
||||
>>>
|
||||
>>> # rewind the tempfile and see what GnuPG gave us
|
||||
>>> temp.seek(0)
|
||||
>>> decrypted_plaintext = temp.read()
|
||||
>>>
|
||||
>>> # compare what GnuPG decrypted with our original input
|
||||
>>> input.seek(0)
|
||||
>>> input_data = input.read()
|
||||
>>>
|
||||
>>> assert decrypted_plaintext == input_data, \
|
||||
"GnuPG decrypted output does not match original input"
|
||||
|
||||
To do things like public-key encryption, simply pass do something
|
||||
like:
|
||||
|
||||
gnupg.passphrase = 'My passphrase'
|
||||
gnupg.options.recipients = [ 'bob@foobar.com' ]
|
||||
gnupg.run( ['--sign', '--encrypt'], create_fhs=..., attach_fhs=...)
|
||||
|
||||
Here is an example of subclassing GnuPGInterface.GnuPG,
|
||||
so that it has an encrypt_string() method that returns
|
||||
ciphertext.
|
||||
|
||||
>>> import GnuPGInterface
|
||||
>>>
|
||||
>>> class MyGnuPG(GnuPGInterface.GnuPG):
|
||||
...
|
||||
... def __init__(self):
|
||||
... GnuPGInterface.GnuPG.__init__(self)
|
||||
... self.setup_my_options()
|
||||
...
|
||||
... def setup_my_options(self):
|
||||
... self.options.armor = 1
|
||||
... self.options.meta_interactive = 0
|
||||
... self.options.extra_args.append('--no-secmem-warning')
|
||||
...
|
||||
... def encrypt_string(self, string, recipients):
|
||||
... gnupg.options.recipients = recipients # a list!
|
||||
...
|
||||
... proc = gnupg.run(['--encrypt'], create_fhs=['stdin', 'stdout'])
|
||||
...
|
||||
... proc.handles['stdin'].write(string)
|
||||
... proc.handles['stdin'].close()
|
||||
...
|
||||
... output = proc.handles['stdout'].read()
|
||||
... proc.handles['stdout'].close()
|
||||
...
|
||||
... proc.wait()
|
||||
... return output
|
||||
...
|
||||
>>> gnupg = MyGnuPG()
|
||||
>>> ciphertext = gnupg.encrypt_string("The secret", ['0x260C4FA3'])
|
||||
>>>
|
||||
>>> # just a small sanity test here for doctest
|
||||
>>> import types
|
||||
>>> assert isinstance(ciphertext, types.StringType), \
|
||||
"What GnuPG gave back is not a string!"
|
||||
|
||||
Here is an example of generating a key:
|
||||
>>> import GnuPGInterface
|
||||
>>> gnupg = GnuPGInterface.GnuPG()
|
||||
>>> gnupg.options.meta_interactive = 0
|
||||
>>>
|
||||
>>> # We will be creative and use the logger filehandle to capture
|
||||
>>> # what GnuPG says this time, instead stderr; no stdout to listen to,
|
||||
>>> # but we capture logger to surpress the dry-run command.
|
||||
>>> # We also have to capture stdout since otherwise doctest complains;
|
||||
>>> # Normally you can let stdout through when generating a key.
|
||||
>>>
|
||||
>>> proc = gnupg.run(['--gen-key'], create_fhs=['stdin', 'stdout',
|
||||
... 'logger'])
|
||||
>>>
|
||||
>>> proc.handles['stdin'].write('''Key-Type: DSA
|
||||
... Key-Length: 1024
|
||||
... # We are only testing syntax this time, so dry-run
|
||||
... %dry-run
|
||||
... Subkey-Type: ELG-E
|
||||
... Subkey-Length: 1024
|
||||
... Name-Real: Joe Tester
|
||||
... Name-Comment: with stupid passphrase
|
||||
... Name-Email: joe@foo.bar
|
||||
... Expire-Date: 2y
|
||||
... Passphrase: abc
|
||||
... %pubring foo.pub
|
||||
... %secring foo.sec
|
||||
... ''')
|
||||
>>>
|
||||
>>> proc.handles['stdin'].close()
|
||||
>>>
|
||||
>>> report = proc.handles['logger'].read()
|
||||
>>> proc.handles['logger'].close()
|
||||
>>>
|
||||
>>> proc.wait()
|
||||
|
||||
|
||||
COPYRIGHT:
|
||||
|
||||
Copyright (C) 2001 Frank J. Tobin, ftobin@neverending.org
|
||||
|
||||
LICENSE:
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
or see http://www.gnu.org/copyleft/lesser.html
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import fcntl
|
||||
|
||||
__author__ = "Frank J. Tobin, ftobin@neverending.org"
|
||||
__version__ = "0.3.2"
|
||||
__revision__ = "$Id: GnuPGInterface.py,v 1.22 2002/01/11 20:22:04 ftobin Exp $"
|
||||
|
||||
# "standard" filehandles attached to processes
|
||||
_stds = [ 'stdin', 'stdout', 'stderr' ]
|
||||
|
||||
# the permissions each type of fh needs to be opened with
|
||||
_fd_modes = { 'stdin': 'w',
|
||||
'stdout': 'r',
|
||||
'stderr': 'r',
|
||||
'passphrase': 'w',
|
||||
'command': 'w',
|
||||
'logger': 'r',
|
||||
'status': 'r'
|
||||
}
|
||||
|
||||
# correlation between handle names and the arguments we'll pass
|
||||
_fd_options = { 'passphrase': '--passphrase-fd',
|
||||
'logger': '--logger-fd',
|
||||
'status': '--status-fd',
|
||||
'command': '--command-fd' }
|
||||
|
||||
class GnuPG:
|
||||
"""Class instances represent GnuPG.
|
||||
|
||||
Instance attributes of a GnuPG object are:
|
||||
|
||||
* call -- string to call GnuPG with. Defaults to "gpg"
|
||||
|
||||
* passphrase -- Since it is a common operation
|
||||
to pass in a passphrase to GnuPG,
|
||||
and working with the passphrase filehandle mechanism directly
|
||||
can be mundane, if set, the passphrase attribute
|
||||
works in a special manner. If the passphrase attribute is set,
|
||||
and no passphrase file object is sent in to run(),
|
||||
then GnuPG instnace will take care of sending the passphrase to
|
||||
GnuPG, the executable instead of having the user sent it in manually.
|
||||
|
||||
* options -- Object of type GnuPGInterface.Options.
|
||||
Attribute-setting in options determines
|
||||
the command-line options used when calling GnuPG.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.call = 'gpg'
|
||||
self.passphrase = None
|
||||
self.options = Options()
|
||||
|
||||
def run(self, gnupg_commands, args=None, create_fhs=None, attach_fhs=None):
|
||||
"""Calls GnuPG with the list of string commands gnupg_commands,
|
||||
complete with prefixing dashes.
|
||||
For example, gnupg_commands could be
|
||||
'["--sign", "--encrypt"]'
|
||||
Returns a GnuPGInterface.Process object.
|
||||
|
||||
args is an optional list of GnuPG command arguments (not options),
|
||||
such as keyID's to export, filenames to process, etc.
|
||||
|
||||
create_fhs is an optional list of GnuPG filehandle
|
||||
names that will be set as keys of the returned Process object's
|
||||
'handles' attribute. The generated filehandles can be used
|
||||
to communicate with GnuPG via standard input, standard output,
|
||||
the status-fd, passphrase-fd, etc.
|
||||
|
||||
Valid GnuPG filehandle names are:
|
||||
* stdin
|
||||
* stdout
|
||||
* stderr
|
||||
* status
|
||||
* passphase
|
||||
* command
|
||||
* logger
|
||||
|
||||
The purpose of each filehandle is described in the GnuPG
|
||||
documentation.
|
||||
|
||||
attach_fhs is an optional dictionary with GnuPG filehandle
|
||||
names mapping to opened files. GnuPG will read or write
|
||||
to the file accordingly. For example, if 'my_file' is an
|
||||
opened file and 'attach_fhs[stdin] is my_file', then GnuPG
|
||||
will read its standard input from my_file. This is useful
|
||||
if you want GnuPG to read/write to/from an existing file.
|
||||
For instance:
|
||||
|
||||
f = open("encrypted.gpg")
|
||||
gnupg.run(["--decrypt"], attach_fhs={'stdin': f})
|
||||
|
||||
Using attach_fhs also helps avoid system buffering
|
||||
issues that can arise when using create_fhs, which
|
||||
can cause the process to deadlock.
|
||||
|
||||
If not mentioned in create_fhs or attach_fhs,
|
||||
GnuPG filehandles which are a std* (stdin, stdout, stderr)
|
||||
are defaulted to the running process' version of handle.
|
||||
Otherwise, that type of handle is simply not used when calling GnuPG.
|
||||
For example, if you do not care about getting data from GnuPG's
|
||||
status filehandle, simply do not specify it.
|
||||
|
||||
run() returns a Process() object which has a 'handles'
|
||||
which is a dictionary mapping from the handle name
|
||||
(such as 'stdin' or 'stdout') to the respective
|
||||
newly-created FileObject connected to the running GnuPG process.
|
||||
For instance, if the call was
|
||||
|
||||
process = gnupg.run(["--decrypt"], stdin=1)
|
||||
|
||||
after run returns 'process.handles["stdin"]'
|
||||
is a FileObject connected to GnuPG's standard input,
|
||||
and can be written to.
|
||||
"""
|
||||
|
||||
if args == None: args = []
|
||||
if create_fhs == None: create_fhs = []
|
||||
if attach_fhs == None: attach_fhs = {}
|
||||
|
||||
for std in _stds:
|
||||
if not attach_fhs.has_key(std) \
|
||||
and std not in create_fhs:
|
||||
attach_fhs.setdefault(std, getattr(sys, std))
|
||||
|
||||
handle_passphrase = 0
|
||||
|
||||
if self.passphrase != None \
|
||||
and not attach_fhs.has_key('passphrase') \
|
||||
and 'passphrase' not in create_fhs:
|
||||
handle_passphrase = 1
|
||||
create_fhs.append('passphrase')
|
||||
|
||||
process = self._attach_fork_exec(gnupg_commands, args,
|
||||
create_fhs, attach_fhs)
|
||||
|
||||
if handle_passphrase:
|
||||
passphrase_fh = process.handles['passphrase']
|
||||
passphrase_fh.write( self.passphrase )
|
||||
passphrase_fh.close()
|
||||
del process.handles['passphrase']
|
||||
|
||||
return process
|
||||
|
||||
|
||||
def _attach_fork_exec(self, gnupg_commands, args, create_fhs, attach_fhs):
|
||||
"""This is like run(), but without the passphrase-helping
|
||||
(note that run() calls this)."""
|
||||
|
||||
process = Process()
|
||||
|
||||
for fh_name in create_fhs + attach_fhs.keys():
|
||||
if not _fd_modes.has_key(fh_name):
|
||||
raise KeyError, \
|
||||
"unrecognized filehandle name '%s'; must be one of %s" \
|
||||
% (fh_name, _fd_modes.keys())
|
||||
|
||||
for fh_name in create_fhs:
|
||||
# make sure the user doesn't specify a filehandle
|
||||
# to be created *and* attached
|
||||
if attach_fhs.has_key(fh_name):
|
||||
raise ValueError, \
|
||||
"cannot have filehandle '%s' in both create_fhs and attach_fhs" \
|
||||
% fh_name
|
||||
|
||||
pipe = os.pipe()
|
||||
# fix by drt@un.bewaff.net noting
|
||||
# that since pipes are unidirectional on some systems,
|
||||
# so we have to 'turn the pipe around'
|
||||
# if we are writing
|
||||
if _fd_modes[fh_name] == 'w': pipe = (pipe[1], pipe[0])
|
||||
process._pipes[fh_name] = Pipe(pipe[0], pipe[1], 0)
|
||||
|
||||
for fh_name, fh in attach_fhs.items():
|
||||
process._pipes[fh_name] = Pipe(fh.fileno(), fh.fileno(), 1)
|
||||
|
||||
process.pid = os.fork()
|
||||
|
||||
if process.pid == 0: self._as_child(process, gnupg_commands, args)
|
||||
return self._as_parent(process)
|
||||
|
||||
|
||||
def _as_parent(self, process):
|
||||
"""Stuff run after forking in parent"""
|
||||
for k, p in process._pipes.items():
|
||||
if not p.direct:
|
||||
os.close(p.child)
|
||||
process.handles[k] = os.fdopen(p.parent, _fd_modes[k])
|
||||
|
||||
# user doesn't need these
|
||||
del process._pipes
|
||||
|
||||
return process
|
||||
|
||||
|
||||
def _as_child(self, process, gnupg_commands, args):
|
||||
"""Stuff run after forking in child"""
|
||||
# child
|
||||
for std in _stds:
|
||||
p = process._pipes[std]
|
||||
os.dup2( p.child, getattr(sys, "__%s__" % std).fileno() )
|
||||
|
||||
for k, p in process._pipes.items():
|
||||
if p.direct and k not in _stds:
|
||||
# we want the fh to stay open after execing
|
||||
fcntl.fcntl( p.child, fcntl.F_SETFD, 0 )
|
||||
|
||||
fd_args = []
|
||||
|
||||
for k, p in process._pipes.items():
|
||||
# set command-line options for non-standard fds
|
||||
if k not in _stds:
|
||||
fd_args.extend([ _fd_options[k], "%d" % p.child ])
|
||||
|
||||
if not p.direct: os.close(p.parent)
|
||||
|
||||
command = [ self.call ] + fd_args + self.options.get_args() \
|
||||
+ gnupg_commands + args
|
||||
|
||||
os.execvp( command[0], command )
|
||||
|
||||
|
||||
class Pipe:
|
||||
"""simple struct holding stuff about pipes we use"""
|
||||
def __init__(self, parent, child, direct):
|
||||
self.parent = parent
|
||||
self.child = child
|
||||
self.direct = direct
|
||||
|
||||
|
||||
class Options:
|
||||
"""Objects of this class encompass options passed to GnuPG.
|
||||
This class is responsible for determining command-line arguments
|
||||
which are based on options. It can be said that a GnuPG
|
||||
object has-a Options object in its options attribute.
|
||||
|
||||
Attributes which correlate directly to GnuPG options:
|
||||
|
||||
Each option here defaults to false or None, and is described in
|
||||
GnuPG documentation.
|
||||
|
||||
Booleans (set these attributes to booleans)
|
||||
|
||||
* armor
|
||||
* no_greeting
|
||||
* no_verbose
|
||||
* quiet
|
||||
* batch
|
||||
* always_trust
|
||||
* rfc1991
|
||||
* openpgp
|
||||
* force_v3_sigs
|
||||
* no_options
|
||||
* textmode
|
||||
|
||||
Strings (set these attributes to strings)
|
||||
|
||||
* homedir
|
||||
* default_key
|
||||
* comment
|
||||
* compress_algo
|
||||
* options
|
||||
|
||||
Lists (set these attributes to lists)
|
||||
|
||||
* recipients (***NOTE*** plural of 'recipient')
|
||||
* encrypt_to
|
||||
|
||||
Meta options
|
||||
|
||||
Meta options are options provided by this module that do
|
||||
not correlate directly to any GnuPG option by name,
|
||||
but are rather bundle of options used to accomplish
|
||||
a specific goal, such as obtaining compatibility with PGP 5.
|
||||
The actual arguments each of these reflects may change with time. Each
|
||||
defaults to false unless otherwise specified.
|
||||
|
||||
meta_pgp_5_compatible -- If true, arguments are generated to try
|
||||
to be compatible with PGP 5.x.
|
||||
|
||||
meta_pgp_2_compatible -- If true, arguments are generated to try
|
||||
to be compatible with PGP 2.x.
|
||||
|
||||
meta_interactive -- If false, arguments are generated to try to
|
||||
help the using program use GnuPG in a non-interactive
|
||||
environment, such as CGI scripts. Default is true.
|
||||
|
||||
extra_args -- Extra option arguments may be passed in
|
||||
via the attribute extra_args, a list.
|
||||
|
||||
>>> import GnuPGInterface
|
||||
>>>
|
||||
>>> gnupg = GnuPGInterface.GnuPG()
|
||||
>>> gnupg.options.armor = 1
|
||||
>>> gnupg.options.recipients = ['Alice', 'Bob']
|
||||
>>> gnupg.options.extra_args = ['--no-secmem-warning']
|
||||
>>>
|
||||
>>> # no need for users to call this normally; just for show here
|
||||
>>> gnupg.options.get_args()
|
||||
['--armor', '--recipient', 'Alice', '--recipient', 'Bob', '--no-secmem-warning']
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
# booleans
|
||||
self.armor = 0
|
||||
self.no_greeting = 0
|
||||
self.verbose = 0
|
||||
self.no_verbose = 0
|
||||
self.quiet = 0
|
||||
self.batch = 0
|
||||
self.always_trust = 0
|
||||
self.rfc1991 = 0
|
||||
self.openpgp = 0
|
||||
self.force_v3_sigs = 0
|
||||
self.no_options = 0
|
||||
self.textmode = 0
|
||||
|
||||
# meta-option booleans
|
||||
self.meta_pgp_5_compatible = 0
|
||||
self.meta_pgp_2_compatible = 0
|
||||
self.meta_interactive = 1
|
||||
|
||||
# strings
|
||||
self.homedir = None
|
||||
self.default_key = None
|
||||
self.comment = None
|
||||
self.compress_algo = None
|
||||
self.options = None
|
||||
|
||||
# lists
|
||||
self.encrypt_to = []
|
||||
self.recipients = []
|
||||
|
||||
# miscellaneous arguments
|
||||
self.extra_args = []
|
||||
|
||||
def get_args( self ):
|
||||
"""Generate a list of GnuPG arguments based upon attributes."""
|
||||
|
||||
return self.get_meta_args() + self.get_standard_args() + self.extra_args
|
||||
|
||||
def get_standard_args( self ):
|
||||
"""Generate a list of standard, non-meta or extra arguments"""
|
||||
args = []
|
||||
if self.homedir != None: args.extend( [ '--homedir', self.homedir ] )
|
||||
if self.options != None: args.extend( [ '--options', self.options ] )
|
||||
if self.comment != None: args.extend( [ '--comment', self.comment ] )
|
||||
if self.compress_algo != None: args.extend( [ '--compress-algo', self.compress_algo ] )
|
||||
if self.default_key != None: args.extend( [ '--default-key', self.default_key ] )
|
||||
|
||||
if self.no_options: args.append( '--no-options' )
|
||||
if self.armor: args.append( '--armor' )
|
||||
if self.textmode: args.append( '--textmode' )
|
||||
if self.no_greeting: args.append( '--no-greeting' )
|
||||
if self.verbose: args.append( '--verbose' )
|
||||
if self.no_verbose: args.append( '--no-verbose' )
|
||||
if self.quiet: args.append( '--quiet' )
|
||||
if self.batch: args.append( '--batch' )
|
||||
if self.always_trust: args.append( '--always-trust' )
|
||||
if self.force_v3_sigs: args.append( '--force-v3-sigs' )
|
||||
if self.rfc1991: args.append( '--rfc1991' )
|
||||
if self.openpgp: args.append( '--openpgp' )
|
||||
|
||||
for r in self.recipients: args.extend( [ '--recipient', r ] )
|
||||
for r in self.encrypt_to: args.extend( [ '--encrypt-to', r ] )
|
||||
|
||||
return args
|
||||
|
||||
def get_meta_args( self ):
|
||||
"""Get a list of generated meta-arguments"""
|
||||
args = []
|
||||
|
||||
if self.meta_pgp_5_compatible: args.extend( [ '--compress-algo', '1',
|
||||
'--force-v3-sigs'
|
||||
] )
|
||||
if self.meta_pgp_2_compatible: args.append( '--rfc1991' )
|
||||
if not self.meta_interactive: args.extend( [ '--batch', '--no-tty' ] )
|
||||
|
||||
return args
|
||||
|
||||
|
||||
class Process:
|
||||
"""Objects of this class encompass properties of a GnuPG
|
||||
process spawned by GnuPG.run().
|
||||
|
||||
# gnupg is a GnuPG object
|
||||
process = gnupg.run( [ '--decrypt' ], stdout = 1 )
|
||||
out = process.handles['stdout'].read()
|
||||
...
|
||||
os.waitpid( process.pid, 0 )
|
||||
|
||||
Data Attributes
|
||||
|
||||
handles -- This is a map of filehandle-names to
|
||||
the file handles, if any, that were requested via run() and hence
|
||||
are connected to the running GnuPG process. Valid names
|
||||
of this map are only those handles that were requested.
|
||||
|
||||
pid -- The PID of the spawned GnuPG process.
|
||||
Useful to know, since once should call
|
||||
os.waitpid() to clean up the process, especially
|
||||
if multiple calls are made to run().
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._pipes = {}
|
||||
self.handles = {}
|
||||
self.pid = None
|
||||
self._waited = None
|
||||
|
||||
def wait(self):
|
||||
"""Wait on the process to exit, allowing for child cleanup.
|
||||
Will raise an IOError if the process exits non-zero."""
|
||||
|
||||
e = os.waitpid(self.pid, 0)[1]
|
||||
if e != 0:
|
||||
raise IOError, "GnuPG exited non-zero, with code %d" % (e << 8)
|
||||
|
||||
def _run_doctests():
|
||||
import doctest, GnuPGInterface
|
||||
return doctest.testmod(GnuPGInterface)
|
||||
|
||||
# deprecated
|
||||
GnuPGInterface = GnuPG
|
||||
|
||||
if __name__ == '__main__':
|
||||
_run_doctests()
|
Loading…
Reference in New Issue