gajim-plural/src/common/GnuPG.py

245 lines
6.7 KiB
Python
Raw Normal View History

## src/common/GnuPG.py
##
## Copyright (C) 2003-2008 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2005 Alex Mauer <hawke AT hawkesnest.net>
## Copyright (C) 2005-2006 Nikos Kouremenos <kourem AT gmail.com>
## Copyright (C) 2005-2008 Yann Leboulanger <asterix AT lagaule.org>
## Copyright (C) 2007 Stephan Erb <steve-e AT h3c.de>
## Copyright (C) 2008 Jean-Marie Traissard <jim AT lapin.org>
## Jonathan Schleifer <js-gajim AT webkeks.org>
##
## This file is part of Gajim.
##
## Gajim is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation; version 3 only.
##
## Gajim is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
##
import gajim
from os import tmpfile
2005-10-09 12:25:02 +02:00
from common import helpers
if gajim.HAVE_GPG:
import GnuPGInterface
class GnuPG(GnuPGInterface.GnuPG):
def __init__(self, use_agent = False):
2005-04-17 19:08:28 +02:00
GnuPGInterface.GnuPG.__init__(self)
self.use_agent = use_agent
2005-04-17 19:08:28 +02:00
self._setup_my_options()
self.always_trust = False
2005-04-17 19:08:28 +02:00
def _setup_my_options(self):
self.options.armor = 1
self.options.meta_interactive = 0
self.options.extra_args.append('--no-secmem-warning')
# disable photo viewer when verifying keys
self.options.extra_args.append('--verify-options')
self.options.extra_args.append('no-show-photo')
if self.use_agent:
self.options.extra_args.append('--use-agent')
2005-04-17 19:08:28 +02:00
def _read_response(self, child_stdout):
# Internal method: reads all the output from GPG, taking notice
# only of lines that begin with the magic [GNUPG:] prefix.
# (See doc/DETAILS in the GPG distribution for info on GPG's
# output when --status-fd is specified.)
#
# Returns a dictionary, mapping GPG's keywords to the arguments
# for that keyword.
resp = {}
while True:
2005-10-09 12:43:14 +02:00
line = helpers.temp_failure_retry(child_stdout.readline)
2005-09-16 02:14:18 +02:00
if line == "": break
2005-04-17 19:08:28 +02:00
line = line.rstrip()
if line[0:9] == '[GNUPG:] ':
2005-09-16 02:14:18 +02:00
# Chop off the prefix
2005-04-17 19:08:28 +02:00
line = line[9:]
L = line.split(None, 1)
2005-09-16 02:14:18 +02:00
keyword = L[0]
if len(L) > 1:
resp[ keyword ] = L[1]
else:
resp[ keyword ] = ""
2005-04-17 19:08:28 +02:00
return resp
def encrypt(self, str_, recipients, always_trust=False):
2005-04-17 19:08:28 +02:00
self.options.recipients = recipients # a list!
opt = ['--encrypt']
if always_trust or self.always_trust:
opt.append('--always-trust')
proc = self.run(opt, create_fhs=['stdin', 'stdout', 'status',
'stderr'])
proc.handles['stdin'].write(str_)
try:
proc.handles['stdin'].close()
except IOError:
pass
2005-04-17 19:08:28 +02:00
output = proc.handles['stdout'].read()
try:
proc.handles['stdout'].close()
except IOError:
pass
stat = proc.handles['status']
resp = self._read_response(stat)
try:
proc.handles['status'].close()
except IOError:
pass
2005-04-17 19:08:28 +02:00
error = proc.handles['stderr'].read()
proc.handles['stderr'].close()
2005-04-17 19:08:28 +02:00
try: proc.wait()
except IOError: pass
if 'INV_RECP' in resp and resp['INV_RECP'].split()[0] == '10':
# unusable recipient "Key not trusted"
return '', 'NOT_TRUSTED'
if 'BEGIN_ENCRYPTION' in resp and 'END_ENCRYPTION' in resp:
# Encryption succeeded, even if there is output on stderr. Maybe
# verbose is on
error = ''
return self._stripHeaderFooter(output), helpers.decode_string(error)
def decrypt(self, str_, keyID):
2005-07-22 19:26:05 +02:00
proc = self.run(['--decrypt', '-q', '-u %s'%keyID], create_fhs=['stdin', 'stdout'])
enc = self._addHeaderFooter(str_, 'MESSAGE')
2005-04-17 19:08:28 +02:00
proc.handles['stdin'].write(enc)
proc.handles['stdin'].close()
2008-12-03 22:56:12 +01:00
2005-04-17 19:08:28 +02:00
output = proc.handles['stdout'].read()
proc.handles['stdout'].close()
try: proc.wait()
except IOError: pass
return output
def sign(self, str_, keyID):
2005-09-16 02:14:18 +02:00
proc = self.run(['-b', '-u %s'%keyID], create_fhs=['stdin', 'stdout', 'status', 'stderr'])
proc.handles['stdin'].write(str_)
2005-11-08 14:43:23 +01:00
try:
proc.handles['stdin'].close()
except IOError:
pass
2005-04-17 19:08:28 +02:00
output = proc.handles['stdout'].read()
2005-11-08 14:43:23 +01:00
try:
proc.handles['stdout'].close()
proc.handles['stderr'].close()
except IOError:
pass
2005-04-17 19:08:28 +02:00
2005-09-16 02:14:18 +02:00
stat = proc.handles['status']
resp = self._read_response(stat)
2005-11-08 14:43:23 +01:00
try:
proc.handles['status'].close()
except IOError:
pass
2005-09-16 02:14:18 +02:00
2005-04-17 19:08:28 +02:00
try: proc.wait()
except IOError: pass
if 'GOOD_PASSPHRASE' in resp or 'SIG_CREATED' in resp:
2005-09-16 02:14:18 +02:00
return self._stripHeaderFooter(output)
2009-02-19 21:17:05 +01:00
if 'KEYEXPIRED' in resp:
return 'KEYEXPIRED'
2005-07-22 19:28:45 +02:00
return 'BAD_PASSPHRASE'
2005-04-17 19:08:28 +02:00
def verify(self, str_, sign):
if str_ is None:
2005-04-17 19:08:28 +02:00
return ''
f = tmpfile()
2005-07-22 19:27:03 +02:00
fd = f.fileno()
f.write(str_)
2005-07-22 19:27:03 +02:00
f.seek(0)
2005-04-17 19:08:28 +02:00
proc = self.run(['--verify', '--enable-special-filenames', '-', '-&%s'%fd], create_fhs=['stdin', 'status', 'stderr'])
2005-07-22 19:27:03 +02:00
f.close()
2005-04-17 19:08:28 +02:00
sign = self._addHeaderFooter(sign, 'SIGNATURE')
proc.handles['stdin'].write(sign)
proc.handles['stdin'].close()
proc.handles['stderr'].close()
stat = proc.handles['status']
resp = self._read_response(stat)
proc.handles['status'].close()
try: proc.wait()
except IOError: pass
2008-12-03 22:56:12 +01:00
2005-04-17 19:08:28 +02:00
keyid = ''
if 'GOODSIG' in resp:
2005-04-17 19:08:28 +02:00
keyid = resp['GOODSIG'].split()[0]
return keyid
def get_keys(self, secret = False):
if secret:
opt = '--list-secret-keys'
else:
opt = '--list-keys'
proc = self.run(['--with-colons', opt],
2005-04-17 19:08:28 +02:00
create_fhs=['stdout'])
output = proc.handles['stdout'].read()
proc.handles['stdout'].close()
try: proc.wait()
except IOError: pass
2005-04-17 19:08:28 +02:00
keys = {}
lines = output.split('\n')
for line in lines:
sline = line.split(':')
if (sline[0] == 'sec' and secret) or \
(sline[0] == 'pub' and not secret):
# decode escaped chars
name = eval('"' + sline[9].replace('"', '\\"') + '"')
# make it unicode instance
keys[sline[4][8:]] = helpers.decode_string(name)
2005-04-17 19:08:28 +02:00
return keys
def get_secret_keys(self):
return self.get_keys(True)
2005-04-17 19:08:28 +02:00
def _stripHeaderFooter(self, data):
2009-11-26 11:21:43 +01:00
"""
Remove header and footer from data
"""
2005-04-26 23:33:01 +02:00
if not data: return ''
2005-04-17 19:08:28 +02:00
lines = data.split('\n')
while lines[0] != '':
lines.remove(lines[0])
while lines[0] == '':
lines.remove(lines[0])
i = 0
for line in lines:
if line:
if line[0] == '-': break
i = i+1
line = '\n'.join(lines[0:i])
return line
def _addHeaderFooter(self, data, type_):
2009-11-26 11:21:43 +01:00
"""
Add header and footer from data
"""
out = "-----BEGIN PGP %s-----\n" % type_
2005-04-17 19:08:28 +02:00
out = out + "Version: PGP\n"
out = out + "\n"
out = out + data + "\n"
out = out + "-----END PGP %s-----\n" % type_
2005-04-17 19:08:28 +02:00
return out
# vim: set ts=3: