Some doc fixes and a fix for "make distcheck".
[gnupg.git] / g10 / call-dirmngr.c
index 10c0e56..5bddbbe 100644 (file)
 #include "call-dirmngr.h"
 
 
+/* Parameter structure used to gather status info.  */
+struct ks_status_parm_s
+{
+  char *source;
+};
+
+
 /* Parameter structure used with the KS_SEARCH command.  */
 struct ks_search_parm_s
 {
@@ -47,8 +54,9 @@ struct ks_search_parm_s
   membuf_t saveddata;   /* Buffer to build complete lines.  */
   char *helpbuf;        /* NULL or malloced buffer.  */
   size_t helpbufsize;   /* Allocated size of HELPBUF.  */
-  gpg_error_t (*data_cb)(void*, char*);  /* Callback.  */
+  gpg_error_t (*data_cb)(void*, int, char*);  /* Callback.  */
   void *data_cb_value;  /* First argument for DATA_CB.  */
+  struct ks_status_parm_s *stparm; /* Link to the status parameter.  */
 };
 
 
@@ -71,10 +79,10 @@ struct ks_put_parm_s
 
 /* Data used to associate an session with dirmngr contexts.  We can't
    use a simple one to one mapping because we sometimes need two
-   connection s to the dirmngr; for example while doing a listing and
+   connections to the dirmngr; for example while doing a listing and
    being in a data callback we may want to retrieve a key.  The local
    dirmngr data takes care of this.  At the end of the session the
-   function dirmngr_deinit_session_data is called bu gpg.c to cleanup
+   function dirmngr_deinit_session_data is called by gpg.c to cleanup
    these resources.  Note that gpg.h defines a typedef dirmngr_local_t
    for this structure. */
 struct dirmngr_local_s
@@ -109,9 +117,8 @@ gpg_dirmngr_deinit_session_data (ctrl_t ctrl)
 }
 
 
-/* Try to connect to the Dirmngr via a socket or fork it off if
-   possible.  Handle the server's initial greeting and set global
-   options.  */
+/* Try to connect to the Dirmngr via a socket or spawn it if possible.
+   Handle the server's initial greeting and set global options.  */
 static gpg_error_t
 create_context (ctrl_t ctrl, assuan_context_t *r_ctx)
 {
@@ -122,7 +129,7 @@ create_context (ctrl_t ctrl, assuan_context_t *r_ctx)
   err = start_new_dirmngr (&ctx,
                            GPG_ERR_SOURCE_DEFAULT,
                            opt.homedir,
-                           NULL,
+                           opt.dirmngr_program,
                            opt.verbose, DBG_ASSUAN,
                            NULL /*gpg_status2*/, ctrl);
   if (!err)
@@ -135,7 +142,9 @@ create_context (ctrl_t ctrl, assuan_context_t *r_ctx)
 
       /* Set all configured keyservers.  We clear existing keyservers
          so that any keyserver configured in GPG overrides keyservers
-         possibly configured in Dirmngr. */
+         possibly still configured in Dirmngr for the session (Note
+         that the keyserver list of a session in Dirmngr survives a
+         RESET. */
       for (ksi = opt.keyserver; !err && ksi; ksi = ksi->next)
         {
           char *line;
@@ -166,8 +175,8 @@ create_context (ctrl_t ctrl, assuan_context_t *r_ctx)
 
 
 /* Get a context for accessing dirmngr.  If no context is available a
-   new one is created and - if requred - dirmngr started.  On success
-   an assuan context is stored at R_CTX.  This Context may only be
+   new one is created and - if required - dirmngr started.  On success
+   an assuan context is stored at R_CTX.  This context may only be
    released by means of close_context.  Note that NULL is stored at
    R_CTX on error.  */
 static gpg_error_t
@@ -199,7 +208,7 @@ open_context (ctrl_t ctrl, assuan_context_t *r_ctx)
           xfree (dml);
           return err;
         }
-      /* To be on the Pth thread safe site we need to add it to a
+      /* To be on the nPth thread safe site we need to add it to a
          list; this is far easier than to have a lock for this
          function.  It should not happen anyway but the code is free
          because we need it for the is_active check above.  */
@@ -234,6 +243,29 @@ close_context (ctrl_t ctrl, assuan_context_t ctx)
 
 
 \f
+/* Status callback for ks_get and ks_search.  */
+static gpg_error_t
+ks_status_cb (void *opaque, const char *line)
+{
+  struct ks_status_parm_s *parm = opaque;
+  gpg_error_t err = 0;
+  const char *s;
+
+  if ((s = has_leading_keyword (line, "SOURCE")))
+    {
+      if (!parm->source)
+        {
+          parm->source = xtrystrdup (s);
+          if (!parm->source)
+            err = gpg_error_from_syserror ();
+        }
+    }
+
+  return err;
+}
+
+
+\f
 /* Data callback for the KS_SEARCH command. */
 static gpg_error_t
 ks_search_data_cb (void *opaque, const void *data, size_t datalen)
@@ -247,6 +279,22 @@ ks_search_data_cb (void *opaque, const void *data, size_t datalen)
   if (parm->lasterr)
     return 0;
 
+  if (parm->stparm->source)
+    {
+      err = parm->data_cb (parm->data_cb_value, 1, parm->stparm->source);
+      if (err)
+        {
+          parm->lasterr = err;
+          return err;
+        }
+      /* Clear it so that we won't get back here unless the server
+         accidentally sends a second source status line.  Note that
+         will not see all accidentally sent source lines because it
+         depends on whether data lines have been send in between.  */
+      xfree (parm->stparm->source);
+      parm->stparm->source = NULL;
+    }
+
   if (!data)
     return 0;  /* Ignore END commands.  */
 
@@ -269,7 +317,7 @@ ks_search_data_cb (void *opaque, const void *data, size_t datalen)
           fixedbuf[linelen] = 0;
           if (linelen && fixedbuf[linelen-1] == '\r')
             fixedbuf[linelen-1] = 0;
-          err = parm->data_cb (parm->data_cb_value, fixedbuf);
+          err = parm->data_cb (parm->data_cb_value, 0, fixedbuf);
         }
       else
         {
@@ -288,7 +336,7 @@ ks_search_data_cb (void *opaque, const void *data, size_t datalen)
           parm->helpbuf[linelen] = 0;
           if (linelen && parm->helpbuf[linelen-1] == '\r')
             parm->helpbuf[linelen-1] = 0;
-          err = parm->data_cb (parm->data_cb_value, parm->helpbuf);
+          err = parm->data_cb (parm->data_cb_value, 0, parm->helpbuf);
         }
       if (err)
         parm->lasterr = err;
@@ -305,17 +353,18 @@ ks_search_data_cb (void *opaque, const void *data, size_t datalen)
 
 /* Run the KS_SEARCH command using the search string SEARCHSTR.  All
    data lines are passed to the CB function.  That function is called
-   with CB_VALUE as its first argument and the decoded data line as
-   second argument.  The callback function may modify the data line
-   and it is guaranteed that this data line is a complete line with a
-   terminating 0 character but without the linefeed.  NULL is passed
-   to the callback to indicate EOF.  */
+   with CB_VALUE as its first argument, a 0 as second argument, and
+   the decoded data line as third argument.  The callback function may
+   modify the data line and it is guaranteed that this data line is a
+   complete line with a terminating 0 character but without the
+   linefeed.  NULL is passed to the callback to indicate EOF.  */
 gpg_error_t
 gpg_dirmngr_ks_search (ctrl_t ctrl, const char *searchstr,
-                       gpg_error_t (*cb)(void*, char *), void *cb_value)
+                       gpg_error_t (*cb)(void*, int, char *), void *cb_value)
 {
   gpg_error_t err;
   assuan_context_t ctx;
+  struct ks_status_parm_s stparm;
   struct ks_search_parm_s parm;
   char line[ASSUAN_LINELENGTH];
 
@@ -335,18 +384,21 @@ gpg_dirmngr_ks_search (ctrl_t ctrl, const char *searchstr,
     xfree (escsearchstr);
   }
 
+  memset (&stparm, 0, sizeof stparm);
   memset (&parm, 0, sizeof parm);
   init_membuf (&parm.saveddata, 1024);
   parm.data_cb = cb;
   parm.data_cb_value = cb_value;
+  parm.stparm = &stparm;
 
   err = assuan_transact (ctx, line, ks_search_data_cb, &parm,
-                        NULL, NULL, NULL, NULL);
+                        NULL, NULL, ks_status_cb, &stparm);
   if (!err)
-    err = cb (cb_value, NULL);  /* Send EOF.  */
+    err = cb (cb_value, 0, NULL);  /* Send EOF.  */
 
   xfree (get_membuf (&parm.saveddata, NULL));
   xfree (parm.helpbuf);
+  xfree (stparm.source);
 
   close_context (ctrl, ctx);
   return err;
@@ -354,7 +406,7 @@ gpg_dirmngr_ks_search (ctrl_t ctrl, const char *searchstr,
 
 
 \f
-/* Data callback for the KS_GET command. */
+/* Data callback for the KS_GET and KS_FETCH commands. */
 static gpg_error_t
 ks_get_data_cb (void *opaque, const void *data, size_t datalen)
 {
@@ -381,24 +433,32 @@ ks_get_data_cb (void *opaque, const void *data, size_t datalen)
    don't need to escape the patterns before sending them to the
    server.
 
+   If R_SOURCE is not NULL the source of the data is stored as a
+   malloced string there.  If a source is not known NULL is stored.
+
    If there are too many patterns the function returns an error.  That
    could be fixed by issuing several search commands or by
    implementing a different interface.  However with long keyids we
    are able to ask for (1000-10-1)/(2+8+1) = 90 keys at once.  */
 gpg_error_t
-gpg_dirmngr_ks_get (ctrl_t ctrl, char **pattern, estream_t *r_fp)
+gpg_dirmngr_ks_get (ctrl_t ctrl, char **pattern,
+                    estream_t *r_fp, char **r_source)
 {
   gpg_error_t err;
   assuan_context_t ctx;
+  struct ks_status_parm_s stparm;
   struct ks_get_parm_s parm;
   char *line = NULL;
   size_t linelen;
   membuf_t mb;
   int idx;
 
+  memset (&stparm, 0, sizeof stparm);
   memset (&parm, 0, sizeof parm);
 
   *r_fp = NULL;
+  if (r_source)
+    *r_source = NULL;
 
   err = open_context (ctrl, &ctx);
   if (err)
@@ -432,6 +492,72 @@ gpg_dirmngr_ks_get (ctrl_t ctrl, char **pattern, estream_t *r_fp)
       goto leave;
     }
   err = assuan_transact (ctx, line, ks_get_data_cb, &parm,
+                         NULL, NULL, ks_status_cb, &stparm);
+  if (err)
+    goto leave;
+
+  es_rewind (parm.memfp);
+  *r_fp = parm.memfp;
+  parm.memfp = NULL;
+
+  if (r_source)
+    {
+      *r_source = stparm.source;
+      stparm.source = NULL;
+    }
+
+ leave:
+  es_fclose (parm.memfp);
+  xfree (stparm.source);
+  xfree (line);
+  close_context (ctrl, ctx);
+  return err;
+}
+
+
+/* Run the KS_FETCH and pass URL as argument.  On success an estream
+   object is returned to retrieve the keys.  On error an error code is
+   returned and NULL stored at R_FP.
+
+   The url is expected to point to a small set of keys; in many cases
+   only to one key.  However, schemes like finger may return several
+   keys.  Note that the configured keyservers are ignored by the
+   KS_FETCH command.  */
+gpg_error_t
+gpg_dirmngr_ks_fetch (ctrl_t ctrl, const char *url, estream_t *r_fp)
+{
+  gpg_error_t err;
+  assuan_context_t ctx;
+  struct ks_get_parm_s parm;
+  char *line = NULL;
+
+  memset (&parm, 0, sizeof parm);
+
+  *r_fp = NULL;
+
+  err = open_context (ctrl, &ctx);
+  if (err)
+    return err;
+
+  line = strconcat ("KS_FETCH -- ", url, NULL);
+  if (!line)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+  if (strlen (line) + 2 >= ASSUAN_LINELENGTH)
+    {
+      err = gpg_error (GPG_ERR_TOO_LARGE);
+      goto leave;
+    }
+
+  parm.memfp = es_fopenmem (0, "rwb");
+  if (!parm.memfp)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+  err = assuan_transact (ctx, line, ks_get_data_cb, &parm,
                          NULL, NULL, NULL, NULL);
   if (err)
     goto leave;
@@ -456,18 +582,18 @@ ks_put_inq_cb (void *opaque, const char *line)
   struct ks_put_parm_s *parm = opaque;
   gpg_error_t err = 0;
 
-  if (!strncmp (line, "KEYBLOCK", 8) && (line[8] == ' ' || !line[8]))
+  if (has_leading_keyword (line, "KEYBLOCK"))
     {
       if (parm->data)
         err = assuan_send_data (parm->ctx, parm->data, parm->datalen);
     }
-  else if (!strncmp (line, "KEYBLOCK_INFO", 13) && (line[13]==' ' || !line[13]))
+  else if (has_leading_keyword (line, "KEYBLOCK_INFO"))
     {
       kbnode_t node;
       estream_t fp;
 
       /* Parse the keyblock and send info lines back to the server.  */
-      fp = es_fopenmem (0, "rw");
+      fp = es_fopenmem (0, "rw,samethread");
       if (!fp)
         err = gpg_error_from_syserror ();