All standard keyserver commands are now using dirmngr.
authorWerner Koch <wk@gnupg.org>
Thu, 20 Jan 2011 13:12:53 +0000 (14:12 +0100)
committerWerner Koch <wk@gnupg.org>
Thu, 20 Jan 2011 13:12:53 +0000 (14:12 +0100)
20 files changed:
common/ChangeLog
common/b64dec.c
common/b64enc.c
common/http.c
common/http.h
common/util.h
dirmngr/ChangeLog
dirmngr/ks-action.c
dirmngr/ks-action.h
dirmngr/ks-engine-hkp.c
dirmngr/ks-engine.h
dirmngr/server.c
g10/ChangeLog
g10/call-dirmngr.c
g10/call-dirmngr.h
g10/export.c
g10/keyserver.c
g10/main.h
keyserver/ChangeLog
keyserver/gpgkeys_hkp.c

index 3ce80cb..3b75064 100644 (file)
@@ -1,3 +1,14 @@
+2011-01-20  Werner Koch  <wk@g10code.com>
+
+       * util.h (struct b64state): Add field LASTERR.
+       * b64enc.c (enc_start, b64enc_write, b64enc_finish): Handle
+       LASTERR.  This is to make sure that we don't leak strduped data.
+       * b64dec.c (b64dec_start, b64dec_proc, b64dec_finish): Ditto.
+
+       * http.c (escape_data): New.
+       (insert_escapes): Implement using escape_data.
+       (http_escape_data): New.
+
 2011-01-18  Werner Koch  <wk@g10code.com>
 
        * iobuf.c (file_es_filter_ctx_t): New.
index af223ae..137dd72 100644 (file)
@@ -1,5 +1,5 @@
 /* b64dec.c - Simple Base64 decoder.
- * Copyright (C) 2008 Free Software Foundation, Inc.
+ * Copyright (C) 2008, 2011 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -72,16 +72,19 @@ b64dec_start (struct b64state *state, const char *title)
   if (title)
     {
       if (!strncmp (title, "PGP", 3) && (!title[3] || title[3] == ' '))
-        return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
-
-      state->title = xtrystrdup (title);
-      if (!state->title)
-        return gpg_error_from_syserror ();
-      state->idx = s_init;
+        state->lasterr = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+      else
+        {
+          state->title = xtrystrdup (title);
+          if (!state->title)
+            state->lasterr = gpg_error_from_syserror ();
+          else
+            state->idx = s_init;
+        }
     }
   else
     state->idx = s_b64_0;
-  return 0;
+  return state->lasterr;
 }
 
 
@@ -96,12 +99,18 @@ b64dec_proc (struct b64state *state, void *buffer, size_t length,
   int pos = state->quad_count; 
   char *d, *s;
 
+  if (state->lasterr)
+    return state->lasterr;
+
   if (state->stop_seen)
     {
       *r_nbytes = 0;
-      return gpg_error (GPG_ERR_EOF);
+      state->lasterr = gpg_error (GPG_ERR_EOF);
+      xfree (state->title);
+      state->title = NULL;
+      return state->lasterr;
     }
-
+  
   for (s=d=buffer; length && !state->stop_seen; length--, s++)
     {
       switch (ds)
@@ -210,6 +219,9 @@ b64dec_proc (struct b64state *state, void *buffer, size_t length,
 gpg_error_t
 b64dec_finish (struct b64state *state)
 {
+  if (state->lasterr)
+    return state->lasterr;
+
   xfree (state->title);
   state->title = NULL;
   return state->invalid_encoding? gpg_error(GPG_ERR_BAD_DATA): 0;
index 1e277f4..5d61619 100644 (file)
@@ -1,5 +1,6 @@
 /* b64enc.c - Simple Base64 encoder.
- * Copyright (C) 2001, 2003, 2004, 2008, 2010 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2003, 2004, 2008, 2010,
+ *               2011 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -143,6 +144,7 @@ enc_start (struct b64state *state, FILE *fp, estream_t stream,
   memset (state, 0, sizeof *state);
   state->fp = fp;
   state->stream = stream;
+  state->lasterr = 0;
   if (title && !*title)
     state->flags |= B64ENC_NO_LINEFEEDS;
   else if (title)
@@ -154,9 +156,9 @@ enc_start (struct b64state *state, FILE *fp, estream_t stream,
         }
       state->title = xtrystrdup (title);
       if (!state->title)
-        return gpg_error_from_syserror ();
+        state->lasterr = gpg_error_from_syserror ();
     }
-  return 0;
+  return state->lasterr;
 }
 
 
@@ -203,6 +205,8 @@ b64enc_write (struct b64state *state, const void *buffer, size_t nbytes)
   int idx, quad_count;
   const unsigned char *p;
 
+  if (state->lasterr)
+    return state->lasterr;
 
   if (!nbytes)
     {
@@ -285,7 +289,13 @@ b64enc_write (struct b64state *state, const void *buffer, size_t nbytes)
   return 0;
 
  write_error:
-  return gpg_error_from_syserror ();
+  state->lasterr = gpg_error_from_syserror ();
+  if (state->title)
+    {
+      xfree (state->title);
+      state->title = NULL;
+    }
+  return state->lasterr;
 }
 
 
@@ -297,6 +307,9 @@ b64enc_finish (struct b64state *state)
   int idx, quad_count;
   char tmp[4];
 
+  if (state->lasterr)
+    return state->lasterr;
+
   if (!(state->flags & B64ENC_DID_HEADER))
     goto cleanup;
 
@@ -404,6 +417,7 @@ b64enc_finish (struct b64state *state)
     }
   state->fp = NULL;
   state->stream = NULL;
+  state->lasterr = err;
   return err;
 }
 
index 3d7c463..4d35361 100644 (file)
@@ -1,6 +1,6 @@
 /* http.c  -  HTTP protocol handler
- * Copyright (C) 1999, 2001, 2002, 2003, 2004, 2006,
- *               2009, 2010 Free Software Foundation, Inc.
+ * Copyright (C) 1999, 2001, 2002, 2003, 2004, 2006, 2009, 2010,
+ *               2011 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -742,14 +742,14 @@ remove_escapes (char *string)
 }
 
 
-static int
-insert_escapes (char *buffer, const char *string,
-               const char *special)
+static size_t
+escape_data (char *buffer, const void *data, size_t datalen,
+             const char *special)
 {
-  const unsigned char *s = (const unsigned char*)string;
-  int n = 0;
+  const unsigned char *s;
+  size_t n = 0;
 
-  for (; *s; s++)
+  for (s = data; datalen; s++, datalen--)
     {
       if (strchr (VALID_URI_CHARS, *s) && !strchr (special, *s))
        {
@@ -771,6 +771,14 @@ insert_escapes (char *buffer, const char *string,
 }
 
 
+static int
+insert_escapes (char *buffer, const char *string,
+               const char *special)
+{
+  return escape_data (buffer, string, strlen (string), special);
+}
+
+
 /* Allocate a new string from STRING using standard HTTP escaping as
    well as escaping of characters given in SPECIALS.  A common pattern
    for SPECIALS is "%;?&=". However it depends on the needs, for
@@ -792,6 +800,27 @@ http_escape_string (const char *string, const char *specials)
   return buf;
 }
 
+/* Allocate a new string from {DATA,DATALEN} using standard HTTP
+   escaping as well as escaping of characters given in SPECIALS.  A
+   common pattern for SPECIALS is "%;?&=".  However it depends on the
+   needs, for example "+" and "/: often needs to be escaped too.
+   Returns NULL on failure and sets ERRNO. */
+char *
+http_escape_data (const void *data, size_t datalen, const char *specials)
+{
+  int n;
+  char *buf;
+
+  n = escape_data (NULL, data, datalen, specials);
+  buf = xtrymalloc (n+1);
+  if (buf)
+    {
+      escape_data (buf, data, datalen, specials);
+      buf[n] = 0;
+    }
+  return buf;
+}
+
 
 
 static uri_tuple_t
index aaa2d3a..7eecbc0 100644 (file)
@@ -117,6 +117,7 @@ unsigned int http_get_status_code (http_t hd);
 const char *http_get_header (http_t hd, const char *name);
 
 char *http_escape_string (const char *string, const char *specials);
+char *http_escape_data (const void *data, size_t datalen, const char *specials);
 
 
 #endif /*GNUPG_COMMON_HTTP_H*/
index 1f7964f..f06701f 100644 (file)
@@ -150,6 +150,7 @@ struct b64state
   u32 crc;
   int stop_seen:1;
   int invalid_encoding:1;
+  gpg_error_t lasterr;
 };
 
 gpg_error_t b64enc_start (struct b64state *state, FILE *fp, const char *title);
index f5b3dea..47c7fe0 100644 (file)
@@ -1,13 +1,9 @@
-2011-01-06  Werner Koch  <wk@g10code.com>
+2011-01-20  Werner Koch  <wk@g10code.com>
 
        * server.c (release_ctrl_keyservers): New.
-       (cmd_keyserver): New.
-
+       (cmd_keyserver, cmd_ks_seach, cmd_ks_get, cmd_ks_put): New.
        * dirmngr.h (uri_item_t): New.
        (struct server_control_s): Add field KEYSERVERS.
-
-2011-01-04  Werner Koch  <wk@g10code.com>
-
        * ks-engine-hkp.c: New.
        * ks-engine.h: New.
        * ks-action.c, ks-action.h: New.
index f376c27..fd2a2b5 100644 (file)
@@ -90,7 +90,7 @@ ks_action_search (ctrl_t ctrl, strlist_t patterns, estream_t outfp)
 }
 
 
-/* Get the requested keys (macthing PATTERNS) using all configured
+/* Get the requested keys (matching PATTERNS) using all configured
    keyservers and write the result to the provided output stream.  */
 gpg_error_t
 ks_action_get (ctrl_t ctrl, strlist_t patterns, estream_t outfp)
@@ -148,3 +148,36 @@ ks_action_get (ctrl_t ctrl, strlist_t patterns, estream_t outfp)
   return err;
 }
 
+
+
+/* Send an OpenPGP key to all keyservers.  The key in {DATA,DATALEN}
+   is expected in OpenPGP binary transport format.  */
+gpg_error_t
+ks_action_put (ctrl_t ctrl, const void *data, size_t datalen)
+{
+  gpg_error_t err = 0;
+  gpg_error_t first_err = 0;
+  int any = 0;
+  uri_item_t uri;
+
+  for (uri = ctrl->keyservers; !err && uri; uri = uri->next)
+    {
+      if (uri->parsed_uri->is_http)
+        {
+          any = 1;
+          err = ks_hkp_put (ctrl, uri->parsed_uri, data, datalen);
+          if (err)
+            {
+              first_err = err;
+              err = 0;
+            }
+        }
+    }
+  
+  if (!any)
+    err = gpg_error (GPG_ERR_NO_KEYSERVER);
+  else if (!err && first_err)
+    err = first_err; /* fixme: Do we really want to do that?  */
+  return err;
+}
+
index 4a92ed1..b3bd3fc 100644 (file)
@@ -22,6 +22,7 @@
 
 gpg_error_t ks_action_search (ctrl_t ctrl, strlist_t patterns, estream_t outfp);
 gpg_error_t ks_action_get (ctrl_t ctrl, strlist_t patterns, estream_t outfp);
+gpg_error_t ks_action_put (ctrl_t ctrl, const void *data, size_t datalen);
 
 
 #endif /*DIRMNGR_KS_ACTION_H*/
index 662e9e4..e25900a 100644 (file)
 
 
 /* Send an HTTP request.  On success returns an estream object at
-   R_FP.  HOSTPORTSTR is only used for diagnostics. */
+   R_FP.  HOSTPORTSTR is only used for diagnostics.  If POST_CB is not
+   NULL a post request is used and that callback is called to allow
+   writing the post data.  */
 static gpg_error_t
 send_request (ctrl_t ctrl, const char *request, const char *hostportstr,
+              gpg_error_t (*post_cb)(void *, http_t), void *post_cb_value,
               estream_t *r_fp)
 {
   gpg_error_t err;
@@ -51,7 +54,9 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr,
 
   *r_fp = NULL;
  once_more:
-  err = http_open (&http, HTTP_REQ_GET, request,
+  err = http_open (&http,
+                   post_cb? HTTP_REQ_POST : HTTP_REQ_GET,
+                   request,
                    /* fixme: AUTH */ NULL,
                    0,
                    /* fixme: proxy*/ NULL,
@@ -65,9 +70,14 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr,
          we're good with both HTTP 1.0 and 1.1.  */
       es_fputs ("Pragma: no-cache\r\n"
                 "Cache-Control: no-cache\r\n", fp);
-      http_start_data (http);
-      if (es_ferror (fp))
-        err = gpg_error_from_syserror ();
+      if (post_cb)
+        err = post_cb (post_cb_value, http);
+      if (!err)
+        {
+          http_start_data (http);
+          if (es_ferror (fp))
+            err = gpg_error_from_syserror ();
+        }
     }
   if (err)
     {
@@ -135,19 +145,76 @@ send_request (ctrl_t ctrl, const char *request, const char *hostportstr,
 
   /* Return the read stream and close the HTTP context.  */
   *r_fp = fp;
-  fp = NULL;
   http_close (http, 1);
   http = NULL;
 
  leave:
-  es_fclose (fp);
   http_close (http, 0);
   xfree (request_buffer);
   return err;
 }
 
 
+static gpg_error_t
+armor_data (char **r_string, const void *data, size_t datalen)
+{
+  gpg_error_t err;
+  struct b64state b64state;
+  estream_t fp;
+  long length;
+  char *buffer;
+  size_t nread;
+
+  *r_string = NULL;
+
+  fp = es_fopenmem (0, "rw");
+  if (!fp)
+    return gpg_error_from_syserror ();
 
+  if ((err=b64enc_start_es (&b64state, fp, "PGP PUBLIC KEY BLOCK"))
+      || (err=b64enc_write (&b64state, data, datalen))
+      || (err = b64enc_finish (&b64state)))
+    {
+      es_fclose (fp);
+      return err;
+    }
+
+  /* FIXME: To avoid the extra buffer allocation estream should
+     provide a function to snatch the internal allocated memory from
+     such a memory stream.  */
+  length = es_ftell (fp);
+  if (length < 0)
+    {
+      err = gpg_error_from_syserror ();
+      es_fclose (fp);
+      return err;
+    }
+
+  buffer = xtrymalloc (length+1);
+  if (!buffer)
+    {
+      err = gpg_error_from_syserror ();
+      es_fclose (fp);
+      return err;
+    }
+  
+  es_rewind (fp);
+  if (es_read (fp, buffer, length, &nread))
+    {
+      err = gpg_error_from_syserror ();
+      es_fclose (fp);
+      return err;
+    }
+  buffer[nread] = 0;
+  es_fclose (fp);
+  
+  *r_string = buffer;
+  return 0;
+}
+
+
+
+\f
 /* Search the keyserver identified by URI for keys matching PATTERN.
    On success R_FP has an open stream to read the data.  */
 gpg_error_t
@@ -251,7 +318,7 @@ ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern,
   }
   
   /* Send the request.  */
-  err = send_request (ctrl, request, hostport, &fp);
+  err = send_request (ctrl, request, hostport, NULL, NULL, &fp);
   if (err)
     goto leave;
 
@@ -368,7 +435,7 @@ ks_hkp_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, estream_t *r_fp)
   }
   
   /* Send the request.  */
-  err = send_request (ctrl, request, hostport, &fp);
+  err = send_request (ctrl, request, hostport, NULL, NULL, &fp);
   if (err)
     goto leave;
 
@@ -384,3 +451,108 @@ ks_hkp_get (ctrl_t ctrl, parsed_uri_t uri, const char *keyspec, estream_t *r_fp)
 }
 
 
+
+\f
+/* Callback parameters for put_post_cb.  */
+struct put_post_parm_s
+{
+  char *datastring;
+};
+
+
+/* Helper for ks_hkp_put.  */
+static gpg_error_t
+put_post_cb (void *opaque, http_t http)
+{
+  struct put_post_parm_s *parm = opaque;
+  gpg_error_t err = 0;
+  estream_t fp;
+  size_t len;
+
+  fp = http_get_write_ptr (http);
+  len = strlen (parm->datastring);
+
+  es_fprintf (fp,
+              "Content-Type: application/x-www-form-urlencoded\r\n"
+              "Content-Length: %zu\r\n", len+8 /* 8 is for "keytext" */);
+  http_start_data (http);
+  if (es_fputs ("keytext=", fp) || es_write (fp, parm->datastring, len, NULL))
+    err = gpg_error_from_syserror ();
+  return err;
+}
+
+
+/* Send the key in {DATA,DATALEN} to the keyserver identified by  URI.  */
+gpg_error_t
+ks_hkp_put (ctrl_t ctrl, parsed_uri_t uri, const void *data, size_t datalen)
+{
+  gpg_error_t err;
+  const char *scheme;
+  char portstr[10];
+  char *hostport = NULL;
+  char *request = NULL;
+  estream_t fp = NULL;
+  struct put_post_parm_s parm;
+  char *armored = NULL;
+
+  parm.datastring = NULL;
+
+  /* Map scheme and port.  */
+  if (!strcmp (uri->scheme,"hkps") || !strcmp (uri->scheme,"https"))
+    {
+      scheme = "https";
+      strcpy (portstr, "443");
+    }
+  else /* HKP or HTTP.  */
+    {
+      scheme = "http";
+      strcpy (portstr, "11371");
+    }
+  if (uri->port)
+    snprintf (portstr, sizeof portstr, "%hu", uri->port);
+  else
+    {} /*fixme_do_srv_lookup ()*/
+
+  err = armor_data (&armored, data, datalen);
+  if (err)
+    goto leave;
+
+  parm.datastring = http_escape_string (armored, EXTRA_ESCAPE_CHARS);
+  if (!parm.datastring)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+  xfree (armored);
+  armored = NULL;
+
+  /* Build the request string.  */
+  hostport = strconcat (scheme, "://", 
+                        *uri->host? uri->host: "localhost",
+                        ":", portstr, NULL);
+  if (!hostport)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+
+  request = strconcat (hostport, "/pks/add", NULL);
+  if (!request)
+    {
+      err = gpg_error_from_syserror ();
+      goto leave;
+    }
+  
+  /* Send the request.  */
+  err = send_request (ctrl, request, hostport, put_post_cb, &parm, &fp);
+  if (err)
+    goto leave;
+
+ leave:
+  es_fclose (fp);
+  xfree (parm.datastring);
+  xfree (armored);
+  xfree (request);
+  xfree (hostport);
+  return err;
+}
index 4b26662..304fc4d 100644 (file)
@@ -28,6 +28,8 @@ gpg_error_t ks_hkp_search (ctrl_t ctrl, parsed_uri_t uri, const char *pattern,
                            estream_t *r_fp);
 gpg_error_t ks_hkp_get (ctrl_t ctrl, parsed_uri_t uri,
                         const char *keyspec, estream_t *r_fp);
+gpg_error_t ks_hkp_put (ctrl_t ctrl, parsed_uri_t uri,
+                        const void *data, size_t datalen);
 
 
 
index 5d61da8..fc7b229 100644 (file)
    something reasonable. */
 #define MAX_CERT_LENGTH (8*1024)
 
+/* The same goes for OpenPGP keyblocks, but here we need to allow for
+   much longer blocks; a 200k keyblock is not too unusual for keys
+   with a lot of signatures (e.g. 0x5b0358a2).  */
+#define MAX_KEYBLOCK_LENGTH (512*1024)
+
+
 #define PARM_ERROR(t) assuan_set_error (ctx, \
                                         gpg_error (GPG_ERR_ASS_PARAMETER), (t))
 #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
@@ -1535,6 +1541,70 @@ cmd_ks_get (assuan_context_t ctx, char *line)
 }
 
 
+\f
+static const char hlp_ks_put[] =
+  "KS_PUT\n"
+  "\n"
+  "Send a key to the configured OpenPGP keyservers.  The actual key material\n"
+  "is then requested by Dirmngr using\n"
+  "\n"
+  "  INQUIRE KEYBLOCK\n"
+  "\n"
+  "The client shall respond with a binary version of the keyblock.  For LDAP\n"
+  "keyservers Dirmngr may ask for meta information of the provided keyblock\n"
+  "using:\n"
+  "\n"
+  "  INQUIRE KEYBLOCK_INFO\n"
+  "\n"
+  "The client shall respond with a colon delimited info lines";
+static gpg_error_t
+cmd_ks_put (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  gpg_error_t err;
+  unsigned char *value = NULL;
+  size_t valuelen; 
+  unsigned char *info = NULL;
+  size_t infolen;
+
+  /* No options for now.  */
+  line = skip_options (line);
+
+  /* Ask for the key material.  */
+  err = assuan_inquire (ctx, "KEYBLOCK",
+                        &value, &valuelen, MAX_KEYBLOCK_LENGTH);
+  if (err)
+    {
+      log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err));
+      goto leave;
+    }
+  
+  if (!valuelen) /* No data returned; return a comprehensible error. */
+    {
+      err = gpg_error (GPG_ERR_MISSING_CERT);
+      goto leave;
+    }
+
+  /* Ask for the key meta data. Not actually needed for HKP servers
+     but we do it anyway test the client implementaion.  */
+  err = assuan_inquire (ctx, "KEYBLOCK_INFO",
+                        &info, &infolen, MAX_KEYBLOCK_LENGTH);
+  if (err)
+    {
+      log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err));
+      goto leave;
+    }
+
+  /* Send the key.  */
+  err = ks_action_put (ctrl, value, valuelen);
+  
+ leave:
+  xfree (info);
+  xfree (value);
+  return leave_cmd (ctx, err);
+}
+
+
 
 \f
 static const char hlp_getinfo[] = 
@@ -1672,6 +1742,7 @@ register_commands (assuan_context_t ctx)
     { "KEYSERVER",  cmd_keyserver,  hlp_keyserver },
     { "KS_SEARCH",  cmd_ks_search,  hlp_ks_search },
     { "KS_GET",     cmd_ks_get,     hlp_ks_get },
+    { "KS_PUT",     cmd_ks_put,     hlp_ks_put },
     { "GETINFO",    cmd_getinfo,    hlp_getinfo },
     { "KILLDIRMNGR",cmd_killdirmngr,hlp_killdirmngr },
     { "RELOADDIRMNGR",cmd_reloaddirmngr,hlp_reloaddirmngr },
index 27f60fe..a60d5d5 100644 (file)
@@ -1,12 +1,11 @@
-2011-01-18  Werner Koch  <wk@g10code.com>
+2011-01-20  Werner Koch  <wk@g10code.com>
 
-       * import.c (import_keys_es_stream): New.
-
-2011-01-14  Werner Koch  <wk@g10code.com>
+       * keyserver.c: Rewrite most stuff for use with dirmngr.  Get rid
+       of all spawn code.  Work work pending.
 
-       * keyserver.c (parse_keyrec): Use trim_trailing_ws.
+       * export.c (export_pubkeys_buffer): New.
 
-2011-01-07  Werner Koch  <wk@g10code.com>
+       * import.c (import_keys_es_stream): New.
 
        * call-dirmngr.c, call-dirmngr.h: New.
        * gpg.h (server_control_s): Add DIRMNGR_LOCAL.
@@ -11671,7 +11670,7 @@ Thu Feb 12 22:24:42 1998  Werner Koch  (wk@frodo)
 
 
  Copyright 1998,1999,2000,2001,2002,2003,2004,2005,
-          2006,2007,2008,2009,2010 Free Software Foundation, Inc.
+          2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc.
 
  This file is free software; as a special exception the author gives
  unlimited permission to copy and/or distribute it, with or without
index 1816a72..f34b94b 100644 (file)
@@ -59,6 +59,16 @@ struct ks_get_parm_s
 };
 
 
+/* Parameter structure used with the KS_PUT command.  */
+struct ks_put_parm_s
+{
+  assuan_context_t ctx;
+  kbnode_t keyblock;  /* The optional keyblock.  */
+  const void *data;   /* The key in OpenPGP binary format.  */
+  size_t datalen;     /* The length of DATA.  */
+};
+
+
 /* 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
@@ -436,3 +446,166 @@ gpg_dirmngr_ks_get (ctrl_t ctrl, char **pattern, estream_t *r_fp)
   close_context (ctrl, ctx);
   return err;
 }
+
+
+\f
+/* Handle the KS_PUT inquiries. */
+static gpg_error_t
+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 (parm->data)
+        err = assuan_send_data (parm->ctx, parm->data, parm->datalen);
+    }
+  else if (!strncmp (line, "KEYBLOCK_INFO", 13) && (line[13]==' ' || !line[13]))
+    {
+      kbnode_t node;
+      estream_t fp;
+
+      /* Parse the keyblock and send info lines back to the server.  */
+      fp = es_fopenmem (0, "rw");
+      if (!fp)
+        err = gpg_error_from_syserror ();
+
+      for (node = parm->keyblock; !err && node; node=node->next)
+        {
+          switch(node->pkt->pkttype)
+            {
+            case PKT_PUBLIC_KEY:
+            case PKT_PUBLIC_SUBKEY:
+              {
+                PKT_public_key *pk = node->pkt->pkt.public_key;
+                
+                keyid_from_pk (pk, NULL);
+                
+                es_fprintf (fp, "%s:%08lX%08lX:%u:%u:%u:%u:%s%s:\n",
+                            node->pkt->pkttype==PKT_PUBLIC_KEY? "pub" : "sub",
+                            (ulong)pk->keyid[0], (ulong)pk->keyid[1],
+                            pk->pubkey_algo,
+                            nbits_from_pk (pk),
+                            pk->timestamp,
+                            pk->expiredate,
+                            pk->flags.revoked? "r":"",
+                            pk->has_expired? "e":"");
+              }
+              break;
+
+            case PKT_USER_ID:
+              {
+                PKT_user_id *uid = node->pkt->pkt.user_id;
+                int r;
+
+                if (!uid->attrib_data)
+                  {
+                    es_fprintf (fp, "uid:");
+                    
+                    /* Quote ':', '%', and any 8-bit characters.  */
+                    for (r=0; r < uid->len; r++)
+                      {
+                        if (uid->name[r] == ':' 
+                            || uid->name[r]== '%'
+                            || (uid->name[r]&0x80))
+                          es_fprintf (fp, "%%%02X", (byte)uid->name[r]);
+                        else
+                          es_putc (uid->name[r], fp);
+                      }
+                    
+                    es_fprintf (fp, ":%u:%u:%s%s:\n",
+                                uid->created,uid->expiredate,
+                                uid->is_revoked? "r":"",
+                                uid->is_expired? "e":"");
+                  }
+              }
+              break;
+
+              /* This bit is really for the benefit of people who
+                 store their keys in LDAP servers.  It makes it easy
+                 to do queries for things like "all keys signed by
+                 Isabella".  */
+            case PKT_SIGNATURE:
+              {
+                PKT_signature *sig = node->pkt->pkt.signature;
+
+                if (IS_UID_SIG (sig))
+                  {
+                    es_fprintf (fp, "sig:%08lX%08lX:%X:%u:%u:\n",
+                                (ulong)sig->keyid[0],(ulong)sig->keyid[1],
+                                sig->sig_class, sig->timestamp,
+                                sig->expiredate);
+                  }
+              }
+              break;
+
+            default:
+              continue;
+            }
+          /* Given that the last operation was an es_fprintf we should
+             get the correct ERRNO if ferror indicates an error.  */
+          if (es_ferror (fp))
+            err = gpg_error_from_syserror ();
+        }
+
+      /* Without an error and if we have an keyblock at all, send the
+         data back.  */
+      if (!err && parm->keyblock)
+        {
+          int rc;
+          char buffer[512];
+          size_t nread;
+
+          es_rewind (fp);
+          while (!(rc=es_read (fp, buffer, sizeof buffer, &nread)) && nread)
+            {
+              err = assuan_send_data (parm->ctx, buffer, nread);
+              if (err)
+                break;
+            }
+          if (!err && rc)
+            err = gpg_error_from_syserror ();
+        }
+      es_fclose (fp);
+    }
+  else
+    return gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
+
+  return err; 
+}
+
+
+/* Send a key to the configured server.  {DATA,DATLEN} contains the
+   key in OpenPGP binary transport format.  If KEYBLOCK is not NULL it
+   has the internal representaion of that key; this is for example
+   used to convey meta data to LDAP keyservers.  */
+gpg_error_t
+gpg_dirmngr_ks_put (ctrl_t ctrl, void *data, size_t datalen, kbnode_t keyblock)
+{ 
+  gpg_error_t err;
+  assuan_context_t ctx;
+  struct ks_put_parm_s parm;
+
+  memset (&parm, 0, sizeof parm);
+
+  /* We are going to parse the keyblock, thus we better make sure the
+     all information is readily available.  */
+  if (keyblock)
+    merge_keys_and_selfsig (keyblock);
+
+  err = open_context (ctrl, &ctx);
+  if (err)
+    return err;
+
+  parm.ctx = ctx;
+  parm.keyblock = keyblock;
+  parm.data = data;
+  parm.datalen = datalen;
+
+  err = assuan_transact (ctx, "KS_PUT", NULL, NULL,
+                         ks_put_inq_cb, &parm, NULL, NULL);
+
+  close_context (ctrl, ctx);
+  return err;
+}
index c848a29..9523bf5 100644 (file)
@@ -25,6 +25,8 @@ 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 gpg_dirmngr_ks_get (ctrl_t ctrl, char *pattern[], estream_t *r_fp);
+gpg_error_t gpg_dirmngr_ks_put (ctrl_t ctrl, void *data, size_t datalen,
+                                kbnode_t keyblock);
 
 
 #endif /*GNUPG_G10_CALL_DIRMNGR_H*/
index 91c6a73..43856ff 100644 (file)
@@ -114,6 +114,60 @@ export_pubkeys_stream (ctrl_t ctrl, iobuf_t out, strlist_t users,
   return rc;
 }
 
+
+/*
+ * Export a single key into a memory buffer.
+ */
+gpg_error_t
+export_pubkey_buffer (ctrl_t ctrl, const char *keyspec, unsigned int options,
+                      kbnode_t *r_keyblock, void **r_data, size_t *r_datalen)
+{
+  gpg_error_t err;
+  iobuf_t iobuf;
+  int any;
+  strlist_t helplist;
+
+  *r_keyblock = NULL;
+  *r_data = NULL;
+  *r_datalen = 0;
+
+  helplist = NULL;
+  if (!add_to_strlist_try (&helplist, keyspec))
+    return gpg_error_from_syserror ();
+
+  iobuf = iobuf_temp ();
+  err = do_export_stream (ctrl, iobuf, helplist, 0, r_keyblock, options, &any);
+  if (!err && !any)
+    err = gpg_error (GPG_ERR_NOT_FOUND);
+  if (!err)
+    {
+      const void *src;
+      size_t datalen;
+
+      iobuf_flush_temp (iobuf);
+      src = iobuf_get_temp_buffer (iobuf);
+      datalen = iobuf_get_temp_length (iobuf);
+      if (!datalen)
+        err = gpg_error (GPG_ERR_NO_PUBKEY);
+      else if (!(*r_data = xtrymalloc (datalen)))
+        err = gpg_error_from_syserror ();
+      else
+        {
+          memcpy (*r_data, src, datalen);
+          *r_datalen = datalen;
+        }
+    }
+  iobuf_close (iobuf);
+  free_strlist (helplist);
+  if (err && *r_keyblock)
+    {
+      release_kbnode (*r_keyblock);
+      *r_keyblock = NULL;
+    }
+  return err;
+}
+
+
 int
 export_seckeys (ctrl_t ctrl, strlist_t users )
 {
index e560d4b..2f055ad 100644 (file)
@@ -111,13 +111,11 @@ static struct parse_options keyserver_opts[]=
     {NULL,0,NULL,NULL}
   };
 
-static int keyserver_work (ctrl_t ctrl, enum ks_action action,strlist_t list,
-                           KEYDB_SEARCH_DESC *desc,int count,
-                           unsigned char **fpr,size_t *fpr_len,
-                           struct keyserver_spec *keyserver);
 static gpg_error_t keyserver_get (ctrl_t ctrl,
                                   KEYDB_SEARCH_DESC *desc, int ndesc,
                                   struct keyserver_spec *keyserver);
+static gpg_error_t keyserver_put (ctrl_t ctrl, strlist_t keyspecs,
+                                  struct keyserver_spec *keyserver);
 
 
 /* Reasonable guess */
@@ -989,664 +987,6 @@ search_line_handler (void *opaque, char *line)
 }
 
 
-/* We sometimes want to use a different gpgkeys_xxx for a given
-   protocol (for example, ldaps is handled by gpgkeys_ldap).  Map
-   these here. */
-static const char *
-keyserver_typemap(const char *type)
-{
-  if(strcmp(type,"ldaps")==0)
-    return "ldap";
-  else if(strcmp(type,"hkps")==0)
-    return "hkp";
-  else
-    return type;
-}
-
-/* The PGP LDAP and the curl fetch-a-LDAP-object methodologies are
-   sufficiently different that we can't use curl to do LDAP. */
-static int
-direct_uri_map(const char *scheme,unsigned int is_direct)
-{
-  if(is_direct && strcmp(scheme,"ldap")==0)
-    return 1;
-
-  return 0;
-}
-
-#if GNUPG_MAJOR_VERSION == 2
-#define GPGKEYS_PREFIX "gpg2keys_"
-#else
-#define GPGKEYS_PREFIX "gpgkeys_"
-#endif
-#define GPGKEYS_CURL GPGKEYS_PREFIX "curl" EXEEXT
-#define GPGKEYS_PREFIX_LEN (strlen(GPGKEYS_CURL))
-#define KEYSERVER_ARGS_KEEP " -o \"%O\" \"%I\""
-#define KEYSERVER_ARGS_NOKEEP " -o \"%o\" \"%i\""
-
-static int
-keyserver_spawn (ctrl_t ctrl,
-                 enum ks_action action,strlist_t list,KEYDB_SEARCH_DESC *desc,
-                 int count,int *prog,unsigned char **fpr,size_t *fpr_len,
-                 struct keyserver_spec *keyserver)
-{
-  int ret=0,i,gotversion=0,outofband=0;
-  strlist_t temp;
-  unsigned int maxlen,buflen;
-  char *command,*end,*searchstr=NULL;
-  byte *line=NULL;
-  struct exec_info *spawn;
-  const char *scheme;
-  const char *libexecdir = gnupg_libexecdir ();
-
-  assert(keyserver);
-
-#ifdef EXEC_TEMPFILE_ONLY
-  opt.keyserver_options.options|=KEYSERVER_USE_TEMP_FILES;
-#endif
-
-  /* Build the filename for the helper to execute */
-  scheme=keyserver_typemap(keyserver->scheme);
-
-#ifdef DISABLE_KEYSERVER_PATH
-  /* Destroy any path we might have.  This is a little tricky,
-     portability-wise.  It's not correct to delete the PATH
-     environment variable, as that may fall back to a system built-in
-     PATH.  Similarly, it is not correct to set PATH to the null
-     string (PATH="") since this actually deletes the PATH environment
-     variable under MinGW.  The safest thing to do here is to force
-     PATH to be GNUPG_LIBEXECDIR.  All this is not that meaningful on
-     Unix-like systems (since we're going to give a full path to
-     gpgkeys_foo), but on W32 it prevents loading any DLLs from
-     directories in %PATH%.
-
-     After some more thinking about this we came to the conclusion
-     that it is better to load the helpers from the directory where
-     the program of this process lives.  Fortunately Windows provides
-     a way to retrieve this and our gnupg_libexecdir function has been
-     modified to return just this.  Setting the exec-path is not
-     anymore required.
-       set_exec_path(libexecdir);
- */
-#else
-  if(opt.exec_path_set)
-    {
-      /* If exec-path was set, and DISABLE_KEYSERVER_PATH is
-        undefined, then don't specify a full path to gpgkeys_foo, so
-        that the PATH can work. */
-      command=xmalloc(GPGKEYS_PREFIX_LEN+strlen(scheme)+3+strlen(EXEEXT)+1);
-      command[0]='\0';
-    }
-  else
-#endif
-    {
-      /* Specify a full path to gpgkeys_foo. */
-      command=xmalloc(strlen(libexecdir)+strlen(DIRSEP_S)+
-                     GPGKEYS_PREFIX_LEN+strlen(scheme)+3+strlen(EXEEXT)+1);
-      strcpy(command,libexecdir);
-      strcat(command,DIRSEP_S);
-    }
-
-  end=command+strlen(command);
-
-  /* Build a path for the keyserver helper.  If it is direct_uri
-     (i.e. an object fetch and not a keyserver), then add "_uri" to
-     the end to distinguish the keyserver helper from an object
-     fetcher that can speak that protocol (this is a problem for
-     LDAP). */
-
-  strcat(command,GPGKEYS_PREFIX);
-  strcat(command,scheme);
-
-  /* This "_uri" thing is in case we need to call a direct handler
-     instead of the keyserver handler.  This lets us use gpgkeys_curl
-     or gpgkeys_ldap_uri (we don't provide it, but a user might)
-     instead of gpgkeys_ldap to fetch things like
-     ldap://keyserver.pgp.com/o=PGP%20keys?pgpkey?sub?pgpkeyid=99242560 */
-
-  if(direct_uri_map(scheme,keyserver->flags.direct_uri))
-    strcat(command,"_uri");
-
-  strcat(command,EXEEXT);
-
-  /* Can we execute it?  If not, try curl as our catchall. */
-  if(path_access(command,X_OK)!=0)
-    strcpy(end,GPGKEYS_CURL);
-
-  if(opt.keyserver_options.options&KEYSERVER_USE_TEMP_FILES)
-    {
-      if(opt.keyserver_options.options&KEYSERVER_KEEP_TEMP_FILES)
-       {
-         command=xrealloc(command,strlen(command)+
-                           strlen(KEYSERVER_ARGS_KEEP)+1);
-         strcat(command,KEYSERVER_ARGS_KEEP);
-       }
-      else
-       {
-         command=xrealloc(command,strlen(command)+
-                           strlen(KEYSERVER_ARGS_NOKEEP)+1);
-         strcat(command,KEYSERVER_ARGS_NOKEEP);
-       }
-
-      ret=exec_write(&spawn,NULL,command,NULL,0,0);
-    }
-  else
-    ret=exec_write(&spawn,command,NULL,NULL,0,0);
-
-  xfree(command);
-
-  if(ret)
-    return ret;
-
-  fprintf(spawn->tochild,
-         "# This is a GnuPG %s keyserver communications file\n",VERSION);
-  fprintf(spawn->tochild,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
-  fprintf(spawn->tochild,"PROGRAM %s\n",VERSION);
-  fprintf(spawn->tochild,"SCHEME %s\n",keyserver->scheme);
-
-  if(keyserver->opaque)
-    fprintf(spawn->tochild,"OPAQUE %s\n",keyserver->opaque);
-  else
-    {
-      if(keyserver->auth)
-       fprintf(spawn->tochild,"AUTH %s\n",keyserver->auth);
-
-      if(keyserver->host)
-       fprintf(spawn->tochild,"HOST %s\n",keyserver->host);
-
-      if(keyserver->port)
-       fprintf(spawn->tochild,"PORT %s\n",keyserver->port);
-
-      if(keyserver->path)
-       fprintf(spawn->tochild,"PATH %s\n",keyserver->path);
-    }
-
-  /* Write global options */
-
-  for(temp=opt.keyserver_options.other;temp;temp=temp->next)
-    fprintf(spawn->tochild,"OPTION %s\n",temp->d);
-
-  /* Write per-keyserver options */
-
-  for(temp=keyserver->options;temp;temp=temp->next)
-    fprintf(spawn->tochild,"OPTION %s\n",temp->d);
-
-  switch(action)
-    {
-    case KS_GET:
-      {
-       fprintf(spawn->tochild,"COMMAND GET\n\n");
-
-       /* Which keys do we want? */
-
-       for(i=0;i<count;i++)
-         {
-           int quiet=0;
-
-           if(desc[i].mode==KEYDB_SEARCH_MODE_FPR20)
-             {
-               int f;
-
-               fprintf(spawn->tochild,"0x");
-
-               for(f=0;f<MAX_FINGERPRINT_LEN;f++)
-                 fprintf(spawn->tochild,"%02X",desc[i].u.fpr[f]);
-
-               fprintf(spawn->tochild,"\n");
-             }
-           else if(desc[i].mode==KEYDB_SEARCH_MODE_FPR16)
-             {
-               int f;
-
-               fprintf(spawn->tochild,"0x");
-
-               for(f=0;f<16;f++)
-                 fprintf(spawn->tochild,"%02X",desc[i].u.fpr[f]);
-
-               fprintf(spawn->tochild,"\n");
-             }
-           else if(desc[i].mode==KEYDB_SEARCH_MODE_LONG_KID)
-             fprintf(spawn->tochild,"0x%08lX%08lX\n",
-                     (ulong)desc[i].u.kid[0],
-                     (ulong)desc[i].u.kid[1]);
-           else if(desc[i].mode==KEYDB_SEARCH_MODE_SHORT_KID)
-             fprintf(spawn->tochild,"0x%08lX\n",
-                     (ulong)desc[i].u.kid[1]);
-           else if(desc[i].mode==KEYDB_SEARCH_MODE_EXACT)
-             {
-               fprintf(spawn->tochild,"0x0000000000000000\n");
-               quiet=1;
-             }
-           else if(desc[i].mode==KEYDB_SEARCH_MODE_NONE)
-             continue;
-           else
-             BUG();
-
-           if(!quiet)
-             {
-               if(keyserver->host)
-                 log_info(_("requesting key %s from %s server %s\n"),
-                          keystr_from_desc(&desc[i]),
-                          keyserver->scheme,keyserver->host);
-               else
-                 log_info(_("requesting key %s from %s\n"),
-                          keystr_from_desc(&desc[i]),keyserver->uri);
-             }
-         }
-
-       fprintf(spawn->tochild,"\n");
-
-       break;
-      }
-
-    case KS_GETNAME:
-      {
-       strlist_t key;
-
-       fprintf(spawn->tochild,"COMMAND GETNAME\n\n");
-
-       /* Which names do we want? */
-
-       for(key=list;key!=NULL;key=key->next)
-         fprintf(spawn->tochild,"%s\n",key->d);
-
-       fprintf(spawn->tochild,"\n");
-
-       if(keyserver->host)
-         log_info(_("searching for names from %s server %s\n"),
-                  keyserver->scheme,keyserver->host);
-       else
-         log_info(_("searching for names from %s\n"),keyserver->uri);
-
-       break;
-      }
-
-    case KS_SEND:
-      {
-       strlist_t key;
-
-       /* Note the extra \n here to send an empty keylist block */
-       fprintf(spawn->tochild,"COMMAND SEND\n\n\n");
-
-       for(key=list;key!=NULL;key=key->next)
-         {
-           armor_filter_context_t *afx;
-           IOBUF buffer = iobuf_temp ();
-           KBNODE block;
-
-           temp=NULL;
-           add_to_strlist(&temp,key->d);
-
-           afx = new_armor_context ();
-           afx->what = 1;
-           /* Tell the armor filter to use Unix-style \n line
-              endings, since we're going to fprintf this to a file
-              that (on Win32) is open in text mode.  The win32 stdio
-              will transform the \n to \r\n and we'll end up with the
-              proper line endings on win32.  This is a no-op on
-              Unix. */
-           afx->eol[0] = '\n';
-           push_armor_filter (afx, buffer);
-            release_armor_context (afx);
-
-           /* TODO: Remove Comment: lines from keys exported this
-              way? */
-
-           if(export_pubkeys_stream (ctrl, buffer,temp,&block,
-                                      opt.keyserver_options.export_options)==-1)
-             iobuf_close(buffer);
-           else
-             {
-               KBNODE node;
-
-               iobuf_flush_temp(buffer);
-
-               merge_keys_and_selfsig(block);
-
-               fprintf(spawn->tochild,"INFO %08lX%08lX BEGIN\n",
-                       (ulong)block->pkt->pkt.public_key->keyid[0],
-                       (ulong)block->pkt->pkt.public_key->keyid[1]);
-
-               for(node=block;node;node=node->next)
-                 {
-                   switch(node->pkt->pkttype)
-                     {
-                     default:
-                       continue;
-
-                     case PKT_PUBLIC_KEY:
-                     case PKT_PUBLIC_SUBKEY:
-                       {
-                         PKT_public_key *pk=node->pkt->pkt.public_key;
-
-                         keyid_from_pk(pk,NULL);
-
-                         fprintf(spawn->tochild,"%sb:%08lX%08lX:%u:%u:%u:%u:",
-                                 node->pkt->pkttype==PKT_PUBLIC_KEY?"pu":"su",
-                                 (ulong)pk->keyid[0],(ulong)pk->keyid[1],
-                                 pk->pubkey_algo,
-                                 nbits_from_pk(pk),
-                                 pk->timestamp,
-                                 pk->expiredate);
-
-                         if(pk->flags.revoked)
-                           fprintf(spawn->tochild,"r");
-                         if(pk->has_expired)
-                           fprintf(spawn->tochild,"e");
-
-                         fprintf(spawn->tochild,"\n");
-                       }
-                       break;
-
-                     case PKT_USER_ID:
-                       {
-                         PKT_user_id *uid=node->pkt->pkt.user_id;
-                         int r;
-
-                         if(uid->attrib_data)
-                           continue;
-
-                         fprintf(spawn->tochild,"uid:");
-
-                         /* Quote ':', '%', and any 8-bit
-                            characters */
-                         for(r=0;r<uid->len;r++)
-                           {
-                             if(uid->name[r]==':' || uid->name[r]=='%'
-                                || uid->name[r]&0x80)
-                               fprintf(spawn->tochild,"%%%02X",
-                                       (byte)uid->name[r]);
-                             else
-                               fprintf(spawn->tochild,"%c",uid->name[r]);
-                           }
-
-                         fprintf(spawn->tochild,":%u:%u:",
-                                 uid->created,uid->expiredate);
-
-                         if(uid->is_revoked)
-                           fprintf(spawn->tochild,"r");
-                         if(uid->is_expired)
-                           fprintf(spawn->tochild,"e");
-
-                         fprintf(spawn->tochild,"\n");
-                       }
-                       break;
-
-                       /* This bit is really for the benefit of
-                          people who store their keys in LDAP
-                          servers.  It makes it easy to do queries
-                          for things like "all keys signed by
-                          Isabella". */
-                     case PKT_SIGNATURE:
-                       {
-                         PKT_signature *sig=node->pkt->pkt.signature;
-
-                         if(!IS_UID_SIG(sig))
-                           continue;
-
-                         fprintf(spawn->tochild,"sig:%08lX%08lX:%X:%u:%u\n",
-                                 (ulong)sig->keyid[0],(ulong)sig->keyid[1],
-                                 sig->sig_class,sig->timestamp,
-                                 sig->expiredate);
-                       }
-                       break;
-                     }
-                 }
-
-               fprintf(spawn->tochild,"INFO %08lX%08lX END\n",
-                       (ulong)block->pkt->pkt.public_key->keyid[0],
-                       (ulong)block->pkt->pkt.public_key->keyid[1]);
-
-               fprintf(spawn->tochild,"KEY %08lX%08lX BEGIN\n",
-                       (ulong)block->pkt->pkt.public_key->keyid[0],
-                       (ulong)block->pkt->pkt.public_key->keyid[1]);
-               fwrite(iobuf_get_temp_buffer(buffer),
-                      iobuf_get_temp_length(buffer),1,spawn->tochild);
-               fprintf(spawn->tochild,"KEY %08lX%08lX END\n",
-                       (ulong)block->pkt->pkt.public_key->keyid[0],
-                       (ulong)block->pkt->pkt.public_key->keyid[1]);
-
-               iobuf_close(buffer);
-
-               if(keyserver->host)
-                 log_info(_("sending key %s to %s server %s\n"),
-                          keystr(block->pkt->pkt.public_key->keyid),
-                          keyserver->scheme,keyserver->host);
-               else
-                 log_info(_("sending key %s to %s\n"),
-                          keystr(block->pkt->pkt.public_key->keyid),
-                          keyserver->uri);
-
-               release_kbnode(block);
-             }
-
-           free_strlist(temp);
-         }
-
-       break;
-      }
-
-    case KS_SEARCH:
-      {
-       strlist_t key;
-
-       fprintf(spawn->tochild,"COMMAND SEARCH\n\n");
-
-       /* Which keys do we want?  Remember that the gpgkeys_ program
-           is going to lump these together into a search string. */
-
-       for(key=list;key!=NULL;key=key->next)
-         {
-           fprintf(spawn->tochild,"%s\n",key->d);
-           if(key!=list)
-             {
-               searchstr=xrealloc(searchstr,
-                                   strlen(searchstr)+strlen(key->d)+2);
-               strcat(searchstr," ");
-             }
-           else
-             {
-               searchstr=xmalloc(strlen(key->d)+1);
-               searchstr[0]='\0';
-             }
-
-           strcat(searchstr,key->d);
-         }
-
-       fprintf(spawn->tochild,"\n");
-
-       if(keyserver->host)
-         log_info(_("searching for \"%s\" from %s server %s\n"),
-                  searchstr,keyserver->scheme,keyserver->host);
-       else
-         log_info(_("searching for \"%s\" from %s\n"),
-                  searchstr,keyserver->uri);
-
-       break;
-      }
-
-    default:
-      log_fatal(_("no keyserver action!\n"));
-      break;
-    }
-
-  /* Done sending, so start reading. */
-  ret=exec_read(spawn);
-  if(ret)
-    goto fail;
-
-  /* Now handle the response */
-
-  for(;;)
-    {
-      int plen;
-      char *ptr;
-
-      maxlen=1024;
-      if(iobuf_read_line(spawn->fromchild,&line,&buflen,&maxlen)==0)
-       {
-         ret = gpg_error_from_syserror ();
-         goto fail; /* i.e. EOF */
-       }
-
-      ptr=line;
-
-      /* remove trailing whitespace */
-      plen=strlen(ptr);
-      while(plen>0 && ascii_isspace(ptr[plen-1]))
-       plen--;
-      plen[ptr]='\0';
-
-      if(*ptr=='\0')
-       break;
-
-      if(ascii_strncasecmp(ptr,"VERSION ",8)==0)
-       {
-         gotversion=1;
-
-         if(atoi(&ptr[8])!=KEYSERVER_PROTO_VERSION)
-           {
-             log_error(_("invalid keyserver protocol (us %d!=handler %d)\n"),
-                       KEYSERVER_PROTO_VERSION,atoi(&ptr[8]));
-             goto fail;
-           }
-       }
-      else if(ascii_strncasecmp(ptr,"PROGRAM ",8)==0)
-       {
-         if(ascii_strncasecmp(&ptr[8],VERSION,strlen(VERSION))!=0)
-           log_info(_("WARNING: keyserver handler from a different"
-                      " version of GnuPG (%s)\n"),&ptr[8]);
-       }
-      else if(ascii_strncasecmp(ptr,"OPTION OUTOFBAND",16)==0)
-       outofband=1; /* Currently the only OPTION */
-    }
-
-  if(!gotversion)
-    {
-      log_error(_("keyserver did not send VERSION\n"));
-      goto fail;
-    }
-
-  if(!outofband)
-    switch(action)
-      {
-      case KS_GET:
-      case KS_GETNAME:
-       {
-         void *stats_handle;
-
-         stats_handle=import_new_stats_handle();
-
-         /* Slurp up all the key data.  In the future, it might be
-            nice to look for KEY foo OUTOFBAND and FAILED indicators.
-            It's harmless to ignore them, but ignoring them does make
-            gpg complain about "no valid OpenPGP data found".  One
-            way to do this could be to continue parsing this
-            line-by-line and make a temp iobuf for each key. */
-
-          /* FIXME: Pass CTRL.  */
-         import_keys_stream (NULL, spawn->fromchild,stats_handle,fpr,fpr_len,
-                             opt.keyserver_options.import_options);
-
-         import_print_stats(stats_handle);
-         import_release_stats_handle(stats_handle);
-
-         break;
-       }
-
-       /* Nothing to do here */
-      case KS_SEND:
-       break;
-
-      case KS_SEARCH:
-       //keyserver_search_prompt (ctrl, spawn->fromchild,searchstr);
-       break;
-
-      default:
-       log_fatal(_("no keyserver action!\n"));
-       break;
-      }
-
- fail:
-  xfree(line);
-  xfree(searchstr);
-
-
-  *prog=exec_finish(spawn);
-
-  return ret;
-}
-
-
-
-
-static int
-keyserver_work (ctrl_t ctrl,
-                enum ks_action action,strlist_t list,KEYDB_SEARCH_DESC *desc,
-                int count,unsigned char **fpr,size_t *fpr_len,
-                struct keyserver_spec *keyserver)
-{
-  int rc=0,ret=0;
-
-  if (!keyserver)
-    {
-      log_error (_("no keyserver known (use option --keyserver)\n"));
-      return gpg_error (GPG_ERR_BAD_URI);
-    }
-
-
-  rc = keyserver_spawn (ctrl, action, list, desc, count,
-                        &ret, fpr, fpr_len, keyserver);
-  if (ret)
-    {
-      switch(ret)
-       {
-       case KEYSERVER_SCHEME_NOT_FOUND:
-         log_error(_("no handler for keyserver scheme `%s'\n"),
-                   keyserver->scheme);
-         break;
-
-       case KEYSERVER_NOT_SUPPORTED:
-         log_error(_("action `%s' not supported with keyserver "
-                     "scheme `%s'\n"),
-                   action==KS_GET?"get":action==KS_SEND?"send":
-                   action==KS_SEARCH?"search":"unknown",
-                   keyserver->scheme);
-         break;
-
-       case KEYSERVER_VERSION_ERROR:
-         log_error(_(GPGKEYS_PREFIX "%s does not support"
-                     " handler version %d\n"),
-                   keyserver_typemap(keyserver->scheme),
-                   KEYSERVER_PROTO_VERSION);
-         break;
-
-       case KEYSERVER_TIMEOUT:
-         log_error(_("keyserver timed out\n"));
-         break;
-
-       case KEYSERVER_INTERNAL_ERROR:
-       default:
-         log_error(_("keyserver internal error\n"));
-         break;
-       }
-
-      return G10ERR_KEYSERVER;
-    }
-
-  if (rc)
-    {
-      log_error (_("keyserver communications error: %s\n"),g10_errstr(rc));
-
-      return rc;
-    }
-
-  return 0;
-}
-
-
-
-
 
 int
 keyserver_export (ctrl_t ctrl, strlist_t users)
@@ -1674,7 +1014,7 @@ keyserver_export (ctrl_t ctrl, strlist_t users)
 
   if(sl)
     {
-      rc = keyserver_work (ctrl, KS_SEND,sl,NULL,0,NULL,NULL,opt.keyserver);
+      rc = keyserver_put (ctrl, sl, opt.keyserver);
       free_strlist(sl);
     }
 
@@ -2113,26 +1453,6 @@ keyserver_search (ctrl_t ctrl, strlist_t tokens)
 
 /* Called using:
 
-show_prompt:
-import:
-import_foo:
-refresh:
-    rc=keyserver_work (ctrl, KS_GET, NULL, desc, count,
-                       NULL, NULL, opt.keyserver);
-
-
-fetch:
-         rc = keyserver_work (ctrl, KS_GET, NULL, &desc, 1, NULL, NULL, spec);
-         if(rc)
-           log_info (_("WARNING: unable to fetch URI %s: %s\n"),
-                    sl->d,g10_errstr(rc));
-
-
-export:
-      rc = keyserver_work (ctrl, KS_SEND,sl,NULL,0,NULL,NULL,opt.keyserver);
-
-
-
 import_name:
   rc = keyserver_work (ctrl, KS_GETNAME, list, NULL,
                        0, fpr, fpr_len, keyserver);
@@ -2267,6 +1587,58 @@ keyserver_get (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc,
 }
 
 
+/* Send all keys specified by KEYSPECS to the KEYSERVERS.  */
+static gpg_error_t
+keyserver_put (ctrl_t ctrl, strlist_t keyspecs,
+               struct keyserver_spec *keyserver)
+
+{
+  gpg_error_t err;
+  strlist_t kspec;
+
+  if (!keyspecs)
+    return 0;  /* Return success if the list is empty.  */
+
+  if (!opt.keyserver)
+    {
+      log_error (_("no keyserver known (use option --keyserver)\n"));
+      return gpg_error (GPG_ERR_NO_KEYSERVER);
+    }
+
+  for (kspec = keyspecs; kspec; kspec = kspec->next)
+    {
+      void *data;
+      size_t datalen;
+      kbnode_t keyblock;
+
+      err = export_pubkey_buffer (ctrl, kspec->d,
+                                  opt.keyserver_options.export_options,
+                                  &keyblock, &data, &datalen);
+      if (err)
+        log_error (_("skipped \"%s\": %s\n"), kspec->d, gpg_strerror (err));
+      else
+        {
+          if (keyserver->host)
+            log_info (_("sending key %s to %s server %s\n"),
+                      keystr (keyblock->pkt->pkt.public_key->keyid),
+                      keyserver->scheme, keyserver->host);
+          else
+            log_info (_("sending key %s to %s\n"),
+                      keystr (keyblock->pkt->pkt.public_key->keyid),
+                      keyserver->uri);
+
+          err = gpg_dirmngr_ks_put (ctrl, data, datalen, keyblock);
+          release_kbnode (keyblock);
+          xfree (data);
+          if (err)
+            log_error (_("keyserver send failed: %s\n"), gpg_strerror (err));
+        }
+    }
+
+
+  return err;
+
+}
 
 
 
@@ -2439,8 +1811,9 @@ keyserver_import_name (ctrl_t ctrl, const char *name,
 
   append_to_strlist(&list,name);
 
-  rc = keyserver_work (ctrl, KS_GETNAME, list, NULL,
-                       0, fpr, fpr_len, keyserver);
+  rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED);  /* FIXME */
+       /* keyserver_work (ctrl, KS_GETNAME, list, NULL, */
+       /*                 0, fpr, fpr_len, keyserver); */
 
   free_strlist(list);
 
@@ -2513,8 +1886,9 @@ keyserver_import_ldap (ctrl_t ctrl,
 
   append_to_strlist(&list,name);
 
-  rc = keyserver_work (ctrl, KS_GETNAME, list, NULL,
-                       0, fpr, fpr_len, keyserver);
+  rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /*FIXME*/
+       /* keyserver_work (ctrl, KS_GETNAME, list, NULL, */
+       /*                 0, fpr, fpr_len, keyserver); */
 
   free_strlist(list);
 
index 2e76084..4278340 100644 (file)
@@ -287,6 +287,10 @@ int parse_export_options(char *str,unsigned int *options,int noisy);
 int export_pubkeys (ctrl_t ctrl, strlist_t users, unsigned int options );
 int export_pubkeys_stream (ctrl_t ctrl, iobuf_t out, strlist_t users,
                           kbnode_t *keyblock_out, unsigned int options );
+gpg_error_t export_pubkey_buffer (ctrl_t ctrl, const char *keyspec,
+                                  unsigned int options, 
+                                  kbnode_t *r_keyblock,
+                                  void **r_data, size_t *r_datalen);
 int export_seckeys (ctrl_t ctrl, strlist_t users);
 int export_secsubkeys (ctrl_t ctrl, strlist_t users);
 
index 14d5f62..cc42a64 100644 (file)
@@ -1,3 +1,9 @@
+2011-01-20  Werner Koch  <wk@g10code.com>
+
+       * gpgkeys_hkp.c (get_name): Remove test for KS_GETNAME.  It is
+       always true.
+       (search_key): Remove test for KS_GETNAME.  It is always false.
+
 2009-08-26  Werner Koch  <wk@g10code.com>
 
        * gpgkeys_hkp.c: Include util.h.
index dd21290..d43a61a 100644 (file)
@@ -340,7 +340,7 @@ get_name(const char *getkey)
      opt->path,
      appendable_path (opt->path,"/pks/lookup?op=get&options=mr&search="),
      searchkey_encoded,
-     opt->action == KS_GETNAME? "&exact=on":"",
+     "&exact=on",
      NULL);
   if(!request)
     {
@@ -429,7 +429,6 @@ search_key(const char *searchkey)
      appendable_path (opt->path, "/pks/lookup?op=index&options=mr&search="),
      hexprefix,
      searchkey_encoded,
-     opt->action == KS_GETNAME? "&exact=on":"",
      NULL);
   if(!request)
     {