* g10.c (main): Use 3DES instead of CAST5 if we don't have CAST5 support.
[gnupg.git] / g10 / keyserver.c
index e2477c6..ed7883d 100644 (file)
 #include "exec.h"
 #include "main.h"
 #include "i18n.h"
-#include "hkp.h"
 #include "iobuf.h"
 #include "memory.h"
+#include "ttyio.h"
 #include "options.h"
 #include "packet.h"
 #include "keyserver-internal.h"
 #include "util.h"
 
-#define KEYSERVER_PROTO_VERSION 0
-
 #define GET    0
 #define SEND   1
 #define SEARCH 2
 
+struct keyrec
+{
+  KEYDB_SEARCH_DESC desc;
+  time_t createtime,expiretime;
+  int size,flags;
+  byte type;
+  IOBUF uidbuf;
+  int lines;
+};
+
 struct kopts
 {
   char *name;
@@ -54,7 +62,6 @@ struct kopts
   {"include-revoked",1,&opt.keyserver_options.include_revoked},
   {"include-disabled",1,&opt.keyserver_options.include_disabled},
   {"include-subkeys",1,&opt.keyserver_options.include_subkeys},
-  {"include-attributes",0,&opt.keyserver_options.include_attributes},
   {"keep-temp-files",0,&opt.keyserver_options.keep_temp_files},
   {"honor-http-proxy",1,&opt.keyserver_options.honor_http_proxy},
   {"broken-http-proxy",1,&opt.keyserver_options.broken_http_proxy},
@@ -63,6 +70,9 @@ struct kopts
   {NULL}
 };
 
+static int keyserver_work(int action,STRLIST list,
+                         KEYDB_SEARCH_DESC *desc,int count);
+
 void 
 parse_keyserver_options(char *options)
 {
@@ -83,7 +93,7 @@ parse_keyserver_options(char *options)
              hit=1;
              break;
            }
-         else if(ascii_memcasecmp("no-",tok,3)==0 &&
+         else if(ascii_strncasecmp("no-",tok,3)==0 &&
                  ascii_strcasecmp(&tok[3],keyserver_opts[i].name)==0)
            {
              *(keyserver_opts[i].flag)=0;
@@ -102,7 +112,7 @@ parse_keyserver_options(char *options)
 #ifdef EXEC_TEMPFILE_ONLY
          else if(ascii_strcasecmp(tok,"use-temp-files")==0 ||
                  ascii_strcasecmp(tok,"no-use-temp-files")==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
          else if(ascii_strcasecmp(tok,"use-temp-files")==0)
@@ -110,8 +120,12 @@ parse_keyserver_options(char *options)
          else if(ascii_strcasecmp(tok,"no-use-temp-files")==0)
            opt.keyserver_options.use_temp_files=0;
 #endif
-         else if(strlen(tok)>0)
-           add_to_strlist(&opt.keyserver_options.other,tok);
+         else
+           if(!parse_import_options(tok,
+                                    &opt.keyserver_options.import_options) &&
+              !parse_export_options(tok,
+                                    &opt.keyserver_options.export_options))
+             add_to_strlist(&opt.keyserver_options.other,tok);
        }
     }
 }
@@ -137,6 +151,14 @@ parse_keyserver_uri(char *uri,const char *configname,unsigned int configlineno)
       uri=opt.keyserver_scheme;
       opt.keyserver_scheme="hkp";
     }
+  else
+    {
+      /* Force to lowercase */
+      char *i;
+
+      for(i=opt.keyserver_scheme;*i!='\0';i++)
+       *i=ascii_tolower(*i);
+    }
 
   if(ascii_strcasecmp(opt.keyserver_scheme,"x-broken-hkp")==0)
     {
@@ -208,115 +230,436 @@ parse_keyserver_uri(char *uri,const char *configname,unsigned int configlineno)
   return 0;
 }
 
-/* Unquote only the delimiter character and backslashes (\x5C) */
-static void 
-printunquoted(char *string,char delim)
+static void
+print_keyrec(int number,struct keyrec *keyrec)
 {
-  char *ch=string;
+  int i;
+
+  iobuf_writebyte(keyrec->uidbuf,0);
+  iobuf_flush_temp(keyrec->uidbuf);
+  printf("(%d)\t%s  ",number,iobuf_get_temp_buffer(keyrec->uidbuf));
+
+  if(keyrec->size>0)
+    printf("%d bit ",keyrec->size);
 
-  while(*ch)
+  if(keyrec->type)
     {
-      if(*ch=='\\')
-       {
-         int c;
+      const char *str=pubkey_algo_to_string(keyrec->type);
 
-         sscanf(ch,"\\x%02x",&c);
-         if(c==delim)
-           {
-             printf("%c",c);
-             ch+=3;
-           }
-         else if(c=='\\')
-           {
-             fputc('\\',stdout);
-             ch+=3;
-           }
-         else
-           fputc(*ch,stdout);
-       }
+      if(str)
+       printf("%s ",str);
       else
-       fputc(*ch,stdout);
+       printf("unknown ");
+    }
+
+  switch(keyrec->desc.mode)
+    {
+    case KEYDB_SEARCH_MODE_SHORT_KID:
+      printf("key %08lX",(ulong)keyrec->desc.u.kid[1]);
+      break;
 
-      ch++;
+    case KEYDB_SEARCH_MODE_LONG_KID:
+      printf("key %08lX%08lX",(ulong)keyrec->desc.u.kid[0],
+            (ulong)keyrec->desc.u.kid[1]);
+      break;
+
+    case KEYDB_SEARCH_MODE_FPR16:
+      printf("key ");
+      for(i=0;i<16;i++)
+       printf("%02X",(unsigned char)keyrec->desc.u.fpr[i]);
+      break;
+
+    case KEYDB_SEARCH_MODE_FPR20:
+      printf("key ");
+      for(i=0;i<20;i++)
+       printf("%02X",(unsigned char)keyrec->desc.u.fpr[i]);
+      break;
+
+    default:
+      BUG();
+      break;
     }
+
+  if(keyrec->createtime>0)
+    printf(", created %s",strtimestamp(keyrec->createtime));
+
+  if(keyrec->expiretime>0)
+    printf(", expires %s",strtimestamp(keyrec->expiretime));
+
+  if(keyrec->flags&1)
+    printf(" (%s)",("revoked"));
+  if(keyrec->flags&2)
+    printf(" (%s)",("disabled"));
+  if(keyrec->flags&4)
+    printf(" (%s)",("expired"));
+
+  printf("\n");
 }
 
-static int 
-print_keyinfo(int count,char *keystring,KEYDB_SEARCH_DESC *desc)
+/* Returns a keyrec (which must be freed) once a key is complete, and
+   NULL otherwise.  Call with a NULL keystring once key parsing is
+   complete to return any unfinished keys. */
+static struct keyrec *
+parse_keyrec(char *keystring)
 {
-  char *certid,*userid,*keytype,*tok;
-  int flags,keysize=0;
-  time_t createtime=0,expiretime=0,modifytime=0;
+  static struct keyrec *work=NULL;
+  struct keyrec *ret=NULL;
+  char *record;
+  int i;
 
-  if((certid=strsep(&keystring,":"))==NULL)
-    return -1;
+  if(keystring==NULL)
+    {
+      if(work==NULL)
+       return NULL;
+      else if(work->desc.mode==KEYDB_SEARCH_MODE_NONE)
+       {
+         m_free(work);
+         return NULL;
+       }
+      else
+       {
+         ret=work;
+         work=NULL;
+         return ret;
+       }
+    }
 
-  classify_user_id (certid, 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)
-    return -1;
+  if(work==NULL)
+    {
+      work=m_alloc_clear(sizeof(struct keyrec));
+      work->uidbuf=iobuf_temp();
+    }
 
-  if((tok=strsep(&keystring,":"))==NULL)
-    return -1;
+  /* Remove trailing whitespace */
+  for(i=strlen(keystring);i>0;i--)
+    if(isspace(keystring[i-1]))
+      keystring[i-1]='\0';
+    else
+      break;
 
-  userid=utf8_to_native(tok,strlen(tok),0);
+  if((record=strsep(&keystring,":"))==NULL)
+    return ret;
 
-  if((tok=strsep(&keystring,":"))==NULL)
-    return -1;
+  if(ascii_strcasecmp("pub",record)==0)
+    {
+      char *tok;
 
-  flags=atoi(tok);
+      if(work->desc.mode)
+       {
+         ret=work;
+         work=m_alloc_clear(sizeof(struct keyrec));
+         work->uidbuf=iobuf_temp();
+       }
 
-  if((tok=strsep(&keystring,":"))==NULL)
-    return -1;
+      if((tok=strsep(&keystring,":"))==NULL)
+       return ret;
 
-  createtime=atoi(tok);
+      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)
+       {
+         work->desc.mode=KEYDB_SEARCH_MODE_NONE;
+         return ret;
+       }
 
-  if((tok=strsep(&keystring,":"))==NULL)
-    return -1;
+      /* Note all items after this are optional.  This allows us to
+         have a pub line as simple as pub:keyid and nothing else. */
 
-  expiretime=atoi(tok);
+      work->lines++;
 
-  if((tok=strsep(&keystring,":"))==NULL)
-    return -1;
+      if((tok=strsep(&keystring,":"))==NULL)
+       return ret;
 
-  modifytime=atoi(tok);
+      work->type=atoi(tok);
 
-  if((keytype=strsep(&keystring,":"))==NULL)
-    return -1;
+      if((tok=strsep(&keystring,":"))==NULL)
+       return ret;
+
+      work->size=atoi(tok);
+
+      if((tok=strsep(&keystring,":"))==NULL)
+       return ret;
+
+      work->createtime=atoi(tok);
+
+      if((tok=strsep(&keystring,":"))==NULL)
+       return ret;
+
+      work->expiretime=atoi(tok);
+
+      if((tok=strsep(&keystring,":"))==NULL)
+       return ret;
+
+      while(*tok)
+       switch(*tok++)
+         {
+         case 'r':
+         case 'R':
+           work->flags|=1;
+           break;
+           
+         case 'd':
+         case 'D':
+           work->flags|=2;
+           break;
+
+         case 'e':
+         case 'E':
+           work->flags|=4;
+           break;
+         }
+
+      if(work->expiretime && work->expiretime<=make_timestamp())
+       work->flags|=4;
+    }
+  else if(ascii_strcasecmp("uid",record)==0 && work->desc.mode)
+    {
+      char *userid,*tok,*decoded;
+
+      if((tok=strsep(&keystring,":"))==NULL)
+       return ret;
+
+      if(strlen(tok)==0)
+       return ret;
 
-  /* The last one */
-  if(keystring!=NULL)
-    keysize=atoi(keystring);
+      userid=tok;
 
-  printf("(%d)\t",count);
+      /* By definition, de-%-encoding is always smaller than the
+         original string so we can decode in place. */
 
-  /* No need to check for control characters, as utf8_to_native does
-     this for us. */
-  printunquoted(userid,':');
+      i=0;
 
-  if(flags&1)
-    printf(" (revoked)");
-  if(flags&2)
-    printf(" (disabled)");
+      while(*tok)
+       if(tok[0]=='%' && tok[1] && tok[2])
+         {
+           if((userid[i]=hextobyte(&tok[1]))==-1)
+             userid[i]='?';
+
+           i++;
+           tok+=3;
+         }
+       else
+         userid[i++]=*tok++;
+
+      /* We don't care about the other info provided in the uid: line
+         since no keyserver supports marking userids with timestamps
+         or revoked/expired/disabled yet. */
+
+      /* No need to check for control characters, as utf8_to_native
+        does this for us. */
 
-  if(keytype[0])
-    printf(" %s",keytype);
+      decoded=utf8_to_native(userid,i,0);
+      iobuf_writestr(work->uidbuf,decoded);
+      m_free(decoded);
+      iobuf_writestr(work->uidbuf,"\n\t");
+      work->lines++;
+    }
+  
+  /* Ignore any records other than "pri" and "uid" for easy future
+     growth. */
 
-  if(keysize>0)
-    printf(" %d",keysize);
+  return ret;
+}
 
-  printf("\n\t  created %s,",strtimestamp(createtime));
+/* 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)
+{
+  char *answer;
+
+  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;
+    }
+
+  answer=cpr_get_no_help("keysearch.prompt",
+                        _("Enter number(s), N)ext, or Q)uit > "));
+  /* control-d */
+  if(answer[0]=='\x04')
+    {
+      printf("Q\n");
+      answer[0]='q';
+    }
+
+  if(answer[0]=='q' || answer[0]=='Q')
+    {
+      m_free(answer);
+      return 1;
+    }
+  else if(atoi(answer)>=1 && atoi(answer)<=numdesc)
+    {
+      char *split=answer,*num;
 
-  if(expiretime>0)
-    printf(" expires %s,",strtimestamp(expiretime));
+      while((num=strsep(&split," ,"))!=NULL)
+       if(atoi(num)>=1 && atoi(num)<=numdesc)
+         keyserver_work(GET,NULL,&desc[atoi(num)-1],1);
 
-  printf(" key %s\n",certid);
+      m_free(answer);
+      return 1;
+    }
 
   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. */
+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;
+  KEYDB_SEARCH_DESC *desc;
+  byte *line=NULL;
+  /* TODO: Something other than 23?  That's 24-1 (the prompt). */
+  int maxlines=23,numlines=0;
+
+  desc=m_alloc(count*sizeof(KEYDB_SEARCH_DESC));
+
+  for(;;)
+    {
+      struct keyrec *keyrec;
+      int rl;
+
+      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=m_realloc(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,searchstr))
+                   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=m_realloc(desc,count*sizeof(KEYDB_SEARCH_DESC));
+         validcount=0;
+       }
+
+      if(keyrec)
+       {
+         desc[i]=keyrec->desc;
+
+         if(!opt.with_colons)
+           {
+             if(numlines+keyrec->lines>maxlines)
+               {
+                 if(show_prompt(desc,i,validcount?count:0,searchstr))
+                   break;
+                 else
+                   numlines=0;
+               }
+
+             print_keyrec(i+1,keyrec);
+           }
+
+         numlines+=keyrec->lines;
+         iobuf_close(keyrec->uidbuf);
+         m_free(keyrec);
+
+         started=1;
+         i++;
+       }
+    }
+
+  m_free(desc);
+  m_free(line);
+
+ notfound:
+  if(count==0)
+    {
+      if(searchstr)
+       log_info(_("key \"%s\" not found on keyserver\n"),searchstr);
+      else
+       log_info(_("key not found on keyserver\n"));
+      return;
+    }
+}
+
 #define KEYSERVER_ARGS_KEEP " -o \"%O\" \"%I\""
 #define KEYSERVER_ARGS_NOKEEP " -o \"%o\" \"%i\""
 
@@ -326,7 +669,7 @@ keyserver_spawn(int action,STRLIST list,
 {
   int ret=0,i,gotversion=0,outofband=0;
   STRLIST temp;
-  unsigned int maxlen=256,buflen;
+  unsigned int maxlen,buflen;
   char *command=NULL,*searchstr=NULL;
   byte *line=NULL;
   struct kopts *kopts;
@@ -336,8 +679,15 @@ keyserver_spawn(int action,STRLIST list,
   opt.keyserver_options.use_temp_files=1;
 #endif
 
-  /* Build the filename for the helper to execute */
+  /* Push the libexecdir into path.  If DISABLE_KEYSERVER_PATH is set,
+     use the 0 arg to replace the path. */
+#ifdef DISABLE_KEYSERVER_PATH
+  set_exec_path(GNUPG_LIBEXECDIR,0);
+#else
+  set_exec_path(GNUPG_LIBEXECDIR,opt.exec_path_set);
+#endif
 
+  /* Build the filename for the helper to execute */
   command=m_alloc(strlen("gpgkeys_")+strlen(opt.keyserver_scheme)+1);
   strcpy(command,"gpgkeys_");
   strcat(command,opt.keyserver_scheme);
@@ -451,10 +801,6 @@ keyserver_spawn(int action,STRLIST list,
          {
            armor_filter_context_t afx;
            IOBUF buffer=iobuf_temp();
-           int attribs=EXPORT_FLAG_ONLYRFC;
-
-           if(!opt.keyserver_options.include_attributes)
-             attribs|=EXPORT_FLAG_SKIPATTRIBS;
 
            temp=NULL;
            add_to_strlist(&temp,key->d);
@@ -463,7 +809,8 @@ keyserver_spawn(int action,STRLIST list,
            afx.what=1;
            iobuf_push_filter(buffer,armor_filter,&afx);
 
-           if(export_pubkeys_stream(buffer,temp,attribs)==-1)
+           if(export_pubkeys_stream(buffer,temp,
+                                    opt.keyserver_options.export_options)==-1)
              iobuf_close(buffer);
            else
              {
@@ -529,8 +876,10 @@ keyserver_spawn(int action,STRLIST list,
 
   for(;;)
     {
+      int plen;
       char *ptr;
 
+      maxlen=1024;
       if(iobuf_read_line(spawn->fromchild,&line,&buflen,&maxlen)==0)
        {
          ret=G10ERR_READ_FILE;
@@ -539,16 +888,16 @@ keyserver_spawn(int action,STRLIST list,
 
       ptr=line;
 
-      if(*ptr=='\r')
-       ptr++;
-
-      if(*ptr=='\n')
-       ptr++;
+      /* remove trailing whitespace */
+      plen=strlen(ptr);
+      while(plen>0 && isspace(ptr[plen-1]))
+       plen--;
+      plen[ptr]='\0';
 
       if(*ptr=='\0')
        break;
 
-      if(ascii_memcasecmp(ptr,"VERSION ",8)==0)
+      if(ascii_strncasecmp(ptr,"VERSION ",8)==0)
        {
          gotversion=1;
 
@@ -559,18 +908,16 @@ keyserver_spawn(int action,STRLIST list,
              goto fail;
            }
        }
-      else if(ascii_memcasecmp(ptr,"PROGRAM ",8)==0)
+      else if(ascii_strncasecmp(ptr,"PROGRAM ",8)==0)
        {
-         if(ascii_memcasecmp(&ptr[8],VERSION,strlen(VERSION))!=0)
-           log_info(_("Warning: keyserver handler from a different "
+         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_memcasecmp(ptr,"OPTION OUTOFBAND",16)==0)
+      else if(ascii_strncasecmp(ptr,"OPTION OUTOFBAND",16)==0)
        outofband=1; /* Currently the only OPTION */
     }
 
-  m_free(line);
-
   if(!gotversion)
     {
       log_error(_("keyserver did not send VERSION\n"));
@@ -586,14 +933,15 @@ keyserver_spawn(int action,STRLIST list,
 
          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. */
+         /* 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,0,stats_handle);
+         import_keys_stream(spawn->fromchild,stats_handle,
+                            opt.keyserver_options.import_options);
 
          import_print_stats(stats_handle);
          import_release_stats_handle(stats_handle);
@@ -607,21 +955,7 @@ keyserver_spawn(int action,STRLIST list,
 
       case SEARCH:
        {
-         line=NULL;
-         buflen = 0;
-         maxlen = 80;
-         /* Look for the COUNT line */
-         do
-           {
-             if(iobuf_read_line(spawn->fromchild,&line,&buflen,&maxlen)==0)
-               {
-                 ret=G10ERR_READ_FILE;
-                 goto fail; /* i.e. EOF */
-               }
-           }
-         while(sscanf(line,"COUNT %d\n",&i)!=1);
-
-         keyserver_search_prompt(spawn->fromchild,i,searchstr);
+         keyserver_search_prompt(spawn->fromchild,searchstr);
 
          break;
        }
@@ -632,6 +966,8 @@ keyserver_spawn(int action,STRLIST list,
       }
 
  fail:
+  m_free(line);
+
   *prog=exec_finish(spawn);
 
   return ret;
@@ -648,46 +984,13 @@ keyserver_work(int action,STRLIST list,KEYDB_SEARCH_DESC *desc,int count)
       return G10ERR_BAD_URI;
     }
 
-#ifndef USE_EXTERNAL_HKP
-  /* Use the internal HKP code */
-  if(ascii_strcasecmp(opt.keyserver_scheme,"hkp")==0)
-    {
-      if(opt.keyserver_host==NULL)
-       {
-         log_error(_("no keyserver known (use option --keyserver)\n"));
-         return G10ERR_BAD_URI;
-       }
-      else
-       {
-         void *stats_handle = import_new_stats_handle ();
-
-         switch(action)
-           {
-           case GET:
-             for(count--;count>=0;count--)
-               if(hkp_ask_import(&desc[count],stats_handle))
-                 log_inc_errorcount();
-             break;
-           case SEND:
-             return hkp_export(list);
-           case SEARCH:
-             return hkp_search(list);
-           }
-
-         import_print_stats (stats_handle);
-         import_release_stats_handle (stats_handle);
-
-         return 0;
-       }
-    }
-#endif
-
 #ifdef DISABLE_KEYSERVER_HELPERS
+
   log_error(_("external keyserver calls are not supported in this build\n"));
   return G10ERR_KEYSERVER;
-#endif
 
-  /* It's not the internal HKP code, so try and spawn a handler for it */
+#else
+  /* Spawn a handler */
 
   rc=keyserver_spawn(action,list,desc,count,&ret);
   if(ret)
@@ -705,6 +1008,12 @@ keyserver_work(int action,STRLIST list,KEYDB_SEARCH_DESC *desc,int count)
                    action==GET?"get":action==SEND?"send":
                    action==SEARCH?"search":"unknown",
                    opt.keyserver_scheme);
+         break;
+
+       case KEYSERVER_VERSION_ERROR:
+         log_error(_("gpgkeys_%s does not support handler version %d\n"),
+                   opt.keyserver_scheme,KEYSERVER_PROTO_VERSION);
+         break;
 
        case KEYSERVER_INTERNAL_ERROR:
        default:
@@ -723,6 +1032,7 @@ keyserver_work(int action,STRLIST list,KEYDB_SEARCH_DESC *desc,int count)
     }
 
   return 0;
+#endif /* ! DISABLE_KEYSERVER_HELPERS*/
 }
 
 int 
@@ -953,20 +1263,26 @@ keyserver_refresh(STRLIST users)
   if(rc)
     return rc;
 
-  if(count==1)
-    log_info(_("refreshing 1 key from %s\n"),opt.keyserver_uri);
-  else
-    log_info(_("refreshing %d keys from %s\n"),count,opt.keyserver_uri);
-
   if(count>0)
-    rc=keyserver_work(GET,NULL,desc,count);
+    {
+      if(opt.keyserver_uri)
+       {
+         if(count==1)
+           log_info(_("refreshing 1 key from %s\n"),opt.keyserver_uri);
+         else
+           log_info(_("refreshing %d keys from %s\n"),
+                    count,opt.keyserver_uri);
+       }
+
+      rc=keyserver_work(GET,NULL,desc,count);
+    }
 
   m_free(desc);
 
-  return 0;
+  return rc;
 }
 
-int 
+int
 keyserver_search(STRLIST tokens)
 {
   if(tokens)
@@ -974,109 +1290,3 @@ keyserver_search(STRLIST tokens)
   else
     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. */
-void 
-keyserver_search_prompt(IOBUF buffer,int count,const char *searchstr)
-{
-  int i=0,validcount=1;
-  unsigned int maxlen=256,buflen=0;
-  KEYDB_SEARCH_DESC *desc;
-  byte *line=NULL;
-  char *answer;
-
-  if(count==0)
-    goto notfound;
-
-  if(count<0)
-    {
-      validcount=0;
-      count=10;
-    }
-
-  desc=m_alloc(count*sizeof(KEYDB_SEARCH_DESC));
-
-  /* Read each line and show it to the user */
-
-  for(;;)
-    {
-      int rl;
-
-      if(validcount && i%10==0)
-       {
-         printf("Keys %d-%d of %d",i+1,(i+10<count)?i+10:count,count);
-         if(searchstr)
-           printf(" for \"%s\"",searchstr);
-         printf("\n");
-       }
-
-      maxlen=1024;
-      rl=iobuf_read_line(buffer,&line,&buflen,&maxlen);
-      if(rl>0)
-       {
-         if(print_keyinfo(i+1,line,&desc[i])==0)
-           {
-             i++;
-
-             if(i==count)
-               {
-                 count+=10;
-                 desc=m_realloc(desc,count*sizeof(KEYDB_SEARCH_DESC));
-                 validcount=0;
-               }
-           }
-         else
-           continue;
-       }
-
-      if(rl==0 && i==0)
-       {
-         count=0;
-         break;
-       }
-
-      if(i%10==0 || rl==0)
-       {
-         answer=cpr_get_no_help("keysearch.prompt",
-                                _("Enter number(s), N)ext, or Q)uit > "));
-         /* control-d */
-         if(answer[0]=='\x04')
-           {
-             printf("Q\n");
-             answer[0]='q';
-           }
-
-         if(answer[0]=='q' || answer[0]=='Q')
-           {
-             m_free(answer);
-             break;
-           }
-         else if(atoi(answer)>=1 && atoi(answer)<=i)
-           {
-             char *split=answer,*num;
-
-             while((num=strsep(&split," ,"))!=NULL)
-               if(atoi(num)>=1 && atoi(num)<=i)
-                 keyserver_work(GET,NULL,&desc[atoi(num)-1],1);
-
-             m_free(answer);
-             break;
-           }
-       }
-    }
-
-  m_free(desc);
-  m_free(line);
-
- notfound:
-  if(count==0)
-    {
-      if(searchstr)
-       log_info(_("key \"%s\" not found on keyserver\n"),searchstr);
-      else
-       log_info(_("key not found on keyserver\n"));
-      return;
-    }
-}