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