gpg: Minor cleanup for key listing related code.
[gnupg.git] / g10 / keyserver.c
index cbf55c0..1b2e128 100644 (file)
@@ -1,12 +1,13 @@
 /* keyserver.c - generic keyserver code
- * Copyright (C) 2001, 2002, 2003, 2004, 2005,
- *               2006 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008,
+ *               2009, 2011, 2012 Free Software Foundation, Inc.
+ * Copyright (C) 2014 Werner Koch
  *
  * This file is part of GnuPG.
  *
  * GnuPG is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * GnuPG is distributed in the hope that it will be useful,
@@ -15,9 +16,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include "util.h"
 #include "dns-cert.h"
 #include "pka.h"
+#ifdef USE_DNS_SRV
+#include "srv.h"
+#endif
+#include "membuf.h"
+#include "call-dirmngr.h"
 
+#ifdef HAVE_W32_SYSTEM
+/* It seems Vista doesn't grok X_OK and so fails access() tests.
+   Previous versions interpreted X_OK as F_OK anyway, so we'll just
+   use F_OK directly. */
+#undef X_OK
+#define X_OK F_OK
+#endif /* HAVE_W32_SYSTEM */
 
 struct keyrec
 {
@@ -56,6 +67,24 @@ struct keyrec
   unsigned int lines;
 };
 
+/* Parameters for the search line handler.  */
+struct search_line_handler_parm_s
+{
+  ctrl_t ctrl;     /* The session control structure.  */
+  char *searchstr_disp;  /* Native encoded search string or NULL.  */
+  KEYDB_SEARCH_DESC *desc; /* Array with search descriptions.  */
+  int count;      /* Number of keys we are currently prepared to
+                     handle.  This is the size of the DESC array.  If
+                     it is too small, it will grow safely.  */
+  int validcount; /* Enable the "Key x-y of z" messages. */
+  int nkeys;      /* Number of processed records.  */
+  int any_lines;  /* At least one line has been processed.  */
+  unsigned int numlines;  /* Counter for displayed lines.  */
+  int eof_seen;   /* EOF encountered.  */
+  int not_found;  /* Set if no keys have been found.  */
+};
+
+
 enum ks_action {KS_UNKNOWN=0,KS_GET,KS_GETNAME,KS_SEND,KS_SEARCH};
 
 static struct parse_options keyserver_opts[]=
@@ -80,18 +109,22 @@ static struct parse_options keyserver_opts[]=
     {NULL,0,NULL,NULL}
   };
 
-static int keyserver_work(enum ks_action action,STRLIST 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,
+                                  unsigned char **r_fpr, size_t *r_fprlen);
+static gpg_error_t keyserver_put (ctrl_t ctrl, strlist_t keyspecs,
+                                  struct keyserver_spec *keyserver);
+
 
-/* Reasonable guess */
-#define DEFAULT_MAX_CERT_SIZE 16384
+/* Reasonable guess.  The commonly used test key simon.josefsson.org
+   is larger than 32k, thus we need at least this value. */
+#define DEFAULT_MAX_CERT_SIZE 65536
 
 static size_t max_cert_size=DEFAULT_MAX_CERT_SIZE;
 
 static void
-add_canonical_option(char *option,STRLIST *list)
+add_canonical_option(char *option,strlist_t *list)
 {
   char *arg=argsplit(option);
 
@@ -142,7 +175,7 @@ parse_keyserver_options(char *options)
 #ifdef EXEC_TEMPFILE_ONLY
       if(ascii_strncasecmp(tok,"use-temp-files",14)==0 ||
              ascii_strncasecmp(tok,"no-use-temp-files",17)==0)
-       log_info(_("WARNING: keyserver option `%s' is not used"
+       log_info(_("WARNING: keyserver option '%s' is not used"
                   " on this platform\n"),tok);
 #else
       if(ascii_strncasecmp(tok,"use-temp-files",14)==0)
@@ -227,9 +260,9 @@ keyserver_match(struct keyserver_spec *spec)
    parser any longer so it can be removed, or at least moved to
    keyserver/ksutil.c for limited use in gpgkeys_ldap or the like. */
 
-struct keyserver_spec *
-parse_keyserver_uri(const char *string,int require_scheme,
-                   const char *configname,unsigned int configlineno)
+keyserver_spec_t
+parse_keyserver_uri (const char *string,int require_scheme,
+                    const char *configname,unsigned int configlineno)
 {
   int assume_hkp=0;
   struct keyserver_spec *keyserver;
@@ -325,7 +358,15 @@ parse_keyserver_uri(const char *string,int require_scheme,
       keyserver->scheme=xstrdup("hkp");
     }
 
-  if(assume_hkp || (uri[0]=='/' && uri[1]=='/'))
+  if (uri[0]=='/' && uri[1]=='/' && uri[2] == '/')
+    {
+      /* Three slashes means network path with a default host name.
+         This is a hack because it does not crok all possible
+         combiantions.  We should better repalce all code bythe parser
+         from http.c.  */
+      keyserver->path = xstrdup (uri+2);
+    }
+  else if(assume_hkp || (uri[0]=='/' && uri[1]=='/'))
     {
       /* Two slashes means network path. */
 
@@ -460,19 +501,21 @@ print_keyrec(int number,struct keyrec *keyrec)
 
   iobuf_writebyte(keyrec->uidbuf,0);
   iobuf_flush_temp(keyrec->uidbuf);
-  printf("(%d)\t%s  ",number,iobuf_get_temp_buffer(keyrec->uidbuf));
+  es_printf ("(%d)\t%s  ", number, iobuf_get_temp_buffer (keyrec->uidbuf));
 
-  if(keyrec->size>0)
-    printf("%d bit ",keyrec->size);
+  if (keyrec->size>0)
+    es_printf ("%d bit ", keyrec->size);
 
   if(keyrec->type)
     {
-      const char *str = gcry_pk_algo_name (keyrec->type);
+      const char *str;
+
+      str = openpgp_pk_algo_name (keyrec->type);
 
-      if(str)
-       printf("%s ",str);
+      if (str && strcmp (str, "?"))
+       es_printf ("%s ",str);
       else
-       printf("unknown ");
+       es_printf ("unknown ");
     }
 
   switch(keyrec->desc.mode)
@@ -481,28 +524,34 @@ print_keyrec(int number,struct keyrec *keyrec)
         choice but to use it.  Do check --keyid-format to add a 0x if
         needed. */
     case KEYDB_SEARCH_MODE_SHORT_KID:
-      printf("key %s%08lX",
-            (opt.keyid_format==KF_0xSHORT
-             || opt.keyid_format==KF_0xLONG)?"0x":"",
-            (ulong)keyrec->desc.u.kid[1]);
+      es_printf ("key %s%08lX",
+                 (opt.keyid_format==KF_0xSHORT
+                  || opt.keyid_format==KF_0xLONG)?"0x":"",
+                 (ulong)keyrec->desc.u.kid[1]);
       break;
 
       /* However, if it gave us a long keyid, we can honor
-        --keyid-format */
+        --keyid-format via keystr(). */
     case KEYDB_SEARCH_MODE_LONG_KID:
-      printf("key %s",keystr(keyrec->desc.u.kid));
+      es_printf ("key %s",keystr(keyrec->desc.u.kid));
       break;
 
+      /* If it gave us a PGP 2.x fingerprint, not much we can do
+        beyond displaying it. */
     case KEYDB_SEARCH_MODE_FPR16:
-      printf("key ");
+      es_printf ("key ");
       for(i=0;i<16;i++)
-       printf("%02X",keyrec->desc.u.fpr[i]);
+       es_printf ("%02X",keyrec->desc.u.fpr[i]);
       break;
 
+      /* If we get a modern fingerprint, we have the most
+        flexibility. */
     case KEYDB_SEARCH_MODE_FPR20:
-      printf("key ");
-      for(i=0;i<20;i++)
-       printf("%02X",keyrec->desc.u.fpr[i]);
+      {
+       u32 kid[2];
+       keyid_from_fingerprint(keyrec->desc.u.fpr,20,kid);
+       es_printf("key %s",keystr(kid));
+      }
       break;
 
     default:
@@ -512,24 +561,24 @@ print_keyrec(int number,struct keyrec *keyrec)
 
   if(keyrec->createtime>0)
     {
-      printf(", ");
-      printf(_("created: %s"),strtimestamp(keyrec->createtime));
+      es_printf (", ");
+      es_printf (_("created: %s"), strtimestamp(keyrec->createtime));
     }
 
   if(keyrec->expiretime>0)
     {
-      printf(", ");
-      printf(_("expires: %s"),strtimestamp(keyrec->expiretime));
+      es_printf (", ");
+      es_printf (_("expires: %s"), strtimestamp(keyrec->expiretime));
     }
 
-  if(keyrec->flags&1)
-    printf(" (%s)",_("revoked"));
+  if (keyrec->flags&1)
+    es_printf (" (%s)", _("revoked"));
   if(keyrec->flags&2)
-    printf(" (%s)",_("disabled"));
+    es_printf (" (%s)", _("disabled"));
   if(keyrec->flags&4)
-    printf(" (%s)",_("expired"));
+    es_printf (" (%s)", _("expired"));
 
-  printf("\n");
+  es_printf ("\n");
 }
 
 /* Returns a keyrec (which must be freed) once a key is complete, and
@@ -538,6 +587,8 @@ print_keyrec(int number,struct keyrec *keyrec)
 static struct keyrec *
 parse_keyrec(char *keystring)
 {
+  /* FIXME: Remove the static and put the data into the parms we use
+     for the caller anyway.  */
   static struct keyrec *work=NULL;
   struct keyrec *ret=NULL;
   char *record;
@@ -566,12 +617,7 @@ parse_keyrec(char *keystring)
       work->uidbuf=iobuf_temp();
     }
 
-  /* Remove trailing whitespace */
-  for(i=strlen(keystring);i>0;i--)
-    if(ascii_isspace(keystring[i-1]))
-      keystring[i-1]='\0';
-    else
-      break;
+  trim_trailing_ws (keystring, strlen (keystring));
 
   if((record=strsep(&keystring,":"))==NULL)
     return ret;
@@ -579,6 +625,7 @@ parse_keyrec(char *keystring)
   if(ascii_strcasecmp("pub",record)==0)
     {
       char *tok;
+      gpg_error_t err;
 
       if(work->desc.mode)
        {
@@ -590,11 +637,11 @@ parse_keyrec(char *keystring)
       if((tok=strsep(&keystring,":"))==NULL)
        return ret;
 
-      classify_user_id(tok,&work->desc);
-      if(work->desc.mode!=KEYDB_SEARCH_MODE_SHORT_KID
-        && work->desc.mode!=KEYDB_SEARCH_MODE_LONG_KID
-        && work->desc.mode!=KEYDB_SEARCH_MODE_FPR16
-        && work->desc.mode!=KEYDB_SEARCH_MODE_FPR20)
+      err = classify_user_id (tok, &work->desc, 1);
+      if (err || (work->desc.mode    != KEYDB_SEARCH_MODE_SHORT_KID
+                  && work->desc.mode != KEYDB_SEARCH_MODE_LONG_KID
+                  && work->desc.mode != KEYDB_SEARCH_MODE_FPR16
+                  && work->desc.mode != KEYDB_SEARCH_MODE_FPR20))
        {
          work->desc.mode=KEYDB_SEARCH_MODE_NONE;
          return ret;
@@ -646,7 +693,7 @@ parse_keyrec(char *keystring)
          case 'R':
            work->flags|=1;
            break;
-           
+
          case 'd':
          case 'D':
            work->flags|=2;
@@ -678,9 +725,9 @@ parse_keyrec(char *keystring)
       while(*tok)
        if(tok[0]=='%' && tok[1] && tok[2])
          {
-           if((userid[i]=hextobyte(&tok[1]))==-1)
-             userid[i]='?';
+            int c;
 
+           userid[i] = (c=hextobyte(&tok[1])) == -1 ? '?' : c;
            i++;
            tok+=3;
          }
@@ -709,879 +756,274 @@ parse_keyrec(char *keystring)
   return ret;
 }
 
-/* TODO: do this as a list sent to keyserver_work rather than calling
-   it once for each key to get the correct counts after the import
-   (cosmetics, really) and to better take advantage of the keyservers
-   that can do multiple fetches in one go (LDAP). */
-static int
-show_prompt(KEYDB_SEARCH_DESC *desc,int numdesc,int count,const char *search)
+/* Show a prompt and allow the user to select keys for retrieval.  */
+static gpg_error_t
+show_prompt (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int numdesc,
+             int count, const char *search)
 {
-  char *answer;
+  gpg_error_t err;
+  char *answer = NULL;
 
-  if(count && opt.command_fd==-1)
-    {
-      static int from=1;
-      tty_printf("Keys %d-%d of %d for \"%s\".  ",from,numdesc,count,search);
-      from=numdesc+1;
-    }
+  es_fflush (es_stdout);
 
-  answer=cpr_get_no_help("keysearch.prompt",
-                        _("Enter number(s), N)ext, or Q)uit > "));
-  /* control-d */
-  if(answer[0]=='\x04')
+  if (count && opt.command_fd == -1)
     {
-      printf("Q\n");
-      answer[0]='q';
+      static int from = 1;
+      tty_printf ("Keys %d-%d of %d for \"%s\".  ",
+                  from, numdesc, count, search);
+      from = numdesc + 1;
     }
 
-  if(answer[0]=='q' || answer[0]=='Q')
-    {
-      xfree(answer);
-      return 1;
-    }
-  else if(atoi(answer)>=1 && atoi(answer)<=numdesc)
+ again:
+  err = 0;
+  xfree (answer);
+  answer = cpr_get_no_help ("keysearch.prompt",
+                            _("Enter number(s), N)ext, or Q)uit > "));
+  /* control-d */
+  if (answer[0]=='\x04')
     {
-      char *split=answer,*num;
-
-      while((num=strsep(&split," ,"))!=NULL)
-       if(atoi(num)>=1 && atoi(num)<=numdesc)
-         keyserver_work(KS_GET,NULL,&desc[atoi(num)-1],1,
-                        NULL,NULL,opt.keyserver);
-
-      xfree(answer);
-      return 1;
+      tty_printf ("Q\n");
+      answer[0] = 'q';
     }
 
-  return 0;
-}
-
-/* Count and searchstr are just for cosmetics.  If the count is too
-   small, it will grow safely.  If negative it disables the "Key x-y
-   of z" messages.  searchstr should be UTF-8 (rather than native). */
-static void
-keyserver_search_prompt(IOBUF buffer,const char *searchstr)
-{
-  int i=0,validcount=0,started=0,header=0,count=1;
-  unsigned int maxlen,buflen,numlines=0;
-  KEYDB_SEARCH_DESC *desc;
-  byte *line=NULL;
-  char *localstr=NULL;
-
-  if(searchstr)
-    localstr=utf8_to_native(searchstr,strlen(searchstr),0);
-
-  desc=xmalloc(count*sizeof(KEYDB_SEARCH_DESC));
-
-  for(;;)
+  if (answer[0]=='q' || answer[0]=='Q')
+    err = gpg_error (GPG_ERR_CANCELED);
+  else if (atoi (answer) >= 1 && atoi (answer) <= numdesc)
     {
-      struct keyrec *keyrec;
-      int rl;
+      char *split = answer;
+      char *num;
+      int numarray[50];
+      int numidx = 0;
+      int idx;
+
+      while ((num = strsep (&split, " ,")))
+       if (atoi (num) >= 1 && atoi (num) <= numdesc)
+          {
+            if (numidx >= DIM (numarray))
+              {
+                tty_printf ("Too many keys selected\n");
+                goto again;
+              }
+            numarray[numidx++] = atoi (num);
+          }
+
+      if (!numidx)
+        goto again;
 
-      maxlen=1024;
-      rl=iobuf_read_line(buffer,&line,&buflen,&maxlen);
-
-      if(opt.with_colons)
-       {
-         if(!header && ascii_strncasecmp("SEARCH ",line,7)==0
-            && ascii_strncasecmp(" BEGIN",&line[strlen(line)-7],6)==0)
-           {
-             header=1;
-             continue;
-           }
-         else if(ascii_strncasecmp("SEARCH ",line,7)==0
-                 && ascii_strncasecmp(" END",&line[strlen(line)-5],4)==0)
-           continue;
-
-         printf("%s",line);
-       }
-
-      /* Look for an info: line.  The only current info: values
-        defined are the version and key count. */
-      if(!started && rl>0 && ascii_strncasecmp("info:",line,5)==0)
-       {
-         char *tok,*str=&line[5];
-
-         if((tok=strsep(&str,":"))!=NULL)
-           {
-             int version;
-
-             if(sscanf(tok,"%d",&version)!=1)
-               version=1;
-
-             if(version!=1)
-               {
-                 log_error(_("invalid keyserver protocol "
-                             "(us %d!=handler %d)\n"),1,version);
-                 break;
-               }
-           }
-
-         if((tok=strsep(&str,":"))!=NULL && sscanf(tok,"%d",&count)==1)
-           {
-             if(count==0)
-               goto notfound;
-             else if(count<0)
-               count=10;
-             else
-               validcount=1;
-
-             desc=xrealloc(desc,count*sizeof(KEYDB_SEARCH_DESC));
-           }
-
-         started=1;
-         continue;
-       }
-
-      if(rl==0)
-       {
-         keyrec=parse_keyrec(NULL);
-
-         if(keyrec==NULL)
-           {
-             if(i==0)
-               {
-                 count=0;
-                 break;
-               }
-
-             if(i!=count)
-               validcount=0;
-
-             for(;;)
-               {
-                 if(show_prompt(desc,i,validcount?count:0,localstr))
-                   break;
-                 validcount=0;
-               }
-
-             break;
-           }
-       }
-      else
-       keyrec=parse_keyrec(line);
-
-      if(i==count)
-       {
-         /* keyserver helper sent more keys than they claimed in the
-            info: line. */
-         count+=10;
-         desc=xrealloc(desc,count*sizeof(KEYDB_SEARCH_DESC));
-         validcount=0;
-       }
-
-      if(keyrec)
-       {
-         desc[i]=keyrec->desc;
-
-         if(!opt.with_colons)
-           {
-             /* screen_lines - 1 for the prompt. */
-             if(numlines+keyrec->lines>opt.screen_lines-1)
-               {
-                 if(show_prompt(desc,i,validcount?count:0,localstr))
-                   break;
-                 else
-                   numlines=0;
-               }
-
-             print_keyrec(i+1,keyrec);
-           }
-
-         numlines+=keyrec->lines;
-         iobuf_close(keyrec->uidbuf);
-         xfree(keyrec);
-
-         started=1;
-         i++;
-       }
-    }
-
- notfound:
-  /* Leave this commented out or now, and perhaps for a very long
-     time.  All HKPish servers return HTML error messages for
-     no-key-found. */
-  /* 
-     if(!started)
-     log_info(_("keyserver does not support searching\n"));
-     else
-  */
-  if(count==0)
-    {
-      if(localstr)
-       log_info(_("key \"%s\" not found on keyserver\n"),localstr);
-      else
-       log_info(_("key not found on keyserver\n"));
+      {
+        KEYDB_SEARCH_DESC *selarray;
+
+        selarray = xtrymalloc (numidx * sizeof *selarray);
+        if (!selarray)
+          {
+            err = gpg_error_from_syserror ();
+            goto leave;
+          }
+        for (idx = 0; idx < numidx; idx++)
+          selarray[idx] = desc[numarray[idx]-1];
+        err = keyserver_get (ctrl, selarray, numidx, NULL, NULL, NULL);
+        xfree (selarray);
+      }
     }
 
-  xfree(localstr);
-  xfree(desc);
-  xfree(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
-    return type;
+ leave:
+  xfree (answer);
+  return err;
 }
 
-/* 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(enum ks_action action,STRLIST list,KEYDB_SEARCH_DESC *desc,
-               int count,int *prog,unsigned char **fpr,size_t *fpr_len,
-               struct keyserver_spec *keyserver)
+/* This is a callback used by call-dirmngr.c to process the result of
+   KS_SEARCH command.  If SPECIAL is 0, LINE is the actual data line
+   received with all escaping removed and guaranteed to be exactly one
+   line with stripped LF; an EOF is indicated by LINE passed as NULL.
+   If special is 1, the line contains the source of the information
+   (usually an URL).  LINE may be modified after return.  */
+static gpg_error_t
+search_line_handler (void *opaque, int special, char *line)
 {
-  int ret=0,i,gotversion=0,outofband=0;
-  STRLIST temp;
-  unsigned int maxlen,buflen;
-  char *command,*end,*searchstr=NULL;
-  byte *line=NULL;
-  struct exec_info *spawn;
-  const char *scheme;
-  const char *libexecdir = get_libexecdir ();
+  struct search_line_handler_parm_s *parm = opaque;
+  gpg_error_t err = 0;
+  struct keyrec *keyrec;
 
-  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 get_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 (special == 1)
     {
-      /* 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';
+      log_info ("data source: %s\n", line);
+      return 0;
     }
-  else
-#endif
+  else if (special)
     {
-      /* 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);
+      log_debug ("unknown value %d for special search callback", special);
+      return 0;
     }
 
-  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 (parm->eof_seen && line)
     {
-      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);
+      log_debug ("ooops: unexpected data after EOF\n");
+      line = NULL;
     }
-  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
+  /* Print the received line.  */
+  if (opt.with_colons && line)
     {
-      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);
+      es_printf ("%s\n", line);
     }
 
-  /* 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)
+  /* Look for an info: line.  The only current info: values defined
+     are the version and key count. */
+  if (line && !parm->any_lines && !ascii_strncasecmp ("info:", line, 5))
     {
-    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 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 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);
-
-           memset(&afx,0,sizeof(afx));
-           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';
-           iobuf_push_filter(buffer,armor_filter,&afx);
-
-           /* TODO: Remove Comment: lines from keys exported this
-              way? */
-
-           if(export_pubkeys_stream(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->is_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 %s BEGIN\n",key->d);
-               fwrite(iobuf_get_temp_buffer(buffer),
-                      iobuf_get_temp_length(buffer),1,spawn->tochild);
-               fprintf(spawn->tochild,"KEY %s END\n",key->d);
-
-               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 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;
-      }
+      char *str = line + 5;
+      char *tok;
 
-    default:
-      log_fatal(_("no keyserver action!\n"));
-      break;
+      if ((tok = strsep (&str, ":")))
+        {
+          int version;
+
+          if (sscanf (tok, "%d", &version) !=1 )
+            version = 1;
+
+          if (version !=1 )
+            {
+              log_error (_("invalid keyserver protocol "
+                           "(us %d!=handler %d)\n"), 1, version);
+              return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL);
+            }
+        }
+
+      if ((tok = strsep (&str, ":"))
+          && sscanf (tok, "%d", &parm->count) == 1)
+        {
+          if (!parm->count)
+            parm->not_found = 1;/* Server indicated that no items follow.  */
+          else if (parm->count < 0)
+            parm->count = 10;   /* Bad value - assume something reasonable.  */
+          else
+            parm->validcount = 1; /* COUNT seems to be okay.  */
+        }
+
+      parm->any_lines = 1;
+      return 0; /* Line processing finished.  */
     }
 
-  /* Done sending, so start reading. */
-  ret=exec_read(spawn);
-  if(ret)
-    goto fail;
-
-  /* Now handle the response */
-
-  for(;;)
+ again:
+  if (line)
+    keyrec = parse_keyrec (line);
+  else
     {
-      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 */
+      /* Received EOF - flush data */
+      parm->eof_seen = 1;
+      keyrec = parse_keyrec (NULL);
+      if (!keyrec)
+        {
+          if (!parm->nkeys)
+            parm->not_found = 1;  /* No keys at all.  */
+          else
+            {
+              if (parm->nkeys != parm->count)
+                parm->validcount = 0;
+
+              if (!(opt.with_colons && opt.batch))
+                {
+                  err = show_prompt (parm->ctrl, parm->desc, parm->nkeys,
+                                     parm->validcount? parm->count : 0,
+                                     parm->searchstr_disp);
+                  return err;
+                }
+            }
+        }
     }
 
-  if(!gotversion)
+  /* Save the key in the key array.  */
+  if (keyrec)
     {
-      log_error(_("keyserver did not send VERSION\n"));
-      goto fail;
+      /* Allocate or enlarge the key array if needed.  */
+      if (!parm->desc)
+        {
+          if (parm->count < 1)
+            {
+              parm->count = 10;
+              parm->validcount = 0;
+            }
+          parm->desc = xtrymalloc (parm->count * sizeof *parm->desc);
+          if (!parm->desc)
+            {
+              err = gpg_error_from_syserror ();
+              iobuf_close (keyrec->uidbuf);
+              xfree (keyrec);
+              return err;
+            }
+        }
+      else if (parm->nkeys == parm->count)
+        {
+          /* Keyserver sent more keys than claimed in the info: line. */
+          KEYDB_SEARCH_DESC *tmp;
+          int newcount = parm->count + 10;
+
+          tmp = xtryrealloc (parm->desc, newcount * sizeof *parm->desc);
+          if (!tmp)
+            {
+              err = gpg_error_from_syserror ();
+              iobuf_close (keyrec->uidbuf);
+              xfree (keyrec);
+              return err;
+            }
+          parm->count = newcount;
+          parm->desc = tmp;
+          parm->validcount = 0;
+        }
+
+      parm->desc[parm->nkeys] = keyrec->desc;
+
+      if (!opt.with_colons)
+        {
+          /* SCREEN_LINES - 1 for the prompt. */
+          if (parm->numlines + keyrec->lines > opt.screen_lines - 1)
+            {
+              err = show_prompt (parm->ctrl, parm->desc, parm->nkeys,
+                                 parm->validcount ? parm->count:0,
+                                 parm->searchstr_disp);
+              if (err)
+                return err;
+              parm->numlines = 0;
+            }
+
+          print_keyrec (parm->nkeys+1, keyrec);
+        }
+
+      parm->numlines += keyrec->lines;
+      iobuf_close (keyrec->uidbuf);
+      xfree (keyrec);
+
+      parm->any_lines = 1;
+      parm->nkeys++;
+
+      /* If we are here due to a flush after the EOF, run again for
+         the last prompt.  Fixme: Make this code better readable. */
+      if (parm->eof_seen)
+        goto again;
     }
 
-  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. */
-
-         import_keys_stream(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(spawn->fromchild,searchstr);
-       break;
-
-      default:
-       log_fatal(_("no keyserver action!\n"));
-       break;
-      }
-
- fail:
-  xfree(line);
-  xfree(searchstr);
-
-
-  *prog=exec_finish(spawn);
-
-  return ret;
+  return 0;
 }
 
-static int 
-keyserver_work(enum ks_action action,STRLIST 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 G10ERR_BAD_URI;
-    }
-
-#ifdef DISABLE_KEYSERVER_HELPERS
-
-  log_error(_("external keyserver calls are not supported in this build\n"));
-  return G10ERR_KEYSERVER;
-
-#else
-  /* Spawn a handler */
 
-  rc=keyserver_spawn(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;
-#endif /* ! DISABLE_KEYSERVER_HELPERS*/
-}
-
-int 
-keyserver_export(STRLIST users)
+int
+keyserver_export (ctrl_t ctrl, strlist_t users)
 {
-  STRLIST sl=NULL;
+  gpg_error_t err;
+  strlist_t sl=NULL;
   KEYDB_SEARCH_DESC desc;
   int rc=0;
 
   /* Weed out descriptors that we don't support sending */
   for(;users;users=users->next)
     {
-      classify_user_id (users->d, &desc);
-      if(desc.mode!=KEYDB_SEARCH_MODE_SHORT_KID &&
-        desc.mode!=KEYDB_SEARCH_MODE_LONG_KID &&
-        desc.mode!=KEYDB_SEARCH_MODE_FPR16 &&
-        desc.mode!=KEYDB_SEARCH_MODE_FPR20)
+      err = classify_user_id (users->d, &desc, 1);
+      if (err || (desc.mode    != KEYDB_SEARCH_MODE_SHORT_KID
+                  && desc.mode != KEYDB_SEARCH_MODE_LONG_KID
+                  && desc.mode != KEYDB_SEARCH_MODE_FPR16
+                  && desc.mode != KEYDB_SEARCH_MODE_FPR20))
        {
          log_error(_("\"%s\" not a key ID: skipping\n"),users->d);
          continue;
@@ -1592,17 +1034,98 @@ keyserver_export(STRLIST users)
 
   if(sl)
     {
-      rc=keyserver_work(KS_SEND,sl,NULL,0,NULL,NULL,opt.keyserver);
+      rc = keyserver_put (ctrl, sl, opt.keyserver);
       free_strlist(sl);
     }
 
   return rc;
 }
 
-int 
-keyserver_import(STRLIST users)
+
+/* Structure to convey the arg to keyserver_retrieval_screener.  */
+struct ks_retrieval_screener_arg_s
 {
   KEYDB_SEARCH_DESC *desc;
+  int ndesc;
+};
+
+
+/* Check whether a key matches the search description.  The function
+   returns 0 if the key shall be imported.  */
+static gpg_error_t
+keyserver_retrieval_screener (kbnode_t keyblock, void *opaque)
+{
+  struct ks_retrieval_screener_arg_s *arg = opaque;
+  KEYDB_SEARCH_DESC *desc = arg->desc;
+  int ndesc = arg->ndesc;
+  kbnode_t node;
+  PKT_public_key *pk;
+  int n;
+  u32 keyid[2];
+  byte fpr[MAX_FINGERPRINT_LEN];
+  size_t fpr_len = 0;
+
+  /* Secret keys are not expected from a keyserver.  We do not
+     care about secret subkeys because the import code takes care
+     of skipping them.  Not allowing an import of a public key
+     with a secret subkey would make it too easy to inhibit the
+     downloading of a public key.  Recall that keyservers do only
+     limited checks.  */
+  node = find_kbnode (keyblock, PKT_SECRET_KEY);
+  if (node)
+    return gpg_error (GPG_ERR_GENERAL);   /* Do not import. */
+
+  if (!ndesc)
+    return 0; /* Okay if no description given.  */
+
+  /* Loop over all key packets.  */
+  for (node = keyblock; node; node = node->next)
+    {
+      if (node->pkt->pkttype != PKT_PUBLIC_KEY
+          && node->pkt->pkttype != PKT_PUBLIC_SUBKEY)
+        continue;
+
+      pk = node->pkt->pkt.public_key;
+      fingerprint_from_pk (pk, fpr, &fpr_len);
+      keyid_from_pk (pk, keyid);
+
+      /* Compare requested and returned fingerprints if available. */
+      for (n = 0; n < ndesc; n++)
+        {
+          if (desc[n].mode == KEYDB_SEARCH_MODE_FPR20)
+            {
+              if (fpr_len == 20 && !memcmp (fpr, desc[n].u.fpr, 20))
+                return 0;
+            }
+          else if (desc[n].mode == KEYDB_SEARCH_MODE_FPR16)
+            {
+              if (fpr_len == 16 && !memcmp (fpr, desc[n].u.fpr, 16))
+                return 0;
+            }
+          else if (desc[n].mode == KEYDB_SEARCH_MODE_LONG_KID)
+            {
+              if (keyid[0] == desc[n].u.kid[0] && keyid[1] == desc[n].u.kid[1])
+                return 0;
+            }
+          else if (desc[n].mode == KEYDB_SEARCH_MODE_SHORT_KID)
+            {
+              if (keyid[1] == desc[n].u.kid[1])
+                return 0;
+            }
+          else /* No keyid or fingerprint - can't check.  */
+            return 0; /* allow import.  */
+        }
+    }
+
+  return gpg_error (GPG_ERR_GENERAL);
+}
+
+
+int
+keyserver_import (ctrl_t ctrl, strlist_t users)
+{
+  gpg_error_t err;
+  KEYDB_SEARCH_DESC *desc;
   int num=100,count=0;
   int rc=0;
 
@@ -1611,13 +1134,13 @@ keyserver_import(STRLIST users)
 
   for(;users;users=users->next)
     {
-      classify_user_id (users->d, &desc[count]);
-      if(desc[count].mode!=KEYDB_SEARCH_MODE_SHORT_KID &&
-        desc[count].mode!=KEYDB_SEARCH_MODE_LONG_KID &&
-        desc[count].mode!=KEYDB_SEARCH_MODE_FPR16 &&
-        desc[count].mode!=KEYDB_SEARCH_MODE_FPR20)
+      err = classify_user_id (users->d, &desc[count], 1);
+      if (err || (desc[count].mode    != KEYDB_SEARCH_MODE_SHORT_KID
+                  && desc[count].mode != KEYDB_SEARCH_MODE_LONG_KID
+                  && desc[count].mode != KEYDB_SEARCH_MODE_FPR16
+                  && desc[count].mode != KEYDB_SEARCH_MODE_FPR20))
        {
-         log_error(_("\"%s\" not a key ID: skipping\n"),users->d);
+         log_error (_("\"%s\" not a key ID: skipping\n"), users->d);
          continue;
        }
 
@@ -1630,16 +1153,34 @@ keyserver_import(STRLIST users)
     }
 
   if(count>0)
-    rc=keyserver_work(KS_GET,NULL,desc,count,NULL,NULL,opt.keyserver);
+    rc=keyserver_get (ctrl, desc, count, NULL, NULL, NULL);
 
   xfree(desc);
 
   return rc;
 }
 
+
+/* Import all keys that exactly match NAME */
 int
-keyserver_import_fprint(const byte *fprint,size_t fprint_len,
-                       struct keyserver_spec *keyserver)
+keyserver_import_name (ctrl_t ctrl, const char *name,
+                       unsigned char **fpr, size_t *fprlen,
+                       struct keyserver_spec *keyserver)
+{
+  KEYDB_SEARCH_DESC desc;
+
+  memset (&desc, 0, sizeof desc);
+
+  desc.mode = KEYDB_SEARCH_MODE_EXACT;
+  desc.u.name = name;
+
+  return keyserver_get (ctrl, &desc, 1, keyserver, fpr, fprlen);
+}
+
+
+int
+keyserver_import_fprint (ctrl_t ctrl, const byte *fprint,size_t fprint_len,
+                        struct keyserver_spec *keyserver)
 {
   KEYDB_SEARCH_DESC desc;
 
@@ -1656,11 +1197,12 @@ keyserver_import_fprint(const byte *fprint,size_t fprint_len,
 
   /* TODO: Warn here if the fingerprint we got doesn't match the one
      we asked for? */
-  return keyserver_work(KS_GET,NULL,&desc,1,NULL,NULL,keyserver);
+  return keyserver_get (ctrl, &desc, 1, keyserver, NULL, NULL);
 }
 
-int 
-keyserver_import_keyid(u32 *keyid,struct keyserver_spec *keyserver)
+int
+keyserver_import_keyid (ctrl_t ctrl,
+                        u32 *keyid,struct keyserver_spec *keyserver)
 {
   KEYDB_SEARCH_DESC desc;
 
@@ -1670,24 +1212,24 @@ keyserver_import_keyid(u32 *keyid,struct keyserver_spec *keyserver)
   desc.u.kid[0]=keyid[0];
   desc.u.kid[1]=keyid[1];
 
-  return keyserver_work(KS_GET,NULL,&desc,1,NULL,NULL,keyserver);
+  return keyserver_get (ctrl, &desc,1, keyserver, NULL, NULL);
 }
 
 /* code mostly stolen from do_export_stream */
-static int 
-keyidlist(STRLIST users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3)
+static int
+keyidlist(strlist_t users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3)
 {
   int rc=0,ndesc,num=100;
   KBNODE keyblock=NULL,node;
   KEYDB_HANDLE kdbhd;
   KEYDB_SEARCH_DESC *desc;
-  STRLIST sl;
+  strlist_t sl;
 
   *count=0;
 
   *klist=xmalloc(sizeof(KEYDB_SEARCH_DESC)*num);
 
-  kdbhd=keydb_new(0);
+  kdbhd=keydb_new ();
 
   if(!users)
     {
@@ -1697,23 +1239,24 @@ keyidlist(STRLIST users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3)
     }
   else
     {
-      for (ndesc=0, sl=users; sl; sl = sl->next, ndesc++) 
+      for (ndesc=0, sl=users; sl; sl = sl->next, ndesc++)
        ;
       desc = xmalloc ( ndesc * sizeof *desc);
-        
+
       for (ndesc=0, sl=users; sl; sl = sl->next)
        {
-         if(classify_user_id (sl->d, desc+ndesc))
+          gpg_error_t err;
+         if (!(err = classify_user_id (sl->d, desc+ndesc, 1)))
            ndesc++;
          else
            log_error (_("key \"%s\" not found: %s\n"),
-                      sl->d, g10_errstr (G10ERR_INV_USER_ID));
+                      sl->d, gpg_strerror (err));
        }
     }
 
-  while (!(rc = keydb_search (kdbhd, desc, ndesc)))
+  while (!(rc = keydb_search (kdbhd, desc, ndesc, NULL)))
     {
-      if (!users) 
+      if (!users)
        desc[0].mode = KEYDB_SEARCH_MODE_NEXT;
 
       /* read the keyblock */
@@ -1814,9 +1357,9 @@ keyidlist(STRLIST users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3)
        }
     }
 
-  if(rc==-1)
-    rc=0;
-  
+  if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND)
+    rc = 0;
+
  leave:
   if(rc)
     xfree(*klist);
@@ -1831,7 +1374,7 @@ keyidlist(STRLIST users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3)
    usernames to refresh only part of the keyring. */
 
 int
-keyserver_refresh(STRLIST users)
+keyserver_refresh (ctrl_t ctrl, strlist_t users)
 {
   int rc,count,numdesc,fakev3=0;
   KEYDB_SEARCH_DESC *desc;
@@ -1873,8 +1416,7 @@ keyserver_refresh(STRLIST users)
              /* We use the keyserver structure we parsed out before.
                 Note that a preferred keyserver without a scheme://
                 will be interpreted as hkp:// */
-
-             rc=keyserver_work(KS_GET,NULL,&desc[i],1,NULL,NULL,keyserver);
+             rc = keyserver_get (ctrl, &desc[i], 1, keyserver, NULL, NULL);
              if(rc)
                log_info(_("WARNING: unable to refresh key %s"
                           " via %s: %s\n"),keystr_from_desc(&desc[i]),
@@ -1904,7 +1446,7 @@ keyserver_refresh(STRLIST users)
                     count,opt.keyserver->uri);
        }
 
-      rc=keyserver_work(KS_GET,NULL,desc,numdesc,NULL,NULL,opt.keyserver);
+      rc=keyserver_get (ctrl, desc, numdesc, NULL, NULL, NULL);
     }
 
   xfree(desc);
@@ -1914,74 +1456,378 @@ keyserver_refresh(STRLIST users)
   /* If the original options didn't have fast import, and the trustdb
      is dirty, rebuild. */
   if(!(opt.keyserver_options.import_options&IMPORT_FAST))
-    trustdb_check_or_update();
+    check_or_update_trustdb ();
 
   return rc;
 }
 
-int
-keyserver_search(STRLIST tokens)
+
+/* Search for keys on the keyservers.  The patterns are given in the
+   string list TOKENS.  */
+gpg_error_t
+keyserver_search (ctrl_t ctrl, strlist_t tokens)
 {
-  if(tokens)
-    return keyserver_work(KS_SEARCH,tokens,NULL,0,NULL,NULL,opt.keyserver);
-  else
-    return 0;
+  gpg_error_t err;
+  char *searchstr;
+  struct search_line_handler_parm_s parm;
+
+  memset (&parm, 0, sizeof parm);
+
+  if (!tokens)
+    return 0;  /* Return success if no patterns are given.  */
+
+  if (!opt.keyserver)
+    {
+      log_error (_("no keyserver known (use option --keyserver)\n"));
+      return gpg_error (GPG_ERR_NO_KEYSERVER);
+    }
+
+  /* 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); */
+
+  {
+    membuf_t mb;
+    strlist_t item;
+
+    init_membuf (&mb, 1024);
+    for (item = tokens; item; item = item->next)
+    {
+      if (item != tokens)
+        put_membuf (&mb, " ", 1);
+      put_membuf_str (&mb, item->d);
+    }
+    put_membuf (&mb, "", 1); /* Append Nul.  */
+    searchstr = get_membuf (&mb, NULL);
+    if (!searchstr)
+      {
+        err = gpg_error_from_syserror ();
+        goto leave;
+      }
+  }
+  /* FIXME: Enable the next line */
+  /* log_info (_("searching for \"%s\" from %s\n"), searchstr, keyserver->uri); */
+
+  parm.ctrl = ctrl;
+  if (searchstr)
+    parm.searchstr_disp = utf8_to_native (searchstr, strlen (searchstr), 0);
+
+  err = gpg_dirmngr_ks_search (ctrl, searchstr, search_line_handler, &parm);
+
+  if (parm.not_found)
+    {
+      if (parm.searchstr_disp)
+        log_info (_("key \"%s\" not found on keyserver\n"),
+                  parm.searchstr_disp);
+      else
+        log_info (_("key not found on keyserver\n"));
+    }
+
+  if (gpg_err_code (err) == GPG_ERR_NO_KEYSERVER)
+    log_error (_("no keyserver known (use option --keyserver)\n"));
+  else if (err)
+    log_error ("error searching keyserver: %s\n", gpg_strerror (err));
+
+  /* switch(ret) */
+  /*   { */
+  /*   case KEYSERVER_SCHEME_NOT_FOUND: */
+  /*     log_error(_("no handler for keyserver scheme '%s'\n"), */
+  /*               opt.keyserver->scheme); */
+  /*     break; */
+
+  /*   case KEYSERVER_NOT_SUPPORTED: */
+  /*     log_error(_("action '%s' not supported with keyserver " */
+  /*                 "scheme '%s'\n"), "search", opt.keyserver->scheme); */
+  /*     break; */
+
+  /*   case KEYSERVER_TIMEOUT: */
+  /*     log_error(_("keyserver timed out\n")); */
+  /*     break; */
+
+  /*   case KEYSERVER_INTERNAL_ERROR: */
+  /*   default: */
+  /*     log_error(_("keyserver internal error\n")); */
+  /*     break; */
+  /*   } */
+
+  /* return gpg_error (GPG_ERR_KEYSERVER); */
+
+
+ leave:
+  xfree (parm.desc);
+  xfree (parm.searchstr_disp);
+  xfree(searchstr);
+
+  return err;
 }
 
-int
-keyserver_fetch(STRLIST urilist)
+
+
+/* Retrieve a key from a keyserver.  The search pattern are in
+   (DESC,NDESC).  Allowed search modes are keyid, fingerprint, and
+   exact searches.  KEYSERVER gives an optional override keyserver. If
+   (R_FPR,R_FPRLEN) are not NULL, the may retrun the fingerprint of
+   one imported key.  */
+static gpg_error_t
+keyserver_get (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc,
+               struct keyserver_spec *keyserver,
+               unsigned char **r_fpr, size_t *r_fprlen)
+
 {
-  KEYDB_SEARCH_DESC desc;
-  STRLIST sl;
-  unsigned int options=opt.keyserver_options.import_options;
+  gpg_error_t err = 0;
+  char **pattern;
+  int idx, npat;
+  estream_t datastream;
+  char *source = NULL;
+
+  /* Create an array filled with a search pattern for each key.  The
+     array is delimited by a NULL entry.  */
+  pattern = xtrycalloc (ndesc+1, sizeof *pattern);
+  if (!pattern)
+    return gpg_error_from_syserror ();
+  for (npat=idx=0; idx < ndesc; idx++)
+    {
+      int quiet = 0;
+
+      if (desc[idx].mode == KEYDB_SEARCH_MODE_FPR20
+          || desc[idx].mode == KEYDB_SEARCH_MODE_FPR16)
+        {
+          pattern[npat] = xtrymalloc (2+2*20+1);
+          if (!pattern[npat])
+            err = gpg_error_from_syserror ();
+          else
+            {
+              strcpy (pattern[npat], "0x");
+              bin2hex (desc[idx].u.fpr,
+                       desc[idx].mode == KEYDB_SEARCH_MODE_FPR20? 20 : 16,
+                       pattern[npat]+2);
+              npat++;
+            }
+        }
+      else if(desc[idx].mode == KEYDB_SEARCH_MODE_LONG_KID)
+        {
+          pattern[npat] = xtryasprintf ("0x%08lX%08lX",
+                                        (ulong)desc[idx].u.kid[0],
+                                        (ulong)desc[idx].u.kid[1]);
+          if (!pattern[npat])
+            err = gpg_error_from_syserror ();
+          else
+            npat++;
+        }
+      else if(desc[idx].mode == KEYDB_SEARCH_MODE_SHORT_KID)
+        {
+          pattern[npat] = xtryasprintf ("0x%08lX", (ulong)desc[idx].u.kid[1]);
+          if (!pattern[npat])
+            err = gpg_error_from_syserror ();
+          else
+            npat++;
+        }
+      else if(desc[idx].mode == KEYDB_SEARCH_MODE_EXACT)
+        {
+          /* The Dirmngr uses also classify_user_id to detect the type
+             of the search string.  By adding the '=' prefix we force
+             Dirmngr's KS_GET to consider this an exact search string.
+             (In gpg 1.4 and gpg 2.0 the keyserver helpers used the
+             KS_GETNAME command to indicate this.)  */
+          pattern[npat] = strconcat ("=", desc[idx].u.name, NULL);
+          if (!pattern[npat])
+            err = gpg_error_from_syserror ();
+          else
+            {
+              npat++;
+              quiet = 1;
+            }
+        }
+      else if (desc[idx].mode == KEYDB_SEARCH_MODE_NONE)
+        continue;
+      else
+        BUG();
+
+      if (err)
+        {
+          for (idx=0; idx < npat; idx++)
+            xfree (pattern[idx]);
+          xfree (pattern);
+          return err;
+        }
+
+      if (!quiet && keyserver)
+        {
+          if (keyserver->host)
+            log_info (_("requesting key %s from %s server %s\n"),
+                      keystr_from_desc (&desc[idx]),
+                      keyserver->scheme, keyserver->host);
+          else
+            log_info (_("requesting key %s from %s\n"),
+                      keystr_from_desc (&desc[idx]), keyserver->uri);
+        }
+    }
 
-  /* Switch on fast-import, since fetch can handle more than one
-     import and we don't want each set to rebuild the trustdb.
-     Instead we do it once at the end. */
-  opt.keyserver_options.import_options|=IMPORT_FAST;
 
-  /* A dummy desc since we're not actually fetching a particular key
-     ID */
-  memset(&desc,0,sizeof(desc));
-  desc.mode=KEYDB_SEARCH_MODE_EXACT;
+  err = gpg_dirmngr_ks_get (ctrl, pattern, &datastream, &source);
+  for (idx=0; idx < npat; idx++)
+    xfree (pattern[idx]);
+  xfree (pattern);
+  if (opt.verbose && source)
+    log_info ("data source: %s\n", source);
 
-  for(sl=urilist;sl;sl=sl->next)
+  if (!err)
     {
-      struct keyserver_spec *spec;
+      void *stats_handle;
+      struct ks_retrieval_screener_arg_s screenerarg;
+
+      stats_handle = import_new_stats_handle();
+
+      /* FIXME: Check whether this comment should be moved to dirmngr.
+
+         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.  Note that we don't allow the
+         import of secret keys from a keyserver.  Keyservers should
+         never accept or send them but we better protect against rogue
+         keyservers. */
+
+      screenerarg.desc = desc;
+      screenerarg.ndesc = ndesc;
+      import_keys_es_stream (ctrl, datastream, stats_handle,
+                             r_fpr, r_fprlen,
+                             (opt.keyserver_options.import_options
+                              | IMPORT_NO_SECKEY),
+                             keyserver_retrieval_screener, &screenerarg);
+
+      import_print_stats (stats_handle);
+      import_release_stats_handle (stats_handle);
+    }
+  es_fclose (datastream);
+  xfree (source);
 
-      spec=parse_keyserver_uri(sl->d,1,NULL,0);
-      if(spec)
-       {
-         int rc;
+  return err;
+}
 
-         rc=keyserver_work(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));
 
-         free_keyserver_spec(spec);
-       }
+/* 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
-       log_info (_("WARNING: unable to parse URI %s\n"),sl->d);
+        {
+          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));
+        }
     }
 
-  opt.keyserver_options.import_options=options;
+
+  return err;
+
+}
+
+
+/* Loop over all URLs in STRLIST and fetch the key at that URL.  Note
+   that the fetch operation ignores the configured key servers and
+   instead directly retrieves the keys.  */
+int
+keyserver_fetch (ctrl_t ctrl, strlist_t urilist)
+{
+  gpg_error_t err;
+  strlist_t sl;
+  estream_t datastream;
+  unsigned int save_options = opt.keyserver_options.import_options;
+
+  /* Switch on fast-import, since fetch can handle more than one
+     import and we don't want each set to rebuild the trustdb.
+     Instead we do it once at the end. */
+  opt.keyserver_options.import_options |= IMPORT_FAST;
+
+  for (sl=urilist; sl; sl=sl->next)
+    {
+      if (!opt.quiet)
+        log_info (_("requesting key from '%s'\n"), sl->d);
+
+      err = gpg_dirmngr_ks_fetch (ctrl, sl->d, &datastream);
+      if (!err)
+        {
+          void *stats_handle;
+
+          stats_handle = import_new_stats_handle();
+          import_keys_es_stream (ctrl, datastream, stats_handle, NULL, NULL,
+                                 opt.keyserver_options.import_options,
+                                 NULL, NULL);
+
+          import_print_stats (stats_handle);
+          import_release_stats_handle (stats_handle);
+        }
+      else
+        log_info (_("WARNING: unable to fetch URI %s: %s\n"),
+                  sl->d, gpg_strerror (err));
+      es_fclose (datastream);
+    }
+
+  opt.keyserver_options.import_options = save_options;
 
   /* If the original options didn't have fast import, and the trustdb
      is dirty, rebuild. */
-  if(!(opt.keyserver_options.import_options&IMPORT_FAST))
-    trustdb_check_or_update();
+  if (!(opt.keyserver_options.import_options&IMPORT_FAST))
+    check_or_update_trustdb ();
 
   return 0;
 }
 
+
 /* Import key in a CERT or pointed to by a CERT */
 int
-keyserver_import_cert(const char *name,unsigned char **fpr,size_t *fpr_len)
+keyserver_import_cert (ctrl_t ctrl,
+                       const char *name,unsigned char **fpr,size_t *fpr_len)
 {
+  gpg_error_t err;
   char *domain,*look,*url;
-  IOBUF key;
-  int type,rc=G10ERR_GENERAL;
+  estream_t key;
+
 
   look=xstrdup(name);
 
@@ -1989,22 +1835,27 @@ keyserver_import_cert(const char *name,unsigned char **fpr,size_t *fpr_len)
   if(domain)
     *domain='.';
 
-  type=get_dns_cert(look,max_cert_size,&key,fpr,fpr_len,&url);
-  if(type==1)
+  err = get_dns_cert (look, &key, fpr, fpr_len, &url);
+  if (err)
+    ;
+  else if (key)
     {
       int armor_status=opt.no_armor;
 
       /* CERTs are always in binary format */
       opt.no_armor=1;
 
-      rc=import_keys_stream(key,NULL,fpr,fpr_len,
-                           opt.keyserver_options.import_options);
+      err = import_keys_es_stream (ctrl, key, NULL, fpr, fpr_len,
+                                   (opt.keyserver_options.import_options
+                                    | IMPORT_NO_SECKEY),
+                                   NULL, NULL);
 
       opt.no_armor=armor_status;
 
-      iobuf_close(key);
+      es_fclose (key);
+      key = NULL;
     }
-  else if(type==2 && *fpr)
+  else if (*fpr)
     {
       /* We only consider the IPGP type if a fingerprint was provided.
         This lets us select the right key regardless of what a URL
@@ -2016,13 +1867,7 @@ keyserver_import_cert(const char *name,unsigned char **fpr,size_t *fpr_len)
          spec=parse_keyserver_uri(url,1,NULL,0);
          if(spec)
            {
-             STRLIST list=NULL;
-
-             add_to_strlist(&list,url);
-
-             rc=keyserver_fetch(list);
-
-             free_strlist(list);
+             err = keyserver_import_fprint (ctrl, *fpr,*fpr_len,spec);
              free_keyserver_spec(spec);
            }
        }
@@ -2031,7 +1876,7 @@ keyserver_import_cert(const char *name,unsigned char **fpr,size_t *fpr_len)
          /* If only a fingerprint is provided, try and fetch it from
             our --keyserver */
 
-         rc=keyserver_import_fprint(*fpr,*fpr_len,opt.keyserver);
+         err = keyserver_import_fprint (ctrl, *fpr,*fpr_len,opt.keyserver);
        }
       else
        log_info(_("no keyserver known (use option --keyserver)\n"));
@@ -2040,72 +1885,70 @@ keyserver_import_cert(const char *name,unsigned char **fpr,size_t *fpr_len)
         found, but no keyserver" " known (use option
         --keyserver)\n" ? */
 
-      xfree(url);
     }
 
+  xfree(url);
   xfree(look);
 
-  return rc;
+  return err;
 }
 
 /* Import key pointed to by a PKA record. Return the requested
    fingerprint in fpr. */
 int
-keyserver_import_pka(const char *name,unsigned char **fpr,size_t *fpr_len)
+keyserver_import_pka (ctrl_t ctrl,
+                      const char *name,unsigned char **fpr,size_t *fpr_len)
 {
   char *uri;
-  int rc=-1;
+  int rc = G10ERR_NO_PUBKEY;
 
-  *fpr=xmalloc(20);
-  *fpr_len=20;
+  *fpr = xmalloc (20);
+  *fpr_len = 20;
 
   uri = get_pka_info (name, *fpr);
-  if (uri)
+  if (uri && *uri)
     {
+      /* An URI is available.  Lookup the key. */
       struct keyserver_spec *spec;
       spec = parse_keyserver_uri (uri, 1, NULL, 0);
       if (spec)
        {
-         rc=keyserver_import_fprint (*fpr, 20, spec);
+         rc = keyserver_import_fprint (ctrl, *fpr, 20, spec);
          free_keyserver_spec (spec);
        }
       xfree (uri);
     }
 
-  if(rc!=0)
-    xfree(*fpr);
+  if (rc)
+    {
+      xfree(*fpr);
+      *fpr = NULL;
+    }
 
   return rc;
 }
 
-/* Import all keys that match name */
-int
-keyserver_import_name(const char *name,unsigned char **fpr,size_t *fpr_len,
-                     struct keyserver_spec *keyserver)
-{
-  STRLIST list=NULL;
-  int rc;
-
-  append_to_strlist(&list,name);
-
-  rc=keyserver_work(KS_GETNAME,list,NULL,0,fpr,fpr_len,keyserver);
-
-  free_strlist(list);
-
-  return rc;
-}
 
-/* Use the PGP Universal trick of asking ldap://keys.(maildomain) for
-   the key. */
+/* Import a key by name using LDAP */
 int
-keyserver_import_ldap(const char *name,unsigned char **fpr,size_t *fpr_len)
+keyserver_import_ldap (ctrl_t ctrl,
+                       const char *name, unsigned char **fpr, size_t *fprlen)
 {
+  (void)ctrl;
+  (void)name;
+  (void)fpr;
+  (void)fprlen;
+  return gpg_error (GPG_ERR_NOT_IMPLEMENTED); /*FIXME*/
+#if 0
   char *domain;
   struct keyserver_spec *keyserver;
-  STRLIST list=NULL;
-  int rc;
-
-  append_to_strlist(&list,name);
+  strlist_t list=NULL;
+  int rc,hostlen=1;
+#ifdef USE_DNS_SRV
+  struct srventry *srvlist=NULL;
+  int srvcount,i;
+  char srvname[MAXDNAME];
+#endif
 
   /* Parse out the domain */
   domain=strrchr(name,'@');
@@ -2115,22 +1958,57 @@ keyserver_import_ldap(const char *name,unsigned char **fpr,size_t *fpr_len)
   domain++;
 
   keyserver=xmalloc_clear(sizeof(struct keyserver_spec));
-
   keyserver->scheme=xstrdup("ldap");
-  keyserver->host=xmalloc(5+strlen(domain)+1);
-  strcpy(keyserver->host,"keys.");
+  keyserver->host=xmalloc(1);
+  keyserver->host[0]='\0';
+
+#ifdef USE_DNS_SRV
+  snprintf(srvname,MAXDNAME,"_pgpkey-ldap._tcp.%s",domain);
+
+  srvcount=getsrv(srvname,&srvlist);
+
+  for(i=0;i<srvcount;i++)
+    {
+      hostlen+=strlen(srvlist[i].target)+1;
+      keyserver->host=xrealloc(keyserver->host,hostlen);
+
+      strcat(keyserver->host,srvlist[i].target);
+
+      if(srvlist[i].port!=389)
+       {
+         char port[7];
+
+         hostlen+=6; /* a colon, plus 5 digits (unsigned 16-bit value) */
+         keyserver->host=xrealloc(keyserver->host,hostlen);
+
+         snprintf(port,7,":%u",srvlist[i].port);
+         strcat(keyserver->host,port);
+       }
+
+      strcat(keyserver->host," ");
+    }
+
+  free(srvlist);
+#endif
+
+  /* If all else fails, do the PGP Universal trick of
+     ldap://keys.(domain) */
+
+  hostlen+=5+strlen(domain);
+  keyserver->host=xrealloc(keyserver->host,hostlen);
+  strcat(keyserver->host,"keys.");
   strcat(keyserver->host,domain);
-  keyserver->uri=xmalloc(strlen(keyserver->scheme)+
-                        3+strlen(keyserver->host)+1);
-  strcpy(keyserver->uri,keyserver->scheme);
-  strcat(keyserver->uri,"://");
-  strcat(keyserver->uri,keyserver->host);
-    
-  rc=keyserver_work(KS_GETNAME,list,NULL,0,fpr,fpr_len,keyserver);
+
+  append_to_strlist(&list,name);
+
+  rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /*FIXME*/
+       /* keyserver_work (ctrl, KS_GETNAME, list, NULL, */
+       /*                 0, fpr, fpr_len, keyserver); */
 
   free_strlist(list);
 
   free_keyserver_spec(keyserver);
 
   return rc;
+#endif
 }