* curl-shim.h (curl_version): No need to provide a version for
[gnupg.git] / keyserver / gpgkeys_hkp.c
1 /* gpgkeys_hkp.c - talk to an HKP keyserver
2  * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007,
3  *               2008 Free Software Foundation, Inc.
4  *
5  * This file is part of GnuPG.
6  *
7  * GnuPG is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * GnuPG is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, see <http://www.gnu.org/licenses/>.
19  *
20  * In addition, as a special exception, the Free Software Foundation
21  * gives permission to link the code of the keyserver helper tools:
22  * gpgkeys_ldap, gpgkeys_curl and gpgkeys_hkp with the OpenSSL
23  * project's "OpenSSL" library (or with modified versions of it that
24  * use the same license as the "OpenSSL" library), and distribute the
25  * linked executables.  You must obey the GNU General Public License
26  * in all respects for all of the code used other than "OpenSSL".  If
27  * you modify this file, you may extend this exception to your version
28  * of the file, but you are not obligated to do so.  If you do not
29  * wish to do so, delete this exception statement from your version.
30  */
31
32 #include <config.h>
33 #include <stdio.h>
34 #include <string.h>
35 #include <stdlib.h>
36 #include <errno.h>
37 #include <unistd.h>
38 #ifdef HAVE_GETOPT_H
39 #include <getopt.h>
40 #endif
41 #ifdef HAVE_LIBCURL
42 #include <curl/curl.h>
43 #else
44 #include "curl-shim.h"
45 #endif
46 #include "compat.h"
47 #include "keyserver.h"
48 #include "ksutil.h"
49
50 extern char *optarg;
51 extern int optind;
52
53 static FILE *input,*output,*console;
54 static CURL *curl;
55 static struct ks_options *opt;
56 static char errorbuffer[CURL_ERROR_SIZE];
57
58 static size_t
59 curl_mrindex_writer(const void *ptr,size_t size,size_t nmemb,void *stream)
60 {
61   static int checked=0,swallow=0;
62
63   if(!checked)
64     {
65       /* If the document begins with a '<', assume it's a HTML
66          response, which we don't support.  Discard the whole message
67          body.  GPG can handle it, but this is an optimization to deal
68          with it on this side of the pipe.  */
69       const char *buf=ptr;
70       if(buf[0]=='<')
71         swallow=1;
72
73       checked=1;
74     }
75
76   if(swallow || fwrite(ptr,size,nmemb,stream)==nmemb)
77     return size*nmemb;
78   else
79     return 0;
80 }
81
82 /* Append but avoid creating a double slash // in the path. */
83 static char *
84 append_path(char *dest,const char *src)
85 {
86   size_t n=strlen(dest);
87
88   if(src[0]=='/' && n>0 && dest[n-1]=='/')
89     dest[n-1]='\0';
90
91   return strcat(dest,src);
92 }
93
94 int
95 send_key(int *eof)
96 {
97   CURLcode res;
98   char request[MAX_URL+15];
99   int begin=0,end=0,ret=KEYSERVER_INTERNAL_ERROR;
100   char keyid[17],state[6];
101   char line[MAX_LINE];
102   char *key=NULL,*encoded_key=NULL;
103   size_t keysize=1;
104
105   key=malloc(1);
106   if(!key)
107     {
108       fprintf(console,"gpgkeys: unable to allocate memory for key\n");
109       ret=KEYSERVER_NO_MEMORY;
110       goto fail;
111     }
112
113   key[0]='\0';
114
115   /* Read and throw away input until we see the BEGIN */
116
117   while(fgets(line,MAX_LINE,input)!=NULL)
118     if(sscanf(line,"KEY%*[ ]%16s%*[ ]%5s\n",keyid,state)==2
119        && strcmp(state,"BEGIN")==0)
120       {
121         begin=1;
122         break;
123       }
124
125   if(!begin)
126     {
127       /* i.e. eof before the KEY BEGIN was found.  This isn't an
128          error. */
129       *eof=1;
130       ret=KEYSERVER_OK;
131       goto fail;
132     }
133
134   /* Now slurp up everything until we see the END */
135
136   while(fgets(line,MAX_LINE,input))
137     if(sscanf(line,"KEY%*[ ]%16s%*[ ]%3s\n",keyid,state)==2
138        && strcmp(state,"END")==0)
139       {
140         end=1;
141         break;
142       }
143     else
144       {
145         char *tempkey;
146         keysize+=strlen(line);
147         tempkey=realloc(key,keysize);
148         if(tempkey==NULL)
149           {
150             fprintf(console,"gpgkeys: unable to reallocate for key\n");
151             ret=KEYSERVER_NO_MEMORY;
152             goto fail;
153           }
154         else
155           key=tempkey;
156
157         strcat(key,line);
158       }
159
160   if(!end)
161     {
162       fprintf(console,"gpgkeys: no KEY %s END found\n",keyid);
163       *eof=1;
164       ret=KEYSERVER_KEY_INCOMPLETE;
165       goto fail;
166     }
167
168   encoded_key=curl_easy_escape(curl,key,keysize);
169   if(!encoded_key)
170     {
171       fprintf(console,"gpgkeys: out of memory\n");
172       ret=KEYSERVER_NO_MEMORY;
173       goto fail;
174     }
175
176   free(key);
177
178   key=malloc(8+strlen(encoded_key)+1);
179   if(!key)
180     {
181       fprintf(console,"gpgkeys: out of memory\n");
182       ret=KEYSERVER_NO_MEMORY;
183       goto fail;
184     }
185
186   strcpy(key,"keytext=");
187   strcat(key,encoded_key);
188
189   strcpy(request,"http://");
190   strcat(request,opt->host);
191   strcat(request,":");
192   if(opt->port)
193     strcat(request,opt->port);
194   else
195     strcat(request,"11371");
196   strcat(request,opt->path);
197   /* request is MAX_URL+15 bytes long - MAX_URL covers the whole URL,
198      including any supplied path.  The 15 covers /pks/add. */
199   append_path(request,"/pks/add");
200
201   if(opt->verbose>2)
202     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
203
204   curl_easy_setopt(curl,CURLOPT_URL,request);
205   curl_easy_setopt(curl,CURLOPT_POST,1L);
206   curl_easy_setopt(curl,CURLOPT_POSTFIELDS,key);
207   curl_easy_setopt(curl,CURLOPT_FAILONERROR,1L);
208
209   res=curl_easy_perform(curl);
210   if(res!=0)
211     {
212       fprintf(console,"gpgkeys: HTTP post error %d: %s\n",res,errorbuffer);
213       ret=curl_err_to_gpg_err(res);
214       goto fail;
215     }
216   else
217     fprintf(output,"\nKEY %s SENT\n",keyid);
218
219   ret=KEYSERVER_OK;
220
221  fail:
222   free(key);
223   curl_free(encoded_key);
224
225   if(ret!=0 && begin)
226     fprintf(output,"KEY %s FAILED %d\n",keyid,ret);
227
228   return ret;
229 }
230
231 static int
232 get_key(char *getkey)
233 {
234   CURLcode res;
235   char request[MAX_URL+60];
236   char *offset;
237   struct curl_writer_ctx ctx;
238
239   memset(&ctx,0,sizeof(ctx));
240
241   /* Build the search string.  HKP only uses the short key IDs. */
242
243   if(strncmp(getkey,"0x",2)==0)
244     getkey+=2;
245
246   fprintf(output,"KEY 0x%s BEGIN\n",getkey);
247
248   if(strlen(getkey)==32)
249     {
250       fprintf(console,
251               "gpgkeys: HKP keyservers do not support v3 fingerprints\n");
252       fprintf(output,"KEY 0x%s FAILED %d\n",getkey,KEYSERVER_NOT_SUPPORTED);
253       return KEYSERVER_NOT_SUPPORTED;
254     }
255
256   strcpy(request,"http://");
257   strcat(request,opt->host);
258   strcat(request,":");
259   if(opt->port)
260     strcat(request,opt->port);
261   else
262     strcat(request,"11371");
263   strcat(request,opt->path);
264   /* request is MAX_URL+55 bytes long - MAX_URL covers the whole URL,
265      including any supplied path.  The 60 overcovers this /pks/... etc
266      string plus the 8 bytes of key id */
267   append_path(request,"/pks/lookup?op=get&options=mr&search=0x");
268
269   /* fingerprint or long key id.  Take the last 8 characters and treat
270      it like a short key id */
271   if(strlen(getkey)>8)
272     offset=&getkey[strlen(getkey)-8];
273   else
274     offset=getkey;
275
276   strcat(request,offset);
277
278   if(opt->verbose>2)
279     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
280
281   curl_easy_setopt(curl,CURLOPT_URL,request);
282   curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_writer);
283   ctx.stream=output;
284   curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
285
286   res=curl_easy_perform(curl);
287   if(res!=CURLE_OK)
288     {
289       fprintf(console,"gpgkeys: HTTP fetch error %d: %s\n",res,errorbuffer);
290       fprintf(output,"\nKEY 0x%s FAILED %d\n",getkey,curl_err_to_gpg_err(res));
291     }
292   else
293     {
294       curl_writer_finalize(&ctx);
295       if(!ctx.flags.done)
296         {
297           fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
298           fprintf(output,"\nKEY 0x%s FAILED %d\n",
299                   getkey,KEYSERVER_KEY_NOT_FOUND);
300         }
301       else
302         fprintf(output,"\nKEY 0x%s END\n",getkey);
303     }
304
305   return KEYSERVER_OK;
306 }
307
308 static int
309 get_name(const char *getkey)
310 {
311   CURLcode res;
312   char *request=NULL;
313   char *searchkey_encoded;
314   int ret=KEYSERVER_INTERNAL_ERROR;
315   struct curl_writer_ctx ctx;
316
317   memset(&ctx,0,sizeof(ctx));
318
319   searchkey_encoded=curl_easy_escape(curl,(char *)getkey,0);
320   if(!searchkey_encoded)
321     {
322       fprintf(console,"gpgkeys: out of memory\n");
323       ret=KEYSERVER_NO_MEMORY;
324       goto fail;
325     }
326
327   request=malloc(MAX_URL+60+strlen(searchkey_encoded));
328   if(!request)
329     {
330       fprintf(console,"gpgkeys: out of memory\n");
331       ret=KEYSERVER_NO_MEMORY;
332       goto fail;
333     }
334
335   fprintf(output,"NAME %s BEGIN\n",getkey);
336
337   strcpy(request,"http://");
338   strcat(request,opt->host);
339   strcat(request,":");
340   if(opt->port)
341     strcat(request,opt->port);
342   else
343     strcat(request,"11371");
344   strcat(request,opt->path);
345   append_path(request,"/pks/lookup?op=get&options=mr&search=");
346   strcat(request,searchkey_encoded);
347
348   if(opt->action==KS_GETNAME)
349     strcat(request,"&exact=on");
350
351   if(opt->verbose>2)
352     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
353
354   curl_easy_setopt(curl,CURLOPT_URL,request);
355   curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_writer);
356   ctx.stream=output;
357   curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
358
359   res=curl_easy_perform(curl);
360   if(res!=CURLE_OK)
361     {
362       fprintf(console,"gpgkeys: HTTP fetch error %d: %s\n",res,errorbuffer);
363       ret=curl_err_to_gpg_err(res);
364     }
365   else
366     {
367       curl_writer_finalize(&ctx);
368       if(!ctx.flags.done)
369         {
370           fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
371           ret=KEYSERVER_KEY_NOT_FOUND;
372         }
373       else
374         {
375           fprintf(output,"\nNAME %s END\n",getkey);
376           ret=KEYSERVER_OK;
377         }
378     }
379
380  fail:
381   curl_free(searchkey_encoded);
382   free(request);
383
384   if(ret!=KEYSERVER_OK)
385     fprintf(output,"\nNAME %s FAILED %d\n",getkey,ret);
386
387   return ret;
388 }
389
390 static int
391 search_key(const char *searchkey)
392 {
393   CURLcode res;
394   char *request=NULL;
395   char *searchkey_encoded;
396   int ret=KEYSERVER_INTERNAL_ERROR;
397   enum ks_search_type search_type;
398
399   search_type=classify_ks_search(&searchkey);
400
401   if(opt->debug)
402     fprintf(console,"gpgkeys: search type is %d, and key is \"%s\"\n",
403             search_type,searchkey);
404
405   searchkey_encoded=curl_easy_escape(curl,(char *)searchkey,0);
406   if(!searchkey_encoded)
407     {
408       fprintf(console,"gpgkeys: out of memory\n");
409       ret=KEYSERVER_NO_MEMORY;
410       goto fail;
411     }
412
413   request=malloc(MAX_URL+60+strlen(searchkey_encoded));
414   if(!request)
415     {
416       fprintf(console,"gpgkeys: out of memory\n");
417       ret=KEYSERVER_NO_MEMORY;
418       goto fail;
419     }
420
421   fprintf(output,"SEARCH %s BEGIN\n",searchkey);
422
423   strcpy(request,"http://");
424   strcat(request,opt->host);
425   strcat(request,":");
426   if(opt->port)
427     strcat(request,opt->port);
428   else
429     strcat(request,"11371");
430   strcat(request,opt->path);
431   append_path(request,"/pks/lookup?op=index&options=mr&search=");
432
433   /* HKP keyservers like the 0x to be present when searching by
434      keyid */
435   if(search_type==KS_SEARCH_KEYID_SHORT || search_type==KS_SEARCH_KEYID_LONG)
436     strcat(request,"0x");
437
438   strcat(request,searchkey_encoded);
439
440   if(search_type!=KS_SEARCH_SUBSTR)
441     strcat(request,"&exact=on");
442
443   if(opt->verbose>2)
444     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
445
446   curl_easy_setopt(curl,CURLOPT_URL,request);
447   curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_mrindex_writer);
448   curl_easy_setopt(curl,CURLOPT_FILE,output);
449
450   res=curl_easy_perform(curl);
451   if(res!=0)
452     {
453       fprintf(console,"gpgkeys: HTTP search error %d: %s\n",res,errorbuffer);
454       ret=curl_err_to_gpg_err(res);
455     }
456   else
457     {
458       fprintf(output,"\nSEARCH %s END\n",searchkey);
459       ret=KEYSERVER_OK;
460     }
461
462  fail:
463
464   curl_free(searchkey_encoded);
465   free(request);
466
467   if(ret!=KEYSERVER_OK)
468     fprintf(output,"\nSEARCH %s FAILED %d\n",searchkey,ret);
469
470   return ret;
471 }
472
473 void
474 fail_all(struct keylist *keylist,int err)
475 {
476   if(!keylist)
477     return;
478
479   if(opt->action==KS_SEARCH)
480     {
481       fprintf(output,"SEARCH ");
482       while(keylist)
483         {
484           fprintf(output,"%s ",keylist->str);
485           keylist=keylist->next;
486         }
487       fprintf(output,"FAILED %d\n",err);
488     }
489   else
490     while(keylist)
491       {
492         fprintf(output,"KEY %s FAILED %d\n",keylist->str,err);
493         keylist=keylist->next;
494       }
495 }
496
497 static void 
498 show_help (FILE *fp)
499 {
500   fprintf (fp,"-h, --help\thelp\n");
501   fprintf (fp,"-V\t\tmachine readable version\n");
502   fprintf (fp,"--version\thuman readable version\n");
503   fprintf (fp,"-o\t\toutput to this file\n");
504 }
505
506 int
507 main(int argc,char *argv[])
508 {
509   int arg,ret=KEYSERVER_INTERNAL_ERROR;
510   char line[MAX_LINE];
511   int failed=0;
512   struct keylist *keylist=NULL,*keyptr=NULL;
513   char *proxy=NULL;
514
515   console=stderr;
516
517   /* Kludge to implement standard GNU options.  */
518   if (argc > 1 && !strcmp (argv[1], "--version"))
519     {
520       printf("gpgkeys_hkp (GnuPG) "VERSION" (uses %s)\n",curl_version());
521       return 0;
522     }
523   else if (argc > 1 && !strcmp (argv[1], "--help"))
524     {
525       show_help (stdout);
526       return 0;
527     }
528
529   while((arg=getopt(argc,argv,"hVo:"))!=-1)
530     switch(arg)
531       {
532       default:
533       case 'h':
534         show_help (console);
535         return KEYSERVER_OK;
536
537       case 'V':
538         fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
539         return KEYSERVER_OK;
540
541       case 'o':
542         output=fopen(optarg,"w");
543         if(output==NULL)
544           {
545             fprintf(console,"gpgkeys: Cannot open output file `%s': %s\n",
546                     optarg,strerror(errno));
547             return KEYSERVER_INTERNAL_ERROR;
548           }
549
550         break;
551       }
552
553   if(argc>optind)
554     {
555       input=fopen(argv[optind],"r");
556       if(input==NULL)
557         {
558           fprintf(console,"gpgkeys: Cannot open input file `%s': %s\n",
559                   argv[optind],strerror(errno));
560           return KEYSERVER_INTERNAL_ERROR;
561         }
562     }
563
564   if(input==NULL)
565     input=stdin;
566
567   if(output==NULL)
568     output=stdout;
569
570   opt=init_ks_options();
571   if(!opt)
572     return KEYSERVER_NO_MEMORY;
573
574   /* Get the command and info block */
575
576   while(fgets(line,MAX_LINE,input)!=NULL)
577     {
578       int err;
579       char option[MAX_OPTION+1];
580
581       if(line[0]=='\n')
582         break;
583
584       err=parse_ks_options(line,opt);
585       if(err>0)
586         {
587           ret=err;
588           goto fail;
589         }
590       else if(err==0)
591         continue;
592
593       if(sscanf(line,"OPTION %" MKSTRING(MAX_OPTION) "s\n",option)==1)
594         {
595           int no=0;
596           char *start=&option[0];
597
598           option[MAX_OPTION]='\0';
599
600           if(ascii_strncasecmp(option,"no-",3)==0)
601             {
602               no=1;
603               start=&option[3];
604             }
605
606           if(ascii_strncasecmp(start,"http-proxy",10)==0)
607             {
608               if(no)
609                 {
610                   free(proxy);
611                   proxy=strdup("");
612                 }
613               else if(start[10]=='=')
614                 {
615                   if(strlen(&start[11])<MAX_PROXY)
616                     {
617                       free(proxy);
618                       proxy=strdup(&start[11]);
619                     }
620                 }
621             }
622 #if 0
623           else if(ascii_strcasecmp(start,"try-dns-srv")==0)
624             {
625               if(no)
626                 http_flags&=~HTTP_FLAG_TRY_SRV;
627               else
628                 http_flags|=HTTP_FLAG_TRY_SRV;
629             }
630 #endif
631           continue;
632         }
633     }
634
635   if(!opt->host)
636     {
637       fprintf(console,"gpgkeys: no keyserver host provided\n");
638       goto fail;
639     }
640
641   if(opt->timeout && register_timeout()==-1)
642     {
643       fprintf(console,"gpgkeys: unable to register timeout handler\n");
644       return KEYSERVER_INTERNAL_ERROR;
645     }
646
647   curl_global_init(CURL_GLOBAL_DEFAULT);
648   curl=curl_easy_init();
649   if(!curl)
650     {
651       fprintf(console,"gpgkeys: unable to initialize curl\n");
652       ret=KEYSERVER_INTERNAL_ERROR;
653       goto fail;
654     }
655
656   curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errorbuffer);
657
658   if(opt->auth)
659     curl_easy_setopt(curl,CURLOPT_USERPWD,opt->auth);
660
661   if(opt->debug)
662     {
663       fprintf(console,"gpgkeys: curl version = %s\n",curl_version());
664       curl_easy_setopt(curl,CURLOPT_STDERR,console);
665       curl_easy_setopt(curl,CURLOPT_VERBOSE,1L);
666     }
667
668   if(proxy)
669     curl_easy_setopt(curl,CURLOPT_PROXY,proxy);
670
671 #if 0
672   /* By suggested convention, if the user gives a :port, then disable
673      SRV. */
674   if(opt->port)
675     http_flags&=~HTTP_FLAG_TRY_SRV;
676 #endif
677
678   /* If it's a GET or a SEARCH, the next thing to come in is the
679      keyids.  If it's a SEND, then there are no keyids. */
680
681   if(opt->action==KS_SEND)
682     while(fgets(line,MAX_LINE,input)!=NULL && line[0]!='\n');
683   else if(opt->action==KS_GET
684           || opt->action==KS_GETNAME || opt->action==KS_SEARCH)
685     {
686       for(;;)
687         {
688           struct keylist *work;
689
690           if(fgets(line,MAX_LINE,input)==NULL)
691             break;
692           else
693             {
694               if(line[0]=='\n' || line[0]=='\0')
695                 break;
696
697               work=malloc(sizeof(struct keylist));
698               if(work==NULL)
699                 {
700                   fprintf(console,"gpgkeys: out of memory while "
701                           "building key list\n");
702                   ret=KEYSERVER_NO_MEMORY;
703                   goto fail;
704                 }
705
706               strcpy(work->str,line);
707
708               /* Trim the trailing \n */
709               work->str[strlen(line)-1]='\0';
710
711               work->next=NULL;
712
713               /* Always attach at the end to keep the list in proper
714                  order for searching */
715               if(keylist==NULL)
716                 keylist=work;
717               else
718                 keyptr->next=work;
719
720               keyptr=work;
721             }
722         }
723     }
724   else
725     {
726       fprintf(console,"gpgkeys: no keyserver command specified\n");
727       goto fail;
728     }
729
730   /* Send the response */
731
732   fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
733   fprintf(output,"PROGRAM %s %s\n\n",VERSION,curl_version());
734
735   if(opt->verbose>1)
736     {
737       fprintf(console,"Host:\t\t%s\n",opt->host);
738       if(opt->port)
739         fprintf(console,"Port:\t\t%s\n",opt->port);
740       if(strcmp(opt->path,"/")!=0)
741         fprintf(console,"Path:\t\t%s\n",opt->path);
742       fprintf(console,"Command:\t%s\n",ks_action_to_string(opt->action));
743     }
744
745   if(opt->action==KS_GET)
746     {
747       keyptr=keylist;
748
749       while(keyptr!=NULL)
750         {
751           set_timeout(opt->timeout);
752
753           if(get_key(keyptr->str)!=KEYSERVER_OK)
754             failed++;
755
756           keyptr=keyptr->next;
757         }
758     }
759   else if(opt->action==KS_GETNAME)
760     {
761       keyptr=keylist;
762
763       while(keyptr!=NULL)
764         {
765           set_timeout(opt->timeout);
766
767           if(get_name(keyptr->str)!=KEYSERVER_OK)
768             failed++;
769
770           keyptr=keyptr->next;
771         }
772     }
773   else if(opt->action==KS_SEND)
774     {
775       int eof=0;
776
777       do
778         {
779           set_timeout(opt->timeout);
780
781           if(send_key(&eof)!=KEYSERVER_OK)
782             failed++;
783         }
784       while(!eof);
785     }
786   else if(opt->action==KS_SEARCH)
787     {
788       char *searchkey=NULL;
789       int len=0;
790
791       set_timeout(opt->timeout);
792
793       /* To search, we stick a space in between each key to search
794          for. */
795
796       keyptr=keylist;
797       while(keyptr!=NULL)
798         {
799           len+=strlen(keyptr->str)+1;
800           keyptr=keyptr->next;
801         }
802
803       searchkey=malloc(len+1);
804       if(searchkey==NULL)
805         {
806           ret=KEYSERVER_NO_MEMORY;
807           fail_all(keylist,KEYSERVER_NO_MEMORY);
808           goto fail;
809         }
810
811       searchkey[0]='\0';
812
813       keyptr=keylist;
814       while(keyptr!=NULL)
815         {
816           strcat(searchkey,keyptr->str);
817           strcat(searchkey," ");
818           keyptr=keyptr->next;
819         }
820
821       /* Nail that last space */
822       if(*searchkey)
823         searchkey[strlen(searchkey)-1]='\0';
824
825       if(search_key(searchkey)!=KEYSERVER_OK)
826         failed++;
827
828       free(searchkey);
829     }
830   else
831     abort();
832
833   if(!failed)
834     ret=KEYSERVER_OK;
835
836  fail:
837   while(keylist!=NULL)
838     {
839       struct keylist *current=keylist;
840       keylist=keylist->next;
841       free(current);
842     }
843
844   if(input!=stdin)
845     fclose(input);
846
847   if(output!=stdout)
848     fclose(output);
849
850   free_ks_options(opt);
851
852   if(curl)
853     curl_easy_cleanup(curl);
854
855   free(proxy);
856
857   return ret;
858 }