## $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