diff options
Diffstat (limited to 'lang/py3-pyme/examples/pygpa.py')
| -rwxr-xr-x | lang/py3-pyme/examples/pygpa.py | 1457 | 
1 files changed, 1457 insertions, 0 deletions
| diff --git a/lang/py3-pyme/examples/pygpa.py b/lang/py3-pyme/examples/pygpa.py new file mode 100755 index 00000000..341a87cb --- /dev/null +++ b/lang/py3-pyme/examples/pygpa.py @@ -0,0 +1,1457 @@ +#!/usr/bin/env python3 +# $Id$ +# Copyright (C) 2005,2008 Igor Belyi <[email protected]> +# +#    This program 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; either version 2 of the License, or +#    (at your option) any later version. +# +#    This program 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 this program; if not, write to the Free Software +#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + +import gtk, gobject, gtk.glade +import gettext +import time, sys, os +from pyme import errors, core +from pyme.core import Context, Data, pubkey_algo_name +from pyme.constants import validity, status, keylist, sig, sigsum + +# Enable internationalization using gpa translation pages. +gettext.install('gpa', None, 1) +gtk.glade.bindtextdomain('gpa') +gtk.glade.textdomain('gpa') + +# Thanks to Bernhard Reiter for pointing out the following: +# gpgme_check_version() necessary for initialisation according to  +# gpgme 1.1.6 and this is not done automatically in pyme-0.7.0 +print("gpgme version:", core.check_version()) + +# Helper functions to convert non-string data into printable strings +def sec2str(secs, empty="_(Unknown)"): +    "Convert seconds since 1970 into mm/dd/yy string" +    if secs > 0:    return time.strftime("%m/%d/%y", time.localtime(secs)) +    elif secs == 0: return empty +    else:           return "" + +trusts = { +    validity.UNDEFINED: "Unknown", +    validity.NEVER: "Never", +    validity.MARGINAL: "Marginal", +    validity.FULL: "Full", +    validity.ULTIMATE: "Ultimate" +    } + +def validity2str(valid): +    "Convert trust integer into a human understandable string" +    if valid in trusts: return _("%s" % trusts[valid]) +    else:                     return _("Unknown") + +def keyvalid2str(key): +    "Create a string representing validity of a key" +    if key.owner_trust==validity.ULTIMATE: +        return _("Fully Valid") +    if key.revoked: return _("Revoked") +    if key.expired: return _("Expired") +    if key.disabled: return _("Disabled") +    if not key.uids or key.uids[0].invalid: return _("Incomplete") +    return _("Unknown") + +def subvalid2str(subkey): +    "Create a string representing validity of a subkey" +    if subkey.revoked: return _("Revoked") +    if subkey.expired: return _("Expired") +    if subkey.disabled: return _("Disabled") +    if subkey.invalid: return _("Unsigned") +    return _("Valid") + +def signstat2str(sig): +    "Create a string representing validity of a signature" +    status = _("Unknown") +    if sig.status == 1: status = _("Valid") +    elif sig.status == 2: status = _("Bad") +    if sig.expired: status = _("Expired") +    elif sig.revoked: status = _("Revoked") +    return status + +def sigsum2str(summary): +    if summary & sigsum.VALID: return _("Valid") +    if summary & sigsum.RED: return _("Bad") +    if summary & sigsum.KEY_MISSING: return _("Unknown Key") +    if summary & sigsum.KEY_REVOKED: return _("Revoked Key") +    if summary & sigsum.KEY_EXPIRED: return _("Expired Key") +    return _("Key NOT valid") + +def class2str(cls): +    "Convert signature class integer into a human understandable string"     +    if cls==0x10: return _("Generic") +    if cls==0x11: return _("Persona") +    if cls==0x12: return _("Casual") +    if cls==0x13: return _("Positive") +    return _("Unknown") + +def algo2str(algo): +    "Convert algorithm integer into a human understandable string" +    return pubkey_algo_name(algo) + +def fpr2str(fpr): +    "Convert fpr string in a sparsely spaced string for easy reading" +    result = [] +    while fpr: +        result.append(fpr[:4]) +        fpr = fpr[4:] +    return " ".join(result) + +# Helper functions for tasks unrelated to any class +def obj2model(objs, columns): +    "Create a model from the obj (key, subkey, or signature) using columns" +    model = gtk.ListStore(*[x.ctype for x in columns]) +    for obj in objs: +        model.append([x.cfunc(obj) for x in columns]) +    return model + +def labels2table(key_labels): +    "Create a gtk.Table from an array of 2-tuples of strings" +    table = gtk.Table(len(key_labels), 2) +    for i, row in enumerate(key_labels): +        if len(row) != 2: +            raise ValueError("Unexpected number of rows in labels2table call") +        label1 = gtk.Label(row[0]) +        label1.set_alignment(1.0, 0.5) +        label2 = gtk.Label(row[1]) +        label2.set_alignment(0.0, 0.5) +        label2.set_padding(5, 0) +        table.attach(label1, 0, 1, i, i+1, gtk.FILL, gtk.FILL) +        table.attach(label2, 1, 2, i, i+1, gtk.FILL, gtk.FILL) +    return table + +status2str = {} +for name in dir(status): +    if not name.startswith('__') and name != "util": +        status2str[getattr(status, name)] = name + +def editor_func(status, args, val_dict): +    prompt = "%s %s" % (val_dict["state"], args) +    if prompt in val_dict: +        val_dict["state"] = val_dict[prompt][0] +        return val_dict[prompt][1] +    elif args and "ignore %s" % status2str[status] not in val_dict: +        for error in ["error %s" % status2str[status], "error %s" % prompt]: +            if error in val_dict: +                raise errors.GPGMEError(val_dict[error]) +        sys.stderr.write(_("Unexpected status and prompt in editor_func: " + +                           "%s %s\n") % (status2str[status], prompt)) +        raise EOFError() +    return "" + +common_dict = { +        "state": "start", +        "quit keyedit.save.okay": ("save", "Y"), +        "ignore NEED_PASSPHRASE": None, +        "ignore NEED_PASSPHRASE_SYM": None, +        "ignore BAD_PASSPHRASE": None, +        "ignore USERID_HINT": None +        }   + +def change_key_expire(context, key, date): +    "Change key's expiration date to date" +    val_dict = common_dict.copy() +    val_dict.update({ +        "start keyedit.prompt": ("expire", "expire"), +        "expire keygen.valid": ("date", date), +        "date keyedit.prompt": ("quit", "quit") +        }) +    out = Data() +    context.op_edit(key, editor_func, val_dict, out) + +def change_key_trust(context, key, new_trust): +    "Change key's trust to new_trust" +    val_dict = common_dict.copy() +    val_dict.update({ +        "start keyedit.prompt": ("trust", "trust"), +        "trust edit_ownertrust.value": ("value", "%d" % new_trust), +        "value edit_ownertrust.set_ultimate.okay": ("value", "Y"), +        "value keyedit.prompt": ("quit", "quit") +        }) +    out = Data() +    context.op_edit(key, editor_func, val_dict, out) + +def sign_key(context, key, sign_key, local): +    "Sign key using sign_key. Signature is exportable if local is False" +    # Values copied from <gpg-error.h> +    GPG_ERR_CONFLICT = 70 +    GPG_ERR_UNUSABLE_PUBKEY = 53 +    val_dict = common_dict.copy() +    val_dict.update({ +        "start keyedit.prompt": ("sign", (local and "lsign") or "sign"), +        "sign keyedit.sign_all.okay": ("sign", "Y"), +        "sign sign_uid.expire": ("sign", "Y"), +        "sign sign_uid.class": ("sign", "0"), +        "sign sign_uid.okay": ("okay", "Y"), +        "okay keyedit.prompt": ("quit", "quit"), +        "error ALREADY_SIGNED": GPG_ERR_CONFLICT, +        "error sign keyedit.prompt": GPG_ERR_UNUSABLE_PUBKEY +        }) +    out = Data() +    context.signers_clear() +    context.signers_add(sign_key) +    context.op_edit(key, editor_func, val_dict, out) + +def trigger_change_password(context, key): +    "Trigger sequence of passphrase_cb callbacks to change password of the key" +    val_dict = common_dict.copy() +    val_dict.update({ +        "start keyedit.prompt": ("passwd", "passwd"), +        "passwd keyedit.prompt": ("quit", "quit") +        }) +    out = Data() +    context.op_edit(key, editor_func, val_dict, out) + +# Helper classes whose instances are used in the major PyGpa class +class KeyInfo: +    """Helper class to represent key information in different views. +    If KeyInfo instance is initialized with an integer as a key the views +    correspond to a state when multiple or no keys are selected""" +    def __init__(self, key, secret=None): +        self.key = key +        self.secret = secret + +    def key_print_labels(self, fpr=False): +        "Create an array of 2-tuples for 'User Name' and 'Key ID' fields" +        labels = [] +        if type(self.key) != int: +            if self.key.uids: +                labels.append((_("User Name:"), self.key.uids[0].uid)) +		for uid in self.key.uids[1:]: +                    labels.append(("", uid.uid)) +            if fpr: +                labels += [(_("Fingerprint:"), fpr2str(self.key.subkeys[0].fpr))] +            else: +                labels += [(_("Key ID:"), self.key.subkeys[0].keyid[-8:])] +        return labels + +    def key_expires_label(self): +        return sec2str(self.key.subkeys[0].expires,_("never expires")) + +    def details(self): +        "Create a widget for 'Details' notebook tab" +        if type(self.key) == int: +            if self.key: +                details=gtk.Label(_("%d keys selected") % self.key) +            else: +                details=gtk.Label(_("No keys selected")) +            details.set_alignment(0.5, 0) +            return details +         +        if self.secret: +            header = _("The key has both a private and a public part") +        else: +            header = _("The key has only a public part") +        key_info_labels = [("", header)] + +        if self.key.can_certify: +            if self.key.can_sign: +                if self.key.can_encrypt: +                    ability = _("The key can be used for certification, " + +                                "signing and encryption.") +                else: +                    ability = _("The key can be used for certification and " + +                                "signing, but not for encryption.") +            else: +                if self.key.can_encrypt: +                    ability = _("The key can be used for certification and " + +                                "encryption.") +                else: +                    ability = _("The key can be used only for certification.") +        else: +            if self.key.can_sign: +                if self.key.can_encrypt: +                    ability = _("The key can be used only for signing and " + +                                "encryption, but not for certification.") +                else: +                    ability = _("The key can be used only for signing.") +            else: +                if self.key.can_encrypt: +                    ability = _("The key can be used only for encryption.") +                else: +                    ability = _("This key is useless.") +        key_info_labels.append(("", ability)) + +        key_info_labels += self.key_print_labels() + [ +            (_("Fingerprint:"), fpr2str(self.key.subkeys[0].fpr)), +            (_("Expires at:"), self.key_expires_label()), +            (_("Owner Trust:"), validity2str(self.key.owner_trust)), +            (_("Key Validity:"), keyvalid2str(self.key)), +            (_("Key Type:"), _("%s %u bits") % \ +             (algo2str(self.key.subkeys[0].pubkey_algo),self.key.subkeys[0].length)), +            (_("Created at:"), sec2str(self.key.subkeys[0].timestamp)) +            ] + +        return labels2table(key_info_labels) + +    def sign_model(self): +        "Create a model for ComboBox of uids in 'Signatures' notebook tab" +        model = gtk.ListStore(str, gtk.ListStore) +        if type(self.key) != int: +            for uid in self.key.uids: +                model.append([uid.uid, obj2model(uid.signatures,sign_columns)]) +        return model + +    def subkey_model(self): +        "Create a model for TreeView in 'Subkeys' notebook tab" +        if type(self.key) == int: +            return gtk.ListStore(*[x.ctype for x in subkey_columns]) +        else: +            return obj2model(self.key.subkeys, subkey_columns) + +class Column: +    "Helper class to represent a column in a TreeView" +    def __init__(self, name, ctype, cfunc, detail=False): +        """Column(name, ctype, cfunc): +        name  - Name to use as a column header +        ctype - type to use in a model definition for this column +        cfunc - function retrieving column's infromation from an object +        detail- indicate if it's a detail visible only in detailed view""" +        self.name = name +        self.ctype = ctype +        self.cfunc = cfunc +        self.detail = detail + +# Columns for the list of keys which can be used as default +def_keys_columns = [ +    Column(_("Key ID"), str, lambda x,y: x.subkeys[0].keyid[-8:]), +    Column(_("User Name"), str, +           lambda x,y: (x.uids and x.uids[0].uid) or _("[Unknown user ID]")), +    Column(None, gobject.TYPE_PYOBJECT, lambda x,y: KeyInfo(x,y)) +    ] + +# Columns for the list of all keys in the keyring +keys_columns = [ +    Column("", str, lambda x,y: (y and "sec") or "pub"), +    def_keys_columns[0], +    Column(_("Expiry Date"), str, +           lambda x,y: sec2str(x.subkeys[0].expires, _("never expires")), True), +    Column(_("Owner Trust"),str,lambda x,y:validity2str(x.owner_trust),True), +    Column(_("Key Validity"), str, lambda x,y: keyvalid2str(x), True) +    ] + def_keys_columns[1:] + +# Columns for the list of signatures on a uid +sign_columns = [ +    Column(_("Key ID"), str, lambda x: x.keyid[-8:]), +    Column(_("Status"), str, lambda x: signstat2str(x)), +    Column(_("Level"), str, lambda x: class2str(x.sig_class)), +    Column(_("Local"), type(True), lambda x: x.exportable==0), +    Column(_("User Name"), str, lambda x: x.uid or _("[Unknown user ID]")) +    ] + +# Columns for the list of subkeys +subkey_columns = [ +    Column(_("Subkey ID"), str, lambda x: x.keyid[-8:]), +    Column(_("Status"), str, lambda x: subvalid2str(x)), +    Column(_("Algorithm"), str, lambda x: algo2str(x.pubkey_algo)), +    Column(_("Size"), str, lambda x: _("%u bits") % x.length), +    Column(_("Expiry Date"), str, +           lambda x: sec2str(x.expires, _("never expires"))), +    Column(_("Can sign"), type(True), lambda x: x.can_sign), +    Column(_("Can certify"), type(True), lambda x: x.can_certify), +    Column(_("Can encrypt"), type(True), lambda x: x.can_encrypt), +    Column(_("Can authenticate"), type(True), lambda x: x.can_authenticate) +    ] + +file_columns = [ +    Column(_("File"), str, lambda x: x) +    ] + +class PyGpa: +    "Major class representing PyGpa application" +    def popup(self, dialog, parent=None, title=None): +        "Common way to popup a dialog defined in Glade" +        dialog.set_transient_for(parent or self.main_window) +        if title: dialog.set_title(title) +        result = dialog.run() +        dialog.hide() +        return result + +    def file_popup(self, dialog, parent=None, title=None): +        return self.popup(dialog, parent or self.filemanager_window, title) +         +    def error_message(self, text, parent=None, title=_("Warning")): +        "Pop up an error message dialog" +        if type(text) == int: +            text = errors.GPGMEError(text).getstring() +            title = "GPGME error" +        elif isinstance(text, errors.GPGMEError): +            text = text.getstring() +            title = "GPGME error" +        self.error_label.set_text(text) +        self.popup(self.error_dialog, parent, title) + +    def file_error_message(self, text, parent=None, title=_("Warning")): +        self.error_message(text, parent or self.filemanager_window, title) + +    def info_message(self, text, parent=None, title=_("Information")): +        "Pop up an information dialog" +        self.info_label.set_text(text) +        self.popup(self.info_dialog, parent, title) + +    def yesno_message(self, text, parent=None): +        "Pop up a dialog requiring yes/no answer" +        self.yesno_label.set_text(text) +        return self.popup(self.yesno_dialog, parent, +                          _("Warning")) == gtk.RESPONSE_YES + +    def on_uid_list_changed(self, uid_list): +        "this callback is called when uid selection is changed" +        index = uid_list.get_active() +        if index == -1: +            self.sign_treeview.set_model(KeyInfo(0).sign_model()) +        else: +            self.sign_treeview.set_model(uid_list.get_model()[index][1]) + +    def get_selected_keys(self, treeview=None): +        "Helper function to get all selected rows in a treeview" +        if not treeview: +            treeview = self.keys_treeview +        model, rows = treeview.get_selection().get_selected_rows() +        return [model[path] for path in rows] + +    def on_keys_changed(self, keys_treeview): +        "this callback is called when key selection is changed" +        selection = keys_treeview.get_selection() +        count = selection.count_selected_rows() +        if count == 1: +            key_info = self.get_selected_keys()[0][-1] +        else: +            key_info = KeyInfo(count) + +        self.update_menu(key_info) + +        # Update Details tab of the notebook +        old_child = self.details_view.get_child() +        if old_child: self.details_view.remove(old_child) +        self.details_view.add(key_info.details()) +        self.details_view.show_all() + +        # Update Subkeys tab of the notebook +        self.subkeys_treeview.set_model(key_info.subkey_model()) + +        # Update Signatures tab of the notebook +        sign_model = key_info.sign_model() +        self.uid_list.set_model(sign_model) +        if len(sign_model) < 2: +            self.uid_list_box.hide() +        else: +            self.uid_list_box.show_all() +        self.uid_list.set_active(0) +        self.on_uid_list_changed(self.uid_list) + +    def on_keys_button_press(self, obj, event): +        "callback on a mouse press in the keys_treeview" +        if event.button == 3: +            self.popup_menu.popup(None, None, None, event.button, event.time) +            return True +        return False + +    def create_popup_menu(self): +        "create the popup menu shown on right mouse click" +        self.items = [ +            (gtk.ImageMenuItem(gtk.STOCK_COPY), self.on_copy_activate), +            (gtk.ImageMenuItem(gtk.STOCK_PASTE), self.on_paste_activate), +            (gtk.ImageMenuItem(gtk.STOCK_DELETE), self.on_delete_activate), +            (gtk.SeparatorMenuItem(), None), +            (gtk.MenuItem(_("_Sign Keys...")), self.on_sign_keys_activate), +            (gtk.MenuItem(_("Set _Owner Trust...")), +             self.on_set_owner_trust_activate), +            (gtk.MenuItem(_("_Edit Private Key...")), +             self.on_edit_private_key_activate), +            (gtk.SeparatorMenuItem(), None), +            (gtk.MenuItem(_("E_xport Keys...")), self.on_export_keys_activate) +            ] +        self.popup_menu = gtk.Menu() +        for item, callback in self.items: +            if callback: item.connect("activate", callback) +            self.popup_menu.append(item) +        self.popup_menu.show_all()         +     +    def update_menu(self, key_info): +        "update sensitivity of menu items depending on what keys are selected" +        #                  copy, delete, sign, trust, edit, export +        if key_info.secret == None: +            if key_info.key:            # more than one key selected +                values = ( True,  True,  True, False, False,  True) +            else:                       # no keys selected +                values = (False, False, False, False, False, False) +        elif key_info.secret: +            if key_info.key == self.default_key: # default key seleted +                values = ( True,  True, False,  True,  True,  True) +            else:                       # secret (not default) key selected +                values = ( True,  True,  True,  True,  True,  True) +        else:                           # public key selected +            values   =   ( True,  True,  True,  True, False,  True) + +        for w,v in zip((self.copy, self.delete, self.sign_keys, +                        self.set_owner_trust, self.edit_private_key, +                        self.export_keys), values): +            w.set_sensitive(v) +        for w,v in zip((self.items[0][0], self.items[2][0], self.items[4][0], +                        self.items[5][0], self.items[6][0], self.items[8][0]), +                       values): +            w.set_sensitive(v)                 +     +    def setup_columns(self): +        "Helper function to setup columns of different treeviews" +        for treeview, columns in \ +                [(self.keys_treeview, keys_columns), +                 (self.sign_treeview, sign_columns), +                 (self.subkeys_treeview, subkey_columns), +                 (self.def_keys_treeview, def_keys_columns), +                 (self.sign_with_keys_treeview, def_keys_columns), +                 (self.encrypt_with_keys_treeview, def_keys_columns), +                 (self.files_treeview, file_columns)]: +            for index, item in enumerate([x for x in columns if x.name!=None]): +                if item.ctype == str: +                    renderer = gtk.CellRendererText() +                    attrs = {"text": index} +                else: +                    renderer = gtk.CellRendererToggle() +                    attrs = {"active": index} +                column = treeview.insert_column_with_attributes( +                    index, item.name, renderer, **attrs) +                column.set_sort_column_id(index) +                column.set_visible(not item.detail) + +        for index,item in enumerate([x for x in keys_columns if x.name!=None]): +            if item.name and not item.detail: +                renderer = gtk.CellRendererText() +                column = gtk.TreeViewColumn(item.name, renderer, text=index) +                column.set_sort_column_id(index) +                self.encrypt_for_keys_treeview.append_column(column) + +        for treeview in [self.encrypt_with_keys_treeview, self.keys_treeview, +                         self.encrypt_for_keys_treeview, self.files_treeview, +                         self.sign_with_keys_treeview]: +            treeview.get_selection().set_mode(gtk.SELECTION_MULTIPLE) +        self.def_keys_treeview.get_selection().set_mode(gtk.SELECTION_SINGLE) + +        cell = gtk.CellRendererText() +        self.uid_list.pack_start(cell, True) +        self.uid_list.add_attribute(cell, 'text', 0) + +        model = gtk.ListStore(str, str) +        for lines in [(_("days"), "d"), (_("weeks"), "w"), +                      (_("months"), "m"), (_("years"), "y")]: +            model.append(lines) +        self.new_expire_unit_combo.set_model(model) +        self.new_expire_unit_combo.child.set_editable(False) +        self.new_algorithm_combo.child.set_editable(False) + +        self.files_treeview.set_model(gtk.ListStore(str)) + +    def setup_default_views(self): +        "Setup initial values for different views" +        self.update_default_keys() +        self.on_advanced_mode_toggled(self.advanced_mode_rb) +        self.create_popup_menu() +        self.on_keys_changed(self.keys_treeview) + +    def load_keys(self): +        "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 +        model = gtk.ListStore(*[x.ctype for x in keys_columns]) +        encrypt_model = gtk.ListStore(*[x.ctype for x in keys_columns]) +        context.set_keylist_mode(keylist.mode.SIGS) +        for key in context.op_keylist_all(None, 0): +            secret = key.subkeys[0].fpr in sec_keys +            data = [x.cfunc(key, secret) for x in keys_columns] +            if key.can_encrypt: encrypt_model.append(data) +            model.append(data) +        self.keys_treeview.set_model(model) +        self.encrypt_for_keys_treeview.set_model(encrypt_model) + +    def set_default_key(self, key): +        "Setup default key and update status bar with it" +        self.default_key = key +        self.status_uid.set_text((key.uids and key.uids[0].uid) or \ +                                 _("[Unknown user ID]")) +        self.status_keyid.set_text(key.subkeys[0].keyid[-8:]) + +    def on_default_keys_changed(self, treeview): +        "This callback is called when default key is changed in Preferences" +        model, rows = treeview.get_selection().get_selected_rows() +        if model and rows: +            self.set_default_key(model[rows[0]][-1].key) + +    def add_default_key(self, model, path, iter, def_model): +        "Helper function to add secret keys to the list of possible defaults" +        key = model[path][-1] +        if key.secret: +            def_model.append([x.cfunc(key.key,True) for x in def_keys_columns]) + +    def add_sig_key(self, model, path, iter, sign_model): +        "Helper function to add secret keys to the list of possible defaults" +        key = model[path][-1].key +        if key.can_sign: +            sign_model.append([x.cfunc(key,True) for x in def_keys_columns]) + +    def select_default_key(self, model, path, iter): +        "Helper function to select current default key from the available list" +        if model[path][-1].key == self.default_key: +            self.def_keys_treeview.get_selection().select_path(path) + +    def update_default_keys(self): +        "Update list of default keys" +        model = gtk.ListStore(*[x.ctype for x in def_keys_columns]) +        self.keys_treeview.get_model().foreach(self.add_default_key, model) +        self.def_keys_treeview.set_model(model) +        model.foreach(self.select_default_key) +        selection = self.def_keys_treeview.get_selection() +        if selection.count_selected_rows() != 1: +            selection.select_path((0,)) +            self.on_default_keys_changed(self.def_keys_treeview) +        model = gtk.ListStore(*[x.ctype for x in def_keys_columns]) +        self.def_keys_treeview.get_model().foreach(self.add_sig_key, model) +        self.sign_with_keys_treeview.set_model(model) +        self.encrypt_with_keys_treeview.set_model(model) + +    def on_select_all_activate(self, obj): +        "This callback is called when SelectAll menu item is selected" +        self.keys_treeview.get_selection().select_all() + +    def on_file_preferences_activate(self, obj): +        "Callback called when Preferences menu item is selected in filemanager" +        self.show_preferences(self.filemanager_window) + +    def on_preferences_activate(self, obj): +        "Callback called when Preferences menu item is selected in key editor" +        self.show_preferences(None) + +    def show_preferences(self, parent): +        "Show preferences positioning its window in the middle of the parent" +        self.popup(self.preferences_dialog, parent) +        self.def_keyserver = self.default_keyserver_combox.child.get_text() + +    def on_advanced_mode_toggled(self, radiobutton): +        "This callback is called when Advanced Mode selection is changed" +        if radiobutton.get_active(): +            self.subkeys_notebook_tab.show() +            self.get_generate_params = self.get_advanced_generate_params +        else: +            self.subkeys_notebook_tab.hide() +            self.get_generate_params = self.get_novice_generate_params + +    def popup_progress_dialog(self, label, parent): +        self.progress_dialog.set_transient_for(parent) +        self.progress_label.set_text(label) +        self.progress_dialog.show_all() +        gobject.timeout_add(100, self.update_progress) + +    def on_progress_cancel_clicked(self, obj): +        self.progress_context.cancel() + +    def update_progress(self): +        "Helper function to show progress while a work on a key is being done" +        try: +            status = self.progress_context.wait(0) +            if status == None or self.progress_func(status): +                self.new_progressbar.pulse() +                return True +        except errors.GPGMEError as exc: +            self.error_message(exc) +         +        self.progress_context = None +        self.progress_func = None +        self.progress_dialog.hide() + +        # Let callback to be removed. +        return False + +    def key_generate_done(self, status): +        "Helper function called on the completion of a key generation" +        if status == 0: +            fpr = self.progress_context.op_genkey_result().fpr +            self.progress_context.set_keylist_mode(keylist.mode.SIGS) +            key = self.progress_context.get_key(fpr, 0) +            data = [x.cfunc(key, True) for x in keys_columns] +            self.keys_treeview.get_model().append(data) +            if key.can_encrypt: +                self.encrypt_for_keys_treeview.get_model().append(data) +            self.update_default_keys() +        else: +            self.error_message(status) +        return False + +    def on_new_activate(self, obj): +        "Callback for 'New Key' menu item" +        params = self.get_generate_params() +        if params == None: +            return + +        (key_algo, subkeys, size, userid, email, +         comment, expire, password, backup) = params + +        gen_string = "<GnupgKeyParms format=\"internal\">\n" + \ +                         "Key-Type: %s\n" % key_algo + \ +                         "Key-Length: %d\n" % size +        if subkeys: +            gen_string += "Subkey-Type: %s\n" % subkeys + \ +                          "Subkey-Length: %d\n" % size +        gen_string += "Name-Real: %s\n" % userid +        if email: +            gen_string += "Name-Email: %s\n" % email +        if comment: +            gen_string += "Name-Comment: %s\n" % comment +        if expire: +            gen_string += "Expire-Date: %s\n" % expire +        if password: +            gen_string += "Passphrase: %s\n" % password +        gen_string += "</GnupgKeyParms>\n" + +        self.progress_context = Context() +        self.progress_context.op_genkey_start(gen_string, None, None) +        self.progress_func = self.key_generate_done +        self.popup_progress_dialog(_("Generating Key..."), self.main_window) + +    def check_passphrase(self, passphrase, repeat_passphrase, parent): +        """Helper function to check that enetered password satisfies our +        requirements""" +        if not passphrase: +            self.error_message(_('You did not enter a passphrase.\n' + +                                 'It is needed to protect your private key.'), +                               parent) +        elif repeat_passphrase != passphrase: +            self.error_message(_('In "Passphrase" and "Repeat passphrase",\n' + +                                 'you must enter the same passphrase.'), +                               parent) +        else: +            return True +        return False + +    def get_novice_generate_params(self): +        "Helper function to get generate key parameter in Novice mode" +        dialogs = [self.generate_userid_dialog, +                   self.generate_email_dialog, +                   self.generate_comment_dialog, +                   self.generate_passphrase_dialog, +                   self.generate_backup_dialog] +        step = 0 +        params = None +        while step>=0: +            dialog = dialogs[step] +            dialog.set_transient_for(self.main_window) +            result = dialog.run() +            newstep = step +            if result == 2: +                if step == 0: +                    userid = self.generate_novice_userid_entry.get_text() +                    if userid: newstep = step + 1 +                    else: self.error_message(_("Please insert your name.")) +                elif step == 1: +                    email = self.generate_novice_email_entry.get_text() +                    if email: newstep = step + 1 +                    else: +                        self.error_message(_("Please insert your email address")) +                elif step == 2: +                    comment = self.generate_novice_comment_entry.get_text() +                    newstep = step + 1 +                elif step == 3: +                    passphrase=self.generate_novice_passphrase_entry.get_text() +                    if self.check_passphrase( +                        passphrase, +                        self.generate_novice_repeat_passphrase_entry.get_text(), +                        dialog): +                        newstep = step + 1 +                elif step == 4: +                    backup = self.generate_novice_backup_rb.get_active() +                    params = ("DSA", "ELG-E", 1024, userid, email, +                              comment, "", passphrase, backup) +                    newstep = -1 +            elif result == 1: +                newstep = step - 1 +            else: +                newstep = -1 + +            if newstep != step: +                dialog.hide() +                step = newstep + +        self.generate_novice_userid_entry.set_text("") +        self.generate_novice_email_entry.set_text("") +        self.generate_novice_comment_entry.set_text("") +        self.generate_novice_passphrase_entry.set_text("") +        self.generate_novice_repeat_passphrase_entry.set_text("") +        return params + +    def on_new_expire_on_rb_toggled(self, expireon_rb): +        self.new_expire_calendar.set_sensitive(expireon_rb.get_active()) + +    def on_new_expire_after_rb_toggled(self, expireafter_rb): +        active = expireafter_rb.get_active() +        self.new_expire_count_entry.set_sensitive(active) +        self.new_expire_unit_combo.set_sensitive(active) + +    def get_advanced_generate_params(self): +        "Helper function to get generate key parameter in Advanced mode" +        params = None +        self.new_expire_unit_combo.set_active(0) +        self.new_algorithm_combo.set_active(0) +        self.new_key_size_combo.set_active(1) +        self.generate_dialog.set_transient_for(self.main_window) +        while params == None and self.generate_dialog.run() == gtk.RESPONSE_OK: +            passphrase = self.new_passphrase_entry.get_text() +            if not self.check_passphrase( +                passphrase, +                self.new_repeat_passphrase_entry.get_text(), +                self.generate_dialog): +                continue +            key_algo, subkeys = { +                'DSA and ElGamal (default)': ("DSA", "ELG-E"), +                'DSA (sign only)': ("DSA", ""), +                'RSA (sign only)': ("RSA", "") +                }[self.new_algorithm_combo.child.get_text()] +            try: +                size = int(self.new_key_size_combo.child.get_text()) +            except ValueError: +                self.new_key_size_combo.child.grab_focus() +                continue +            userid = self.new_userid_entry.get_text() +            email = self.new_email_entry.get_text() +            comment = self.new_comment_entry.get_text() +            expire = "" +            if self.new_expire_after_rb.get_active(): +                model = self.new_expire_unit_combo.get_model() +                unit = model[(self.new_expire_unit_combo.get_active(),)][1] +                try: +                    value = int(self.new_expire_count_entry.get_text()) +                except ValueError: +                    self.new_expire_count_entry.grab_focus() +                    continue +                expire = "%d%s" % (value, unit) +            elif self.new_expire_on_rb.get_active(): +                (year, month, day) = self.new_expire_calendar.get_date() +                expire = "%04d-%02d-%02d" % (year, month+1, day) +            params = (key_algo, subkeys, size, userid, email, +                      comment, expire, passphrase, False) +        self.generate_dialog.hide() +        self.new_passphrase_entry.set_text("") +        self.new_repeat_passphrase_entry.set_text("") +        return params + +    def del_key(self, key, treeview): +        "Helper function to delete a key from a treeview list" +        row_list = [] +        treeview.get_model().foreach(lambda m,p,i,l: l.append(m[p]), row_list) +        for row in row_list: +            if row[-1].key.subkeys[0].fpr == key.subkeys[0].fpr: +                row.model.remove(row.iter) + +    def on_delete_activate(self, obj): +        "Callback for 'Delete Keys' menu item" +        message = { +            True:  _("This key has a secret key. Deleting this key cannot be"+ +                     " undone, unless you have a backup copy."), +            False: _("This key is a public key. Deleting this key cannot be "+ +                     "undone easily, although you may be able to get a new " + +                     "copy  from the owner or from a key server.") +            } +        keytag = self.delete_key_keyinfo +        for row in self.get_selected_keys(): +            self.delete_key_label.set_text(message[row[-1].secret]) +            table = labels2table(row[-1].key_print_labels()) +            keytag.add(table) +            keytag.show_all() +            if self.popup(self.delete_key_dialog) == gtk.RESPONSE_YES: +                context = Context() +                context.op_delete(row[-1].key, 1) +                if row[-1].key.can_encrypt: +                    self.del_key(row[-1].key, self.encrypt_for_keys_treeview) +                row.model.remove(row.iter) +                self.update_default_keys() +                self.on_keys_changed(self.keys_treeview) +            keytag.remove(table) + +    def password_cb(self, hint, desc, prev_bad, hook=None): +        "Callback to setup verification of a passphrase" +        if prev_bad: +            header = _("Wrong passphrase, please try again:") +        else: +            header = _("Please enter the passphrase for the following key:") +        self.password_prompt_label.set_text(header) +        keyid, userid = hint.split(" ", 1) +        table = labels2table([(_("User Name:"), userid), +                              (_("Key ID:"), keyid[-8:])]) +        self.password_prompt_keyinfo.add(table) +        self.password_prompt_keyinfo.show_all() +        password = None +        if self.popup(self.password_prompt_dialog) == gtk.RESPONSE_OK: +            password = self.password_prompt_entry.get_text() +        self.password_prompt_keyinfo.remove(table) +        self.password_prompt_entry.set_text("") +        if not password: +            GPG_ERR_CANCELED = 99 +            raise errors.GPGMEError(GPG_ERR_CANCELED) +        return password + +    def password_change_cb(self, hint, desc, prev_bad, hook): +        "Callback to setup for passphrase change" +        if not prev_bad: +            hook["count"] += 1 +             +        if hook["count"] == 1: +            return self.password_cb(hint, desc, prev_bad) +        else: +            password = None +            self.password_change_dialog.set_transient_for(self.main_window) +            while password == None and \ +                      self.password_change_dialog.run() == gtk.RESPONSE_OK: +                password = self.password_change_passphrase.get_text() +                if not self.check_passphrase( +                    password, +                    self.password_change_repeat_passphrase.get_text(), +                    self.password_change_dialog): +                    password = None +            self.password_change_dialog.hide() +            self.password_change_passphrase.set_text("") +            self.password_change_repeat_passphrase.set_text("") +            if not password: +                GPG_ERR_CANCELED = 99 +                raise errors.GPGMEError(GPG_ERR_CANCELED) +            return password + +    def on_sign_keys_activate(self, obj): +        "Callback for 'Sign keys' menu item" +        context = Context() +        context.set_passphrase_cb(self.password_cb) +        context.set_keylist_mode(keylist.mode.SIGS) +        keytag = self.sign_key_keyinfo +        for row in self.get_selected_keys(): +            if row[-1].key == self.default_key: +                continue +            if len(row[-1].key.uids) > 1: +                self.sign_manyuids_label.show() +            else: +                self.sign_manyuids_label.hide() +            table = labels2table(row[-1].key_print_labels(True)) +            keytag.add(table) +            keytag.show_all() +            if self.popup(self.sign_key_dialog) == gtk.RESPONSE_YES: +                try: +                    sign_key(context, row[-1].key, self.default_key, +                             self.sign_locally_cb.get_active()) +                    row[-1].key=context.get_key(row[-1].key.subkeys[0].fpr,0) +                    self.on_keys_changed(self.keys_treeview) +                except errors.GPGMEError as exc: +                    self.error_message(exc) +            keytag.remove(table) + +    def on_change_passphrase_clicked(self, obj, key_info): +        "Callback for 'Change passphrase' button in editor for a private key" +        try: +            context = Context() +            context.set_passphrase_cb(self.password_change_cb, {"count": 0}) +            trigger_change_password(context, key_info.key) +        except errors.GPGMEError as exc: +            self.error_message(exc) + +    def on_change_expiry_expireon_rb_toggled(self, expire_rb): +        "Callback for 'never expire' radiobutton in editor for a private key" +        self.change_expiry_calendar.set_sensitive(expire_rb.get_active()) + +    def on_change_expiration_clicked(self, obj, key_info): +        "Callback for 'Change expiration' button in editor for a private key" +        if key_info.key.subkeys[0].expires: +            year, month, day = time.localtime(key_info.key.subkeys[0].expires)[:3] +            self.change_expiry_calendar.select_month(month-1, year) +            self.change_expiry_calendar.select_day(day) +            self.change_expiry_expireon_rb.set_active(True) +        else: +            self.change_expiry_never_rb.set_active(True) +        if self.popup(self.change_expiry_dialog, +                      self.edit_key_dialog) == gtk.RESPONSE_OK: +            year, month, day = self.change_expiry_calendar.get_date() +            expire = "%04d-%02d-%02d" % (year, month+1, day) +            try: +                context = Context() +                context.set_passphrase_cb(self.password_cb) +                change_key_expire(context, key_info.key, expire) +                context.set_keylist_mode(keylist.mode.SIGS) +                key_info.key=context.get_key(key_info.key.subkeys[0].fpr,0) +                self.on_keys_changed(self.keys_treeview) +                self.edit_key_date_label.set_text(key_info.key_expires_label()) +            except errors.GPGMEError as exc: +                self.error_message(exc) + +    def on_edit_private_key_activate(self, obj): +        "Callback for 'Edit Private Key' menu item" +        keys = self.get_selected_keys() +        if len(keys) != 1 or not keys[0][-1].secret: +            return +         +        key_info = keys[0][-1] +        table = labels2table(key_info.key_print_labels()) +        self.edit_key_date_label.set_text(key_info.key_expires_label()) +        self.edit_key_keyinfo.add(table) +        self.edit_key_keyinfo.show_all() +        connect1_id = self.edit_key_change_expiration.connect( +            "clicked", self.on_change_expiration_clicked, key_info) +        connect2_id = self.edit_key_change_passphrase.connect( +            "clicked", self.on_change_passphrase_clicked, key_info) +        self.popup(self.edit_key_dialog) +        self.edit_key_change_expiration.disconnect(connect1_id) +        self.edit_key_change_passphrase.disconnect(connect2_id) +        self.edit_key_keyinfo.remove(table) + +    def on_set_owner_trust_activate(self, obj): +        "Callback for 'Set Owner Trust' menu item" +        keys = self.get_selected_keys() +        if len(keys) != 1: +            return + +        key_info = keys[0][-1] +        table = labels2table(key_info.key_print_labels()) +        self.ownertrust_key.add(table) +        self.ownertrust_key.show_all() +        trust = key_info.key.owner_trust +        if trust < 0 or trust not in trusts: +            trust = validity.UNDEFINED +        getattr(self, "ownertrust_"+trusts[trust]).set_active(True) +        if self.popup(self.ownertrust_dialog) == gtk.RESPONSE_OK: +            for trust, name in trusts.items(): +                if getattr(self, "ownertrust_"+name).get_active(): +                    try: +                        context = Context() +                        change_key_trust(context, key_info.key, trust) +                        key_info.key.owner_trust = trust +                        self.on_keys_changed(self.keys_treeview) +                    except errors.GPGMEError as exc: +                        self.error_message(exc)             +                    break +        self.ownertrust_key.remove(table) + +    def import_keys_from_data(self, data): +        "Helper function to import keys into application from a Data() object" +        context = Context() +        status = context.op_import(data) +        if status: +            self.error_message(status) +        else: +            result = context.op_import_result() +            if result.considered == 0: +                self.error_message(_("No keys were found.")) +            else: +                self.load_keys() +                self.info_message(_("%i public keys read\n" + +                                    "%i public keys imported\n" + +                                    "%i public keys unchanged\n" + +                                    "%i secret keys read\n" + +                                    "%i secret keys imported\n" + +                                    "%i secret keys unchanged") % \ +                                  (result.considered, +                                   result.imported, +                                   result.unchanged, +                                   result.secret_read, +                                   result.secret_imported, +                                   result.secret_unchanged)) + +    def import_from_clipboard(self, clipboard, text, data): +        "Callback to setup extraction of data from a clipboard" +        if text: +            self.import_keys_from_data(Data(text)) + +    def on_paste_activate(self, obj): +        "Callback for 'Paste' menu item" +        gtk.clipboard_get().request_text(self.import_from_clipboard) + +    def on_import_keys_activate(self, obj): +        "Callback for 'Import Keys' menu item" +        import_file = None +        dialog = self.import_file_dialog +        dialog.set_transient_for(self.main_window) +        while import_file == None and dialog.run() == gtk.RESPONSE_OK: +            try: +                import_file = open(dialog.get_filename(), "rb") +            except IOError as strerror: +                self.error_message(strerror, dialog) +                import_file = None +        dialog.hide() +        if import_file != None: +            self.import_keys_from_data(Data(file=import_file)) +            import_file.close() + +    def export_selected_keys(self, armor): +        "Helper function to export selected keys into a Data() object" +        context = Context() +        context.set_armor(armor) +        export_keys = Data() +        for row in self.get_selected_keys(): +            context.op_export(row[-1].key.subkeys[0].fpr, 0, export_keys) +        export_keys.seek(0,0) +        return export_keys +         +    def on_copy_activate(self, obj): +        "Callback for 'Copy' menu item" +        if self.keys_treeview.get_selection().count_selected_rows() > 0: +            export_keys = self.export_selected_keys(True) +            gtk.clipboard_get().set_text(export_keys.read()) + +    def verify_output(self, filename, parent): +        "Helper function to verify that user can write into the filename" +        if os.path.exists(filename): +            if os.path.isdir(filename): +                self.error_message(_("%s is a directory")%filename, parent) +                return False +            else: +                return self.yesno_message(_("The file %s already exists.\n" + +                                            "Do you want to overwrite it?") % +                                          filename, parent) +        return True +         +    def on_export_keys_activate(self, obj): +        "Callback for 'Export Keys' menu item" +        if self.keys_treeview.get_selection().count_selected_rows() < 1: +            return + +        export_file = None +        dialog = self.export_file_dialog +        dialog.set_transient_for(self.main_window) +        while export_file == None and dialog.run() == gtk.RESPONSE_OK: +            filename = dialog.get_filename() +            if self.verify_output(filename, dialog): +                try: +                    export_file = open(filename, "wb") +                except IOError as strerror: +                    self.error_message(strerror, dialog) +                    export_file = None +        dialog.hide() +        if export_file == None: +            return + +        export_keys = self.export_selected_keys(export_armor_cb.get_active()) +        export_file.write(export_keys.read()) +        export_file.close()                 + +    def on_files_changed(self, obj): +        "Callback called when selection of files in filemanager is changed" +        if self.files_treeview.get_selection().count_selected_rows() < 1: +            value = False +        else: +            value = True +        for item in (self.sign, self.verify, self.encrypt, self.decrypt): +            item.set_sensitive(value) + +    def open(self, filename, complain=False): +        "Helper function to add a file into filemanager treeview" +        model = self.files_treeview.get_model() +        row_list = [] +        model.foreach(lambda m,p,i,l: l.append(m[p][0]), row_list) +        if filename in row_list: +            if complain: +                self.file_error_message(_("The file is already open.")) +        else: +            item = model.append([filename]) +            self.files_treeview.get_selection().select_iter(item) +            self.on_files_changed(None)         + +    def on_open_activate(self, obj): +        "Callback for 'Open' menu item" +        if self.file_popup(self.open_file_dialog) == gtk.RESPONSE_OK: +            self.add_file(self.open_file_dialog.get_filename(), True) +        self.open_file_dialog.unselect_all() + +    def get_selected_files(self): +        "Helper function to return selected rows in filemanager treeview" +        return self.get_selected_keys(self.files_treeview) + +    def on_clear_activate(self, obj): +        "Callback for 'Clear' menu item" +        for row in self.get_selected_files(): +            row.model.remove(row.iter) + +    def process_file_start(self, in_name, out_name): +        "Helper function to start asynchronous processing of one file" +        if self.verify_output(out_name, self.filemanager_window): +            try: +                self.in_data = Data(file=in_name) +                self.out_data = Data() +                self.out_name = out_name +                self.file_func(self.in_data, self.out_data) +            except errors.GPGMEError as exc: +                self.file_error_message(exc) + +    def process_file_done(self, status): +        "The function called when asynchronous processing of the file is done." +        try: +            errors.errorcheck(status) +            self.out_data.seek(0,0) +            out_file = file(self.out_name, "wb") +            out_file.write(self.out_data.read()) +            out_file.close() +            self.add_file(self.out_name) +            if self.file_list: +                self.process_file_start(*(self.file_list.pop(0))) +                return True +        except (errors.GPGMEError, IOError) as exc: +            self.file_error_message(exc) + +        # Let python to free the memory. +        self.out_data = None +        self.in_data = None +        self.out_name = None +        self.file_list = [] +        self.file_func = None +        return False + +    def process_files_async(self, file_list, func, label): +        "Helper function to initialize async processing of the file list" +        self.file_list = file_list +        self.file_func = func +        self.progress_func = self.process_file_done +        self.process_file_start(*(self.file_list.pop(0))) +        self.popup_progress_dialog(label, self.filemanager_window) + +    def on_sign_activate(self, obj): +        "Callback for 'Sign' menu item" +        files = self.get_selected_files() +        if not files: return +         +        if self.file_popup(self.sign_dialog) == gtk.RESPONSE_OK: +            context = Context() +            context.set_passphrase_cb(self.password_cb) +            context.set_armor(self.sign_armor_cb.get_active()) +            for rw in self.get_selected_keys(self.sign_with_keys_treeview): +                context.signers_add(rw[-1].key) +            for cb,md,ext in [(self.sign_normal, sig.mode.NORMAL, ".gpg"), +                              (self.sign_clear, sig.mode.CLEAR, ".asc"), +                              (self.sign_separate,sig.mode.DETACH,".sig")]: +                if cb.get_active(): +                    sigmode = md +                    sigext = ext +                    break +            self.progress_context = context +            def sign(x,y):self.progress_context.op_sign_start(x,y,sigmode) +            self.process_files_async([(f[0], f[0]+sigext) for f in files], +                                     sign, _("Signing...")) + +    def verify_file_start(self, in_name, out_name): +        "Helper function to start file signature verification process" +        try: +            self.in_name = in_name +            self.out_name = out_name +            self.signed = Data(file=self.in_name) +            if out_name: +                self.plain1 = Data(file=self.out_name) +                self.plain2 = None +            else: +                self.plain1 = None +                self.plain2 = Data() +            self.progress_context.op_verify_start(self.signed, self.plain1, +                                                  self.plain2) +        except errors.GPGMEError as exc: +            self.file_error_message(exc) + +    def verify_file_done(self, status): +        "The function called when asynchronous file signature verify is done." +        try: +            errors.errorcheck(status) +            result = self.progress_context.op_verify_result() + +            model = gtk.ListStore(str, str, str) +            treeview = gtk.TreeView(model) +            treeview.set_rules_hint(True) +            for index, title in enumerate([_("Key ID"), _("Status"), +                                           _("User Name")]): +                treeview.append_column(gtk.TreeViewColumn( +                    title, gtk.CellRendererText(), text=index)) +            for sign in result.signatures: +                key = self.progress_context.get_key(sign.fpr, 0) +                if key and key.uids: +                    keyid = key.subkeys[0].keyid[-8:] +                    userid = key.uids[0].uid +                else: +                    keyid = sign.fpr[-8:] +                    userid = _("[Unknown user ID]") +                model.append([keyid, sigsum2str(sign.summary), userid]) +                     +            vbox = gtk.VBox() +            if self.out_name: +                vbox.add(gtk.Label(_("Verified data in file: %s") % +                                   self.out_name)) +            label = gtk.Label(_("Signatures:")) +            label.set_alignment(0, 1) +            vbox.add(label) +            vbox.add(treeview) +            self.verified.append((vbox, gtk.Label(self.in_name))) +            if self.file_list: +                self.verify_file_start(*(self.file_list.pop(0))) +                return True +        except errors.GPGMEError as exc: +            self.file_error_message(exc) + +        # Let python to free the memory. +        self.signed = None +        self.plain1 = None +        self.plain2 = None +        self.in_name = None +        self.out_name = None +        self.file_list = [] +        self.progress_dialog.hide() +         +        if self.verified: +            notebook = gtk.Notebook() +            for page in self.verified: notebook.append_page(*page) +            self.verify_result.add(notebook) +            self.verify_result.show_all() +            self.file_popup(self.verify_dialog) +            self.verify_result.remove(notebook) +            self.verified = [] +         +        return False + +    def on_verify_activate(self, obj): +        "Callback for 'Verify' menu item" +        files = self.get_selected_files() +        if not files: return +         +        self.file_list = [] +        for onefile in files: +            in_name = onefile[0] +            if in_name[-4:] == ".sig": +                out_name = in_name[:-4] +            elif in_name[-5:] == ".sign": +                out_name = in_name[:-5] +            else: +                out_name = None +            self.file_list.append((in_name, out_name)) +        self.verified = [] +        self.progress_context = Context() +        self.progress_func = self.verify_file_done +        self.verify_file_start(*(self.file_list.pop(0))) +        self.popup_progress_dialog(_("Verifying..."), self.filemanager_window) + +    def on_encrypt_sign_toggled(self, cb): +        "Callback for change of the 'Sign' check box in 'Encrypt files' dialog" +        self.encrypt_with_keys_treeview.set_sensitive(cb.get_active()) + +    def on_encrypt_activate(self, obj): +        "Callback for 'Encrypt' menu item" +        files = self.get_selected_files() +        if not files: return +         +        self.on_encrypt_sign_toggled(self.encrypt_sign_cb) +        if self.file_popup(self.encrypt_dialog) == gtk.RESPONSE_OK: +            context = Context() +            context.set_passphrase_cb(self.password_cb) +            if self.encrypt_armor_cb.get_active(): +                context.set_armor(True) +                ext = ".asc" +            else: +                context.set_armor(False) +                ext = ".gpg" +            keylist = [row[-1].key for row in self.get_selected_keys( +                self.encrypt_for_keys_treeview)] +            if self.encrypt_sign_cb.get_active(): +                for row in self.get_selected_keys( +                    self.encrypt_with_keys_treeview): +                    context.signers_add(row[-1].key) +                def encrypt(x,y): +                    self.progress_context.op_encrypt_sign_start( +                        keylist, 1, x, y) +            else: +                def encrypt(x,y): +                    self.progress_context.op_encrypt_start( +                        keylist, 1, x, y) +            self.progress_context = context +            self.process_files_async([(f[0], f[0]+sigext) for f in files], +                                     encrypt, _("Encrypting...")) +                 +    def on_decrypt_activate(self, obj): +        "Callback for 'Decrypt' menu item" +        files = self.get_selected_files() +        if not files: return +         +        file_list = [] +        for onefile in self.get_selected_files(): +            in_name = onefile[0] +            if in_name[-4:] in [".asc", ".gpg", ".pgp"]: +                out_name = in_name[:-4] +            else: +                out_name = in_name + ".txt" +            file_list.append((in_name, out_name)) +        self.process_context = Context() +        self.process_files_async(file_list, +                                 self.process_context.op_decrypt_start, +                                 _("Decrypting...")) + +    def on_select_all_files_activate(self, obj): +        "Callback for 'Select All' menu item in filemanager" +        self.files_treeview.get_selection().select_all() + +    def on_keyring_editor_activate(self, obj): +        "Callback for 'Keyring Editor' menu item" +        self.main_window.show() + +    def on_keyring_editor_close_activate(self, obj, event=None): +        "Callback for 'Close' menu item in Keyring Editor" +        if self.filemanager_window.get_property("visible"): +            self.main_window.hide() +            return True +        else: +            self.on_quit_activate(None)     + +    def on_filemanager_activate(self, obj): +        "Callback for 'Filemanager' menu item" +        self.on_files_changed(None) +        self.filemanager_window.show() + +    def on_filemanager_close_activate(self, obj, event=None): +        "Callback for 'Close' menu item in Filemanager" +        if self.main_window.get_property("visible"): +            self.filemanager_window.hide() +            return True +        else: +            self.on_quit_activate(None) + +    def on_about_activate(self, obj): +        "Callback for 'About' menu item" +        self.popup(self.about_dialog) + +    def __repr__(self): +        return self.__class__.__name__ + +    def __getattr__(self, name): +        "Dynamic retrieval of widgets from the glade XML" +        if name.startswith("on_"): +            self.__dict__[name] = lambda x: sys.stderr.write( +                _("Callback %s is not implimented yet\n") % name) +        elif name.startswith("_"): +            return None +        else: +            self.__dict__[name] = self.wtree.get_widget(name) +            return self.__dict__[name] +         +    def __init__(self, path): +        "PyGpa(path) - path is where pygpa.glade file can be found" +        gladefile = os.path.join(path, "pygpa.glade") +        self.wtree = gtk.glade.XML(gladefile, None, gtk.glade.textdomain()) +        self.wtree.signal_autoconnect(self) + +        self.default_key = None +        self.load_keys() +        self.setup_columns() +        self.setup_default_views() +        self.in_progress = {} +         +        gtk.main() + +    def on_quit_activate(self, obj): +        gtk.main_quit() + +PyGpa(os.path.dirname(sys.argv[0])) | 
