* curl-shim.h, gpgkeys_curl.c, gpgkeys_hkp.c (main): Always show curl
[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) %s\n", VERSION);
521       printf ("Uses: %s\n", curl_version());
522       return 0;
523     }
524   else if (argc > 1 && !strcmp (argv[1], "--help"))
525     {
526       show_help (stdout);
527       return 0;
528     }
529
530   while((arg=getopt(argc,argv,"hVo:"))!=-1)
531     switch(arg)
532       {
533       default:
534       case 'h':
535         show_help (console);
536         return KEYSERVER_OK;
537
538       case 'V':
539         fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
540         return KEYSERVER_OK;
541
542       case 'o':
543         output=fopen(optarg,"w");
544         if(output==NULL)
545           {
546             fprintf(console,"gpgkeys: Cannot open output file `%s': %s\n",
547                     optarg,strerror(errno));
548             return KEYSERVER_INTERNAL_ERROR;
549           }
550
551         break;
552       }
553
554   if(argc>optind)
555     {
556       input=fopen(argv[optind],"r");
557       if(input==NULL)
558         {
559           fprintf(console,"gpgkeys: Cannot open input file `%s': %s\n",
560                   argv[optind],strerror(errno));
561           return KEYSERVER_INTERNAL_ERROR;
562         }
563     }
564
565   if(input==NULL)
566     input=stdin;
567
568   if(output==NULL)
569     output=stdout;
570
571   opt=init_ks_options();
572   if(!opt)
573     return KEYSERVER_NO_MEMORY;
574
575   /* Get the command and info block */
576
577   while(fgets(line,MAX_LINE,input)!=NULL)
578     {
579       int err;
580       char option[MAX_OPTION+1];
581
582       if(line[0]=='\n')
583         break;
584
585       err=parse_ks_options(line,opt);
586       if(err>0)
587         {
588           ret=err;
589           goto fail;
590         }
591       else if(err==0)
592         continue;
593
594       if(sscanf(line,"OPTION %" MKSTRING(MAX_OPTION) "s\n",option)==1)
595         {
596           int no=0;
597           char *start=&option[0];
598
599           option[MAX_OPTION]='\0';
600
601           if(ascii_strncasecmp(option,"no-",3)==0)
602             {
603               no=1;
604               start=&option[3];
605             }
606
607           if(ascii_strncasecmp(start,"http-proxy",10)==0)
608             {
609               if(no)
610                 {
611                   free(proxy);
612                   proxy=strdup("");
613                 }
614               else if(start[10]=='=')
615                 {
616                   if(strlen(&start[11])<MAX_PROXY)
617                     {
618                       free(proxy);
619                       proxy=strdup(&start[11]);
620                     }
621                 }
622             }
623 #if 0
624           else if(ascii_strcasecmp(start,"try-dns-srv")==0)
625             {
626               if(no)
627                 http_flags&=~HTTP_FLAG_TRY_SRV;
628               else
629                 http_flags|=HTTP_FLAG_TRY_SRV;
630             }
631 #endif
632           continue;
633         }
634     }
635
636   if(!opt->host)
637     {
638       fprintf(console,"gpgkeys: no keyserver host provided\n");
639       goto fail;
640     }
641
642   if(opt->timeout && register_timeout()==-1)
643     {
644       fprintf(console,"gpgkeys: unable to register timeout handler\n");
645       return KEYSERVER_INTERNAL_ERROR;
646     }
647
648   curl_global_init(CURL_GLOBAL_DEFAULT);
649   curl=curl_easy_init();
650   if(!curl)
651     {
652       fprintf(console,"gpgkeys: unable to initialize curl\n");
653       ret=KEYSERVER_INTERNAL_ERROR;
654       goto fail;
655     }
656
657   curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errorbuffer);
658
659   if(opt->auth)
660     curl_easy_setopt(curl,CURLOPT_USERPWD,opt->auth);
661
662   if(opt->debug)
663     {
664       fprintf(console,"gpgkeys: curl version = %s\n",curl_version());
665       curl_easy_setopt(curl,CURLOPT_STDERR,console);
666       curl_easy_setopt(curl,CURLOPT_VERBOSE,1L);
667     }
668
669   if(proxy)
670     curl_easy_setopt(curl,CURLOPT_PROXY,proxy);
671
672 #if 0
673   /* By suggested convention, if the user gives a :port, then disable
674      SRV. */
675   if(opt->port)
676     http_flags&=~HTTP_FLAG_TRY_SRV;
677 #endif
678
679   /* If it's a GET or a SEARCH, the next thing to come in is the
680      keyids.  If it's a SEND, then there are no keyids. */
681
682   if(opt->action==KS_SEND)
683     while(fgets(line,MAX_LINE,input)!=NULL && line[0]!='\n');
684   else if(opt->action==KS_GET
685           || opt->action==KS_GETNAME || opt->action==KS_SEARCH)
686     {
687       for(;;)
688         {
689           struct keylist *work;
690
691           if(fgets(line,MAX_LINE,input)==NULL)
692             break;
693           else
694             {
695               if(line[0]=='\n' || line[0]=='\0')
696                 break;
697
698               work=malloc(sizeof(struct keylist));
699               if(work==NULL)
700                 {
701                   fprintf(console,"gpgkeys: out of memory while "
702                           "building key list\n");
703                   ret=KEYSERVER_NO_MEMORY;
704                   goto fail;
705                 }
706
707               strcpy(work->str,line);
708
709               /* Trim the trailing \n */
710               work->str[strlen(line)-1]='\0';
711
712               work->next=NULL;
713
714               /* Always attach at the end to keep the list in proper
715                  order for searching */
716               if(keylist==NULL)
717                 keylist=work;
718               else
719                 keyptr->next=work;
720
721               keyptr=work;
722             }
723         }
724     }
725   else
726     {
727       fprintf(console,"gpgkeys: no keyserver command specified\n");
728       goto fail;
729     }
730
731   /* Send the response */
732
733   fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
734   fprintf(output,"PROGRAM %s %s\n\n",VERSION,curl_version());
735
736   if(opt->verbose>1)
737     {
738       fprintf(console,"Host:\t\t%s\n",opt->host);
739       if(opt->port)
740         fprintf(console,"Port:\t\t%s\n",opt->port);
741       if(strcmp(opt->path,"/")!=0)
742         fprintf(console,"Path:\t\t%s\n",opt->path);
743       fprintf(console,"Command:\t%s\n",ks_action_to_string(opt->action));
744     }
745
746   if(opt->action==KS_GET)
747     {
748       keyptr=keylist;
749
750       while(keyptr!=NULL)
751         {
752           set_timeout(opt->timeout);
753
754           if(get_key(keyptr->str)!=KEYSERVER_OK)
755             failed++;
756
757           keyptr=keyptr->next;
758         }
759     }
760   else if(opt->action==KS_GETNAME)
761     {
762       keyptr=keylist;
763
764       while(keyptr!=NULL)
765         {
766           set_timeout(opt->timeout);
767
768           if(get_name(keyptr->str)!=KEYSERVER_OK)
769             failed++;
770
771           keyptr=keyptr->next;
772         }
773     }
774   else if(opt->action==KS_SEND)
775     {
776       int eof=0;
777
778       do
779         {
780           set_timeout(opt->timeout);
781
782           if(send_key(&eof)!=KEYSERVER_OK)
783             failed++;
784         }
785       while(!eof);
786     }
787   else if(opt->action==KS_SEARCH)
788     {
789       char *searchkey=NULL;
790       int len=0;
791
792       set_timeout(opt->timeout);
793
794       /* To search, we stick a space in between each key to search
795          for. */
796
797       keyptr=keylist;
798       while(keyptr!=NULL)
799         {
800           len+=strlen(keyptr->str)+1;
801           keyptr=keyptr->next;
802         }
803
804       searchkey=malloc(len+1);
805       if(searchkey==NULL)
806         {
807           ret=KEYSERVER_NO_MEMORY;
808           fail_all(keylist,KEYSERVER_NO_MEMORY);
809           goto fail;
810         }
811
812       searchkey[0]='\0';
813
814       keyptr=keylist;
815       while(keyptr!=NULL)
816         {
817           strcat(searchkey,keyptr->str);
818           strcat(searchkey," ");
819           keyptr=keyptr->next;
820         }
821
822       /* Nail that last space */
823       if(*searchkey)
824         searchkey[strlen(searchkey)-1]='\0';
825
826       if(search_key(searchkey)!=KEYSERVER_OK)
827         failed++;
828
829       free(searchkey);
830     }
831   else
832     abort();
833
834   if(!failed)
835     ret=KEYSERVER_OK;
836
837  fail:
838   while(keylist!=NULL)
839     {
840       struct keylist *current=keylist;
841       keylist=keylist->next;
842       free(current);
843     }
844
845   if(input!=stdin)
846     fclose(input);
847
848   if(output!=stdout)
849     fclose(output);
850
851   free_ks_options(opt);
852
853   if(curl)
854     curl_easy_cleanup(curl);
855
856   free(proxy);
857
858   return ret;
859 }