## $Id: privacy.py,v 1.26 2001/10/10 12:09:09 kjetilja Exp $
import popen2
import re, os, string
from gnome.ui import *
from gtk import *
gpg_installed = (os.system('gpg --version >/dev/null') == 0)
re_pgp = re.compile('(^-----BEGIN PGP MESSAGE-----$.*?^-----END PGP MESSAGE-----$)',
re.DOTALL|re.MULTILINE)
def is_encrypted(msg):
return re_pgp.search(msg)
def split_encrypted(msg):
return re_pgp.split(msg)
re_sign = re.compile('^-----BEGIN PGP SIGNED MESSAGE-----$.*?^-----END PGP SIGNATURE-----$',
re.DOTALL|re.MULTILINE)
def is_signed(msg):
return re_sign.search(msg)
re_isok = re.compile('(bad)|(failed)|(no valid)', re.IGNORECASE)
def is_ok(msg):
return not re_isok.search(msg)
re_signok = re.compile('Good signature.*"(.*)"')
def signed_ok(msg):
_from = re_signok.findall(msg)
if len(_from):
return _from[0]
return None
re_keys = re.compile('(pub|uid|sub)(.*\d|\s*) (.*) (<.*\@.*>)')
class Privacy:
def __init__(self, appbar, parent):
self.passphrase = None
self.appbar = appbar
self.callback = None
self.need_sign, self.need_encrypt = (0,0)
self.recipient = None
self.parent = parent
if not gpg_installed:
self.not_installed()
def not_installed(self):
w = GnomeWarningDialog('GnuPG is not installed on your system!\nSigned or encrypted messages will not handled correctly by Pygmy unless you install GnuPG.')
w.set_parent(self.parent)
w.show()
def handle_read(self, msg, callback):
if is_encrypted(msg):
if not gpg_installed:
self.not_installed()
return msg
passphrase = self.ask_passphrase(callback)
if passphrase:
msg = self.decrypt(msg, passphrase)
if is_signed(msg):
if not gpg_installed:
self.not_installed()
return msg
msg = self.decrypt(msg)
return msg
def handle_write(self, msg, callback):
if self.need_sign:
if not gpg_installed:
self.not_installed()
return msg
passphrase = self.ask_passphrase(callback)
if not passphrase:
return None
msg = self.encrypt_and_sign(msg, passphrase)
elif self.need_encrypt:
if not gpg_installed:
self.not_installed()
return msg
msg = self.encrypt_and_sign(msg)
return msg
def decrypt(self, msg, pw=''):
if not gpg_installed:
self.not_installed()
return msg
import tempfile
fname = tempfile.mktemp()
cmd = 'gpg --armor --batch --decrypt --passphrase-fd=0 --output %s' % fname
r,w,e = popen2.popen3(cmd, 8192)
ret = w.write(pw + '\n' + msg)
w.close()
error = e.read()
if not is_ok(error):
self.passphrase = None
self.appbar.set_status('Decryption failed!')
self.error_dialog(error)
return msg
_from = signed_ok(error)
if _from:
self.appbar.set_status('Signature by %s checked OK!' % _from)
else:
self.appbar.set_status(error[string.rfind(error, 'gpg:'):-1])
try:
buf = open(fname).read()
os.unlink(fname)
except:
self.appbar.set_status('Error reading result of decrypt or signature check!')
buf = ''
#buf = re_sign.sub(buf, msg)
return buf
def ask_passphrase(self, callback):
# Check if we know the passphrase already
if self.passphrase:
return self.passphrase
self.callback = callback
self.parent.request_password('GnuPG password:', self.passphrase_callback)
return None
def passphrase_callback(self, passphrase=None, b=None):
# Kick the module which requested the passphrase
self.passphrase = passphrase
if passphrase:
self.callback()
self.callback = None
def error_dialog(self, msg):
w = GnomeErrorDialog(msg)
w.set_parent(self.parent)
w.show()
def dialog(self, fld, default=None):
if not gpg_installed:
self.not_installed()
return msg
self.fld = fld
self.prefs = self.fld.prefs
self.win = GnomeDialog(':Pygmy - Security',
STOCK_BUTTON_OK, STOCK_BUTTON_CANCEL)
self.win.set_parent(self.parent)
self.vbox = self.win.vbox
self.win.connect('clicked', self.handle_callbacks)
self.win.connect('delete_event', self.destroy)
self.win.connect('destroy', self.destroy)
if self.init_contents(default) == 0:
self.error_dialog("Found no keys!")
self.destroy()
return
else:
self.win.show()
def destroy(self, a=None, b=None):
self.win.destroy()
def handle_callbacks(self, button, no):
if no == 0:
self.handle_settings()
elif no == 1:
self.destroy()
else:
print "security -- got unknown button callback event"
def handle_settings(self):
self.need_sign = self.sign_button.get_active()
self.need_encrypt = self.encrypt_button.get_active()
if self.need_encrypt:
self.recipient = self.keys_combo.entry.get_text()
else:
self.recipient = None
self.win.destroy()
def init_contents(self, default):
t = GtkTable(2,3,0)
t.show()
self.sign_button = GtkCheckButton("Sign")
self.sign_button.set_active(self.need_sign)
self.sign_button.show()
self.encrypt_button = GtkCheckButton("Encrypt")
self.encrypt_button.set_active(self.need_encrypt)
self.encrypt_button.show()
# Fetch all keys in key-ring
f = os.popen('gpg --list-keys')
msg = f.read()
f.close()
keys = []
info = re_keys.findall(msg)
if info == []:
return 0
for entry in info:
str = entry[2]+' '+entry[3]
keys.append(str)
if not self.recipient and (string.find(entry[3], default) != -1):
self.recipient = str
self.keys_combo = GtkCombo()
self.keys_combo.entry.set_editable(FALSE)
self.keys_combo.set_popdown_strings(keys)
if self.recipient:
self.keys_combo.entry.set_text(self.recipient)
self.keys_combo.show()
self.keys_combo.set_usize(250, 0)
t.attach(self.sign_button, 0, 1, 0, 1, yoptions=0, ypadding=3, xpadding=5)
t.attach(self.encrypt_button, 1, 2, 0, 1, yoptions=0, ypadding=3, xpadding=5)
t.attach(self.keys_combo, 0, 2, 1, 2, yoptions=0, ypadding=3, xpadding=5)
self.vbox.pack_start(t)
return 1
def encrypt_and_sign(self, msg, pw=''):
# Check if we have something to do at all?
if not self.need_encrypt and not self.need_sign:
return
args = ['--armor', '--batch']
if self.need_encrypt:
args.append('--encrypt')
args.append("--recipient '%s'" % self.recipient)
if self.need_sign:
if self.need_encrypt:
args.append('--sign')
else:
args.append('--clearsign')
import tempfile
fname = tempfile.mktemp()
tmpfile = open(fname, 'w')
tmpfile.write(msg)
tmpfile.close()
cmd = 'gpg %s --passphrase-fd=0 %s' % (string.join(args), fname)
r,w,e = popen2.popen3(cmd, 8192)
w.write(pw + '\n')
w.close()
error = e.read()
if not is_ok(error):
self.passphrase = None
self.appbar.set_status('Encryption failed!')
self.error_dialog(error)
return None
msg = open(fname+'.asc').read()
os.unlink(fname)
os.unlink(fname+'.asc')
# Reset settings
self.need_encrypt, self.need_sign, self.recipient = (0, 0, None)
return msg
syntax highlighted by Code2HTML, v. 0.9.1