def main():
    k = Keyring()
    secret_keys = k.get_keys(public=False, secret=True)
    log.debug('Secret Keys: %s', secret_keys)
    
    for fpr, key in secret_keys.items():
        # Because gnupg does not list whether the key has been revoked
        # when listing secret keys, we need to list public keys
        pub_keys = k.get_keys(fpr, public=True)
        assert len(pub_keys) == 1
        pub_key = pub_keys[fpr]
        
        revoked = pub_key.revoked
        log.debug("Key %s is revoked: %r", pub_key, revoked)
Exemplo n.º 2
0
    def __init__(self, base_keyring=None, *args, **kwargs):
        # Not a new style class...
        if issubclass(self.__class__, object):
            super(TempSigningKeyring, self).__init__(*args, **kwargs)
        else:
            TempKeyring.__init__(self, *args, **kwargs)

        if base_keyring is None:
            base_keyring = Keyring()
        # Copy the public parts of the secret keys to the tmpkeyring
        for fpr, key in base_keyring.get_keys(None, secret=True,
                                              public=False).items():
            self.import_data(base_keyring.export_data(fpr))
Exemplo n.º 3
0
    def __init__(self, base_keyring=None, *args, **kwargs):
        # Not a new style class...
        if issubclass(self.__class__, object):
            super(TempSigningKeyring, self).__init__(*args, **kwargs)
        else:
            TempSplitKeyring.__init__(self, *args, **kwargs)

        if base_keyring is None:
            base_keyring = Keyring()

        # Copy the public parts of the secret keys to the tmpkeyring
        for fpr, key in base_keyring.get_keys(None,
                                              secret=True,
                                              public=False).items():
            self.import_data (base_keyring.export_data (fpr))
def main():
    k = Keyring()
    secret_keys = k.get_keys(public=False, secret=True)
    log.debug('Secret Keys: %s', secret_keys)
    
    for fpr, key in secret_keys.items():
        log.debug("Keys: %s", key)
        exp = key.expiry
        log.debug("Key's expiry: %r", exp)
        if exp:
            expiry = datetime.fromtimestamp(int(exp))
            now = datetime.now()
            expired = now > expiry
        else:
            expired = False
        log.debug("Key's expired: %s - %s", key.expired, expired)
def main():
    k = Keyring()
    secret_keys = k.get_keys(public=False, secret=True)
    log.debug('Secret Keys: %s', secret_keys)

    for fpr, key in secret_keys.items():
        log.debug("Keys: %s", key)
        exp = key.expiry
        log.debug("Key's expiry: %r", exp)
        if exp:
            expiry = datetime.fromtimestamp(int(exp))
            now = datetime.now()
            expired = now > expiry
        else:
            expired = False
        log.debug("Key's expired: %s - %s", key.expired, expired)
Exemplo n.º 6
0
def get_usable_secret_keys(keyring=None, pattern=None):
    '''Returns all secret keys which can be used to sign a key

    Uses get_keys on the keyring and filters for
    non revoked, expired, disabled, or invalid keys'''
    if keyring is None:
        keyring = Keyring()
    secret_keys_dict = keyring.get_keys(pattern=pattern,
                                        public=False,
                                        secret=True)
    secret_key_fprs = secret_keys_dict.keys()
    log.debug('Detected secret keys: %s', secret_key_fprs)
    usable_keys_fprs = filter(
        lambda fpr: get_usable_keys(keyring, pattern=fpr, public=True),
        secret_key_fprs)
    usable_keys = [
        Key.from_monkeysign(secret_keys_dict[fpr]) for fpr in usable_keys_fprs
    ]

    log.info('Returning usable private keys: %s', usable_keys)
    return usable_keys
Exemplo n.º 7
0
def main():
    import sys
    key = sys.argv[1]
    keyring = Keyring()
    keys = keyring.get_keys(key)
    # Heh, we take the first key here. Maybe we should raise a warning
    # or so, when there is more than one key.
    fpr = keys.items()[0][0]
    data = 'OPENPGP4FPR:' + fpr
    
    w = Gtk.Window()
    w.connect("delete-event", Gtk.main_quit)
    w.set_default_size(100,100)
    v = Gtk.VBox()
    label = Gtk.Label(data)
    qr = QRImage(data)
    v.add(label)
    v.add(qr)
    w.add(v)
    w.show_all()
    Gtk.main()
Exemplo n.º 8
0
def main():
    import sys
    key = sys.argv[1]
    keyring = Keyring()
    keys = keyring.get_keys(key)
    # Heh, we take the first key here. Maybe we should raise a warning
    # or so, when there is more than one key.
    key = next(iter(keys.items()))[1]
    fpr = key.fpr
    data = 'OPENPGP4FPR:' + fpr

    w = Gtk.Window()
    w.connect("delete-event", Gtk.main_quit)
    w.set_default_size(100, 100)
    v = Gtk.VBox()
    label = Gtk.Label(data)
    qr = QRImage(data)
    v.add(label)
    v.add(qr)
    w.add(v)
    w.show_all()
    Gtk.main()
Exemplo n.º 9
0
def get_usable_keys(keyring=None, *args, **kwargs):
    '''Uses get_keys on the keyring and filters for
    non revoked, expired, disabled, or invalid keys'''
    log.debug('Retrieving keys for %s, %s', args, kwargs)
    if keyring is None:
        keyring = Keyring()
    keys_dict = keyring.get_keys(*args, **kwargs)
    assert keys_dict is not None, keyring.context.stderr

    def is_usable(key):
        unusable =    key.invalid or key.disabled \
                   or key.expired or key.revoked
        log.debug('Key %s is invalid: %s (i:%s, d:%s, e:%s, r:%s)', key,
                  unusable, key.invalid, key.disabled, key.expired,
                  key.revoked)
        return not unusable

    # keys_fpr = keys_dict.items()
    keys = keys_dict.values()
    usable_keys = [Key.from_monkeysign(key) for key in keys if is_usable(key)]

    log.debug('Identified usable keys: %s', usable_keys)
    return usable_keys
Exemplo n.º 10
0
class KeysPage(Gtk.VBox):

    def __init__(self, keySection):
        super(KeysPage, self).__init__()

        # pass a reference to KeySignSection in order to access its widgets
        self.keySection = keySection

        # set up the list store to be filled up with user's gpg keys
        self.store = Gtk.ListStore(str, str, str)

        # FIXME: this should be moved to KeySignSection
        self.keyring = Keyring() # the user's keyring

        self.keysDict = {}

        # FIXME: this should be a callback function to update the display
        # when a key is changed/deleted
        for key in self.keyring.get_keys(None, True, False).values():
            if key.invalid or key.disabled or key.expired or key.revoked:
                continue

            uidslist = key.uidslist #UIDs: Real Name (Comment) <email@address>
            keyid = str(key.keyid()) # the key's short id

            if not keyid in self.keysDict:
                self.keysDict[keyid] = key

            for e in uidslist:
                uid = str(e.uid)
                # remove the comment from UID (if it exists)
                com_start = uid.find('(')
                if com_start != -1:
                    com_end = uid.find(')')
                    uid = uid[:com_start].strip() + uid[com_end+1:].strip()

                # split into user's name and email
                tokens = uid.split('<')
                name = tokens[0].strip()
                email = 'unknown'
                if len(tokens) > 1:
                    email = tokens[1].replace('>','').strip()

                self.store.append((name, email, keyid))

        # create the tree view
        self.treeView = Gtk.TreeView(model=self.store)

        # setup 'Name' column
        nameRenderer = Gtk.CellRendererText()
        nameColumn = Gtk.TreeViewColumn("Name", nameRenderer, text=0)

        # setup 'Email' column
        emailRenderer = Gtk.CellRendererText()
        emailColumn = Gtk.TreeViewColumn("Email", emailRenderer, text=1)

        # setup 'Key' column
        keyRenderer = Gtk.CellRendererText()
        keyColumn = Gtk.TreeViewColumn("Key", keyRenderer, text=2)

        self.treeView.append_column(nameColumn)
        self.treeView.append_column(emailColumn)
        self.treeView.append_column(keyColumn)

        # make the tree view resposive to single click selection
        self.treeView.get_selection().connect('changed', self.on_selection_changed)

        # make the tree view scrollable
        self.scrolled_window = Gtk.ScrolledWindow()
        self.scrolled_window.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
        self.scrolled_window.add(self.treeView)
        self.scrolled_window.set_min_content_height(200)

        self.pack_start(self.scrolled_window, True, True, 0)

    def on_selection_changed(self, *args):
        self.keySection.nextButton.set_sensitive(True)
Exemplo n.º 11
0
class KeysPage(Gtk.VBox):
    '''This represents a list of keys with the option for the user
    to select one key to proceed.
    
    This class emits a `key-selection-changed' signal when the user
    initially selects a key such that it is highlighted.
    
    The `key-selected' signal is emitted when the user commits
    to a key, i.e. by pressing a designated button to make his
    selection public.
    '''
    __gsignals__ = {
        str('key-selected'): (GObject.SIGNAL_RUN_LAST, None,
                         # Hm, this is a str for now, but ideally
                         # it'd be the full key object
                         (str,)),
        str('key-selection-changed'): (GObject.SIGNAL_RUN_LAST, None,
                         # Hm, this is a str for now, but ideally
                         # it'd be the full key object
                         (str,)),
    }

    def __init__(self, show_public_keys=False):
        '''Sets the widget up.
        
        The show_public_keys parameter is meant for development
        purposes only.  If set to True, the widget will show
        the public keys, too.  Otherwise, secret keys are shown.
        '''
        super(KeysPage, self).__init__()

        # set up the list store to be filled up with user's gpg keys
        # Note that other functions expect a certain structure to
        # this ListStore, e.g. when parsing the selection of the
        # TreeView, i.e. in get_items_from_selection.
        self.store = Gtk.ListStore(str, str, str)

        # FIXME: this should be moved to KeySignSection
        self.keyring = Keyring() # the user's keyring

        self.keysDict = {}

        # FIXME: this should be a callback function to update the display
        # when a key is changed/deleted
        keys = self.keyring.get_keys(None, secret=True,
                                           public=show_public_keys)
        for fpr, key in keys.items():
            if key.invalid or key.disabled or key.expired or key.revoked:
                continue

            uidslist = key.uidslist #UIDs: Real Name (Comment) <email@address>
            keyid = str(key.keyid()) # the key's short id

            if not keyid in self.keysDict:
                self.keysDict[keyid] = key

            for e in uidslist:
                uid = str(e.uid)
                # remove the comment from UID (if it exists)
                com_start = uid.find('(')
                if com_start != -1:
                    com_end = uid.find(')')
                    uid = uid[:com_start].strip() + uid[com_end+1:].strip()

                # split into user's name and email
                tokens = uid.split('<')
                name = tokens[0].strip()
                email = 'unknown'
                if len(tokens) > 1:
                    email = tokens[1].replace('>','').strip()

                self.store.append((name, email, keyid))

        if len(self.store) == 0:
            self.pack_start(Gtk.Label("You don't have a private key"), True, True, 0)
        else:
            # create the tree view
            self.treeView = Gtk.TreeView(model=self.store)
            # setup 'Name' column
            nameRenderer = Gtk.CellRendererText()
            nameColumn = Gtk.TreeViewColumn("Name", nameRenderer, text=0)

            # setup 'Email' column
            emailRenderer = Gtk.CellRendererText()
            emailColumn = Gtk.TreeViewColumn("Email", emailRenderer, text=1)

            # setup 'Key' column
            keyRenderer = Gtk.CellRendererText()
            keyColumn = Gtk.TreeViewColumn("Key", keyRenderer, text=2)

            self.treeView.append_column(nameColumn)
            self.treeView.append_column(emailColumn)
            self.treeView.append_column(keyColumn)
            
            self.treeView.connect('row-activated', self.on_row_activated)

            # make the tree view resposive to single click selection
            self.treeView.get_selection().connect('changed', self.on_selection_changed)

            # make the tree view scrollable
            self.scrolled_window = Gtk.ScrolledWindow()
            self.scrolled_window.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
            self.scrolled_window.add(self.treeView)
            self.scrolled_window.set_min_content_height(200)

            #self.pack_start(self.scrolled_window, True, True, 0)

            self.hpane = Gtk.HPaned()
            self.hpane.pack1(self.scrolled_window, False, False)
            self.right_pane = Gtk.VBox()
            right_label = Gtk.Label(label='Select key on the left')
            self.right_pane.add(right_label)
            # Hm, right now, the width of the right pane changes, when
            # a key is selected, because the right pane's content will be
            # wider when it displays expiration et al.
            # Can we hint at that fact and make the VBox a bit wider than necessary?
            #padded_label = Gtk.Label(label='Select key on the left'*3)
            #self.right_pane.add(padded_label)
            self.hpane.pack2(self.right_pane, True, False)

            self.pack_start(self.hpane, True, True, 0)


    # We could make it a @staticmethod, but the returned items
    # are bound to the model, anyway.  So it probably doesn't
    # make much sense to have a static function, anyway.
    def get_items_from_selection(self, selection=None):
        '''Returns the elements in the ListStore for the given selection'''
        s = selection or self.treeView.get_selection()
        model, paths = s.get_selected_rows()
        name = email = keyid = None
        for path in paths:
            iterator = model.get_iter(path)
            (name, email, keyid) = model.get(iterator, 0, 1, 2)
            break

        return (name, email, keyid)


    def on_selection_changed(self, selection, *args):
        log.debug('Selected new TreeView item %s = %s', selection, args)
        
        name, email, keyid = self.get_items_from_selection(selection)
        
        key = self.keysDict[keyid]
        self.emit('key-selection-changed', keyid)
        
        try:
            exp_date = datetime.fromtimestamp(float(key.expiry))
        except TypeError as e:
            # This might be the case when the key.expiry is already a timedate
            exp_date = key.expiry
        except ValueError as e:
            # This happens when converting an empty string to a datetime.
            exp_date = None

        if exp_date is None:
            expiry = "No expiration date"
        else:
            expiry = "{:%Y-%m-%d %H:%M:%S}".format(exp_date)

        pane = self.right_pane
        for child in pane.get_children():
            # Ouch, this is not very efficient.
            # But this deals with the fact that the first
            # label in the pane is a "Select a key on the left"
            # text.
            pane.remove(child)
        ctx = {'keyid':keyid, 'expiry':expiry, 'sigs':''}
        keyid_label = Gtk.Label(label='Key {keyid}'.format(**ctx))
        expiration_label = Gtk.Label(label='Expires: {expiry}'.format(**ctx))
        #signatures_label = Gtk.Label(label='{sigs} signatures'.format(**ctx))
        publish_button = Gtk.Button(label='Go ahead!'.format(**ctx))
        publish_button.connect('clicked', self.on_publish_button_clicked, key)
        for w in (keyid_label
                  , expiration_label
                  #, signatures_label
                  , publish_button
                  ):
            pane.add(w)
        pane.show_all()


    def on_row_activated(self, treeview, tree_path, column):
        '''A callback for when the user "activated" a row,
        e.g. by double-clicking an entry.
        
        It emits the key-selected signal.
        '''
        # We just hijack the existing function.
        # I'm sure we could get the required information out of
        # the tree_path and column, but I don't know how.
        name, email, keyid = self.get_items_from_selection()
        self.emit('key-selected', keyid)


    def on_publish_button_clicked(self, button, key, *args):
        '''Callback for when the user has expressed their wish
        to publish a key on the network.  It will emit a "key-selected"
        signal with the ID of the selected key.'''
        log.debug('Clicked publish for key (%s) %s (%s)', type(key), key, args)
        keyid = key.keyid()
        self.emit('key-selected', keyid)
Exemplo n.º 12
0
class KeySignSection(Gtk.VBox):
    def __init__(self):
        '''Initialises the section which lets the user
        choose a key to be signed by other person.
        '''
        super(KeySignSection, self).__init__()

        self.log = logging.getLogger(__name__)
        self.keyring = Keyring()

        # these are needed later when we need to get details about
        # a selected key
        self.keysPage = KeysPage()
        self.keysPage.connect('key-selection-changed',
                              self.on_key_selection_changed)
        self.keysPage.connect('key-selected', self.on_key_selected)
        self.keyDetailsPage = KeyDetailsPage()
        self.keyPresentPage = KeyPresentPage()

        # create back button
        self.backButton = Gtk.Button('Back')
        self.backButton.set_image(
            Gtk.Image.new_from_icon_name("go-previous", Gtk.IconSize.BUTTON))
        self.backButton.set_always_show_image(True)
        self.backButton.connect('clicked', self.on_button_clicked)

        # set up notebook container
        self.notebook = Gtk.Notebook()
        self.notebook.append_page(self.keysPage, None)
        vbox = Gtk.VBox()
        # We place the button at the top, but that might not be the
        # smartest thing to do. Feel free to rearrange
        # FIXME: Consider a GtkHeaderBar for the application
        vbox.pack_start(self.backButton, False, False, 0)
        vbox.pack_start(self.keyPresentPage, True, True, 10)
        self.notebook.append_page(vbox, None)
        self.notebook.set_show_tabs(False)

        self.pack_start(self.notebook, True, True, 0)

        # this will hold a reference to the last key selected
        self.last_selected_key = None

        # When obtaining a key is successful,
        # it will save the key data in this field
        self.received_key_data = None

        self.keyserver = None

    def on_key_selection_changed(self, pane, keyid):
        '''This callback is attached to the signal which is emitted
        when the user changes their selection in the list of keys
        '''
        pass

    def on_key_selected(self, pane, keyid):
        '''This is the callback for when the user has committed
        to a key, i.e. the user has made a selection and wants to
        advance the program.
        '''
        log.debug('User selected key %s', keyid)

        key = list(self.keyring.get_keys(keyid).values())[0]

        keyid = key.keyid()
        fpr = key.fpr
        self.keyring.export_data(fpr, secret=False)
        keydata = self.keyring.context.stdout

        self.log.debug("Keyserver switched on! Serving key with fpr: %s", fpr)
        self.setup_server(keydata, fpr)

        self.switch_to_key_present_page(key)

    def switch_to_key_present_page(self, key):
        '''This switches the notebook to the page which
        presents the information that is needed to securely
        transfer the keydata, i.e. the fingerprint and its barcode.
        '''
        self.keyPresentPage.display_fingerprint_qr_page(key)
        self.notebook.next_page()
        # This is more of a crude hack. Once the next page is presented,
        # the back button has the focus. This is not desirable because
        # you will go back when accidentally pressing space or enter.
        self.keyPresentPage.fingerprintLabel.grab_focus()
        # FIXME: we better use set_current_page, but that requires
        # knowing which page our desired widget is on.
        # FWIW: A headerbar has named pages.

    def on_next_button_clicked(self, button):
        '''A helper for legacy reasons to enable a next button
        
        All it does is retrieve the selection from the TreeView and
        call the signal handler for when the user committed to a key
        '''
        name, email, keyid = self.keysPage.get_items_from_selection()
        return self.on_key_selected(button, keyid)

    def on_button_clicked(self, button):

        page_index = self.notebook.get_current_page()

        if button == self.backButton:

            if page_index == 1:
                self.log.debug("Keyserver switched off")
                self.stop_server()

            self.notebook.prev_page()

    def setup_server(self, keydata, fingerprint):
        """
        Starts the key-server which serves the provided keydata and
        announces the fingerprint as TXT record using Avahi
        """
        self.log.info('Serving now')
        self.log.debug('About to call %r', Keyserver.ServeKeyThread)
        self.keyserver = Keyserver.ServeKeyThread(str(keydata), fingerprint)
        self.log.info('Starting thread %r', self.keyserver)
        self.keyserver.start()
        self.log.info('Finsihed serving')
        return False

    def stop_server(self):
        self.keyserver.shutdown()
Exemplo n.º 13
0
class KeysPage(Gtk.VBox):
    '''This represents a list of keys with the option for the user
    to select one key to proceed.
    
    This class emits a `key-selection-changed' signal when the user
    initially selects a key such that it is highlighted.
    
    The `key-selected' signal is emitted when the user commits
    to a key, i.e. by pressing a designated button to make his
    selection public.
    '''
    __gsignals__ = {
        str('key-selected'): (
            GObject.SIGNAL_RUN_LAST,
            None,
            # Hm, this is a str for now, but ideally
            # it'd be the full key object
            (
                str, )),
        str('key-selection-changed'): (
            GObject.SIGNAL_RUN_LAST,
            None,
            # Hm, this is a str for now, but ideally
            # it'd be the full key object
            (
                str, )),
    }

    def __init__(self, show_public_keys=False):
        '''Sets the widget up.
        
        The show_public_keys parameter is meant for development
        purposes only.  If set to True, the widget will show
        the public keys, too.  Otherwise, secret keys are shown.
        '''
        super(KeysPage, self).__init__()

        # set up the list store to be filled up with user's gpg keys
        # Note that other functions expect a certain structure to
        # this ListStore, e.g. when parsing the selection of the
        # TreeView, i.e. in get_items_from_selection.
        self.store = Gtk.ListStore(str, str, str)

        # FIXME: this should be moved to KeySignSection
        self.keyring = Keyring()  # the user's keyring

        self.keysDict = {}

        # FIXME: this should be a callback function to update the display
        # when a key is changed/deleted
        keys = self.keyring.get_keys(None,
                                     secret=True,
                                     public=show_public_keys)
        for fpr, key in keys.items():
            if key.invalid or key.disabled or key.expired or key.revoked:
                continue

            uidslist = key.uidslist  #UIDs: Real Name (Comment) <email@address>
            keyid = str(key.keyid())  # the key's short id

            if not keyid in self.keysDict:
                self.keysDict[keyid] = key

            for e in uidslist:
                uid = str(e.uid)
                # remove the comment from UID (if it exists)
                com_start = uid.find('(')
                if com_start != -1:
                    com_end = uid.find(')')
                    uid = uid[:com_start].strip() + uid[com_end + 1:].strip()

                # split into user's name and email
                tokens = uid.split('<')
                name = tokens[0].strip()
                email = 'unknown'
                if len(tokens) > 1:
                    email = tokens[1].replace('>', '').strip()

                self.store.append((name, email, keyid))

        if len(self.store) == 0:
            self.pack_start(Gtk.Label("You don't have a private key"), True,
                            True, 0)
        else:
            # create the tree view
            self.treeView = Gtk.TreeView(model=self.store)
            # setup 'Name' column
            nameRenderer = Gtk.CellRendererText()
            nameColumn = Gtk.TreeViewColumn("Name", nameRenderer, text=0)

            # setup 'Email' column
            emailRenderer = Gtk.CellRendererText()
            emailColumn = Gtk.TreeViewColumn("Email", emailRenderer, text=1)

            # setup 'Key' column
            keyRenderer = Gtk.CellRendererText()
            keyColumn = Gtk.TreeViewColumn("Key", keyRenderer, text=2)

            self.treeView.append_column(nameColumn)
            self.treeView.append_column(emailColumn)
            self.treeView.append_column(keyColumn)

            self.treeView.connect('row-activated', self.on_row_activated)

            # make the tree view resposive to single click selection
            self.treeView.get_selection().connect('changed',
                                                  self.on_selection_changed)

            # make the tree view scrollable
            self.scrolled_window = Gtk.ScrolledWindow()
            self.scrolled_window.set_policy(Gtk.PolicyType.NEVER,
                                            Gtk.PolicyType.AUTOMATIC)
            self.scrolled_window.add(self.treeView)
            self.scrolled_window.set_min_content_height(200)

            #self.pack_start(self.scrolled_window, True, True, 0)

            self.hpane = Gtk.HPaned()
            self.hpane.pack1(self.scrolled_window, False, False)
            self.right_pane = Gtk.VBox()
            right_label = Gtk.Label(label='Select key on the left')
            self.right_pane.add(right_label)
            # Hm, right now, the width of the right pane changes, when
            # a key is selected, because the right pane's content will be
            # wider when it displays expiration et al.
            # Can we hint at that fact and make the VBox a bit wider than necessary?
            #padded_label = Gtk.Label(label='Select key on the left'*3)
            #self.right_pane.add(padded_label)
            self.hpane.pack2(self.right_pane, True, False)

            self.pack_start(self.hpane, True, True, 0)

    # We could make it a @staticmethod, but the returned items
    # are bound to the model, anyway.  So it probably doesn't
    # make much sense to have a static function, anyway.
    def get_items_from_selection(self, selection=None):
        '''Returns the elements in the ListStore for the given selection'''
        s = selection or self.treeView.get_selection()
        model, paths = s.get_selected_rows()
        name = email = keyid = None
        for path in paths:
            iterator = model.get_iter(path)
            (name, email, keyid) = model.get(iterator, 0, 1, 2)
            break

        return (name, email, keyid)

    def on_selection_changed(self, selection, *args):
        log.debug('Selected new TreeView item %s = %s', selection, args)

        name, email, keyid = self.get_items_from_selection(selection)

        key = self.keysDict[keyid]
        self.emit('key-selection-changed', keyid)

        try:
            exp_date = datetime.fromtimestamp(float(key.expiry))
        except TypeError as e:
            # This might be the case when the key.expiry is already a timedate
            exp_date = key.expiry
        except ValueError as e:
            # This happens when converting an empty string to a datetime.
            exp_date = None

        if exp_date is None:
            expiry = "No expiration date"
        else:
            expiry = "{:%Y-%m-%d %H:%M:%S}".format(exp_date)

        pane = self.right_pane
        for child in pane.get_children():
            # Ouch, this is not very efficient.
            # But this deals with the fact that the first
            # label in the pane is a "Select a key on the left"
            # text.
            pane.remove(child)
        ctx = {'keyid': keyid, 'expiry': expiry, 'sigs': ''}
        keyid_label = Gtk.Label(label='Key {keyid}'.format(**ctx))
        expiration_label = Gtk.Label(label='Expires: {expiry}'.format(**ctx))
        #signatures_label = Gtk.Label(label='{sigs} signatures'.format(**ctx))
        publish_button = Gtk.Button(label='Go ahead!'.format(**ctx))
        publish_button.connect('clicked', self.on_publish_button_clicked, key)
        for w in (
                keyid_label,
                expiration_label
                #, signatures_label
                ,
                publish_button):
            pane.add(w)
        pane.show_all()

    def on_row_activated(self, treeview, tree_path, column):
        '''A callback for when the user "activated" a row,
        e.g. by double-clicking an entry.
        
        It emits the key-selected signal.
        '''
        # We just hijack the existing function.
        # I'm sure we could get the required information out of
        # the tree_path and column, but I don't know how.
        name, email, keyid = self.get_items_from_selection()
        self.emit('key-selected', keyid)

    def on_publish_button_clicked(self, button, key, *args):
        '''Callback for when the user has expressed their wish
        to publish a key on the network.  It will emit a "key-selected"
        signal with the ID of the selected key.'''
        log.debug('Clicked publish for key (%s) %s (%s)', type(key), key, args)
        keyid = key.keyid()
        self.emit('key-selected', keyid)