def __init__(self, path): "new(path) path - location of the glade file" gladefile = os.path.join(path, "PyGtkGpgKeys.glade") self.wtree = gtk.glade.XML(gladefile) self.wtree.signal_autoconnect(self) self.mainwin = self.wtree.get_widget("GPGAdminWindow") self.treeview = self.wtree.get_widget("GPGKeysView") self.model = gtk.TreeStore( *[x.type for x in visible_columns + helper_columns]) self.context = Context() self.context.set_passphrase_cb(self.get_password, "") self.progress = None self.context.set_progress_cb(self.gen_progress, None) # Use mode.SIGS to include signatures in the list. self.context.set_keylist_mode(mode.SIGS) self.load_keys(True) self.treeview.set_model(self.model) self.treeview.get_selection().set_mode(gtk.SELECTION_MULTIPLE) self.add_columns() gtk.main()
def load_keys(): "Download keys from the keyring" context = Context() sec_keys = {} for key in context.op_keylist_all(None, 1): sec_keys[key.subkeys[0].fpr] = 1 # print sec_keys context.set_keylist_mode(keylist.mode.SIGS) for key in context.op_keylist_all(None, 0): secret = sec_keys.has_key(key.subkeys[0].fpr) # print key, secret if key.can_encrypt: print key
def __init__(self, path): "new(path) path - location of the glade file" gladefile = os.path.join(path, "PyGtkGpgKeys.glade") self.wtree = gtk.glade.XML(gladefile) self.wtree.signal_autoconnect(self) self.mainwin = self.wtree.get_widget("GPGAdminWindow") self.treeview = self.wtree.get_widget("GPGKeysView") self.model = gtk.TreeStore(*[x.type for x in visible_columns + helper_columns]) self.context = Context() self.context.set_passphrase_cb(self.get_password, "") self.progress = None self.context.set_progress_cb(self.gen_progress, None) # Use mode.SIGS to include signatures in the list. self.context.set_keylist_mode(mode.SIGS) self.load_keys(True) self.treeview.set_model(self.model) self.treeview.get_selection().set_mode(gtk.SELECTION_MULTIPLE) self.add_columns() gtk.main()
def aroundKeyPressEvent(self, evt, _old=''): if self.state == "showAnswer": key = unicode(evt.text()) if evt.key() == SHOW_PASS_KEY: if self.currentCard.fact.model.name == MODEL: mf = self.mainWin.mainText.page().mainFrame() """ Set up the PyMe stuff """ c = Context(); c.set_passphrase_cb(passCallback) c.set_armor(1); cipher = Data(re.sub("<br[^>]*>","\n",self.currentCard.fact['GPG'].encode('ascii'))) cipher.seek(0,0) """ Try to decrypt the password """ try: plain = Data(); c.op_decrypt(cipher, plain); plain.seek(0,0); QMessageBox.information(mw, "Password", "Your password is %s" % unicode(plain.read(), 'utf-8')) except GPGMEError, e: QMessageBox.information(mw, "Error", "Could not decrypt your password.\nError: " + str(e)) evt.accept() return
def edit_fnc(self, status, args, out): print "[-- Response --]" out.seek(0,0) print out.read(), print "[-- Code: %d, %s --]" % (status, args) if args == "keyedit.prompt": result = self.steps[self.step] self.step += 1 elif args == "keyedit.save.okay": result = "Y" elif args == "keygen.valid": result = "0" else: result = None return result if not os.getenv("GNUPGHOME"): print "Please, set GNUPGHOME env.var. pointing to GPGME's tests/gpg dir" else: c = Context() c.set_passphrase_cb(lambda x,y,z: "abc") out = Data() c.op_keylist_start("Alpha", 0) key = c.op_keylist_next() c.op_edit(key, KeyEditor().edit_fnc, out, out) print "[-- Last response --]" out.seek(0,0) print out.read(),
if not name.startswith('__') and name != "util": stat2str[getattr(status, name)] = name # Print the output received since the last prompt before giving the new prompt def edit_fnc(stat, args, helper): global stat_strings try: while True: helper["data"].seek(helper["skip"], 0) data = helper["data"].read() helper["skip"] += len(data) sys.stdout.buffer.write(data) return input("(%s) %s > " % (stat2str[stat], args)) except EOFError: pass # Simple interactive editor to test editor scripts if len(sys.argv) != 2: sys.stderr.write("Usage: %s <Gpg key pattern>\n" % sys.argv[0]) else: c = Context() out = Data() c.op_keylist_start(sys.argv[1], 0) key = c.op_keylist_next() helper = {"skip": 0, "data": out} c.op_edit(key, edit_fnc, helper, out) print("[-- Final output --]") out.seek(helper["skip"], 0) sys.stdout.buffer.write(out.read())
""" This program will try to encrypt a simple message to each key on your keyring. If your keyring has any invalid keys on it, those keys will be skipped and it will re-try the encryption.""" import sys import os from pyme import core from pyme.core import Data, Context core.check_version(None) plain = Data('This is my message.') c = Context() c.set_armor(1) def sendto(keylist): cipher = Data() c.op_encrypt(keylist, 1, plain, cipher) cipher.seek(0, os.SEEK_SET) return cipher.read() names = [] for key in c.op_keylist_all(None, 0): try: print(" *** Found key for %s" % key.uids[0].uid) valid = 0 for subkey in key.subkeys: keyid = subkey.keyid
stat2str[getattr(status, name)] = name # Print the output received since the last prompt before giving the new prompt def edit_fnc(stat, args, helper): global stat_strings try: while True: helper["data"].seek(helper["skip"], 0) data = helper["data"].read() helper["skip"] += len(data) print data return raw_input("(%s) %s > " % (stat2str[stat], args)) except EOFError: pass # Simple interactive editor to test editor scripts if len(sys.argv) != 2: sys.stderr.write("Usage: %s <Gpg key patter>\n" % sys.argv[0]) else: c = Context() out = Data() c.op_keylist_start(sys.argv[1], 0) key = c.op_keylist_next() helper = {"skip": 0, "data": out} c.op_edit(key, edit_fnc, helper, out) print "[-- Final output --]" out.seek(helper["skip"], 0) print out.read()
class PyGtkGpgKeys: "Main class representing PyGtkGpgKeys application" def error_message(self, text, parent=None): dialog = gtk.MessageDialog(parent or self.mainwin, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, text) dialog.run() dialog.destroy() def yesno_message(self, text, parent=None): dialog = gtk.MessageDialog(parent or self.mainwin, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, text) result = dialog.run() == gtk.RESPONSE_YES dialog.destroy() return result def load_keys(self, first_time=False): if not first_time: self.model.clear() secret_keys = {} for key in self.context.op_keylist_all(None, 1): secret_keys[key.subkeys[0].fpr] = 1 for key in self.context.op_keylist_all(None, 0): self.add_key(key, secret_keys.has_key(key.subkeys[0].fpr)) def add_key(self, key, secret): "self.add_key(key) - add key to the TreeStore model" iter = self.model.append(None) # Can delete only the whole key param = (iter,) + pair("Secret", secret) # Key information is a combination of the key and first uid and subkey for col in key_columns: param += pair(col, key) for col in uid_columns: param += pair(col, key.uids[0]) for col in sub_columns: param += pair(col, key.subkeys[0]) for col in sub_sign_columns: param += pair(col, key.subkeys[0]) self.model.set(*param) if key.uids: self.add_signatures(key.uids[0].signatures, iter) self.add_uids(key.uids[1:], iter) self.add_subkeys(key.subkeys[1:], iter) def add_subkeys(self, subkeys, iter): "self.add_subkeys(subkey, iter) - add subkey as child to key's iter" if not subkeys: return key_iter = self.model.append(iter) self.model.set(key_iter, columns["Name"].index, "Subkeys", *name_only) for subkey in subkeys: child_iter = self.model.append(key_iter) param = (child_iter,) for col in sub_columns: param += pair(col, subkey) for col in sub_sign_columns: param += pair(col, subkey) self.model.set(*param) def add_uids(self, uids, iter): "self.add_uids(uid, iter) - add uid as a child to key's iter" if not uids: return uid_iter = self.model.append(iter) self.model.set(uid_iter,columns["Name"].index,"Other UIDs",*name_only) for uid in uids: child_iter = self.model.append(uid_iter) param = (child_iter,) for col in uid_columns: param += pair(col, uid) self.model.set(*param) self.add_signatures(uid.signatures, child_iter) def add_signatures(self, signs, iter): "self.add_signatures(sign, iter) - add signature as a child to iter" if not signs: return sign_iter = self.model.append(iter) self.model.set(sign_iter,columns["Name"].index,"Signatures",*name_only) for sign in signs: child_iter = self.model.append(sign_iter) param = (child_iter,) for col in uid_columns: param += pair(col, sign) for col in sign_columns: param += pair(col, sign) for col in sub_sign_columns: param += pair(col, sign) self.model.set(*param) def add_columns(self): "Add viewable columns for the data in TreeStore model" view_menu = gtk.Menu() for item in visible_columns: if item.type == gobject.TYPE_BOOLEAN: renderer = gtk.CellRendererToggle() item.attrs["active"] = item.index else: renderer = gtk.CellRendererText() item.attrs["text"] = item.index column = self.treeview.insert_column_with_attributes( item.index, item.name, renderer, **item.attrs) column.set_sort_column_id(item.index) # Create callback for a View menu item if item.view != None: check = gtk.CheckMenuItem(item.name) check.set_active(item.view) check.connect("activate", lambda x, y: y.set_visible(x.get_active()), column) view_menu.append(check) column.set_visible(check.get_active()) view_menu.show_all() self.wtree.get_widget("view_menu").set_submenu(view_menu) def on_GPGKeysView_button_press_event(self, obj, event): if event.button != 3: return False menu = gtk.Menu() for title, callback in [ ("Reload", self.on_reload_activate), (None, None), ("Delete", self.on_delete_activate), ("Export (txt)", self.on_export_keys_text_activate), ("Export (bin)", self.on_export_keys_activate) ]: if title: item = gtk.MenuItem(title) item.connect("activate", callback) else: item = gtk.SeparatorMenuItem() menu.append(item) menu.show_all() menu.popup(None, None, None, event.button, event.time) return True def editor_func(self, status, args, val_dict): state = val_dict["state"] prompt = "%s %s" % (state, args) if val_dict.has_key(prompt): val_dict["state"] = val_dict[prompt][0] return val_dict[prompt][1] elif args: sys.stderr.write("Unexpected prompt in editor_func: %s\n" % prompt) raise EOFError() return "" def change_key_trust(self, key, new_trust): val_dict = { "state": "start", "start keyedit.prompt": ("trust", "trust"), "trust edit_ownertrust.value": ("prompt", "%d" % new_trust), "prompt edit_ownertrust.set_ultimate.okay": ("prompt", "Y"), "prompt keyedit.prompt": ("finish", "quit") } out = Data() self.context.op_edit(key, self.editor_func, val_dict, out) def on_change_trust(self, new_trust): selection = self.treeview.get_selection() if selection.count_selected_rows() <= 0: return key_list = [] selection.selected_foreach(self.collect_keys, key_list) message = "Change trust to %s on the following keys?\n" % \ trusts[new_trust] for key, row in key_list: message += "\n%s\t" % key.subkeys[0].keyid if key.uids: message += key.uids[0].uid else: message += "<undefined>" if self.yesno_message(message): for key, row in key_list: if key.owner_trust != new_trust: self.change_key_trust(key, new_trust) row[columns["Owner Trust"].index] = trusts[new_trust] def on_undefined_trust_activate(self, obj): self.on_change_trust(1) def on_never_trust_activate(self, obj): self.on_change_trust(2) def on_marginal_trust_activate(self, obj): self.on_change_trust(3) def on_full_trust_activate(self, obj): self.on_change_trust(4) def on_ultimate_trust_activate(self, obj): self.on_change_trust(5) def collect_keys(self, model, path, iter, key_list): row = model[path[:1]] keyid = row[columns["FPR"].index] key = self.context.get_key(keyid, 0) key_list.append((key, row)) def export_keys(self): selection = self.treeview.get_selection() if selection.count_selected_rows() <= 0: return export_file = None dialog = gtk.FileChooserDialog("Export Keys (Public only) into a File", self.mainwin, gtk.FILE_CHOOSER_ACTION_SAVE, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)) while dialog.run() == gtk.RESPONSE_OK: filename = dialog.get_filename() if os.path.exists(filename): if os.path.isdir(filename): self.error_message("%s is a directory!" % filename, dialog) continue elif not self.yesno_message("%s exists. Override?" % filename, dialog): continue # FIXME. Verify that file can be written to export_file = file(filename, "wb") break dialog.destroy() if export_file == None: return key_list = [] selection.selected_foreach(self.collect_keys, key_list) expkeys = Data() for key, row in key_list: self.context.op_export(key.subkeys[0].fpr, 0, expkeys) expkeys.seek(0,0) export_file.write(expkeys.read()) export_file.close() def on_export_keys_activate(self, obj): self.context.set_armor(0) self.export_keys() def on_export_keys_text_activate(self, obj): self.context.set_armor(1) self.export_keys() def on_import_keys_activate(self, obj): import_file = None dialog = gtk.FileChooserDialog("Import Keys from a File", self.mainwin, gtk.FILE_CHOOSER_ACTION_OPEN, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)) while dialog.run() == gtk.RESPONSE_OK: filename = dialog.get_filename() if os.path.exists(filename): if os.path.isdir(filename): self.error_message("%s is a directory!" % filename, dialog) else: # FIXME. Verify that file can be open. import_file = filename break else: self.error_message("%s does not exist." % filename, dialog) dialog.destroy() if import_file == None: return impkeys = Data(file=import_file) status = self.context.op_import(impkeys) if status: self.error_message("Import return an error message %d" % status) result = self.context.op_import_result() if result.considered == 0: self.error_message("There's no keys in the file.") # FIXME. Instead of rereading everything we could find out what's new # from the result based on the ORed value of impkey: # constants.import.NEW - The key was new. # constants.import.UID - The key contained new user IDs. # constants.import.SIG - The key contained new signatures. # constants.import.SUBKEY - The key contained new sub keys. # constants.import.SECRET - The key contained a secret key. # It would be nice to highlight new things as well. self.load_keys() #if result: # impkey = result.imports # while impkey: # if impkey.status & constants.import.NEW: # self.add_key(self.context.get_key(impkey.fpr, 0)) # impkey = impkey.next def on_delete_activate(self, obj): "self.on_delete_activate(obj) - callback for key deletion request" selection = self.treeview.get_selection() if selection.count_selected_rows() > 0: key_list = [] selection.selected_foreach(self.collect_keys, key_list) message = "Delete selected keys?\n" for key, row in key_list: message += "\n%s\t" % key.subkeys[0].keyid if key.uids: message += key.uids[0].uid else: message += "<undefined>" if self.yesno_message(message): for key, row in key_list: self.context.op_delete(key, 1) row.model.remove(row.iter) def get_widget_values(self, widgets): "Create an array of values from widgets' getter methods" return [getattr(self.wtree.get_widget(w),"get_"+f)() for w,f in widgets] def set_widget_values(self, widgets, values): "Set values using widgets' setter methods" for (w,f), v in zip(widgets, values): # ComboBox.set_active_iter(None) does not reset active. Fixing. if f == "active_iter" and v == None: f, v = "active", -1 getattr(self.wtree.get_widget(w), "set_"+f)(v) def key_type_changed(self, which): """self.key_type_changed([\"key\"|\"subkey\"]) - helper function to adjust allowed key length based on the Algorithm selected""" (key_type,) = self.get_widget_values([(which+"_type", "active_iter")]) if key_type: key_type = self.wtree.get_widget(which+"_type").get_model( ).get_value(key_type,0) length_widget = self.wtree.get_widget(which+"_length") if key_type == "DSA": length_widget.set_range(1024, 1024) length_widget.set_value(1024) elif key_type == "RSA" or key_type == "ELG-E": length_widget.set_range(1024, 4096) def on_key_type_changed(self, obj): self.key_type_changed("key") def on_subkey_type_changed(self, obj): self.key_type_changed("subkey") def on_expire_calendar_day_selected(self, obj): "Callback for selecting a day on the calendar" (year, month, day)=self.wtree.get_widget("expire_calendar").get_date() expander = self.wtree.get_widget("expire_date") # Past dates means no expiration date if time.localtime() < (year, month+1, day): expander.set_label("%04d-%02d-%02d" % (year, month+1, day)) else: expander.set_label("Unlimited") expander.set_expanded(False) def on_generate_activate(self, obj): "Callback to generate new key" # Set of (widget, common suffix of getter/setter function) tuples # from the GenerateDialog prompt for new key properties. widgets = [ ("key_type", "active_iter"), ("key_length", "value"), ("key_encrypt", "active"), ("key_sign", "active"), ("subkey_type", "active_iter"), ("subkey_length", "value"), ("subkey_encrypt", "active"), ("subkey_sign", "active"), ("name_real", "text"), ("name_comment", "text"), ("name_email", "text"), ("expire_date", "label"), ("passphrase", "text"), ("passphrase_repeat", "text") ] saved_values = self.get_widget_values(widgets) result = None dialog = self.wtree.get_widget("GenerateDialog") if dialog.run() == gtk.RESPONSE_OK: (key_type, key_length, key_encrypt, key_sign, subkey_type, subkey_length, subkey_encrypt, subkey_sign, name_real, name_comment, name_email, expire_date, passphrase, passphrase2) = self.get_widget_values(widgets) if key_type and passphrase == passphrase2: key_type = self.wtree.get_widget("key_type").get_model( ).get_value(key_type,0) result = "<GnupgKeyParms format=\"internal\">\n" result += "Key-Type: %s\n" % key_type result += "Key-Length: %d\n" % int(key_length) if key_encrypt or key_sign: result += "Key-Usage:" + \ ((key_encrypt and " encrypt") or "") + \ ((key_sign and " sign") or "") + "\n" if subkey_type: subkey_type=self.wtree.get_widget("subkey_type").get_model( ).get_value(subkey_type,0) result += "Subkey-Type: %s\n" % subkey_type result += "Subkey-Length: %d\n" % int(subkey_length) if subkey_encrypt or subkey_sign: result += "Subkey-Usage:" + \ ((subkey_encrypt and " encrypt") or "") + \ ((subkey_sign and " sign") or "") + "\n" if name_real: result += "Name-Real: %s\n" % name_real if name_comment: result += "Name-Comment: %s\n" % name_comment if name_email: result += "Name-Email: %s\n" % name_email if passphrase: result += "Passphrase: %s\n" % passphrase if expire_date != "Unlimited": result += "Expire-Date: %s\n" % expire_date else: result += "Expire-Date: 0\n" result += "</GnupgKeyParms>\n" else: if not key_type: message = "Type of the primary key is not specified." elif passphrase != passphrase2: message = "Passphrases do not match." else: message = "Unknown error." self.error_message(message, dialog) else: self.set_widget_values(widgets, saved_values) dialog.hide() if result: # Setup and show progress Dialog self.progress = "" self.progress_entry = self.wtree.get_widget( "progress_entry").get_buffer() self.progress_entry.set_text("") gobject.timeout_add(500, self.update_progress) self.wtree.get_widget("GenerateProgress").show_all() # Start asynchronous key generation self.context.op_genkey_start(result, None, None) def gen_progress(self, what=None, type=None, current=None, total=None, hook=None): "Gpg's progress_cb" if self.progress != None: self.progress += "%c" % type else: sys.stderr.write("%c" % type) def update_progress(self): "Timeout callback to yeild to gpg and update progress Dialog view" status = self.context.wait(False) if status == None: self.progress_entry.set_text(self.progress) return True elif status == 0: fpr = self.context.op_genkey_result().fpr self.add_key(self.context.get_key(fpr, 0), True) self.wtree.get_widget("GenerateProgress").hide() self.progress = None if status: self.error_message("Got an error during key generation:\n%s" % errors.GPGMEError(status).getstring()) # Let callback to be removed. return False def on_generating_close_clicked(self, obj): # Request cancelation of the outstanding asynchronous call self.context.cancel() def get_password(self, hint, desc, hook): "Gpg's password_cb" dialog = self.wtree.get_widget("PasswordDialog") label = self.wtree.get_widget("pwd_prompt") entry = self.wtree.get_widget("password") label.set_text("Please supply %s's password%s:" % (hint, (hook and (' '+hook)) or '')) if dialog.run() == gtk.RESPONSE_OK: result = entry.get_text() else: result = "" entry.set_text("") dialog.hide() return result def on_reload_activate(self, obj): self.load_keys() def on_about_activate(self, obj): about = self.wtree.get_widget("AboutDialog") about.run() about.hide() def __init__(self, path): "new(path) path - location of the glade file" gladefile = os.path.join(path, "PyGtkGpgKeys.glade") self.wtree = gtk.glade.XML(gladefile) self.wtree.signal_autoconnect(self) self.mainwin = self.wtree.get_widget("GPGAdminWindow") self.treeview = self.wtree.get_widget("GPGKeysView") self.model = gtk.TreeStore(*[x.type for x in visible_columns + helper_columns]) self.context = Context() self.context.set_passphrase_cb(self.get_password, "") self.progress = None self.context.set_progress_cb(self.gen_progress, None) # Use mode.SIGS to include signatures in the list. self.context.set_keylist_mode(mode.SIGS) self.load_keys(True) self.treeview.set_model(self.model) self.treeview.get_selection().set_mode(gtk.SELECTION_MULTIPLE) self.add_columns() gtk.main() def on_Exit(self, obj): gtk.main_quit()
def addPassword(): """ Model's not there? Fix it before we get into worse trouble. """ if not [m for m in mw.deck.models if m.name == MODEL]: m = Model(unicode(MODEL)) m.addFieldModel(FieldModel(u'Description', True, True)) m.addFieldModel(FieldModel(u'HMAC_%s' % HASH, True, True)) m.addFieldModel(FieldModel(u'GPG', True, True)) cm=CardModel(u'Password',u'%(Description)s',u'%%(HMAC_%s)s' % HASH) cm.typeAnswer = u'HMAC_%s' % HASH m.addCardModel(cm) mw.deck.addModel(m) else: m, = [m for m in mw.deck.models if m.name == MODEL] # Monkey patch existing models if 'HMAC_%s' % HASH not in [ a.name for a in m.fieldModels ]: m.addFieldModel(FieldModel(u'HMAC_%s' % HASH, True, True)) ret = QInputDialog.getText(mw, "Description", "Enter a description for the password") if ret[1]: desc = unicode(ret[0]) else: return ret = QInputDialog.getText(mw, "Password", "Enter the password", QLineEdit.Password) if ret[1]: pass1 = unicode(ret[0]) else: return ret = QInputDialog.getText(mw, "Confirm", "Confirm the password", QLineEdit.Password) if ret[1]: pass2 = unicode(ret[0]) else: return if pass1 != pass2: QMessageBox.information(mw, "Mismatch", "Your passwords didn't match") return """ Attempt to add the card. """ try: fact = mw.deck.newFact() fact['Description'] = desc """ Fill in the MAC """ mac = hmac.new(config.key, pass1.encode('utf-8'), getattr(hashlib, HASH)) fact['HMAC_%s' % HASH] = unicode(mac.hexdigest()) """ PyMe setup """ c = Context(); c.set_armor(1); c.op_keylist_start(GPG_USER_NAME, 0) key = c.op_keylist_next() if not key: QMessageBox.information(mw, "Error", "Could not find your key. Check you've set it up in the plugin file.") return print "Encrypting with key: " + key.uids[0].uid """ Do the encryption, or try to """ plain = Data(pass1.encode('utf-8')) cipher = Data() try: c.op_encrypt([key], 1, plain, cipher) cipher.seek(0,0) fact['GPG'] = unicode(re.sub("\n","<br>",cipher.read()), 'ascii') mw.deck.addFact(fact) except GPGMEError, e: QMessageBox.information(mw, "Error", "Could not encrypt your password.\nError: " + str(e)) except FactInvalidError, e: QMessageBox.information(mw, "Error", "Could not store your password.\nError: " + str(e))
class PyGtkGpgKeys: "Main class representing PyGtkGpgKeys application" def error_message(self, text, parent=None): dialog = gtk.MessageDialog( parent or self.mainwin, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, text) dialog.run() dialog.destroy() def yesno_message(self, text, parent=None): dialog = gtk.MessageDialog( parent or self.mainwin, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, text) result = dialog.run() == gtk.RESPONSE_YES dialog.destroy() return result def load_keys(self, first_time=False): if not first_time: self.model.clear() secret_keys = {} for key in self.context.op_keylist_all(None, 1): secret_keys[key.subkeys[0].fpr] = 1 for key in self.context.op_keylist_all(None, 0): self.add_key(key, secret_keys.has_key(key.subkeys[0].fpr)) def add_key(self, key, secret): "self.add_key(key) - add key to the TreeStore model" iter = self.model.append(None) # Can delete only the whole key param = (iter, ) + pair("Secret", secret) # Key information is a combination of the key and first uid and subkey for col in key_columns: param += pair(col, key) for col in uid_columns: param += pair(col, key.uids[0]) for col in sub_columns: param += pair(col, key.subkeys[0]) for col in sub_sign_columns: param += pair(col, key.subkeys[0]) self.model.set(*param) if key.uids: self.add_signatures(key.uids[0].signatures, iter) self.add_uids(key.uids[1:], iter) self.add_subkeys(key.subkeys[1:], iter) def add_subkeys(self, subkeys, iter): "self.add_subkeys(subkey, iter) - add subkey as child to key's iter" if not subkeys: return key_iter = self.model.append(iter) self.model.set(key_iter, columns["Name"].index, "Subkeys", *name_only) for subkey in subkeys: child_iter = self.model.append(key_iter) param = (child_iter, ) for col in sub_columns: param += pair(col, subkey) for col in sub_sign_columns: param += pair(col, subkey) self.model.set(*param) def add_uids(self, uids, iter): "self.add_uids(uid, iter) - add uid as a child to key's iter" if not uids: return uid_iter = self.model.append(iter) self.model.set(uid_iter, columns["Name"].index, "Other UIDs", *name_only) for uid in uids: child_iter = self.model.append(uid_iter) param = (child_iter, ) for col in uid_columns: param += pair(col, uid) self.model.set(*param) self.add_signatures(uid.signatures, child_iter) def add_signatures(self, signs, iter): "self.add_signatures(sign, iter) - add signature as a child to iter" if not signs: return sign_iter = self.model.append(iter) self.model.set(sign_iter, columns["Name"].index, "Signatures", *name_only) for sign in signs: child_iter = self.model.append(sign_iter) param = (child_iter, ) for col in uid_columns: param += pair(col, sign) for col in sign_columns: param += pair(col, sign) for col in sub_sign_columns: param += pair(col, sign) self.model.set(*param) def add_columns(self): "Add viewable columns for the data in TreeStore model" view_menu = gtk.Menu() for item in visible_columns: if item.type == gobject.TYPE_BOOLEAN: renderer = gtk.CellRendererToggle() item.attrs["active"] = item.index else: renderer = gtk.CellRendererText() item.attrs["text"] = item.index column = self.treeview.insert_column_with_attributes( item.index, item.name, renderer, **item.attrs) column.set_sort_column_id(item.index) # Create callback for a View menu item if item.view != None: check = gtk.CheckMenuItem(item.name) check.set_active(item.view) check.connect("activate", lambda x, y: y.set_visible(x.get_active()), column) view_menu.append(check) column.set_visible(check.get_active()) view_menu.show_all() self.wtree.get_widget("view_menu").set_submenu(view_menu) def on_GPGKeysView_button_press_event(self, obj, event): if event.button != 3: return False menu = gtk.Menu() for title, callback in [ ("Reload", self.on_reload_activate), (None, None), ("Delete", self.on_delete_activate), ("Export (txt)", self.on_export_keys_text_activate), ("Export (bin)", self.on_export_keys_activate) ]: if title: item = gtk.MenuItem(title) item.connect("activate", callback) else: item = gtk.SeparatorMenuItem() menu.append(item) menu.show_all() menu.popup(None, None, None, event.button, event.time) return True def editor_func(self, status, args, val_dict): state = val_dict["state"] prompt = "%s %s" % (state, args) if val_dict.has_key(prompt): val_dict["state"] = val_dict[prompt][0] return val_dict[prompt][1] elif args: sys.stderr.write("Unexpected prompt in editor_func: %s\n" % prompt) raise EOFError() return "" def change_key_trust(self, key, new_trust): val_dict = { "state": "start", "start keyedit.prompt": ("trust", "trust"), "trust edit_ownertrust.value": ("prompt", "%d" % new_trust), "prompt edit_ownertrust.set_ultimate.okay": ("prompt", "Y"), "prompt keyedit.prompt": ("finish", "quit") } out = Data() self.context.op_edit(key, self.editor_func, val_dict, out) def on_change_trust(self, new_trust): selection = self.treeview.get_selection() if selection.count_selected_rows() <= 0: return key_list = [] selection.selected_foreach(self.collect_keys, key_list) message = "Change trust to %s on the following keys?\n" % \ trusts[new_trust] for key, row in key_list: message += "\n%s\t" % key.subkeys[0].keyid if key.uids: message += key.uids[0].uid else: message += "<undefined>" if self.yesno_message(message): for key, row in key_list: if key.owner_trust != new_trust: self.change_key_trust(key, new_trust) row[columns["Owner Trust"].index] = trusts[new_trust] def on_undefined_trust_activate(self, obj): self.on_change_trust(1) def on_never_trust_activate(self, obj): self.on_change_trust(2) def on_marginal_trust_activate(self, obj): self.on_change_trust(3) def on_full_trust_activate(self, obj): self.on_change_trust(4) def on_ultimate_trust_activate(self, obj): self.on_change_trust(5) def collect_keys(self, model, path, iter, key_list): row = model[path[:1]] keyid = row[columns["FPR"].index] key = self.context.get_key(keyid, 0) key_list.append((key, row)) def export_keys(self): selection = self.treeview.get_selection() if selection.count_selected_rows() <= 0: return export_file = None dialog = gtk.FileChooserDialog("Export Keys (Public only) into a File", self.mainwin, gtk.FILE_CHOOSER_ACTION_SAVE, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)) while dialog.run() == gtk.RESPONSE_OK: filename = dialog.get_filename() if os.path.exists(filename): if os.path.isdir(filename): self.error_message("%s is a directory!" % filename, dialog) continue elif not self.yesno_message("%s exists. Override?" % filename, dialog): continue # FIXME. Verify that file can be written to export_file = file(filename, "wb") break dialog.destroy() if export_file == None: return key_list = [] selection.selected_foreach(self.collect_keys, key_list) expkeys = Data() for key, row in key_list: self.context.op_export(key.subkeys[0].fpr, 0, expkeys) expkeys.seek(0, 0) export_file.write(expkeys.read()) export_file.close() def on_export_keys_activate(self, obj): self.context.set_armor(0) self.export_keys() def on_export_keys_text_activate(self, obj): self.context.set_armor(1) self.export_keys() def on_import_keys_activate(self, obj): import_file = None dialog = gtk.FileChooserDialog("Import Keys from a File", self.mainwin, gtk.FILE_CHOOSER_ACTION_OPEN, (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, gtk.STOCK_OK, gtk.RESPONSE_OK)) while dialog.run() == gtk.RESPONSE_OK: filename = dialog.get_filename() if os.path.exists(filename): if os.path.isdir(filename): self.error_message("%s is a directory!" % filename, dialog) else: # FIXME. Verify that file can be open. import_file = filename break else: self.error_message("%s does not exist." % filename, dialog) dialog.destroy() if import_file == None: return impkeys = Data(file=import_file) status = self.context.op_import(impkeys) if status: self.error_message("Import return an error message %d" % status) result = self.context.op_import_result() if result.considered == 0: self.error_message("There's no keys in the file.") # FIXME. Instead of rereading everything we could find out what's new # from the result based on the ORed value of impkey: # constants.import.NEW - The key was new. # constants.import.UID - The key contained new user IDs. # constants.import.SIG - The key contained new signatures. # constants.import.SUBKEY - The key contained new sub keys. # constants.import.SECRET - The key contained a secret key. # It would be nice to highlight new things as well. self.load_keys() #if result: # impkey = result.imports # while impkey: # if impkey.status & constants.import.NEW: # self.add_key(self.context.get_key(impkey.fpr, 0)) # impkey = impkey.next def on_delete_activate(self, obj): "self.on_delete_activate(obj) - callback for key deletion request" selection = self.treeview.get_selection() if selection.count_selected_rows() > 0: key_list = [] selection.selected_foreach(self.collect_keys, key_list) message = "Delete selected keys?\n" for key, row in key_list: message += "\n%s\t" % key.subkeys[0].keyid if key.uids: message += key.uids[0].uid else: message += "<undefined>" if self.yesno_message(message): for key, row in key_list: self.context.op_delete(key, 1) row.model.remove(row.iter) def get_widget_values(self, widgets): "Create an array of values from widgets' getter methods" return [ getattr(self.wtree.get_widget(w), "get_" + f)() for w, f in widgets ] def set_widget_values(self, widgets, values): "Set values using widgets' setter methods" for (w, f), v in zip(widgets, values): # ComboBox.set_active_iter(None) does not reset active. Fixing. if f == "active_iter" and v == None: f, v = "active", -1 getattr(self.wtree.get_widget(w), "set_" + f)(v) def key_type_changed(self, which): """self.key_type_changed([\"key\"|\"subkey\"]) - helper function to adjust allowed key length based on the Algorithm selected""" (key_type, ) = self.get_widget_values([(which + "_type", "active_iter") ]) if key_type: key_type = self.wtree.get_widget(which + "_type").get_model().get_value( key_type, 0) length_widget = self.wtree.get_widget(which + "_length") if key_type == "DSA": length_widget.set_range(1024, 1024) length_widget.set_value(1024) elif key_type == "RSA" or key_type == "ELG-E": length_widget.set_range(1024, 4096) def on_key_type_changed(self, obj): self.key_type_changed("key") def on_subkey_type_changed(self, obj): self.key_type_changed("subkey") def on_expire_calendar_day_selected(self, obj): "Callback for selecting a day on the calendar" (year, month, day) = self.wtree.get_widget("expire_calendar").get_date() expander = self.wtree.get_widget("expire_date") # Past dates means no expiration date if time.localtime() < (year, month + 1, day): expander.set_label("%04d-%02d-%02d" % (year, month + 1, day)) else: expander.set_label("Unlimited") expander.set_expanded(False) def on_generate_activate(self, obj): "Callback to generate new key" # Set of (widget, common suffix of getter/setter function) tuples # from the GenerateDialog prompt for new key properties. widgets = [("key_type", "active_iter"), ("key_length", "value"), ("key_encrypt", "active"), ("key_sign", "active"), ("subkey_type", "active_iter"), ("subkey_length", "value"), ("subkey_encrypt", "active"), ("subkey_sign", "active"), ("name_real", "text"), ("name_comment", "text"), ("name_email", "text"), ("expire_date", "label"), ("passphrase", "text"), ("passphrase_repeat", "text")] saved_values = self.get_widget_values(widgets) result = None dialog = self.wtree.get_widget("GenerateDialog") if dialog.run() == gtk.RESPONSE_OK: (key_type, key_length, key_encrypt, key_sign, subkey_type, subkey_length, subkey_encrypt, subkey_sign, name_real, name_comment, name_email, expire_date, passphrase, passphrase2) = self.get_widget_values(widgets) if key_type and passphrase == passphrase2: key_type = self.wtree.get_widget( "key_type").get_model().get_value(key_type, 0) result = "<GnupgKeyParms format=\"internal\">\n" result += "Key-Type: %s\n" % key_type result += "Key-Length: %d\n" % int(key_length) if key_encrypt or key_sign: result += "Key-Usage:" + \ ((key_encrypt and " encrypt") or "") + \ ((key_sign and " sign") or "") + "\n" if subkey_type: subkey_type = self.wtree.get_widget( "subkey_type").get_model().get_value(subkey_type, 0) result += "Subkey-Type: %s\n" % subkey_type result += "Subkey-Length: %d\n" % int(subkey_length) if subkey_encrypt or subkey_sign: result += "Subkey-Usage:" + \ ((subkey_encrypt and " encrypt") or "") + \ ((subkey_sign and " sign") or "") + "\n" if name_real: result += "Name-Real: %s\n" % name_real if name_comment: result += "Name-Comment: %s\n" % name_comment if name_email: result += "Name-Email: %s\n" % name_email if passphrase: result += "Passphrase: %s\n" % passphrase if expire_date != "Unlimited": result += "Expire-Date: %s\n" % expire_date else: result += "Expire-Date: 0\n" result += "</GnupgKeyParms>\n" else: if not key_type: message = "Type of the primary key is not specified." elif passphrase != passphrase2: message = "Passphrases do not match." else: message = "Unknown error." self.error_message(message, dialog) else: self.set_widget_values(widgets, saved_values) dialog.hide() if result: # Setup and show progress Dialog self.progress = "" self.progress_entry = self.wtree.get_widget( "progress_entry").get_buffer() self.progress_entry.set_text("") gobject.timeout_add(500, self.update_progress) self.wtree.get_widget("GenerateProgress").show_all() # Start asynchronous key generation self.context.op_genkey_start(result, None, None) def gen_progress(self, what=None, type=None, current=None, total=None, hook=None): "Gpg's progress_cb" if self.progress != None: self.progress += "%c" % type else: sys.stderr.write("%c" % type) def update_progress(self): "Timeout callback to yeild to gpg and update progress Dialog view" status = self.context.wait(False) if status == None: self.progress_entry.set_text(self.progress) return True elif status == 0: fpr = self.context.op_genkey_result().fpr self.add_key(self.context.get_key(fpr, 0), True) self.wtree.get_widget("GenerateProgress").hide() self.progress = None if status: self.error_message("Got an error during key generation:\n%s" % errors.GPGMEError(status).getstring()) # Let callback to be removed. return False def on_generating_close_clicked(self, obj): # Request cancelation of the outstanding asynchronous call self.context.cancel() def get_password(self, hint, desc, hook): "Gpg's password_cb" dialog = self.wtree.get_widget("PasswordDialog") label = self.wtree.get_widget("pwd_prompt") entry = self.wtree.get_widget("password") label.set_text("Please supply %s's password%s:" % (hint, (hook and (' ' + hook)) or '')) if dialog.run() == gtk.RESPONSE_OK: result = entry.get_text() else: result = "" entry.set_text("") dialog.hide() return result def on_reload_activate(self, obj): self.load_keys() def on_about_activate(self, obj): about = self.wtree.get_widget("AboutDialog") about.run() about.hide() def __init__(self, path): "new(path) path - location of the glade file" gladefile = os.path.join(path, "PyGtkGpgKeys.glade") self.wtree = gtk.glade.XML(gladefile) self.wtree.signal_autoconnect(self) self.mainwin = self.wtree.get_widget("GPGAdminWindow") self.treeview = self.wtree.get_widget("GPGKeysView") self.model = gtk.TreeStore( *[x.type for x in visible_columns + helper_columns]) self.context = Context() self.context.set_passphrase_cb(self.get_password, "") self.progress = None self.context.set_progress_cb(self.gen_progress, None) # Use mode.SIGS to include signatures in the list. self.context.set_keylist_mode(mode.SIGS) self.load_keys(True) self.treeview.set_model(self.model) self.treeview.get_selection().set_mode(gtk.SELECTION_MULTIPLE) self.add_columns() gtk.main() def on_Exit(self, obj): gtk.main_quit()
# along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """ This program will try to encrypt a simple message to each key on your keyring. If your keyring has any invalid keys on it, those keys will be removed and it will re-try the encryption.""" from pyme import core from pyme.core import Data, Context from pyme.constants import validity core.check_version(None) plain = Data('This is my message.') c = Context() c.set_armor(1) def sendto(keylist): cipher = Data() c.op_encrypt(keylist, 1, plain, cipher) cipher.seek(0, 0) return cipher.read() names = [] for key in c.op_keylist_all(None, 0): print " *** Found key for %s" % key.uids[0].uid valid = 0 for subkey in key.subkeys: