summaryrefslogtreecommitdiff
path: root/keyserver/gpgkeys_hkp.c
diff options
context:
space:
mode:
authorKibum Kim <kb0929.kim@samsung.com>2012-01-07 00:46:38 +0900
committerKibum Kim <kb0929.kim@samsung.com>2012-01-07 00:46:38 +0900
commitf5660c6460a863b19f9ef745575780e37cc192a9 (patch)
tree0b478679da32d706de7b0de546d2e4daf03b160c /keyserver/gpgkeys_hkp.c
parent06b9124a4f9d38acc78e6af686bc49a06f6354f8 (diff)
downloadgnupg-f5660c6460a863b19f9ef745575780e37cc192a9.tar.gz
gnupg-f5660c6460a863b19f9ef745575780e37cc192a9.tar.bz2
gnupg-f5660c6460a863b19f9ef745575780e37cc192a9.zip
Diffstat (limited to 'keyserver/gpgkeys_hkp.c')
-rw-r--r--keyserver/gpgkeys_hkp.c854
1 files changed, 854 insertions, 0 deletions
diff --git a/keyserver/gpgkeys_hkp.c b/keyserver/gpgkeys_hkp.c
new file mode 100644
index 0000000..e393b85
--- /dev/null
+++ b/keyserver/gpgkeys_hkp.c
@@ -0,0 +1,854 @@
+/* gpgkeys_hkp.c - talk to an HKP keyserver
+ * Copyright (C) 2001, 2002, 2003, 2004, 2005
+ * 2006 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ * In addition, as a special exception, the Free Software Foundation
+ * gives permission to link the code of the keyserver helper tools:
+ * gpgkeys_ldap, gpgkeys_curl and gpgkeys_hkp with the OpenSSL
+ * project's "OpenSSL" library (or with modified versions of it that
+ * use the same license as the "OpenSSL" library), and distribute the
+ * linked executables. You must obey the GNU General Public License
+ * in all respects for all of the code used other than "OpenSSL". If
+ * you modify this file, you may extend this exception to your version
+ * of the file, but you are not obligated to do so. If you do not
+ * wish to do so, delete this exception statement from your version.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#ifdef HAVE_GETOPT_H
+#include <getopt.h>
+#endif
+#ifdef HAVE_LIBCURL
+#include <curl/curl.h>
+#else
+#include "curl-shim.h"
+#endif
+#include "keyserver.h"
+#include "ksutil.h"
+
+extern char *optarg;
+extern int optind;
+
+static FILE *input,*output,*console;
+static CURL *curl;
+static struct ks_options *opt;
+static char errorbuffer[CURL_ERROR_SIZE];
+
+static size_t
+curl_mrindex_writer(const void *ptr,size_t size,size_t nmemb,void *stream)
+{
+ static int checked=0,swallow=0;
+
+ if(!checked)
+ {
+ /* If the document begins with a '<', assume it's a HTML
+ response, which we don't support. Discard the whole message
+ body. GPG can handle it, but this is an optimization to deal
+ with it on this side of the pipe. */
+ const char *buf=ptr;
+ if(buf[0]=='<')
+ swallow=1;
+
+ checked=1;
+ }
+
+ if(swallow || fwrite(ptr,size,nmemb,stream)==nmemb)
+ return size*nmemb;
+ else
+ return 0;
+}
+
+/* Append but avoid creating a double slash // in the path. */
+static char *
+append_path(char *dest,const char *src)
+{
+ size_t n=strlen(dest);
+
+ if(src[0]=='/' && n>0 && dest[n-1]=='/')
+ dest[n-1]='\0';
+
+ return strcat(dest,src);
+}
+
+int
+send_key(int *eof)
+{
+ CURLcode res;
+ char request[MAX_URL+15];
+ int begin=0,end=0,ret=KEYSERVER_INTERNAL_ERROR;
+ char keyid[17],state[6];
+ char line[MAX_LINE];
+ char *key=NULL,*encoded_key=NULL;
+ size_t keylen=0,keymax=0;
+
+ /* Read and throw away input until we see the BEGIN */
+
+ while(fgets(line,MAX_LINE,input)!=NULL)
+ if(sscanf(line,"KEY%*[ ]%16s%*[ ]%5s\n",keyid,state)==2
+ && strcmp(state,"BEGIN")==0)
+ {
+ begin=1;
+ break;
+ }
+
+ if(!begin)
+ {
+ /* i.e. eof before the KEY BEGIN was found. This isn't an
+ error. */
+ *eof=1;
+ ret=KEYSERVER_OK;
+ goto fail;
+ }
+
+ /* Now slurp up everything until we see the END */
+
+ while(fgets(line,MAX_LINE,input))
+ if(sscanf(line,"KEY%*[ ]%16s%*[ ]%3s\n",keyid,state)==2
+ && strcmp(state,"END")==0)
+ {
+ end=1;
+ break;
+ }
+ else
+ {
+ if(strlen(line)+keylen>keymax)
+ {
+ char *tmp;
+
+ keymax+=200;
+ tmp=realloc(key,keymax+1);
+ if(!tmp)
+ {
+ free(key);
+ fprintf(console,"gpgkeys: out of memory\n");
+ ret=KEYSERVER_NO_MEMORY;
+ goto fail;
+ }
+
+ key=tmp;
+ }
+
+ strcpy(&key[keylen],line);
+ keylen+=strlen(line);
+ }
+
+ if(!end)
+ {
+ fprintf(console,"gpgkeys: no KEY %s END found\n",keyid);
+ *eof=1;
+ ret=KEYSERVER_KEY_INCOMPLETE;
+ goto fail;
+ }
+
+ encoded_key=curl_escape(key,keylen);
+ if(!encoded_key)
+ {
+ fprintf(console,"gpgkeys: out of memory\n");
+ ret=KEYSERVER_NO_MEMORY;
+ goto fail;
+ }
+
+ free(key);
+
+ key=malloc(8+strlen(encoded_key)+1);
+ if(!key)
+ {
+ fprintf(console,"gpgkeys: out of memory\n");
+ ret=KEYSERVER_NO_MEMORY;
+ goto fail;
+ }
+
+ strcpy(key,"keytext=");
+ strcat(key,encoded_key);
+
+ strcpy(request,"http://");
+ strcat(request,opt->host);
+ strcat(request,":");
+ if(opt->port)
+ strcat(request,opt->port);
+ else
+ strcat(request,"11371");
+ strcat(request,opt->path);
+ /* request is MAX_URL+15 bytes long - MAX_URL covers the whole URL,
+ including any supplied path. The 15 covers /pks/add. */
+ append_path(request,"/pks/add");
+
+ if(opt->verbose>2)
+ fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
+
+ curl_easy_setopt(curl,CURLOPT_URL,request);
+ curl_easy_setopt(curl,CURLOPT_POST,1);
+ curl_easy_setopt(curl,CURLOPT_POSTFIELDS,key);
+ curl_easy_setopt(curl,CURLOPT_FAILONERROR,1);
+
+ res=curl_easy_perform(curl);
+ if(res!=0)
+ {
+ fprintf(console,"gpgkeys: HTTP post error %d: %s\n",res,errorbuffer);
+ ret=curl_err_to_gpg_err(res);
+ goto fail;
+ }
+ else
+ fprintf(output,"\nKEY %s SENT\n",keyid);
+
+ ret=KEYSERVER_OK;
+
+ fail:
+ free(key);
+ curl_free(encoded_key);
+
+ if(ret!=0 && begin)
+ fprintf(output,"KEY %s FAILED %d\n",keyid,ret);
+
+ return ret;
+}
+
+static int
+get_key(char *getkey)
+{
+ CURLcode res;
+ char request[MAX_URL+60];
+ char *offset;
+ struct curl_writer_ctx ctx;
+
+ memset(&ctx,0,sizeof(ctx));
+
+ /* Build the search string. HKP only uses the short key IDs. */
+
+ if(strncmp(getkey,"0x",2)==0)
+ getkey+=2;
+
+ fprintf(output,"KEY 0x%s BEGIN\n",getkey);
+
+ if(strlen(getkey)==32)
+ {
+ fprintf(console,
+ "gpgkeys: HKP keyservers do not support v3 fingerprints\n");
+ fprintf(output,"KEY 0x%s FAILED %d\n",getkey,KEYSERVER_NOT_SUPPORTED);
+ return KEYSERVER_NOT_SUPPORTED;
+ }
+
+ strcpy(request,"http://");
+ strcat(request,opt->host);
+ strcat(request,":");
+ if(opt->port)
+ strcat(request,opt->port);
+ else
+ strcat(request,"11371");
+ strcat(request,opt->path);
+ /* request is MAX_URL+55 bytes long - MAX_URL covers the whole URL,
+ including any supplied path. The 60 overcovers this /pks/... etc
+ string plus the 8 bytes of key id */
+ append_path(request,"/pks/lookup?op=get&options=mr&search=0x");
+
+ /* fingerprint or long key id. Take the last 8 characters and treat
+ it like a short key id */
+ if(strlen(getkey)>8)
+ offset=&getkey[strlen(getkey)-8];
+ else
+ offset=getkey;
+
+ strcat(request,offset);
+
+ if(opt->verbose>2)
+ fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
+
+ curl_easy_setopt(curl,CURLOPT_URL,request);
+ curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_writer);
+ ctx.stream=output;
+ curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
+
+ res=curl_easy_perform(curl);
+ if(res!=CURLE_OK)
+ {
+ fprintf(console,"gpgkeys: HTTP fetch error %d: %s\n",res,errorbuffer);
+ fprintf(output,"\nKEY 0x%s FAILED %d\n",getkey,curl_err_to_gpg_err(res));
+ }
+ else
+ {
+ curl_writer_finalize(&ctx);
+ if(!ctx.flags.done)
+ {
+ fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
+ fprintf(output,"\nKEY 0x%s FAILED %d\n",
+ getkey,KEYSERVER_KEY_NOT_FOUND);
+ }
+ else
+ fprintf(output,"\nKEY 0x%s END\n",getkey);
+ }
+
+ return KEYSERVER_OK;
+}
+
+static int
+get_name(const char *getkey)
+{
+ CURLcode res;
+ char *request=NULL;
+ char *searchkey_encoded;
+ int ret=KEYSERVER_INTERNAL_ERROR;
+ struct curl_writer_ctx ctx;
+
+ memset(&ctx,0,sizeof(ctx));
+
+ searchkey_encoded=curl_escape((char *)getkey,0);
+ if(!searchkey_encoded)
+ {
+ fprintf(console,"gpgkeys: out of memory\n");
+ ret=KEYSERVER_NO_MEMORY;
+ goto fail;
+ }
+
+ request=malloc(MAX_URL+60+strlen(searchkey_encoded));
+ if(!request)
+ {
+ fprintf(console,"gpgkeys: out of memory\n");
+ ret=KEYSERVER_NO_MEMORY;
+ goto fail;
+ }
+
+ fprintf(output,"NAME %s BEGIN\n",getkey);
+
+ strcpy(request,"http://");
+ strcat(request,opt->host);
+ strcat(request,":");
+ if(opt->port)
+ strcat(request,opt->port);
+ else
+ strcat(request,"11371");
+ strcat(request,opt->path);
+ append_path(request,"/pks/lookup?op=get&options=mr&search=");
+ strcat(request,searchkey_encoded);
+
+ if(opt->action==KS_GETNAME)
+ strcat(request,"&exact=on");
+
+ if(opt->verbose>2)
+ fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
+
+ curl_easy_setopt(curl,CURLOPT_URL,request);
+ curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_writer);
+ ctx.stream=output;
+ curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
+
+ res=curl_easy_perform(curl);
+ if(res!=CURLE_OK)
+ {
+ fprintf(console,"gpgkeys: HTTP fetch error %d: %s\n",res,errorbuffer);
+ ret=curl_err_to_gpg_err(res);
+ }
+ else
+ {
+ curl_writer_finalize(&ctx);
+ if(!ctx.flags.done)
+ {
+ fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
+ ret=KEYSERVER_KEY_NOT_FOUND;
+ }
+ else
+ {
+ fprintf(output,"\nNAME %s END\n",getkey);
+ ret=KEYSERVER_OK;
+ }
+ }
+
+ fail:
+ curl_free(searchkey_encoded);
+ free(request);
+
+ if(ret!=KEYSERVER_OK)
+ fprintf(output,"\nNAME %s FAILED %d\n",getkey,ret);
+
+ return ret;
+}
+
+static int
+search_key(const char *searchkey)
+{
+ CURLcode res;
+ char *request=NULL;
+ char *searchkey_encoded;
+ int ret=KEYSERVER_INTERNAL_ERROR;
+ enum ks_search_type search_type;
+
+ search_type=classify_ks_search(&searchkey);
+
+ if(opt->debug)
+ fprintf(console,"gpgkeys: search type is %d, and key is \"%s\"\n",
+ search_type,searchkey);
+
+ searchkey_encoded=curl_escape((char *)searchkey,0);
+ if(!searchkey_encoded)
+ {
+ fprintf(console,"gpgkeys: out of memory\n");
+ ret=KEYSERVER_NO_MEMORY;
+ goto fail;
+ }
+
+ request=malloc(MAX_URL+60+strlen(searchkey_encoded));
+ if(!request)
+ {
+ fprintf(console,"gpgkeys: out of memory\n");
+ ret=KEYSERVER_NO_MEMORY;
+ goto fail;
+ }
+
+ fprintf(output,"SEARCH %s BEGIN\n",searchkey);
+
+ strcpy(request,"http://");
+ strcat(request,opt->host);
+ strcat(request,":");
+ if(opt->port)
+ strcat(request,opt->port);
+ else
+ strcat(request,"11371");
+ strcat(request,opt->path);
+ append_path(request,"/pks/lookup?op=index&options=mr&search=");
+
+ /* HKP keyservers like the 0x to be present when searching by
+ keyid */
+ if(search_type==KS_SEARCH_KEYID_SHORT || search_type==KS_SEARCH_KEYID_LONG)
+ strcat(request,"0x");
+
+ strcat(request,searchkey_encoded);
+
+ if(search_type!=KS_SEARCH_SUBSTR)
+ strcat(request,"&exact=on");
+
+ if(opt->verbose>2)
+ fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
+
+ curl_easy_setopt(curl,CURLOPT_URL,request);
+ curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_mrindex_writer);
+ curl_easy_setopt(curl,CURLOPT_FILE,output);
+
+ res=curl_easy_perform(curl);
+ if(res!=0)
+ {
+ fprintf(console,"gpgkeys: HTTP search error %d: %s\n",res,errorbuffer);
+ ret=curl_err_to_gpg_err(res);
+ }
+ else
+ {
+ fprintf(output,"\nSEARCH %s END\n",searchkey);
+ ret=KEYSERVER_OK;
+ }
+
+ fail:
+
+ curl_free(searchkey_encoded);
+ free(request);
+
+ if(ret!=KEYSERVER_OK)
+ fprintf(output,"\nSEARCH %s FAILED %d\n",searchkey,ret);
+
+ return ret;
+}
+
+void
+fail_all(struct keylist *keylist,int err)
+{
+ if(!keylist)
+ return;
+
+ if(opt->action==KS_SEARCH)
+ {
+ fprintf(output,"SEARCH ");
+ while(keylist)
+ {
+ fprintf(output,"%s ",keylist->str);
+ keylist=keylist->next;
+ }
+ fprintf(output,"FAILED %d\n",err);
+ }
+ else
+ while(keylist)
+ {
+ fprintf(output,"KEY %s FAILED %d\n",keylist->str,err);
+ keylist=keylist->next;
+ }
+}
+
+static void
+show_help (FILE *fp)
+{
+ fprintf (fp,"-h\thelp\n");
+ fprintf (fp,"-V\tversion\n");
+ fprintf (fp,"-o\toutput to this file\n");
+}
+
+int
+main(int argc,char *argv[])
+{
+ int arg,ret=KEYSERVER_INTERNAL_ERROR;
+ char line[MAX_LINE];
+ int failed=0;
+ struct keylist *keylist=NULL,*keyptr=NULL;
+ char *proxy=NULL;
+
+ console=stderr;
+
+ /* Kludge to implement standard GNU options. */
+ if (argc > 1 && !strcmp (argv[1], "--version"))
+ {
+ fputs ("gpgkeys_hkp (GnuPG) " VERSION"\n", stdout);
+ return 0;
+ }
+ else if (argc > 1 && !strcmp (argv[1], "--help"))
+ {
+ show_help (stdout);
+ return 0;
+ }
+
+ while((arg=getopt(argc,argv,"hVo:"))!=-1)
+ switch(arg)
+ {
+ default:
+ case 'h':
+ show_help (console);
+ return KEYSERVER_OK;
+
+ case 'V':
+ fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
+ return KEYSERVER_OK;
+
+ case 'o':
+ output=fopen(optarg,"w");
+ if(output==NULL)
+ {
+ fprintf(console,"gpgkeys: Cannot open output file `%s': %s\n",
+ optarg,strerror(errno));
+ return KEYSERVER_INTERNAL_ERROR;
+ }
+
+ break;
+ }
+
+ if(argc>optind)
+ {
+ input=fopen(argv[optind],"r");
+ if(input==NULL)
+ {
+ fprintf(console,"gpgkeys: Cannot open input file `%s': %s\n",
+ argv[optind],strerror(errno));
+ return KEYSERVER_INTERNAL_ERROR;
+ }
+ }
+
+ if(input==NULL)
+ input=stdin;
+
+ if(output==NULL)
+ output=stdout;
+
+ opt=init_ks_options();
+ if(!opt)
+ return KEYSERVER_NO_MEMORY;
+
+ /* Get the command and info block */
+
+ while(fgets(line,MAX_LINE,input)!=NULL)
+ {
+ int err;
+ char option[MAX_OPTION+1];
+
+ if(line[0]=='\n')
+ break;
+
+ err=parse_ks_options(line,opt);
+ if(err>0)
+ {
+ ret=err;
+ goto fail;
+ }
+ else if(err==0)
+ continue;
+
+ if(sscanf(line,"OPTION %" MKSTRING(MAX_OPTION) "s\n",option)==1)
+ {
+ int no=0;
+ char *start=&option[0];
+
+ option[MAX_OPTION]='\0';
+
+ if(strncasecmp(option,"no-",3)==0)
+ {
+ no=1;
+ start=&option[3];
+ }
+
+ if(strncasecmp(start,"http-proxy",10)==0)
+ {
+ if(no)
+ {
+ free(proxy);
+ proxy=strdup("");
+ }
+ else if(start[10]=='=')
+ {
+ if(strlen(&start[11])<MAX_PROXY)
+ {
+ free(proxy);
+ proxy=strdup(&start[11]);
+ }
+ }
+ }
+#if 0
+ else if(strcasecmp(start,"try-dns-srv")==0)
+ {
+ if(no)
+ http_flags&=~HTTP_FLAG_TRY_SRV;
+ else
+ http_flags|=HTTP_FLAG_TRY_SRV;
+ }
+#endif
+ continue;
+ }
+ }
+
+ if(!opt->host)
+ {
+ fprintf(console,"gpgkeys: no keyserver host provided\n");
+ goto fail;
+ }
+
+ if(opt->timeout && register_timeout()==-1)
+ {
+ fprintf(console,"gpgkeys: unable to register timeout handler\n");
+ return KEYSERVER_INTERNAL_ERROR;
+ }
+
+ curl_global_init(CURL_GLOBAL_DEFAULT);
+ curl=curl_easy_init();
+ if(!curl)
+ {
+ fprintf(console,"gpgkeys: unable to initialize curl\n");
+ ret=KEYSERVER_INTERNAL_ERROR;
+ goto fail;
+ }
+
+ curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errorbuffer);
+
+ if(opt->auth)
+ curl_easy_setopt(curl,CURLOPT_USERPWD,opt->auth);
+
+ if(opt->debug)
+ {
+ fprintf(console,"gpgkeys: curl version = %s\n",curl_version());
+ curl_easy_setopt(curl,CURLOPT_STDERR,console);
+ curl_easy_setopt(curl,CURLOPT_VERBOSE,1);
+ }
+
+ if(proxy)
+ curl_easy_setopt(curl,CURLOPT_PROXY,proxy);
+
+#if 0
+ /* By suggested convention, if the user gives a :port, then disable
+ SRV. */
+ if(opt->port)
+ http_flags&=~HTTP_FLAG_TRY_SRV;
+#endif
+
+ /* If it's a GET or a SEARCH, the next thing to come in is the
+ keyids. If it's a SEND, then there are no keyids. */
+
+ if(opt->action==KS_SEND)
+ while(fgets(line,MAX_LINE,input)!=NULL && line[0]!='\n');
+ else if(opt->action==KS_GET
+ || opt->action==KS_GETNAME || opt->action==KS_SEARCH)
+ {
+ for(;;)
+ {
+ struct keylist *work;
+
+ if(fgets(line,MAX_LINE,input)==NULL)
+ break;
+ else
+ {
+ if(line[0]=='\n' || line[0]=='\0')
+ break;
+
+ work=malloc(sizeof(struct keylist));
+ if(work==NULL)
+ {
+ fprintf(console,"gpgkeys: out of memory while "
+ "building key list\n");
+ ret=KEYSERVER_NO_MEMORY;
+ goto fail;
+ }
+
+ strcpy(work->str,line);
+
+ /* Trim the trailing \n */
+ work->str[strlen(line)-1]='\0';
+
+ work->next=NULL;
+
+ /* Always attach at the end to keep the list in proper
+ order for searching */
+ if(keylist==NULL)
+ keylist=work;
+ else
+ keyptr->next=work;
+
+ keyptr=work;
+ }
+ }
+ }
+ else
+ {
+ fprintf(console,"gpgkeys: no keyserver command specified\n");
+ goto fail;
+ }
+
+ /* Send the response */
+
+ fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
+ fprintf(output,"PROGRAM %s\n\n",VERSION);
+
+ if(opt->verbose>1)
+ {
+ fprintf(console,"Host:\t\t%s\n",opt->host);
+ if(opt->port)
+ fprintf(console,"Port:\t\t%s\n",opt->port);
+ if(strcmp(opt->path,"/")!=0)
+ fprintf(console,"Path:\t\t%s\n",opt->path);
+ fprintf(console,"Command:\t%s\n",ks_action_to_string(opt->action));
+ }
+
+ if(opt->action==KS_GET)
+ {
+ keyptr=keylist;
+
+ while(keyptr!=NULL)
+ {
+ set_timeout(opt->timeout);
+
+ if(get_key(keyptr->str)!=KEYSERVER_OK)
+ failed++;
+
+ keyptr=keyptr->next;
+ }
+ }
+ else if(opt->action==KS_GETNAME)
+ {
+ keyptr=keylist;
+
+ while(keyptr!=NULL)
+ {
+ set_timeout(opt->timeout);
+
+ if(get_name(keyptr->str)!=KEYSERVER_OK)
+ failed++;
+
+ keyptr=keyptr->next;
+ }
+ }
+ else if(opt->action==KS_SEND)
+ {
+ int eof=0;
+
+ do
+ {
+ set_timeout(opt->timeout);
+
+ if(send_key(&eof)!=KEYSERVER_OK)
+ failed++;
+ }
+ while(!eof);
+ }
+ else if(opt->action==KS_SEARCH)
+ {
+ char *searchkey=NULL;
+ int len=0;
+
+ set_timeout(opt->timeout);
+
+ /* To search, we stick a space in between each key to search
+ for. */
+
+ keyptr=keylist;
+ while(keyptr!=NULL)
+ {
+ len+=strlen(keyptr->str)+1;
+ keyptr=keyptr->next;
+ }
+
+ searchkey=malloc(len+1);
+ if(searchkey==NULL)
+ {
+ ret=KEYSERVER_NO_MEMORY;
+ fail_all(keylist,KEYSERVER_NO_MEMORY);
+ goto fail;
+ }
+
+ searchkey[0]='\0';
+
+ keyptr=keylist;
+ while(keyptr!=NULL)
+ {
+ strcat(searchkey,keyptr->str);
+ strcat(searchkey," ");
+ keyptr=keyptr->next;
+ }
+
+ /* Nail that last space */
+ if(*searchkey)
+ searchkey[strlen(searchkey)-1]='\0';
+
+ if(search_key(searchkey)!=KEYSERVER_OK)
+ failed++;
+
+ free(searchkey);
+ }
+ else
+ abort();
+
+ if(!failed)
+ ret=KEYSERVER_OK;
+
+ fail:
+ while(keylist!=NULL)
+ {
+ struct keylist *current=keylist;
+ keylist=keylist->next;
+ free(current);
+ }
+
+ if(input!=stdin)
+ fclose(input);
+
+ if(output!=stdout)
+ fclose(output);
+
+ free_ks_options(opt);
+
+ if(curl)
+ curl_easy_cleanup(curl);
+
+ free(proxy);
+
+ return ret;
+}