gpa: Detect homedir via gpgconf.
authorWerner Koch <wk@gnupg.org>
Mon, 12 Aug 2013 16:51:54 +0000 (18:51 +0200)
committerWerner Koch <wk@gnupg.org>
Mon, 12 Aug 2013 16:51:54 +0000 (18:51 +0200)
* patches/gpa-0.9.4/03-homedir-via-gpgconf.patch: New.
* Makefile.am (EXTRA_DIST): Add patch.
--

This improves the portable use of GPA.

Makefile.am
patches/gpa-0.9.4/03-homedir-via-gpgconf.patch [new file with mode: 0755]

index 45d5caf..7ef46bb 100644 (file)
@@ -50,8 +50,8 @@ EXTRA_DIST = autogen.sh README.GIT ONEWS \
         patches/libgpg-error-1.12/01-fix-get-string.patch \
        patches/gpa-0.9.4/01-bin-encrypt.patch \
         patches/gpa-0.9.4/02-cms-detection.patch \
-        patches/gpa-0.9.4/02-x509-import.patch
-
+        patches/gpa-0.9.4/02-x509-import.patch \
+        patches/gpa-0.9.4/03-homedir-via-gpgconf.patch
 
 copy-news:
        cp NEWS doc/website/NEWS.last
diff --git a/patches/gpa-0.9.4/03-homedir-via-gpgconf.patch b/patches/gpa-0.9.4/03-homedir-via-gpgconf.patch
new file mode 100755 (executable)
index 0000000..6f83e98
--- /dev/null
@@ -0,0 +1,1032 @@
+#! /bin/sh
+patch -p1 -l -f $* < $0
+exit $?
+
+From 398fd028c762dd6c0fc7a5945f55eb2dbd2edaec Mon Sep 17 00:00:00 2001
+From: Werner Koch <wk@gnupg.org>
+Date: Mon, 12 Aug 2013 18:44:06 +0200
+Subject: [PATCH] Detect default homedir via gpgconf.
+
+* src/server.c (decode_percent_string): Move to ..
+* src/utils.c (decode_percent_string): here.
+* src/gpgmetools.c (gpa_start_simple_gpg_command): Add arg use_stderr
+and change all callers.
+(gpg_simple_stdio_cb): Implement the !use_stderr case.
+* src/get-path.c: Include string.h and gpa.h.
+(struct homedir_from_gpgconf_s): New.
+(homedir_from_gpgconf_parser): New.
+(homedir_from_gpgconf): New.
+(default_homedir): First try to detect via gpgconf.
+--
+
+It would be useful to equip GPGME with a function to parse the output
+of "gpgconf --list-dir".  Until then we need to go into some length to
+read it.
+---
+ src/cm-netkey.c  |  132 +++++++++++++++++++++++++++---------------------------
+ src/get-path.c   |   85 ++++++++++++++++++++++++++++++++---
+ src/gpa.h        |    1 +
+ src/gpgmetools.c |   75 ++++++++++++++++++++-----------
+ src/gpgmetools.h |    1 +
+ src/server.c     |   47 -------------------
+ src/utils.c      |   65 +++++++++++++++++++++++----
+ 7 files changed, 252 insertions(+), 154 deletions(-)
+
+diff --git a/src/cm-netkey.c b/src/cm-netkey.c
+index 8b4aba2..8ddbcb7 100644
+--- a/src/cm-netkey.c
++++ b/src/cm-netkey.c
+@@ -14,7 +14,7 @@
+  * License for more details.
+  *
+  * You should have received a copy of the GNU General Public License
+- * along with this program; if not, see <http://www.gnu.org/licenses/>.
++ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+  */
+
+ #ifdef HAVE_CONFIG_H
+@@ -26,7 +26,7 @@
+ #include <string.h>
+ #include <assert.h>
+
+-#include "gpa.h"
++#include "gpa.h"
+ #include "gtktools.h"
+ #include "convert.h"
+ #include "gpa-key-details.h"
+@@ -49,7 +49,7 @@ enum
+     ENTRY_SIGG_PUK_RETRYCOUNTER,
+
+     ENTRY_LAST
+-  };
++  };
+
+
+ /* A structure for PIN information.  */
+@@ -65,7 +65,7 @@ struct pininfo_s
+
+
+ /* Object's class definition.  */
+-struct _GpaCMNetkeyClass
++struct _GpaCMNetkeyClass
+ {
+   GpaCMObjectClass parent_class;
+ };
+@@ -106,7 +106,7 @@ static void gpa_cm_netkey_finalize (GObject *object);
+
+
\f
+-/************************************************************
++/************************************************************
+  *******************   Implementation   *********************
+  ************************************************************/
+
+@@ -127,13 +127,13 @@ clear_card_data (GpaCMNetkey *card)
+ /* Put the PIN information into the field with ENTRY_ID.  If BUTTON is
+    not NULL its sensitivity is set as well. */
+ static void
+-update_entry_retry_counter (GpaCMNetkey *card, int entry_id,
++update_entry_retry_counter (GpaCMNetkey *card, int entry_id,
+                             struct pininfo_s *info, int any_isnull,
+                             int is_puk, GtkWidget *button)
+ {
+   char numbuf[50];
+   const char *string;
+-
++
+   if (!info->valid)
+     string = _("unknown");
+   else if (info->nullpin)
+@@ -150,12 +150,12 @@ update_entry_retry_counter (GpaCMNetkey *card, int entry_id,
+   gtk_label_set_text (GTK_LABEL (card->entries[entry_id]), string);
+   if (button)
+     {
+-      gtk_button_set_label (GTK_BUTTON (button),
++      gtk_button_set_label (GTK_BUTTON (button),
+                             (info->valid && !info->nullpin
+                              && (info->blocked || !info->tries_left))
+                             ? (is_puk? _("Reset PUK") : _("Reset PIN"))
+                             : (is_puk? _("Change PUK"): _("Change PIN")));
+-      gtk_widget_set_sensitive (button,
++      gtk_widget_set_sensitive (button,
+                                 (info->valid && !any_isnull
+                                  && !info->nullpin && !info->blocked
+                                  && !info->nopin));
+@@ -168,12 +168,12 @@ update_entry_retry_counter (GpaCMNetkey *card, int entry_id,
+ static void
+ update_entry_chv_status (GpaCMNetkey *card, int entry_id, char *string)
+ {
+-  struct {
++  struct {
+     int info_idx;
+     int is_puk;
+     int entry_id;
+     GtkWidget *widget;
+-  } tbl[] =
++  } tbl[] =
+     {
+       {  0, 0, ENTRY_PIN_RETRYCOUNTER },
+       {  1, 1, ENTRY_PUK_RETRYCOUNTER  },
+@@ -190,7 +190,7 @@ update_entry_chv_status (GpaCMNetkey *card, int entry_id, char *string)
+   tbl[3].widget = card->change_sigg_puk_btn;
+
+   (void)entry_id; /* Not used.  */
+-
++
+   for (idx=0; idx < DIM (card->pininfo); idx++)
+     memset (&card->pininfo[idx], 0, sizeof card->pininfo[0]);
+
+@@ -203,7 +203,7 @@ update_entry_chv_status (GpaCMNetkey *card, int entry_id, char *string)
+         string++;
+       while (spacep (string))
+         string++;
+-
++
+       card->pininfo[idx].valid = 1;
+       if (value >= 0)
+         card->pininfo[idx].tries_left = value;
+@@ -224,7 +224,7 @@ update_entry_chv_status (GpaCMNetkey *card, int entry_id, char *string)
+     if (tbl[idx].info_idx < DIM (card->pininfo))
+       update_entry_retry_counter (card, tbl[idx].entry_id,
+                                   &card->pininfo[tbl[idx].info_idx],
+-                                  any_isnull,
++                                  any_isnull,
+                                   tbl[idx].is_puk, tbl[idx].widget);
+
+   gtk_widget_set_no_show_all (card->warning_frame, FALSE);
+@@ -371,7 +371,7 @@ reload_more_data (GpaCMNetkey *card)
+
+       align = gtk_alignment_new (0.5, 0, 0, 0);
+       button = gtk_button_new_with_label (_("Learn keys"));
+-      gpa_add_tooltip
++      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"
+@@ -404,7 +404,7 @@ static gboolean
+ reload_more_data_idle_cb (void *user_data)
+ {
+   GpaCMNetkey *card = user_data;
+-
++
+   if (card->reloading)
+     {
+       g_debug ("already reloading (count=%d)", card->reloading);
+@@ -414,7 +414,7 @@ reload_more_data_idle_cb (void *user_data)
+
+   card->reloading++;
+   reload_more_data (card);
+-  g_object_unref (card);
++  g_object_unref (card);
+   card->reloading--;
+
+   return FALSE;  /* Remove us from the idle queue.  */
+@@ -450,17 +450,17 @@ scd_getattr_cb (void *opaque, const char *status, const char *args)
+           if (parm->updfnc)
+             parm->updfnc (parm->card, entry_id, tmp);
+           else if (GTK_IS_LABEL (parm->card->entries[entry_id]))
+-            gtk_label_set_text
++            gtk_label_set_text
+               (GTK_LABEL (parm->card->entries[entry_id]), tmp);
+           else
+-            gtk_entry_set_text
++            gtk_entry_set_text
+               (GTK_ENTRY (parm->card->entries[entry_id]), tmp);
+           xfree (tmp);
+         }
+     }
+
+   return 0;
+-}
++}
+
+
+ /* Use the assuan machinery to load the bulk of the OpenPGP card data.  */
+@@ -509,7 +509,7 @@ reload_data (GpaCMNetkey *card)
+         {
+           /* The NKS-VERSION is only supported by GnuPG > 2.0.11
+              thus we ignore the error.  */
+-          gtk_label_set_text
++          gtk_label_set_text
+             (GTK_LABEL (card->entries[attrtbl[attridx].entry_id]),
+              _("unknown"));
+         }
+@@ -519,7 +519,7 @@ reload_data (GpaCMNetkey *card)
+             ; /* Lost the card.  */
+           else
+             {
+-              g_debug ("assuan command `%s' failed: %s <%s>\n",
++              g_debug ("assuan command `%s' failed: %s <%s>\n",
+                        command, gpg_strerror (err), gpg_strsource (err));
+             }
+           clear_card_data (card);
+@@ -537,7 +537,7 @@ reload_data (GpaCMNetkey *card)
+
+
+ /* A structure used to pass data to the learn_keys_gpg_status_cb.  */
+-struct learn_keys_gpg_status_parm
++struct learn_keys_gpg_status_parm
+ {
+   GpaCMNetkey    *card;
+   GtkWidget      *button;
+@@ -546,11 +546,11 @@ struct learn_keys_gpg_status_parm
+
+
+ /* Helper for learn_keys_clicked_cb.  */
+-static gboolean
++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.  */
+@@ -590,7 +590,7 @@ learn_keys_clicked_cb (GtkButton *button, void *user_data)
+
+   widget = gtk_progress_bar_new ();
+   gtk_container_add (GTK_CONTAINER (button), widget);
+-  gtk_progress_bar_set_text (GTK_PROGRESS_BAR (widget),
++  gtk_progress_bar_set_text (GTK_PROGRESS_BAR (widget),
+                               _("Learning keys ..."));
+   gtk_widget_show_all (GTK_WIDGET (button));
+
+@@ -600,8 +600,8 @@ learn_keys_clicked_cb (GtkButton *button, void *user_data)
+   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,
++  err = gpa_start_simple_gpg_command
++    (learn_keys_gpg_status_cb, parm, GPGME_PROTOCOL_CMS, 1,
+      "--learn-card", "-v", NULL);
+   if (err)
+     {
+@@ -633,7 +633,7 @@ change_nullpin (GpaCMNetkey *card)
+   else
+     return; /* Oops: No NullPIN.  */
+
+-  string = g_strdup_printf
++  string = g_strdup_printf
+     (_("<b>Setting the Initial PIN</b> (%s)\n\n"
+        "You selected to set the initial PIN of your card.  "
+        "The PIN is currently set to the NullPIN.  Setting an "
+@@ -653,12 +653,12 @@ change_nullpin (GpaCMNetkey *card)
+          "PIN to the same value as used for the NKS keys.")
+      : _("You are now setting the PIN for the NKS keys used for standard "
+          "signatures, encryption and authentication."));
+-
++
+   /* 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,
++                                               GTK_MESSAGE_INFO,
++                                               GTK_BUTTONS_OK_CANCEL,
+                                                NULL);
+   gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (dialog), string);
+   g_free (string);
+@@ -679,7 +679,7 @@ change_nullpin (GpaCMNetkey *card)
+         okay = 0; /* No need to reload the data.  */
+       else if (err)
+         {
+-          char *message = g_strdup_printf
++          char *message = g_strdup_printf
+             (_("Error changing the NullPIN.\n"
+                "(%s <%s>)"), gpg_strerror (err), gpg_strsource (err));
+           gpa_window_error (message, NULL);
+@@ -709,7 +709,7 @@ change_or_reset_pin (GpaCMNetkey *card, int info_idx)
+   g_return_if_fail (gpgagent);
+   g_return_if_fail (info_idx < DIM (card->pininfo));
+
+-  if (!card->pininfo[info_idx].valid
++  if (!card->pininfo[info_idx].valid
+       || card->pininfo[info_idx].nopin
+       || card->pininfo[info_idx].nullpin)
+     {
+@@ -741,7 +741,7 @@ change_or_reset_pin (GpaCMNetkey *card, int info_idx)
+                 "of the PIN and the corresponding PUK are both down "
+                 "to zero, the keys controlled by the PIN are not anymore "
+                 "usable and there is no way to unblock them!")
+-            : is_puk
++            : is_puk
+             ? _("<b>Resetting a PUK</b>\n"
+                 "\n"
+                 "Although <i>PUK</i> stands for <i>PIN Unblocking Code</i> "
+@@ -766,12 +766,12 @@ change_or_reset_pin (GpaCMNetkey *card, int info_idx)
+                 "value of the PUK and then to enter a new value for the "
+                 "blocked PIN and repeat that new value at another prompt.")
+             );
+-
++
+   /* 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,
++                                               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);
+@@ -783,13 +783,13 @@ change_or_reset_pin (GpaCMNetkey *card, int info_idx)
+                 reset_mode? " --reset":"", pwidstr);
+       err = gpgme_op_assuan_transact (gpgagent, command,
+                                       NULL, NULL, NULL, NULL, NULL, NULL);
+-      if (!err)
++      if (!err)
+         err = gpgme_op_assuan_result (gpgagent)->err;
+       if (gpg_err_code (err) == GPG_ERR_CANCELED)
+         okay = 0; /* No need to reload the data.  */
+-      else if (err)
++      else if (err)
+         {
+-          char *message = g_strdup_printf
++          char *message = g_strdup_printf
+             (_("Error changing or resetting the PIN/PUK.\n"
+                "(%s <%s>)"), gpg_strerror (err), gpg_strsource (err));
+           gpa_window_error (message, NULL);
+@@ -840,12 +840,12 @@ add_table_row (GtkWidget *table, int *rowidx,
+   label = gtk_label_new (labelstr);
+   gtk_label_set_width_chars  (GTK_LABEL (label), 22);
+   gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
+-  gtk_table_attach (GTK_TABLE (table), label, 0, 1,
+-                    *rowidx, *rowidx + 1, GTK_FILL, GTK_SHRINK, 0, 0);
++  gtk_table_attach (GTK_TABLE (table), label, 0, 1,
++                    *rowidx, *rowidx + 1, GTK_FILL, GTK_SHRINK, 0, 0);
+
+   if (is_label)
+     gtk_misc_set_alignment (GTK_MISC (widget), 0, 0.5);
+-
++
+   if (readonly)
+     {
+       if (!is_label && GTK_IS_ENTRY (widget))
+@@ -859,7 +859,7 @@ add_table_row (GtkWidget *table, int *rowidx,
+       if (is_label)
+         gtk_label_set_selectable (GTK_LABEL (widget), TRUE);
+     }
+-
++
+   gtk_table_attach (GTK_TABLE (table), widget, 1, 2,
+                     *rowidx, *rowidx + 1, GTK_FILL, GTK_SHRINK, 0, 0);
+   if (widget2)
+@@ -893,17 +893,17 @@ construct_data_widget (GpaCMNetkey *card)
+   gtk_container_set_border_width (GTK_CONTAINER (table), 10);
+   gtk_container_add (GTK_CONTAINER (frame), table);
+   rowidx = 0;
+-
++
+   card->entries[ENTRY_SERIALNO] = gtk_label_new (NULL);
+   add_table_row (table, &rowidx, _("Serial number:"),
+                  card->entries[ENTRY_SERIALNO], NULL, 0);
+
+   card->entries[ENTRY_NKS_VERSION] = gtk_label_new (NULL);
+-  add_table_row (table, &rowidx, _("Card version:"),
++  add_table_row (table, &rowidx, _("Card version:"),
+                  card->entries[ENTRY_NKS_VERSION], NULL, 0);
+
+   gtk_box_pack_start (GTK_BOX (card), frame, FALSE, TRUE, 0);
+-
++
+
+   /* Warning frame.  */
+   frame = gtk_frame_new (NULL);
+@@ -939,7 +939,7 @@ construct_data_widget (GpaCMNetkey *card)
+      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;
+
+@@ -957,42 +957,42 @@ construct_data_widget (GpaCMNetkey *card)
+
+   card->entries[ENTRY_PIN_RETRYCOUNTER] = gtk_label_new (NULL);
+   button = gtk_button_new ();
+-  add_table_row (table, &rowidx, _("PIN retry counter:"),
++  add_table_row (table, &rowidx, _("PIN retry counter:"),
+                  card->entries[ENTRY_PIN_RETRYCOUNTER], button, 1);
+-  card->change_pin_btn = button;
++  card->change_pin_btn = button;
+   g_signal_connect (G_OBJECT (button), "clicked",
+                     G_CALLBACK (change_pin_clicked_cb), card);
+
+   card->entries[ENTRY_PUK_RETRYCOUNTER] = gtk_label_new (NULL);
+   button = gtk_button_new ();
+-  add_table_row (table, &rowidx, _("PUK retry counter:"),
++  add_table_row (table, &rowidx, _("PUK retry counter:"),
+                  card->entries[ENTRY_PUK_RETRYCOUNTER], button, 1);
+-  card->change_puk_btn = button;
++  card->change_puk_btn = button;
+   g_signal_connect (G_OBJECT (button), "clicked",
+                     G_CALLBACK (change_pin_clicked_cb), card);
+
+   card->entries[ENTRY_SIGG_PIN_RETRYCOUNTER] = gtk_label_new (NULL);
+   button = gtk_button_new ();
+-  add_table_row (table, &rowidx, _("SigG PIN retry counter:"),
++  add_table_row (table, &rowidx, _("SigG PIN retry counter:"),
+                  card->entries[ENTRY_SIGG_PIN_RETRYCOUNTER], button, 1);
+-  card->change_sigg_pin_btn = button;
++  card->change_sigg_pin_btn = button;
+   g_signal_connect (G_OBJECT (button), "clicked",
+                     G_CALLBACK (change_pin_clicked_cb), card);
+
+   card->entries[ENTRY_SIGG_PUK_RETRYCOUNTER] = gtk_label_new (NULL);
+   button = gtk_button_new ();
+-  add_table_row (table, &rowidx, _("SigG PUK retry counter:"),
++  add_table_row (table, &rowidx, _("SigG PUK retry counter:"),
+                  card->entries[ENTRY_SIGG_PUK_RETRYCOUNTER], button, 1);
+-  card->change_sigg_puk_btn = button;
++  card->change_sigg_puk_btn = button;
+   g_signal_connect (G_OBJECT (button), "clicked",
+                     G_CALLBACK (change_pin_clicked_cb), card);
+-
++
+   gtk_box_pack_start (GTK_BOX (card), frame, FALSE, TRUE, 0);
+ }
+
+
\f
+-/************************************************************
++/************************************************************
+  ******************   Object Management  ********************
+  ************************************************************/
+
+@@ -1002,7 +1002,7 @@ gpa_cm_netkey_class_init (void *class_ptr, void *class_data)
+   GpaCMNetkeyClass *klass = class_ptr;
+
+   parent_class = g_type_class_peek_parent (klass);
+-
++
+   G_OBJECT_CLASS (klass)->finalize = gpa_cm_netkey_finalize;
+ }
+
+@@ -1019,7 +1019,7 @@ gpa_cm_netkey_init (GTypeInstance *instance, void *class_ptr)
+
+ static void
+ gpa_cm_netkey_finalize (GObject *object)
+-{
++{
+ /*   GpaCMNetkey *card = GPA_CM_NETKEY (object); */
+
+   parent_class->finalize (object);
+@@ -1031,7 +1031,7 @@ GType
+ gpa_cm_netkey_get_type (void)
+ {
+   static GType this_type = 0;
+-
++
+   if (!this_type)
+     {
+       static const GTypeInfo this_info =
+@@ -1046,23 +1046,23 @@ gpa_cm_netkey_get_type (void)
+         0,    /* n_preallocs */
+         gpa_cm_netkey_init
+       };
+-
++
+       this_type = g_type_register_static (GPA_CM_OBJECT_TYPE,
+                                           "GpaCMNetkey",
+                                           &this_info, 0);
+     }
+-
++
+   return this_type;
+ }
+
\f
+-/************************************************************
++/************************************************************
+  **********************  Public API  ************************
+  ************************************************************/
+ GtkWidget *
+ gpa_cm_netkey_new ()
+ {
+-  return GTK_WIDGET (g_object_new (GPA_CM_NETKEY_TYPE, NULL));
++  return GTK_WIDGET (g_object_new (GPA_CM_NETKEY_TYPE, NULL));
+ }
+
+
+diff --git a/src/get-path.c b/src/get-path.c
+index f50e21b..71fe0e1 100644
+--- a/src/get-path.c
++++ b/src/get-path.c
+@@ -1,6 +1,6 @@
+ /* get-path.c - Find a system path.
+    Copyright (C) 2000-2002 G-N-U GmbH.
+-   Copyright (C) 2005, 2008 g10 Code GmbH.
++   Copyright (C) 2005, 2008, 2013 g10 Code GmbH.
+
+    This file is part of GPA.
+
+@@ -23,7 +23,10 @@
+ #endif
+
+ #include <glib.h>
++#include <string.h>
++#include <gpgme.h>
+
++#include "gpa.h"
+ #include "get-path.h"
+
\f
+@@ -31,7 +34,7 @@
+
+ #include <unistd.h>
+
+-#include <windows.h>
++#include <windows.h>
+
+ #include "w32reg.h"
+
+@@ -55,7 +58,7 @@ w32_strerror (int w32_errno)
+ {
+   static char strerr[256];
+   int ec = (int) GetLastError ();
+-
++
+   if (w32_errno == 0)
+     w32_errno = ec;
+   FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL, w32_errno,
+@@ -103,7 +106,7 @@ dlclose (void *hd)
+       return 0;
+     }
+   return -1;
+-}
++}
+
+
+ static HRESULT
+@@ -143,13 +146,83 @@ w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
+
+ #endif        /* G_OS_WIN32 */
+
++
++
++
\f
++struct homedir_from_gpgconf_s
++{
++  GMainLoop *loop;
++  char *homedir;
++};
++
++static gboolean
++homedir_from_gpgconf_parser (void *opaque, char *line)
++{
++  struct homedir_from_gpgconf_s *parm = opaque;
++  char *value, *p;
++
++  if (!line)
++    {
++      /* We are finished with the command.  Stop the loop.  */
++      g_main_loop_quit (parm->loop);
++      return FALSE; /* (The return code does not matter here.)  */
++    }
++
++  value = strchr (line, ':');
++  if (!value)
++    return TRUE; /* Invalid line - keep on running.  */;
++  *value++ = 0;
++  if (strcmp (line, "homedir"))
++    return TRUE; /* Not the right item - keep on running.  */
++
++  p = strchr (value, ':');
++  if (p)
++    *p = 0;
++  decode_percent_string (value);
++  parm->homedir = g_strdup (value);
++  return FALSE; /* Ready - force an EOF.  */
++}
++
++
++/* Retrieve the default home directory via gpgconf and return it as a
++   malloced string.  If this is not possible, return NULL.  */
++static char *
++homedir_from_gpgconf (void)
++{
++  struct homedir_from_gpgconf_s parm;
++
++  memset (&parm, 0, sizeof parm);
++
++  parm.loop = g_main_loop_new (NULL, TRUE);
++
++  if (gpa_start_simple_gpg_command
++      (homedir_from_gpgconf_parser, &parm,
++       GPGME_PROTOCOL_GPGCONF, 0, "--list-dirs", NULL))
++    {
++      g_main_loop_unref (parm.loop);
++      return NULL;
++    }
++
++  g_main_loop_run (parm.loop);
++  g_main_loop_unref (parm.loop);
++  return parm.homedir;
++}
++
++
+ /* Get the path to the default home directory.  */
+ gchar *
+ default_homedir (void)
+ {
+   gchar *dir;
+
++  dir = homedir_from_gpgconf ();
++  if (dir)
++    {
++      g_debug ("Found homedir '%s' via gpgconf", dir);
++      return dir;
++    }
++
+   /* g_getenv returns string in filename encoding.  */
+   dir = (gchar *) g_getenv ("GNUPGHOME");
+   if (dir && dir[0])
+@@ -166,7 +239,7 @@ default_homedir (void)
+         dir = NULL;
+       }
+     }
+-
++
+   if (! dir)
+     {
+       char path[MAX_PATH];
+@@ -178,7 +251,7 @@ default_homedir (void)
+          using a system roaming serives might be better than to let
+          them do it manually.  A security conscious user will anyway
+          use the registry entry to have better control.  */
+-      if (w32_shgetfolderpath (NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE,
++      if (w32_shgetfolderpath (NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE,
+                                NULL, 0, path) >= 0)
+         {
+         dir = g_build_filename (path, "gnupg", NULL);
+diff --git a/src/gpa.h b/src/gpa.h
+index a20d704..a321698 100644
+--- a/src/gpa.h
++++ b/src/gpa.h
+@@ -131,6 +131,7 @@ char *decode_c_string (const char *src);
+ char *percent_escape (const char *string,
+                       const char *delimiters, int space2plus);
+ size_t percent_unescape (char *string, int plus2space);
++void decode_percent_string (char *str);
+
+
+ /*-- Convenience macros. -- */
+diff --git a/src/gpgmetools.c b/src/gpgmetools.c
+index 5d4beae..a6ff3d8 100644
+--- a/src/gpgmetools.c
++++ b/src/gpgmetools.c
+@@ -1435,20 +1435,21 @@ is_gpg_version_at_least (const char *need_version)
+ }
+
+
+-/* Structure used to communicate with gpg_simple_stderr_cb.  */
+-struct gpg_simple_stderr_parm_s
++/* Structure used to communicate with gpg_simple_stdio_cb.  */
++struct gpg_simple_stdio_parm_s
+ {
+   gboolean (*cb)(void *opaque, char *line);
+   void *cb_arg;
+   GString *string;
++  int only_status_lines;
+ };
+
+ /* Helper for gpa_start_simple_gpg_command.  */
+ static gboolean
+-gpg_simple_stderr_cb (GIOChannel *channel, GIOCondition condition,
+-                      void *user_data)
++gpg_simple_stdio_cb (GIOChannel *channel, GIOCondition condition,
++                     void *user_data)
+ {
+-  struct gpg_simple_stderr_parm_s *parm = user_data;
++  struct gpg_simple_stdio_parm_s *parm = user_data;
+   GIOStatus status;
+   char *line, *p;
+
+@@ -1461,27 +1462,41 @@ gpg_simple_stderr_cb (GIOChannel *channel, GIOCondition condition,
+         {
+           line = parm->string->str;
+
++          /* Strip line terminator.  */
++          p = strchr (line, '\n');
++          if (p)
++            {
++              if (p > line && p[-1] == '\r')
++                p[-1] = 0;
++              else
++                *p = 0;
++            }
++
+           /* We care only about status lines.  */
+-          if (!strncmp (line, "[GNUPG:] ", 9))
++          if (parm->only_status_lines && !strncmp (line, "[GNUPG:] ", 9))
+             {
+               line += 9;
+
+-              /* Strip line terminator.  */
+-              p = strchr (line, '\n');
+-              if (p)
++              /* Call user callback.  */
++              if (parm->cb && !parm->cb (parm->cb_arg, line))
+                 {
+-                  if (p > line && p[-1] == '\r')
+-                    p[-1] = 0;
+-                  else
+-                    *p = 0;
++                  /* User requested EOF.  */
++                  goto cleanup;
+                 }
+-
++              /* Return direct so that we do not run into the G_IO_HUP
++                 check.  This is required to read all buffered input.  */
++              return TRUE;  /* Keep on watching this channel. */
++            }
++          else if (!parm->only_status_lines)
++            {
+               /* Call user callback.  */
+               if (parm->cb && !parm->cb (parm->cb_arg, line))
+                 {
+                   /* User requested EOF.  */
+                   goto cleanup;
+                 }
++
++              return TRUE;  /* Keep on watching this channel. */
+             }
+         }
+       if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN )
+@@ -1517,20 +1532,22 @@ gpg_simple_stderr_cb (GIOChannel *channel, GIOCondition condition,
+    is used to call gpg-connect-agent.
+
+    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.  */
++   for each line received on stdout (respective stderr if USE_STADERR
++   is true).  EOF is send to this callback by passing a LINE as NULL.
++   The callback may use this for cleanup.  If the callback returns
++   FALSE, an EOF is forced so 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,
++                              int use_stderr,
+                               const char *first_arg, ...)
+ {
+   char *argv[24];
+   int argc;
+-  int fd_stderr;
++  int fd_stdio;
+   GIOChannel *channel;
+-  struct gpg_simple_stderr_parm_s *parm = NULL;
++  struct gpg_simple_stdio_parm_s *parm = NULL;
+   char *freeme = NULL;
+
+   if (protocol == GPGME_PROTOCOL_OpenPGP)
+@@ -1571,11 +1588,17 @@ gpa_start_simple_gpg_command (gboolean (*cb)(void *opaque, char *line),
+   parm->cb = cb;
+   parm->cb_arg = cb_arg;
+   parm->string = g_string_sized_new (200);
++  parm->only_status_lines = use_stderr;
+
+   if (!g_spawn_async_with_pipes (NULL, argv, NULL,
+-                                 (G_SPAWN_STDOUT_TO_DEV_NULL),
++                                 (use_stderr
++                                  ? G_SPAWN_STDOUT_TO_DEV_NULL
++                                  : G_SPAWN_STDERR_TO_DEV_NULL),
+                                  NULL, NULL, NULL,
+-                                 NULL, NULL, &fd_stderr, NULL))
++                                 NULL,
++                                 use_stderr? NULL : &fd_stdio,
++                                 use_stderr? &fd_stdio : NULL,
++                                 NULL))
+     {
+       gpa_window_error (_("Calling the crypto engine program failed."), NULL);
+       xfree (parm);
+@@ -1584,9 +1607,9 @@ gpa_start_simple_gpg_command (gboolean (*cb)(void *opaque, char *line),
+     }
+   g_free (freeme);
+ #ifdef G_OS_WIN32
+-  channel = g_io_channel_win32_new_fd (fd_stderr);
++  channel = g_io_channel_win32_new_fd (fd_stdio);
+ #else
+-  channel = g_io_channel_unix_new (fd_stderr);
++  channel = g_io_channel_unix_new (fd_stdio);
+ #endif
+   g_io_channel_set_encoding (channel, NULL, NULL);
+   /* Note that we need a buffered channel, so that we can use the read
+@@ -1595,7 +1618,7 @@ gpa_start_simple_gpg_command (gboolean (*cb)(void *opaque, char *line),
+
+   /* Create a watch for the channel.  */
+   if (!g_io_add_watch (channel, (G_IO_IN|G_IO_HUP),
+-                       gpg_simple_stderr_cb, parm))
++                       gpg_simple_stdio_cb, parm))
+     {
+       g_debug ("error creating watch for gpg command");
+       g_io_channel_unref (channel);
+@@ -1613,7 +1636,7 @@ gpa_start_simple_gpg_command (gboolean (*cb)(void *opaque, char *line),
+ void
+ gpa_start_agent (void)
+ {
+-  gpa_start_simple_gpg_command (NULL, NULL, GPGME_PROTOCOL_ASSUAN,
++  gpa_start_simple_gpg_command (NULL, NULL, GPGME_PROTOCOL_ASSUAN, 1,
+                                 "NOP", "/bye", NULL);
+ }
+
+diff --git a/src/gpgmetools.h b/src/gpgmetools.h
+index 2953362..e8b2c34 100644
+--- a/src/gpgmetools.h
++++ b/src/gpgmetools.h
+@@ -209,6 +209,7 @@ gpg_error_t gpa_start_simple_gpg_command (gboolean (*cb)
+                                           (void *opaque, char *line),
+                                           void *cb_arg,
+                                           gpgme_protocol_t protocol,
++                                          int use_stderr,
+                                           const char *first_arg, ...
+                                           ) G_GNUC_NULL_TERMINATED;
+
+diff --git a/src/server.c b/src/server.c
+index d220ccb..1ce9a89 100644
+--- a/src/server.c
++++ b/src/server.c
+@@ -1255,53 +1255,6 @@ hextobyte (const char *str)
+ }
+
+
+-/* Decode the percent escaped string STR in place.  */
+-static void
+-decode_percent_string (char *str)
+-{
+-  char *src = str;
+-  char *dest = str;
+-
+-  /* Convert the string.  */
+-  while (*src)
+-    {
+-      if (*src != '%')
+-        {
+-          *(dest++) = *(src++);
+-          continue;
+-        }
+-      else
+-        {
+-          int val = hextobyte (&src[1]);
+-
+-          if (val == -1)
+-            {
+-              /* Should not happen.  */
+-              *(dest++) = *(src++);
+-              if (*src)
+-                *(dest++) = *(src++);
+-              if (*src)
+-                *(dest++) = *(src++);
+-            }
+-          else
+-            {
+-              if (!val)
+-                {
+-                  /* A binary zero is not representable in a C
+-                     string.  */
+-                  *(dest++) = '\\';
+-                  *(dest++) = '0';
+-                }
+-              else
+-                *((unsigned char *) dest++) = val;
+-              src += 3;
+-            }
+-        }
+-    }
+-  *(dest++) = 0;
+-}
+-
+-
+ /* FILE <file> [--continued]
+
+    Set the files on which to operate.
+diff --git a/src/utils.c b/src/utils.c
+index 1b8dbaa..0d3379f 100644
+--- a/src/utils.c
++++ b/src/utils.c
+@@ -65,7 +65,7 @@ xcalloc (size_t n, size_t m)
+     memset (p, 0, nbytes);
+   else
+     {
+-      g_error ("%s: failed to allocate %lu bytes",
++      g_error ("%s: failed to allocate %lu bytes",
+                G_STRLOC, (unsigned long)nbytes);
+       abort (); /* Just in case g_error returns.  */
+     }
+@@ -97,7 +97,7 @@ translate_sys2libc_fd (assuan_fd_t fd, int for_write)
+
+   if (fd == ASSUAN_INVALID_FD)
+     return -1;
+-
++
+   /* Note that _open_osfhandle is currently defined to take and return
+      a long.  */
+   x = _open_osfhandle ((long)fd, for_write ? 1 : 0);
+@@ -111,15 +111,15 @@ translate_sys2libc_fd (assuan_fd_t fd, int for_write)
+
+
+ #ifdef HAVE_W32_SYSTEM
+-int
++int
+ inet_aton (const char *cp, struct in_addr *inp)
+ {
+   if (!cp || !*cp || !inp)
+     {
+       errno = EINVAL;
+-      return 0;
++      return 0;
+     }
+-
++
+   if (!strcmp(cp, "255.255.255.255"))
+     {
+       /*  Although this is a valid address, the old inet_addr function
+@@ -127,7 +127,7 @@ inet_aton (const char *cp, struct in_addr *inp)
+         inp->s_addr = INADDR_NONE;
+         return 1;
+     }
+-
++
+   inp->s_addr = inet_addr (cp);
+   return (inp->s_addr != INADDR_NONE);
+ }
+@@ -226,9 +226,9 @@ decode_c_string (const char *src)
+                      this will also never be larger than the source
+                      string.  */
+                   *(dest++) = '\\';
+-                  *(dest++) = '0';
++                  *(dest++) = '0';
+                 }
+-              else
++              else
+                 *((unsigned char *) dest++) = val;
+               src += 4;
+             }
+@@ -322,7 +322,7 @@ percent_unescape (char *string, int plus2space)
+   while (*string)
+     {
+       if (*string == '%' && string[1] && string[2])
+-        {
++        {
+           string++;
+           *p++ = xtoi_2 (string);
+           n++;
+@@ -345,3 +345,50 @@ percent_unescape (char *string, int plus2space)
+ }
+
+
++/* Decode the percent escaped string STR in place.  In contrast to
++   percent_unescape, this make sure that the result is a string and in
++   addition escapes embedded nuls. */
++void
++decode_percent_string (char *str)
++{
++  char *src = str;
++  char *dest = str;
++
++  /* Convert the string.  */
++  while (*src)
++    {
++      if (*src != '%')
++        {
++          *(dest++) = *(src++);
++          continue;
++        }
++      else
++        {
++          int val = hextobyte (&src[1]);
++
++          if (val == -1)
++            {
++              /* Should not happen.  */
++              *(dest++) = *(src++);
++              if (*src)
++                *(dest++) = *(src++);
++              if (*src)
++                *(dest++) = *(src++);
++            }
++          else
++            {
++              if (!val)
++                {
++                  /* A binary zero is not representable in a C
++                     string.  */
++                  *(dest++) = '\\';
++                  *(dest++) = '0';
++                }
++              else
++                *((unsigned char *) dest++) = val;
++              src += 3;
++            }
++        }
++    }
++  *(dest++) = 0;
++}
+--
+1.7.7.1