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