diff options
Diffstat (limited to '')
-rw-r--r-- | g10/keyserver.c | 1033 |
1 files changed, 1033 insertions, 0 deletions
diff --git a/g10/keyserver.c b/g10/keyserver.c new file mode 100644 index 000000000..9338bfbb7 --- /dev/null +++ b/g10/keyserver.c @@ -0,0 +1,1033 @@ +/* keyserver.c - generic keyserver code + * Copyright (C) 2001, 2002 Free Software Foundation, Inc. + * + * 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 + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + */ + +#include <config.h> +#include <ctype.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include "filter.h" +#include "keydb.h" +#include "status.h" +#include "exec.h" +#include "main.h" +#include "i18n.h" +#include "hkp.h" +#include "iobuf.h" +#include "memory.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 kopts +{ + char *name; + int tell; /* tell remote process about this one */ + int *flag; +} keyserver_opts[]= +{ + {"include-revoked",1,&opt.keyserver_options.include_revoked}, + {"include-disabled",1,&opt.keyserver_options.include_disabled}, + {"include-subkeys",1,&opt.keyserver_options.include_subkeys}, + {"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}, + {"refresh-add-fake-v3-keyids",0,&opt.keyserver_options.fake_v3_keyids}, + {"auto-key-retrieve",0,&opt.keyserver_options.auto_key_retrieve}, + {NULL} +}; + +void +parse_keyserver_options(char *options) +{ + char *tok=""; + + do + { + struct kopts *kopts=keyserver_opts; + int i,hit=0; + + for(i=0,kopts=keyserver_opts;kopts[i].name;i++) + { + if(ascii_strcasecmp(tok,kopts[i].name)==0) + { + *(kopts[i].flag)=1; + hit=1; + break; + } + else if(ascii_memcasecmp("no-",tok,3)==0 && strlen(tok)>3 && + ascii_strcasecmp(&tok[3],kopts[i].name)==0) + { + *(kopts[i].flag)=0; + hit=1; + break; + } + } + + /* These options need more than just a flag */ + if(!hit) + { + if(ascii_strcasecmp(tok,"verbose")==0) + opt.keyserver_options.verbose++; + else if(ascii_strcasecmp(tok,"no-verbose")==0) + opt.keyserver_options.verbose--; +#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 " + "on this platform\n"),tok); +#else + else if(ascii_strcasecmp(tok,"use-temp-files")==0) + opt.keyserver_options.use_temp_files=1; + 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); + } + + tok=strsep(&options," ,"); + } + while(tok!=NULL); +} + +int +parse_keyserver_uri(char *uri,const char *configname,unsigned int configlineno) +{ + /* Get the scheme */ + + opt.keyserver_scheme=strsep(&uri,":"); + if(uri==NULL) + { + uri=opt.keyserver_scheme; + opt.keyserver_scheme="hkp"; + } + + if(ascii_strcasecmp(opt.keyserver_scheme,"x-broken-hkp")==0) + { + deprecated_warning(configname,configlineno,"x-broken-hkp", + "--keyserver-options ","broken-http-proxy"); + opt.keyserver_scheme="hkp"; + opt.keyserver_options.broken_http_proxy=1; + } + else if(ascii_strcasecmp(opt.keyserver_scheme,"x-hkp")==0) + { + /* Canonicalize this to "hkp" so it works with both the internal + and external keyserver interface. */ + opt.keyserver_scheme="hkp"; + } + + /* Skip the "//", if any */ + if(strlen(uri)>2 && uri[0]=='/' && uri[1]=='/') + uri+=2; + + /* Get the host */ + opt.keyserver_host=strsep(&uri,":/"); + if(uri==NULL) + opt.keyserver_port="0"; + else + { + char *ch; + + /* Get the port */ + opt.keyserver_port=strsep(&uri,"/"); + + /* Ports are digits only */ + ch=opt.keyserver_port; + while(*ch!='\0') + { + if(!isdigit(*ch)) + return G10ERR_BAD_URI; + + ch++; + } + + if(strlen(opt.keyserver_port)==0 || + atoi(opt.keyserver_port)<1 || atoi(opt.keyserver_port)>65535) + return G10ERR_BAD_URI; + } + + /* (any path part of the URI is discarded for now as no keyserver + uses it) */ + + if(opt.keyserver_scheme[0]=='\0' || opt.keyserver_host[0]=='\0') + return G10ERR_BAD_URI; + + return 0; +} + +/* Unquote only the delimiter character and backslashes (\x5C) */ +static void +printunquoted(char *string,char delim) +{ + char *ch=string; + + while(*ch) + { + if(*ch=='\\') + { + int c; + + sscanf(ch,"\\x%02x",&c); + if(c==delim) + { + printf("%c",c); + ch+=3; + } + else if(c=='\\') + { + fputc('\\',stdout); + ch+=3; + } + else + fputc(*ch,stdout); + } + else + fputc(*ch,stdout); + + ch++; + } +} + +static int +print_keyinfo(int count,char *keystring,KEYDB_SEARCH_DESC *desc) +{ + char *certid,*userid,*keytype,*tok; + int flags,keysize=0; + time_t createtime=0,expiretime=0,modifytime=0; + + if((certid=strsep(&keystring,":"))==NULL) + return -1; + + 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((tok=strsep(&keystring,":"))==NULL) + return -1; + + userid=utf8_to_native(tok,strlen(tok),0); + + if((tok=strsep(&keystring,":"))==NULL) + return -1; + + flags=atoi(tok); + + if((tok=strsep(&keystring,":"))==NULL) + return -1; + + createtime=atoi(tok); + + if((tok=strsep(&keystring,":"))==NULL) + return -1; + + expiretime=atoi(tok); + + if((tok=strsep(&keystring,":"))==NULL) + return -1; + + modifytime=atoi(tok); + + if((keytype=strsep(&keystring,":"))==NULL) + return -1; + + /* The last one */ + if(keystring!=NULL) + keysize=atoi(keystring); + + printf("(%d)\t",count); + + /* No need to check for control characters, as utf8_to_native does + this for us. */ + printunquoted(userid,':'); + + if(flags&1) + printf(" (revoked)"); + if(flags&2) + printf(" (disabled)"); + + if(keytype[0]) + printf(" %s",keytype); + + if(keysize>0) + printf(" %d",keysize); + + printf("\n\t created %s,",strtimestamp(createtime)); + + if(expiretime>0) + printf(" expires %s,",strtimestamp(expiretime)); + + printf(" key %s\n",certid); + + return 0; +} + +#define KEYSERVER_ARGS_KEEP " -o \"%O\" \"%I\"" +#define KEYSERVER_ARGS_NOKEEP " -o \"%o\" \"%i\"" + +static int +keyserver_spawn(int action,STRLIST list, + KEYDB_SEARCH_DESC *desc,int count,int *prog) +{ + int ret=0,i,gotversion=0,outofband=0; + STRLIST temp; + unsigned int maxlen=256,buflen; + char *command=NULL,*searchstr=NULL; + byte *line=NULL; + struct kopts *kopts; + struct exec_info *spawn; + +#ifdef EXEC_TEMPFILE_ONLY + opt.keyserver_options.use_temp_files=1; +#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); + + if(opt.keyserver_options.use_temp_files) + { + if(opt.keyserver_options.keep_temp_files) + { + command=m_realloc(command,strlen(command)+ + strlen(KEYSERVER_ARGS_KEEP)+1); + strcat(command,KEYSERVER_ARGS_KEEP); + } + else + { + command=m_realloc(command,strlen(command)+ + strlen(KEYSERVER_ARGS_NOKEEP)+1); + strcat(command,KEYSERVER_ARGS_NOKEEP); + } + + ret=exec_write(&spawn,NULL,command,NULL,0,0); + } + else + ret=exec_write(&spawn,command,NULL,NULL,0,0); + + if(ret) + return ret; + + fprintf(spawn->tochild,"# This is a gpg keyserver communications file\n"); + fprintf(spawn->tochild,"VERSION %d\n",KEYSERVER_PROTO_VERSION); + fprintf(spawn->tochild,"PROGRAM %s\n",VERSION); + fprintf(spawn->tochild,"HOST %s\n",opt.keyserver_host); + + if(atoi(opt.keyserver_port)>0) + fprintf(spawn->tochild,"PORT %s\n",opt.keyserver_port); + + /* Write options */ + + for(i=0,kopts=keyserver_opts;kopts[i].name;i++) + if(*(kopts[i].flag) && kopts[i].tell) + fprintf(spawn->tochild,"OPTION %s\n",kopts[i].name); + + for(i=0;i<opt.keyserver_options.verbose;i++) + fprintf(spawn->tochild,"OPTION verbose\n"); + + temp=opt.keyserver_options.other; + + for(;temp;temp=temp->next) + fprintf(spawn->tochild,"OPTION %s\n",temp->d); + + switch(action) + { + case GET: + { + fprintf(spawn->tochild,"COMMAND GET\n\n"); + + /* Which keys do we want? */ + + for(i=0;i<count;i++) + { + 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",(byte)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",(byte)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 + fprintf(spawn->tochild,"0x%08lX\n", + (ulong)desc[i].u.kid[1]); + } + + fprintf(spawn->tochild,"\n"); + + break; + } + + case 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(); + + temp=NULL; + add_to_strlist(&temp,key->d); + + memset(&afx,0,sizeof(afx)); + afx.what=1; + iobuf_push_filter(buffer,armor_filter,&afx); + + if(export_pubkeys_stream(buffer,temp,1)==-1) + iobuf_close(buffer); + else + { + iobuf_flush_temp(buffer); + + 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); + } + + free_strlist(temp); + } + + break; + } + + case 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=m_realloc(searchstr, + strlen(searchstr)+strlen(key->d)+2); + strcat(searchstr," "); + } + else + { + searchstr=m_alloc(strlen(key->d)+1); + searchstr[0]='\0'; + } + + strcat(searchstr,key->d); + } + + fprintf(spawn->tochild,"\n"); + + break; + } + + default: + log_fatal(_("no keyserver action!\n")); + break; + } + + /* Done sending, so start reading. */ + ret=exec_read(spawn); + if(ret) + goto fail; + + /* Now handle the response */ + + for(;;) + { + char *ptr; + + if(iobuf_read_line(spawn->fromchild,&line,&buflen,&maxlen)==0) + { + ret=G10ERR_READ_FILE; + goto fail; /* i.e. EOF */ + } + + ptr=line; + + if(*ptr=='\r') + ptr++; + + if(*ptr=='\n') + ptr++; + + if(*ptr=='\0') + break; + + if(ascii_memcasecmp(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_memcasecmp(ptr,"PROGRAM ",8)==0) + { + if(ascii_memcasecmp(&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) + outofband=1; /* Currently the only OPTION */ + } + + m_free(line); + + if(!gotversion) + { + log_error(_("keyserver did not send VERSION\n")); + goto fail; + } + + if(!outofband) + switch(action) + { + case GET: + { + 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,0,stats_handle); + + import_print_stats(stats_handle); + import_release_stats_handle(stats_handle); + + break; + } + + /* Nothing to do here */ + case SEND: + break; + + 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); + + break; + } + + default: + log_fatal(_("no keyserver action!\n")); + break; + } + + fail: + *prog=exec_finish(spawn); + + return ret; +} + +static int +keyserver_work(int action,STRLIST list,KEYDB_SEARCH_DESC *desc,int count) +{ + int rc=0,ret=0; + + if(opt.keyserver_scheme==NULL || + opt.keyserver_host==NULL || + opt.keyserver_port==NULL) + { + log_error(_("no keyserver known (use option --keyserver)\n")); + return G10ERR_BAD_URI; + } + +#ifndef USE_EXTERNAL_HKP + /* Use the internal HKP code */ + if(ascii_strcasecmp(opt.keyserver_scheme,"hkp")==0) + { + 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 + + /* It's not the internal HKP code, so try and spawn a handler for it */ + + rc=keyserver_spawn(action,list,desc,count,&ret); + if(ret) + { + 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"), + action==GET?"get":action==SEND?"send": + action==SEARCH?"search":"unknown", + opt.keyserver_scheme); + + case KEYSERVER_INTERNAL_ERROR: + default: + log_error(_("keyserver internal error\n")); + break; + } + + return G10ERR_KEYSERVER; + } + + if(rc) + { + log_error(_("keyserver communications error: %s\n"),g10_errstr(rc)); + + return rc; + } + + return 0; +} + +int +keyserver_export(STRLIST users) +{ + /* We better ask for confirmation when the user entered --send-keys + without arguments. Sending all keys might not be the thing he + intended to do */ + if (users || opt.batch || opt.answer_yes) + ; + else if ( !cpr_get_answer_is_yes + ("keyserver_export.send_all", + _("Do you really want to send all your " + "public keys to the keyserver? (y/N) "))) + return -1; + + return keyserver_work(SEND,users,NULL,0); +} + +int +keyserver_import(STRLIST users) +{ + KEYDB_SEARCH_DESC *desc; + int num=100,count=0; + int rc=0; + + /* Build a list of key ids */ + desc=m_alloc(sizeof(KEYDB_SEARCH_DESC)*num); + + 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) + { + log_error(_("skipping invalid key ID \"%s\"\n"),users->d); + continue; + } + + count++; + if(count==num) + { + num+=100; + desc=m_realloc(desc,sizeof(KEYDB_SEARCH_DESC)*num); + } + } + + if(count>0) + rc=keyserver_work(GET,NULL,desc,count); + + m_free(desc); + + return rc; +} + +int +keyserver_import_fprint(const byte *fprint,size_t fprint_len) +{ + KEYDB_SEARCH_DESC desc; + + memset(&desc,0,sizeof(desc)); + + if(fprint_len==16) + desc.mode=KEYDB_SEARCH_MODE_FPR16; + else if(fprint_len==20) + desc.mode=KEYDB_SEARCH_MODE_FPR20; + else + return -1; + + memcpy(desc.u.fpr,fprint,fprint_len); + + return keyserver_work(GET,NULL,&desc,1); +} + +int +keyserver_import_keyid(u32 *keyid) +{ + KEYDB_SEARCH_DESC desc; + + memset(&desc,0,sizeof(desc)); + + desc.mode=KEYDB_SEARCH_MODE_LONG_KID; + desc.u.kid[0]=keyid[0]; + desc.u.kid[1]=keyid[1]; + + return keyserver_work(GET,NULL,&desc,1); +} + +/* code mostly stolen from do_export_stream */ +static int +keyidlist(STRLIST 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; + + *count=0; + + *klist=m_alloc(sizeof(KEYDB_SEARCH_DESC)*num); + + kdbhd=keydb_new(0); + + if(!users) + { + ndesc = 1; + desc = m_alloc_clear ( ndesc * sizeof *desc); + desc[0].mode = KEYDB_SEARCH_MODE_FIRST; + } + else + { + for (ndesc=0, sl=users; sl; sl = sl->next, ndesc++) + ; + desc = m_alloc ( ndesc * sizeof *desc); + + for (ndesc=0, sl=users; sl; sl = sl->next) + { + if(classify_user_id (sl->d, desc+ndesc)) + ndesc++; + else + log_error (_("key `%s' not found: %s\n"), + sl->d, g10_errstr (G10ERR_INV_USER_ID)); + } + } + + while (!(rc = keydb_search (kdbhd, desc, ndesc))) + { + if (!users) + desc[0].mode = KEYDB_SEARCH_MODE_NEXT; + + /* read the keyblock */ + rc = keydb_get_keyblock (kdbhd, &keyblock ); + if( rc ) + { + log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) ); + goto leave; + } + + if((node=find_kbnode(keyblock,PKT_PUBLIC_KEY))) + { + /* This is to work around a bug in some keyservers (pksd and + OKS) that calculate v4 RSA keyids as if they were v3 RSA. + The answer is to refresh both the correct v4 keyid + (e.g. 99242560) and the fake v3 keyid (e.g. 68FDDBC7). + This only happens for key refresh using the HKP scheme + and if the refresh-add-fake-v3-keyids keyserver option is + set. */ + if(fakev3 && is_RSA(node->pkt->pkt.public_key->pubkey_algo) && + node->pkt->pkt.public_key->version>=4) + { + (*klist)[*count].mode=KEYDB_SEARCH_MODE_LONG_KID; + mpi_get_keyid(node->pkt->pkt.public_key->pkey[0], + (*klist)[*count].u.kid); + (*count)++; + + if(*count==num) + { + num+=100; + *klist=m_realloc(*klist,sizeof(KEYDB_SEARCH_DESC)*num); + } + } + + /* v4 keys get full fingerprints. v3 keys get long keyids. + This is because it's easy to calculate any sort of key id + from a v4 fingerprint, but not a v3 fingerprint. */ + + if(node->pkt->pkt.public_key->version<4) + { + (*klist)[*count].mode=KEYDB_SEARCH_MODE_LONG_KID; + keyid_from_pk(node->pkt->pkt.public_key, + (*klist)[*count].u.kid); + } + else + { + size_t dummy; + + (*klist)[*count].mode=KEYDB_SEARCH_MODE_FPR20; + fingerprint_from_pk(node->pkt->pkt.public_key, + (*klist)[*count].u.fpr,&dummy); + } + + (*count)++; + + if(*count==num) + { + num+=100; + *klist=m_realloc(*klist,sizeof(KEYDB_SEARCH_DESC)*num); + } + } + } + + if(rc==-1) + rc=0; + + leave: + m_free(desc); + keydb_release(kdbhd); + release_kbnode(keyblock); + + return rc; +} + +/* Note this is different than the original HKP refresh. It allows + usernames to refresh only part of the keyring. */ + +int +keyserver_refresh(STRLIST users) +{ + int rc,count,fakev3=0; + KEYDB_SEARCH_DESC *desc; + + /* We switch merge_only on during a refresh, as 'refresh' should + never import new keys, even if their keyids match. Is it worth + preserving the old merge_only value here? */ + opt.merge_only=1; + + /* If refresh_add_fake_v3_keyids is on and it's a HKP or MAILTO + scheme, then enable fake v3 keyid generation. */ + if(opt.keyserver_options.fake_v3_keyids && opt.keyserver_scheme && + (ascii_strcasecmp(opt.keyserver_scheme,"hkp")==0 || + ascii_strcasecmp(opt.keyserver_scheme,"mailto")==0)) + fakev3=1; + + rc=keyidlist(users,&desc,&count,fakev3); + if(rc) + return rc; + + if(count==1) + log_info(_("%d key to refresh\n"),count); + else + log_info(_("%d keys to refresh\n"),count); + + if(count>0) + rc=keyserver_work(GET,NULL,desc,count); + + m_free(desc); + + return 0; +} + +int +keyserver_search(STRLIST tokens) +{ + if(tokens) + return keyserver_work(SEARCH,tokens,NULL,0); + 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; + } +} |