Updated patch. It parses "gpgconf --list-dir" output to find gpg-agent socket.

Now it also unescapes the path according to gpgconf man page which
says the path is percent-escaped.

I'm not sure about using svn_cstring_split on svn_stringbuf_t data
member though, but I could not find a similar functionality for string
buffer.

[[[
Find gpg-agent socket using gpgconf if possible.
This allows detection of socket with Gnupg >= 2.1.13
which changed the default socket path to /run/user/UID/gnupg

* subversion/libsvn_subr/gpg_agent.c
    (find_gpgconf_agent_socket): new function to find
    gpg-agent socket using gpgconf
    (find_running_gpg_agent): use find_gpgconf_agent_socket
    to detect socket when possible.
]]]
Index: libsvn_subr/gpg_agent.c
===================================================================
--- libsvn_subr/gpg_agent.c     (revision 1793701)
+++ libsvn_subr/gpg_agent.c     (working copy)
@@ -64,10 +64,13 @@
 #include <sys/socket.h>
 #include <sys/un.h>
 
+#include <apr_file_io.h>
 #include <apr_pools.h>
+#include <apr_strings.h>
 #include "svn_auth.h"
 #include "svn_config.h"
 #include "svn_error.h"
+#include "svn_io.h"
 #include "svn_pools.h"
 #include "svn_cmdline.h"
 #include "svn_checksum.h"
@@ -225,6 +228,65 @@
   close(sd);
 }
 
+/* Find gpg-agent socket location using gpgconf. Returns the path to socket, or
+ * NULL if the socket path cannot be determined using gpgconf.
+ */
+static const char *
+find_gpgconf_agent_socket(apr_pool_t *pool)
+{
+  apr_proc_t proc;
+  svn_stringbuf_t *line;
+  svn_error_t *err;
+  svn_boolean_t eof;
+  const char* agent_socket;
+  const char* const gpgargv[] = { "gpgconf", "--list-dir", NULL };
+
+  /* execute "gpgconf --list-dir" */
+  err = svn_io_start_cmd3(&proc, NULL, "gpgconf", gpgargv,
+                          NULL /* env */, TRUE /* inherit */,
+                          FALSE /* infile_pipe */, NULL /* infile */,
+                          TRUE /* outfile_pipe */, NULL /* outfile */,
+                          FALSE /* errfile_pipe */, NULL /* errfile */,
+                          pool);
+  if (err != SVN_NO_ERROR)
+    {
+      svn_error_clear(err);
+      return NULL;
+    }
+
+  /* Parse the gpgconf output.
+   * The format of output is a list of directories and sockets witch each
+   * directory/socket listen on a separate line in format "field:/some/path" */
+  eof = FALSE;
+  while (((err = svn_io_file_readline(proc.out, &line, NULL, &eof, 
APR_SIZE_MAX,
+                                      pool, pool)) == SVN_NO_ERROR)
+         && !eof)
+    {
+      if (strncmp(line->data, "agent-socket:", strlen("agent-socket:")) == 0)
+        {
+          apr_array_header_t *dir_details;
+          dir_details = svn_cstring_split(line->data, ":", TRUE, pool);
+          /* note: unescape_assuan modifies dir_details in place */
+          agent_socket = unescape_assuan(APR_ARRAY_IDX(dir_details, 1, char*));
+          break;
+        }
+    }
+  if (err != SVN_NO_ERROR)
+    {
+      svn_error_clear(err);
+      return NULL;
+    }
+  apr_file_close(proc.out);
+  err = svn_io_wait_for_cmd(&proc, "gpgconf", NULL, NULL, pool);
+  if (err != SVN_NO_ERROR)
+    {
+      svn_error_clear(err);
+      return NULL;
+    }
+
+  return agent_socket;
+}
+
 /* Locate a running GPG Agent, and return an open file descriptor
  * for communication with the agent in *NEW_SD. If no running agent
  * can be found, set *NEW_SD to -1. */
@@ -242,37 +304,43 @@
 
   *new_sd = -1;
 
-  /* This implements the method of finding the socket as described in
-   * the gpg-agent man page under the --use-standard-socket option.
-   * The manage page says the standard socket is "named 'S.gpg-agent' located
-   * in the home directory."  GPG's home directory is either the directory
-   * specified by $GNUPGHOME or ~/.gnupg. */
-  gpg_agent_info = getenv("GPG_AGENT_INFO");
-  if (gpg_agent_info != NULL)
-    {
-      apr_array_header_t *socket_details;
+  /* Query socket location using gpgconf if possible */
+  socket_name = find_gpgconf_agent_socket(pool);
 
-      /* For reference GPG_AGENT_INFO consists of 3 : separated fields.
-       * The path to the socket, the pid of the gpg-agent process and
-       * finally the version of the protocol the agent talks. */
-      socket_details = svn_cstring_split(gpg_agent_info, ":", TRUE,
-                                         pool);
-      socket_name = APR_ARRAY_IDX(socket_details, 0, const char *);
-    }
-  else if ((gnupghome = getenv("GNUPGHOME")) != NULL)
+  /* fallback to the old method used with Gnupg 1.x */
+  if (socket_name == NULL)
     {
-      const char *homedir = svn_dirent_canonicalize(gnupghome, pool);
-      socket_name = svn_dirent_join(homedir, "S.gpg-agent", pool);
-    }
-  else
-    {
-      const char *homedir = svn_user_get_homedir(pool);
+      /* This implements the method of finding the socket as described in
+       * the gpg-agent man page under the --use-standard-socket option.
+       * The manage page says the standard socket is "named 'S.gpg-agent' 
located
+       * in the home directory."  GPG's home directory is either the directory
+       * specified by $GNUPGHOME or ~/.gnupg. */
+      if ((gpg_agent_info = getenv("GPG_AGENT_INFO")) != NULL)
+        {
+          apr_array_header_t *socket_details;
 
-      if (!homedir)
-        return SVN_NO_ERROR;
+          /* For reference GPG_AGENT_INFO consists of 3 : separated fields.
+           * The path to the socket, the pid of the gpg-agent process and
+           * finally the version of the protocol the agent talks. */
+          socket_details = svn_cstring_split(gpg_agent_info, ":", TRUE,
+                                             pool);
+          socket_name = APR_ARRAY_IDX(socket_details, 0, const char *);
+        }
+      else if ((gnupghome = getenv("GNUPGHOME")) != NULL)
+        {
+          const char *homedir = svn_dirent_canonicalize(gnupghome, pool);
+          socket_name = svn_dirent_join(homedir, "S.gpg-agent", pool);
+        }
+      else
+        {
+          const char *homedir = svn_user_get_homedir(pool);
+
+          if (!homedir)
+            return SVN_NO_ERROR;
 
-      socket_name = svn_dirent_join_many(pool, homedir, ".gnupg",
-                                         "S.gpg-agent", SVN_VA_NULL);
+          socket_name = svn_dirent_join_many(pool, homedir, ".gnupg",
+                                             "S.gpg-agent", SVN_VA_NULL);
+        }
     }
 
   if (socket_name != NULL)

Reply via email to