[Gpa-commits] r974 - in trunk: . src

scm-commit@wald.intevation.org scm-commit at wald.intevation.org
Tue Mar 24 11:50:59 CET 2009


Author: werner
Date: 2009-03-24 11:50:53 +0100 (Tue, 24 Mar 2009)
New Revision: 974

Modified:
   trunk/TODO
   trunk/src/ChangeLog
   trunk/src/cardman.c
   trunk/src/cm-netkey.c
   trunk/src/cm-openpgp.c
   trunk/src/gpa-key-details.c
   trunk/src/gpa.c
   trunk/src/gpa.h
   trunk/src/gpgmetools.c
   trunk/src/gpgmetools.h
Log:
Various cardman related updates.


Modified: trunk/TODO
===================================================================
--- trunk/TODO	2009-03-22 16:17:54 UTC (rev 973)
+++ trunk/TODO	2009-03-24 10:50:53 UTC (rev 974)
@@ -1,4 +1,4 @@
--*- outline -*-
+-*- org -*-
 
 In no particular order:
 
@@ -55,6 +55,14 @@
   We might also want to have a scheduled refresh mode.  But that might
   be better implemented in gpg.
 
+* Cardman
+
+** We don't care about duplicate keys
+   In particular with X.509 it is common to use the same key (keygrip)
+   with several certificates.  GpaKeyDetails can't cope with that but
+   uses the first key found.
+
+
 * MB:
 ** Support getting plaintext from verify operation (should emit "created file"
    then).  Done for clipboard.  Also needed for normal file ops and server.
@@ -116,6 +124,7 @@
    When doing multi-file verify/decrypt, we may ignore more errors as
    to make more progress.  This seems more useful than the current
    behavior.
+
 ** FileChooser Dialog with pseudo-random window size
    When clicking "import" in the keyring manager, the FileChooser pops
    up in a tiny window most of the time -- sometimes the window size is just right.
@@ -130,3 +139,4 @@
    It seems, GPA windows have no icon set. Besides: GTK+ has a rather
    nice key icon in it's stock: GTK_STOCK_AUTHENTICATION_DIALOG. Using
    that would probably look more natural in a GNOME environment.
+

Modified: trunk/src/ChangeLog
===================================================================
--- trunk/src/ChangeLog	2009-03-22 16:17:54 UTC (rev 973)
+++ trunk/src/ChangeLog	2009-03-24 10:50:53 UTC (rev 974)
@@ -1,3 +1,14 @@
+2009-03-23  Werner Koch  <wk at g10code.com>
+
+	* gpgmetools.c (gpg_simple_stderr_cb): Use GString and skip
+	non-status lines. 
+
+	* gpa.h (disable_ticker): New.
+	* gpa.c: Add hidden option --disable-ticker.
+	* cardman.c (start_ticker): Implement option.
+	(ticker_cb): Ignore tick while reloading.
+	(watcher_cb): Avoid reload already here.
+
 2009-03-22  Moritz  <moritz at gnu.org>
 
 	* cardman.c (cardman_action_new): Call
@@ -4,6 +15,33 @@
 	gtk_action_group_set_translation_domain before
 	gtk_action_group_add_actions.
 
+2009-03-19  Werner Koch  <wk at g10code.com>
+
+	* gpgmetools.c (gpa_backup_key): Replace g_assert for the gpg path
+	test by a cleaner return_if_fail.
+	(gpa_start_simple_gpg_command): New.
+
+2009-03-18  Werner Koch  <wk at g10code.com>
+
+	* cardman.c (construct_widgets): Do not update the card widget here.
+	(card_reload_finish_idle_cb): New.
+	(card_reload): Decrement card via idle handler.
+
+	* cm-netkey.c (reload_more_data_idle_cb, reload_more_data) 
+	(reload_more_data_cb): New.
+	(reload_data): Call reload_more_data.
+	(construct_data_widget): Add a keys frame.
+	(reload_data): Clear the frame.
+
+2009-03-17  Werner Koch  <wk at g10code.com>
+
+	* cm-openpgp.c (show_admin_pin_notice): New.
+	(save_attr): Call it.
+
+	* gpa-key-details.c (gpa_key_details_update): Keep track of the
+	current key.
+	(signatures_uid_changed): Select sigs according to current key.
+
 2009-03-16  Werner Koch  <wk at g10code.com>
 
 	* cm-openpgp.c (construct_data_widget): Use an expander for the

Modified: trunk/src/cardman.c
===================================================================
--- trunk/src/cardman.c	2009-03-22 16:17:54 UTC (rev 973)
+++ trunk/src/cardman.c	2009-03-24 10:50:53 UTC (rev 974)
@@ -259,6 +259,19 @@
 }     
 
 
+/* Idle queue callback to mark a relaod operation finished.  */
+static gboolean
+card_reload_finish_idle_cb (void *user_data)
+{
+  GpaCardManager *cardman = user_data;
+  
+  cardman->in_card_reload--;
+  g_object_unref (cardman);
+
+  return FALSE;  /* Remove us from the idle queue. */
+}
+
+
 /* This function is called to trigger a card-reload.  */
 static void
 card_reload (GpaCardManager *cardman)
@@ -275,7 +288,6 @@
 
   /* Start the ticker if not yet done.  */
   start_ticker (cardman);
-    
   if (!cardman->in_card_reload)
     {
       cardman->in_card_reload++;
@@ -336,7 +348,8 @@
         }
 
 
-      if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT)
+      if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT
+          || gpg_err_code (err) == GPG_ERR_CARD_REMOVED)
         {
           err_desc = _("No card found.");
         }
@@ -387,8 +400,9 @@
                                           scd_status_cb, cardman);
           if (!err)
             err = gpgme_op_assuan_result (cardman->gpgagent)->err;
-
-          if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT)
+          
+          if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT
+              || gpg_err_code (err) == GPG_ERR_CARD_REMOVED)
             statusbar_update (cardman, _("No card"));
           else if (err)
             {
@@ -403,12 +417,12 @@
       update_title (cardman);
       
       update_info_visibility (cardman);
-      /* Fixme: We should decrement the sentinel only after finishing
-         the operation, so that we don't run them over and over if the
-         user clicks too often.  Note however that the primary reason
-         for this sentinel is to avoid concurrent reloads triggered by
-         the user and by the file watcher.  */
-      cardman->in_card_reload--;
+      /* We decrement our lock using a idle handler with lo priority.
+         This gives us a better chance not to do a reload a second
+         time on behalf of the file watcher or ticker.  */
+      g_object_ref (cardman);
+      g_idle_add_full (G_PRIORITY_LOW,
+                       card_reload_finish_idle_cb, cardman, NULL);
     }
 }
 
@@ -469,7 +483,7 @@
   return 0;
 }
 
-/* This fucntion is called by the timerout ticker started by
+/* This function is called by the timeout ticker started by
    start_ticker.  It is used to poll scdaemon to detect a card status
    change.  */
 static gboolean
@@ -477,7 +491,8 @@
 {
   GpaCardManager *cardman = user_data;
 
-  if (!cardman || !cardman->ticker_timeout_id || !cardman->gpgagent)
+  if (!cardman || !cardman->ticker_timeout_id || !cardman->gpgagent
+      || cardman->in_card_reload)
     return TRUE;  /* Keep on ticking.  */
   
   /* Note that we are single threaded and thus there is no need to
@@ -497,6 +512,9 @@
 static void
 start_ticker (GpaCardManager *cardman)
 {
+  if (disable_ticker)
+    return;
+
   if (!cardman->ticker_timeout_id)
     {
 #if GTK_CHECK_VERSION (2, 14, 0)
@@ -571,7 +589,7 @@
 {
   GpaCardManager *cardman = opaque;
 
-  if (cardman && strchr (reason, 'w') )
+  if (cardman && strchr (reason, 'w') && !cardman->in_card_reload)
     {
       card_reload (cardman);
     }

Modified: trunk/src/cm-netkey.c
===================================================================
--- trunk/src/cm-netkey.c	2009-03-22 16:17:54 UTC (rev 973)
+++ trunk/src/cm-netkey.c	2009-03-24 10:50:53 UTC (rev 974)
@@ -29,6 +29,7 @@
 #include "gpa.h"   
 #include "gtktools.h"
 #include "convert.h"
+#include "gpa-key-details.h"
 
 #include "cm-object.h"
 #include "cm-netkey.h"
@@ -77,6 +78,8 @@
 
   GtkWidget *warning_frame;  /* The frame used to display warnings etc.  */
 
+  GtkWidget *keys_frame;     /* The frame containing the keys.  */
+
   GtkWidget *entries[ENTRY_LAST];
 
   GtkWidget *change_pin_btn;      /* The button to change the PIN for NKS.  */
@@ -98,6 +101,7 @@
 
 
 /* Local prototypes */
+static void learn_keys_clicked_cb (GtkButton *widget, void *user_data);
 static void gpa_cm_netkey_finalize (GObject *object);
 
 
@@ -223,14 +227,188 @@
                                   any_isnull, 
                                   tbl[idx].is_puk, tbl[idx].widget);
 
+  gtk_widget_set_no_show_all (card->warning_frame, FALSE);
   if (any_isnull)
     gtk_widget_show_all (card->warning_frame);
   else
-    gtk_widget_hide_all (card->warning_frame);
+    {
+      gtk_widget_hide_all (card->warning_frame);
+      gtk_widget_set_no_show_all (card->warning_frame, TRUE);
+    }
 }
 
 
+/* Structure form comminucation between reload_more_data and
+   reload_more_data_cb.  */
+struct reload_more_data_parm
+{
+  GpaCMNetkey *card; /* self  */
+  gpgme_ctx_t ctx;   /* A prepared context for relaod_more_data_cb.  */
+  int any_unknown;   /* Set if at least one key is not known.  */
+};
 
+
+/* Helper for relaod_more_data.  This is actually an Assuan status
+   callback  */
+static gpg_error_t
+reload_more_data_cb (void *opaque, const char *status, const char *args)
+{
+  struct reload_more_data_parm *parm = opaque;
+  gpgme_key_t key = NULL;
+  const char *s;
+  char pattern[100];
+  int idx;
+  gpg_error_t err;
+  const char *keyid;
+  int any = 0;
+
+  if (strcmp (status, "KEYPAIRINFO") )
+    return 0;
+
+  idx = 0;
+  pattern[idx++] = '&';
+  for (s=args; hexdigitp (s) && idx < sizeof pattern - 1; s++)
+    pattern[idx++] = *s;
+  pattern[idx] = 0;
+  if (!spacep (s) || (s - args != 40))
+    return 0;  /* Invalid formatted keygrip.  */
+  while (spacep (s))
+    s++;
+  keyid = s;
+
+  if (!(err=gpgme_op_keylist_start (parm->ctx, pattern, 0)))
+    {
+      if (!(err=gpgme_op_keylist_next (parm->ctx, &key)))
+        {
+          GtkWidget *vbox, *expander, *details, *hbox, *label;
+
+          vbox = gtk_bin_get_child (GTK_BIN (parm->card->keys_frame));
+          if (!vbox)
+            g_debug ("Ooops, vbox missing in key frame");
+          else
+            {
+              expander = gtk_expander_new (keyid);
+              details = gpa_key_details_new ();
+              gtk_container_add (GTK_CONTAINER (expander), details);
+              gpa_key_details_update (details, key, 1);
+
+              hbox = gtk_hbox_new (FALSE, 0);
+              label = gtk_label_new (NULL);
+              gtk_label_set_width_chars  (GTK_LABEL (label), 22);
+              gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
+              gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
+              gtk_box_pack_start (GTK_BOX (hbox), expander, TRUE, TRUE, 0);
+              gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
+
+              any = 1;
+            }
+          gpgme_key_unref (key);
+        }
+    }
+  gpgme_op_keylist_end (parm->ctx);
+  if (!any)
+    parm->any_unknown = 1;
+  return 0;
+}
+
+
+/* Reload more data.  This function is called from the idle handler.  */
+static void
+reload_more_data (GpaCMNetkey *card)
+{
+  gpg_error_t err;
+  gpgme_ctx_t gpgagent;
+  GtkWidget *vbox;
+  struct reload_more_data_parm parm;
+
+  gpgagent = GPA_CM_OBJECT (card)->agent_ctx;
+  g_return_if_fail (gpgagent);
+  g_return_if_fail (card->keys_frame);
+
+  /* We remove any existing children of the keys frame and then we add
+     a new vbox to be filled with new widgets by the callback.  */
+  vbox = gtk_bin_get_child (GTK_BIN (card->keys_frame));
+  if (vbox)
+    gtk_widget_destroy (vbox);
+  vbox = gtk_vbox_new (FALSE, 5);
+  gtk_container_add (GTK_CONTAINER (card->keys_frame), vbox);
+
+  /* Create a context for key listings.  */
+  parm.any_unknown = 0;
+  parm.card = card;
+  err = gpgme_new (&parm.ctx);
+  if (err)
+    {
+      /* We don't want an error window because we are run from an idle
+         handler and the information is not that important.  */
+      g_debug ("failed to create a context: %s", gpg_strerror (err));
+      return;
+    }
+  gpgme_set_protocol (parm.ctx, GPGME_PROTOCOL_CMS);
+  /* We include ephemeral keys in the listing.  */
+  gpgme_set_keylist_mode (parm.ctx, GPGME_KEYLIST_MODE_EPHEMERAL);
+
+  err = gpgme_op_assuan_transact (gpgagent,
+                                  "SCD LEARN --keypairinfo",
+                                  NULL, NULL, NULL, NULL,
+                                  reload_more_data_cb, &parm);
+  if (!err)
+    err = gpgme_op_assuan_result (gpgagent)->err;
+  if (err)
+    g_debug ("SCD LEARN failed: %s", gpg_strerror (err));
+
+  if (parm.any_unknown)
+    {
+      GtkWidget *button, *align;
+
+      align = gtk_alignment_new (0.5, 0, 0, 0);
+      button = gtk_button_new_with_label (_("Learn keys"));
+      gpa_add_tooltip 
+        (button, _("For some or all of the keys available on the card, "
+                   "the GnuPG crypto engine does not yet know the "
+                   "corresponding certificates.\n"
+                   "\n"
+                   "If you click this button, GnuPG will be asked to "
+                   "\"learn\" "
+                   "this card and import all certificates stored on the "
+                   "card into its own certificate store.  This is not done "
+                   "automatically because it may take several seconds to "
+                   "read all certificates from the card.\n"
+                   "\n"
+                   "If you are unsure what to do, just click the button."));
+      gtk_container_add (GTK_CONTAINER (align), button);
+      gtk_box_pack_start (GTK_BOX (vbox), align, FALSE, FALSE, 5);
+      gtk_box_reorder_child (GTK_BOX (vbox), align, 0);
+
+      g_signal_connect (G_OBJECT (button), "clicked",
+                        G_CALLBACK (learn_keys_clicked_cb), card);
+    }
+
+  gpgme_release (parm.ctx);
+  gtk_widget_show_all (card->keys_frame);
+  return;
+}
+
+
+/* Idle queue callback to reload more data.  */
+static gboolean
+reload_more_data_idle_cb (void *user_data)
+{
+  GpaCMNetkey *card = user_data;
+  
+  if (card->reloading)
+    return TRUE; /* Just in case we are still reloading, wait for the
+                    next idle slot.  */
+
+  card->reloading++;
+  reload_more_data (card);
+  g_object_unref (card); 
+  card->reloading--;
+
+  return FALSE;  /* Remove us from the idle queue.  */
+}
+
+
 struct scd_getattr_parm
 {
   GpaCMNetkey *card;  /* The object.  */
@@ -297,6 +475,8 @@
   g_return_if_fail (gpgagent);
 
   card->reloading++;
+
+  /* Show all attributes.  */
   parm.card = card;
   for (attridx=0; attrtbl[attridx].name; attridx++)
     {
@@ -333,11 +513,96 @@
           break;
         }
     }
+  if (!err)
+    {
+      g_object_ref (card);
+      g_idle_add (reload_more_data_idle_cb, card);
+    }
   card->reloading--;
 }
 
 
+/* A structure used to pass data to the learn_keys_gpg_status_cb.  */
+struct learn_keys_gpg_status_parm 
+{
+  GpaCMNetkey    *card;
+  GtkWidget      *button;
+  GtkProgressBar *pbar;
+};
+
+
+/* Helper for learn_keys_clicked_cb.  */
+static gboolean 
+learn_keys_gpg_status_cb (void *opaque, char *line)
+{
+  struct learn_keys_gpg_status_parm *parm = opaque;
+  
+  if (!line)
+    {
+      /* We are finished with the command.  */
+      g_debug ("learn_keys_gpg_status_cb: cleanup");
+      /* Trigger a reload of the key data.  */
+      g_object_ref (parm->card);
+      g_idle_add (reload_more_data_idle_cb, parm->card);
+      /* Cleanup.  */
+      gtk_widget_destroy (parm->button);
+      g_object_unref (parm->button);
+      g_object_unref (parm->card);
+      xfree (parm);
+      return FALSE; /* (The return code does not matter here.)  */
+    }
+
+  g_debug ("learn_keys_gpg_status_cb: `%s'", line);
+  if (!strncmp (line, "PROGRESS", 8))
+    gtk_progress_bar_pulse (parm->pbar);
+
+  return TRUE; /* Keep on running.  */
+}
+
+
+/* Fire up the learn command.  */
 static void
+learn_keys_clicked_cb (GtkButton *button, void *user_data)
+{
+  GpaCMNetkey *card = user_data;
+  GtkWidget *widget;
+  gpg_error_t err;
+  struct learn_keys_gpg_status_parm *parm;
+
+  parm = xcalloc (1, sizeof *parm);
+
+  g_debug ("learn keys clicked");
+  gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);
+  widget = gtk_bin_get_child (GTK_BIN (button));
+  if (widget)
+    gtk_widget_destroy (widget);
+
+  widget = gtk_progress_bar_new ();
+  gtk_container_add (GTK_CONTAINER (button), widget);
+  gtk_progress_bar_set_text (GTK_PROGRESS_BAR (widget), 
+                              _("Learning keys ..."));
+  gtk_widget_show_all (GTK_WIDGET (button));
+
+  g_object_ref (card);
+  parm->card = card;
+  g_object_ref (button);
+  parm->button = GTK_WIDGET (button);
+  parm->pbar = GTK_PROGRESS_BAR (widget);
+
+  err = gpa_start_simple_gpg_command 
+    (learn_keys_gpg_status_cb, parm, GPGME_PROTOCOL_CMS,
+     "--learn-card", "-v", NULL);
+  if (err)
+    {
+      g_debug ("error starting gpg: %s", gpg_strerror (err));
+      learn_keys_gpg_status_cb (parm, NULL);
+      return;
+    }
+}
+
+
+/* Run the dialog to change the NullPIN.  */
+static void
 change_nullpin (GpaCMNetkey *card)
 {
   gpg_error_t err;
@@ -652,6 +917,22 @@
                     G_CALLBACK (set_initial_pin_clicked_cb), card);
 
 
+  /* Keys frame.  */
+  frame = gtk_frame_new (NULL);
+  gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);
+  label = gtk_label_new (_("<b>Keys</b>"));
+  gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
+  gtk_frame_set_label_widget (GTK_FRAME (frame), label);
+
+  /* There is only a label widget yet in the frame for now.  The real
+     widgets are added while figuring out the keys of the card.  */
+  label = gtk_label_new (_("scanning ..."));
+  gtk_container_add (GTK_CONTAINER (frame), label);
+  
+  gtk_box_pack_start (GTK_BOX (card), frame, FALSE, TRUE, 0);
+  card->keys_frame = frame;
+
+
   /* PIN frame.  */
   frame = gtk_frame_new (NULL);
   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);
@@ -696,7 +977,6 @@
                     G_CALLBACK (change_pin_clicked_cb), card);
   
   gtk_box_pack_start (GTK_BOX (card), frame, FALSE, TRUE, 0);
-  
 }
 
 

Modified: trunk/src/cm-openpgp.c
===================================================================
--- trunk/src/cm-openpgp.c	2009-03-22 16:17:54 UTC (rev 973)
+++ trunk/src/cm-openpgp.c	2009-03-24 10:50:53 UTC (rev 974)
@@ -104,8 +104,6 @@
 /* The parent class.  */
 static GObjectClass *parent_class;
 
-
-
 /* Local prototypes */
 static void gpa_cm_openpgp_finalize (GObject *object);
 
@@ -137,6 +135,52 @@
 }
 
 
+/* If not yet done show a warning to tell the user about the Admin
+   PIN.  Returns true if the operation shall continue.  */
+static int
+show_admin_pin_notice (GpaCMOpenpgp *card)
+{
+  static int shown;
+  GtkWidget *dialog;
+  const char *string;
+  int okay;
+
+  if (shown)
+    return 1;
+
+  string = _("<b>Admin-PIN Required</b>\n"
+             "\n"
+             "Depending on the previous operations you may now "
+             "be asked for the Admin-PIN.  Entering a wrong value "
+             "for the Admin-PIN decrements the corresponding retry counter. "
+             "If the retry counter is down to zero, the Admin-PIN can't "
+             "be restored anymore and thus the data on the card can't be "
+             "modified.\n"
+             "\n"
+             "Unless changed, a fresh standard card has set the Admin-PIN "
+             "to the value <i>12345678</i>.  "
+             "However, the issuer of your card might "
+             "have initialized the card with a different Admin-PIN and "
+             "that Admin-PIN might only be known to the issuer.  "
+             "Please check the instructions of your issuer.\n"
+             "\n"
+             "This notice will be shown only once per session.");
+
+  /* FIXME:  How do we figure out our GtkWindow?  */
+  dialog = gtk_message_dialog_new_with_markup (NULL /*GTK_WINDOW (card)*/,
+                                               GTK_DIALOG_DESTROY_WITH_PARENT,
+                                               GTK_MESSAGE_INFO, 
+                                               GTK_BUTTONS_OK_CANCEL, 
+                                               NULL);
+  gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog), string);
+  okay = (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK);
+  gtk_widget_destroy (GTK_WIDGET (dialog));
+  if (okay)
+    shown = 1;
+  return okay;
+}
+
+
 /* Clears the info contained in the card widget. */
 static void
 clear_card_data (GpaCMOpenpgp *card)
@@ -521,6 +565,9 @@
   gpgagent = GPA_CM_OBJECT (card)->agent_ctx;
   g_return_val_if_fail (gpgagent,gpg_error (GPG_ERR_BUG));
 
+  if (!show_admin_pin_notice (card))
+    return gpg_error (GPG_ERR_CANCELED);
+
   if (is_escaped)
     command = g_strdup_printf ("SCD SETATTR %s %s", name, value);
   else

Modified: trunk/src/gpa-key-details.c
===================================================================
--- trunk/src/gpa-key-details.c	2009-03-22 16:17:54 UTC (rev 973)
+++ trunk/src/gpa-key-details.c	2009-03-24 10:50:53 UTC (rev 974)
@@ -75,6 +75,10 @@
   /* The widgets in the subkeys list.  */
   GtkWidget *subkeys_page;
   GtkWidget *subkeys_list;
+
+  /* The key currently shown or NULL.  */
+  gpgme_key_t current_key;
+
 };
 
 
@@ -99,13 +103,11 @@
 signatures_uid_changed (GtkComboBox *combo, gpointer user_data)
 {
   GpaKeyDetails *kdt = user_data;
-  gpgme_key_t key;
 
-  /* FIXME  key = keyring_editor_current_key (kdt);*/ key = NULL;
   /* Note that we need to subtract one, as the first entry (with index
      0 means) "all user names".  */
   gpa_siglist_set_signatures 
-    (kdt->signatures_list, key,
+    (kdt->signatures_list, kdt->current_key,
      gtk_combo_box_get_active (GTK_COMBO_BOX (combo)) - 1);
 }
 
@@ -450,9 +452,6 @@
   kdt->subkeys_page = vbox;
   gtk_notebook_append_page (GTK_NOTEBOOK (kdt), kdt->subkeys_page,
                             gtk_label_new (_("Subkeys")));
-/* FIXME gtk_widget_show_all (kdt->notebook_details); */
-/*       keyring_update_details_notebook (editor); */
-/*       idle_update_details (editor); */
 }
 
 
@@ -531,17 +530,23 @@
 static void
 gpa_key_details_init (GTypeInstance *instance, void *class_ptr)
 {
-  GpaKeyDetails *obj = GPA_KEY_DETAILS (instance);
+  GpaKeyDetails *kdt = GPA_KEY_DETAILS (instance);
 
-  construct_main_widget (obj);
+  construct_main_widget (kdt);
 }
 
 
 static void
 gpa_key_details_finalize (GObject *object)
 {  
-/*   GpaKeyDetails *obj = GPA_KEY_DETAILS (object); */
+  GpaKeyDetails *kdt = GPA_KEY_DETAILS (object);
 
+  if (kdt->current_key)
+    {
+      gpgme_key_unref (kdt->current_key);
+      kdt->current_key = NULL;
+    }
+
   parent_class->finalize (object);
 }
 
@@ -598,8 +603,15 @@
   g_return_if_fail (GPA_IS_KEY_DETAILS (keydetails));
   kdt = GPA_KEY_DETAILS (keydetails);
 
+  if (kdt->current_key)
+    {
+      gpgme_key_unref (kdt->current_key);
+      kdt->current_key = NULL;
+    }
   if (key && keycount == 1)
     {
+      gpgme_key_ref (key);
+      kdt->current_key = key;
       details_page_fill_key (kdt, key);
       signatures_page_fill_key (kdt, key);
       subkeys_page_fill_key (kdt, key);

Modified: trunk/src/gpa.c
===================================================================
--- trunk/src/gpa.c	2009-03-22 16:17:54 UTC (rev 973)
+++ trunk/src/gpa.c	2009-03-24 10:50:53 UTC (rev 974)
@@ -54,7 +54,10 @@
 /* True if CMS hack mode is enabled.  */
 gboolean cms_hack;
 
+/* True if the ticker used for card operations should not be started.  */
+gboolean disable_ticker;
 
+
 /* Local variables.  */
 typedef struct
 {
@@ -108,6 +111,8 @@
       N_("Read options from file"), "FILE" },
     { "cms", 'x', 0, G_OPTION_ARG_NONE, &cms_hack,
       "Enable CMS/X.509 support", NULL },
+    { "disable-ticker", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE,
+      &disable_ticker, NULL, NULL },
     { NULL }
   };
 

Modified: trunk/src/gpa.h
===================================================================
--- trunk/src/gpa.h	2009-03-22 16:17:54 UTC (rev 973)
+++ trunk/src/gpa.h	2009-03-24 10:50:53 UTC (rev 974)
@@ -52,8 +52,8 @@
 extern GList *global_defaultRecipients;
 extern gchar *gnupg_homedir;
 extern gboolean cms_hack;
+extern gboolean disable_ticker;
 
-
 /* Show the keyring editor dialog.  */
 void gpa_open_keyring_editor (GtkAction *action, void *data);
 

Modified: trunk/src/gpgmetools.c
===================================================================
--- trunk/src/gpgmetools.c	2009-03-22 16:17:54 UTC (rev 973)
+++ trunk/src/gpgmetools.c	2009-03-24 10:50:53 UTC (rev 974)
@@ -1,6 +1,6 @@
 /* gpgmetools.h - Additional gpgme support functions for GPA.
    Copyright (C) 2002 Miguel Coca.
-   Copyright (C) 2005, 2008 g10 Code GmbH.
+   Copyright (C) 2005, 2008, 2009 g10 Code GmbH.
 
    This file is part of GPA
 
@@ -25,6 +25,8 @@
 
 #include <errno.h>
 #include <ctype.h>
+#include <stdarg.h>
+
 #include "gpa.h"
 #include "gtktools.h"
 #include "gpgmetools.h"
@@ -433,7 +435,7 @@
 }
 
 
-/* Retrieve the path to the GnuPG executable.  */
+/* Retrieve the path to the GPG executable.  */
 static const gchar *
 get_gpg_path (void)
 {
@@ -450,6 +452,24 @@
 }
 
 
+/* Retrieve the path to the GPGSM executable.  */
+static const gchar *
+get_gpgsm_path (void)
+{
+  gpgme_engine_info_t engine;
+  
+  gpgme_get_engine_info (&engine);
+  while (engine)
+    {
+      if (engine->protocol == GPGME_PROTOCOL_CMS)
+	return engine->file_name;
+      engine = engine->next;
+    }
+  return NULL;
+}
+
+
+
 /* Backup a key. It exports both the public and secret keys to a file.
    Returns TRUE on success and FALSE on error. It displays errors to
    the user.  */
@@ -476,9 +496,10 @@
   const gchar *path;
   mode_t mask;
 
-  /* Get the gpg path */
+  /* Get the gpg path.  */
   path = get_gpg_path ();
-  g_assert (path != NULL);
+  g_return_val_if_fail (path && *path, FALSE);
+
   /* Add the executable to the arg arrays */
   header_argv[0] = (gchar*) path;
   pub_argv[0] = (gchar*) path;
@@ -1232,3 +1253,163 @@
   return 0; /* No gpg-engine available. */
 }
 
+
+/* Structure used to communicate with gpg_simple_stderr_cb.  */
+struct gpg_simple_stderr_parm_s 
+{
+  gboolean (*cb)(void *opaque, char *line);
+  void *cb_arg;
+  GString *string;
+};
+
+/* Helper for gpa_start_simple_gpg_command.  */
+static gboolean 
+gpg_simple_stderr_cb (GIOChannel *channel, GIOCondition condition, 
+                      void *user_data)
+{
+  struct gpg_simple_stderr_parm_s *parm = user_data;
+  GIOStatus status;
+  char *line, *p;
+
+  if ((condition & G_IO_IN))
+    {
+      /* We don't use a while but an if because that allows to update
+         progress bars nicely.  A bit slower, but no real problem.  */
+      if ((status = g_io_channel_read_line_string
+           (channel, parm->string, NULL, NULL)) == G_IO_STATUS_NORMAL)
+        {
+          line = parm->string->str;
+
+          /* We care only about status lines.  */
+          if (!strncmp (line, "[GNUPG:] ", 9)) 
+            {
+              line += 9;
+
+              /* Strip line terminator.  */
+              p = strchr (line, '\n');
+              if (p)
+                {
+                  if (p > line && p[-1] == '\r')
+                    p[-1] = 0;
+                  else
+                    *p = 0;
+                }
+
+              /* Call user callback.  */ 
+              if (parm->cb && !parm->cb (parm->cb_arg, line))
+                {
+                  g_debug ("User requested EOF");
+                  goto cleanup;
+                }
+            }
+        }
+      if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN )
+        {
+          /* Error or EOF.  */
+          g_debug ("Received %s", status==G_IO_STATUS_EOF? "EOF":"ERROR");
+          goto cleanup;
+        }
+    }
+  if ((condition & G_IO_HUP))
+    {
+      g_debug ("Received HUP");
+      goto cleanup;
+    }
+
+  return TRUE;  /* Keep on watching this channel. */
+
+ cleanup:
+  if (parm->cb)
+    parm->cb (parm->cb_arg, NULL);  /* Tell user about EOF.  */
+  g_string_free (parm->string, TRUE);
+  xfree (parm);
+  g_io_channel_unref (channel);  /* Close channel.  */
+  return FALSE;  /* Remove us from the watch list.  */
+}
+
+
+/* Run gpg asynchronously with the given arguments and return a gpg
+   error code on error.  The list of arguments are all of type (const
+   char*) and end with a NULL argument (FIRST_ARG may already be NULL,
+   but that does not make any sense).  STDIN and STDOUT are connected
+   to /dev/null.  No more than 20 arguments may be given.
+
+   If the function returns success the provided callback CB is called
+   for each line received on STDERR.  EOF is send to this callback by
+   passing a LINE argument of NULL.  The callback may use this for
+   cleanup. If the callback returns FALSE, an EOF is forced with the
+   result that the callback is called once more with LINE set to NULL.  */
+gpg_error_t
+gpa_start_simple_gpg_command (gboolean (*cb)(void *opaque, char *line),
+                              void *cb_arg, gpgme_protocol_t protocol,
+                              const char *first_arg, ...)
+{
+  char *argv[24];
+  int argc;
+  int fd_stderr;
+  GIOChannel *channel;
+  struct gpg_simple_stderr_parm_s *parm = NULL;
+
+  if (protocol == GPGME_PROTOCOL_OpenPGP)
+    argv[0] = (char*)get_gpg_path ();
+  else if (protocol == GPGME_PROTOCOL_CMS)
+    argv[0] = (char*)get_gpgsm_path ();
+  else
+    argv[0] = NULL;
+  g_return_val_if_fail (argv[0], gpg_error (GPG_ERR_INV_ARG));
+  argc = 1;
+  argv[argc++] = (char*)"--status-fd";
+  argv[argc++] = (char*)"2";
+  argv[argc++] = (char*)first_arg;
+  if (first_arg)
+    {
+      va_list arg_ptr;
+      const char *s;
+
+      va_start (arg_ptr, first_arg);
+      while (argc < DIM (argv)-1 && (s=va_arg (arg_ptr, const char *)))
+        argv[argc++] = (char*)s;
+      va_end (arg_ptr);
+      argv[argc] = NULL;
+      g_return_val_if_fail (argc < DIM (argv), gpg_error (GPG_ERR_INV_ARG));
+    }
+
+  parm = g_try_malloc (sizeof *parm);
+  if (!parm)
+    return gpg_error_from_syserror ();
+  parm->cb = cb;
+  parm->cb_arg = cb_arg;
+  parm->string = g_string_sized_new (200);
+
+  if (!g_spawn_async_with_pipes (NULL, argv, NULL, 
+                                 (G_SPAWN_STDOUT_TO_DEV_NULL),
+                                 NULL, NULL, NULL,
+                                 NULL, NULL, &fd_stderr, NULL))
+    {
+      gpa_window_error (_("Calling the crypto engine program failed."), NULL);
+      xfree (parm);
+      return gpg_error (GPG_ERR_GENERAL);
+    }
+#ifdef G_OS_WIN32
+  channel = g_io_channel_win32_new_fd (fd_stderr);
+#else
+  channel = g_io_channel_unix_new (fd_stderr);
+#endif 
+  g_io_channel_set_encoding (channel, NULL, NULL);
+  /* Note that we need a buffered channel, so that we can use the read
+     line function.  */
+  g_io_channel_set_close_on_unref (channel, TRUE);
+
+  /* Create a watch for the channel.  */
+  if (!g_io_add_watch (channel, (G_IO_IN|G_IO_HUP), 
+                       gpg_simple_stderr_cb, parm))
+    {
+      g_debug ("error creating watch for gpg command");
+      g_io_channel_unref (channel);
+      xfree (parm);
+      return gpg_error (GPG_ERR_GENERAL);
+    }
+
+  return 0;
+}
+

Modified: trunk/src/gpgmetools.h
===================================================================
--- trunk/src/gpgmetools.h	2009-03-22 16:17:54 UTC (rev 973)
+++ trunk/src/gpgmetools.h	2009-03-24 10:50:53 UTC (rev 974)
@@ -194,5 +194,13 @@
 /* Return true if the gpg engine has at least version NEED_VERSION.  */
 int is_gpg_version_at_least (const char *need_version);
 
+/* Run a simple gpg command.  */
+gpg_error_t gpa_start_simple_gpg_command (gboolean (*cb)
+                                          (void *opaque, char *line),
+                                          void *cb_arg, 
+                                          gpgme_protocol_t protocol,
+                                          const char *first_arg, ...)
+     G_GNUC_NULL_TERMINATED;
 
+
 #endif /*GPGMETOOLS_H*/



More information about the Gpa-commits mailing list