4328a2f53aba34dfdb91b30727bd260dc5341aa8
[gnupg.git] / keyserver / gpgkeys_hkp.c
1 /* gpgkeys_hkp.c - talk to an HKP keyserver
2  * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006
3  *               2007 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 *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 keysize=1;
105
106   key=malloc(1);
107   if(!key)
108     {
109       fprintf(console,"gpgkeys: unable to allocate memory for key\n");
110       ret=KEYSERVER_NO_MEMORY;
111       goto fail;
112     }
113
114   key[0]='\0';
115
116   /* Read and throw away input until we see the BEGIN */
117
118   while(fgets(line,MAX_LINE,input)!=NULL)
119     if(sscanf(line,"KEY%*[ ]%16s%*[ ]%5s\n",keyid,state)==2
120        && strcmp(state,"BEGIN")==0)
121       {
122         begin=1;
123         break;
124       }
125
126   if(!begin)
127     {
128       /* i.e. eof before the KEY BEGIN was found.  This isn't an
129          error. */
130       *eof=1;
131       ret=KEYSERVER_OK;
132       goto fail;
133     }
134
135   /* Now slurp up everything until we see the END */
136
137   while(fgets(line,MAX_LINE,input))
138     if(sscanf(line,"KEY%*[ ]%16s%*[ ]%3s\n",keyid,state)==2
139        && strcmp(state,"END")==0)
140       {
141         end=1;
142         break;
143       }
144     else
145       {
146         char *tempkey;
147         keysize+=strlen(line);
148         tempkey=realloc(key,keysize);
149         if(tempkey==NULL)
150           {
151             fprintf(console,"gpgkeys: unable to reallocate for key\n");
152             ret=KEYSERVER_NO_MEMORY;
153             goto fail;
154           }
155         else
156           key=tempkey;
157
158         strcat(key,line);
159       }
160
161   if(!end)
162     {
163       fprintf(console,"gpgkeys: no KEY %s END found\n",keyid);
164       *eof=1;
165       ret=KEYSERVER_KEY_INCOMPLETE;
166       goto fail;
167     }
168
169   encoded_key=curl_easy_escape(curl,key,keysize);
170   if(!encoded_key)
171     {
172       fprintf(console,"gpgkeys: out of memory\n");
173       ret=KEYSERVER_NO_MEMORY;
174       goto fail;
175     }
176
177   free(key);
178
179   key=malloc(8+strlen(encoded_key)+1);
180   if(!key)
181     {
182       fprintf(console,"gpgkeys: out of memory\n");
183       ret=KEYSERVER_NO_MEMORY;
184       goto fail;
185     }
186
187   strcpy(key,"keytext=");
188   strcat(key,encoded_key);
189
190   strcpy(request,"http://");
191   strcat(request,opt->host);
192   strcat(request,":");
193   if(opt->port)
194     strcat(request,opt->port);
195   else
196     strcat(request,"11371");
197   strcat(request,opt->path);
198   /* request is MAX_URL+15 bytes long - MAX_URL covers the whole URL,
199      including any supplied path.  The 15 covers /pks/add. */
200   append_path(request,"/pks/add");
201
202   if(opt->verbose>2)
203     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
204
205   curl_easy_setopt(curl,CURLOPT_URL,request);
206   curl_easy_setopt(curl,CURLOPT_POST,1);
207   curl_easy_setopt(curl,CURLOPT_POSTFIELDS,key);
208   curl_easy_setopt(curl,CURLOPT_FAILONERROR,1);
209
210   res=curl_easy_perform(curl);
211   if(res!=0)
212     {
213       fprintf(console,"gpgkeys: HTTP post error %d: %s\n",res,errorbuffer);
214       ret=curl_err_to_gpg_err(res);
215       goto fail;
216     }
217   else
218     fprintf(output,"\nKEY %s SENT\n",keyid);
219
220   ret=KEYSERVER_OK;
221
222  fail:
223   free(key);
224   curl_free(encoded_key);
225
226   if(ret!=0 && begin)
227     fprintf(output,"KEY %s FAILED %d\n",keyid,ret);
228
229   return ret;
230 }
231
232 static int
233 get_key(char *getkey)
234 {
235   CURLcode res;
236   char request[MAX_URL+60];
237   char *offset;
238   struct curl_writer_ctx ctx;
239
240   memset(&ctx,0,sizeof(ctx));
241
242   /* Build the search string.  HKP only uses the short key IDs. */
243
244   if(strncmp(getkey,"0x",2)==0)
245     getkey+=2;
246
247   fprintf(output,"KEY 0x%s BEGIN\n",getkey);
248
249   if(strlen(getkey)==32)
250     {
251       fprintf(console,
252               "gpgkeys: HKP keyservers do not support v3 fingerprints\n");
253       fprintf(output,"KEY 0x%s FAILED %d\n",getkey,KEYSERVER_NOT_SUPPORTED);
254       return KEYSERVER_NOT_SUPPORTED;
255     }
256
257   strcpy(request,"http://");
258   strcat(request,opt->host);
259   strcat(request,":");
260   if(opt->port)
261     strcat(request,opt->port);
262   else
263     strcat(request,"11371");
264   strcat(request,opt->path);
265   /* request is MAX_URL+55 bytes long - MAX_URL covers the whole URL,
266      including any supplied path.  The 60 overcovers this /pks/... etc
267      string plus the 8 bytes of key id */
268   append_path(request,"/pks/lookup?op=get&options=mr&search=0x");
269
270   /* fingerprint or long key id.  Take the last 8 characters and treat
271      it like a short key id */
272   if(strlen(getkey)>8)
273     offset=&getkey[strlen(getkey)-8];
274   else
275     offset=getkey;
276
277   strcat(request,offset);
278
279   if(opt->verbose>2)
280     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
281
282   curl_easy_setopt(curl,CURLOPT_URL,request);
283   curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_writer);
284   ctx.stream=output;
285   curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
286
287   res=curl_easy_perform(curl);
288   if(res!=CURLE_OK)
289     {
290       fprintf(console,"gpgkeys: HTTP fetch error %d: %s\n",res,errorbuffer);
291       fprintf(output,"\nKEY 0x%s FAILED %d\n",getkey,curl_err_to_gpg_err(res));
292     }
293   else
294     {
295       curl_writer_finalize(&ctx);
296       if(!ctx.flags.done)
297         {
298           fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
299           fprintf(output,"\nKEY 0x%s FAILED %d\n",
300                   getkey,KEYSERVER_KEY_NOT_FOUND);
301         }
302       else
303         fprintf(output,"\nKEY 0x%s END\n",getkey);
304     }
305
306   return KEYSERVER_OK;
307 }
308
309 static int
310 get_name(const char *getkey)
311 {
312   CURLcode res;
313   char *request=NULL;
314   char *searchkey_encoded;
315   int ret=KEYSERVER_INTERNAL_ERROR;
316   struct curl_writer_ctx ctx;
317
318   memset(&ctx,0,sizeof(ctx));
319
320   searchkey_encoded=curl_easy_escape(curl,(char *)getkey,0);
321   if(!searchkey_encoded)
322     {
323       fprintf(console,"gpgkeys: out of memory\n");
324       ret=KEYSERVER_NO_MEMORY;
325       goto fail;
326     }
327
328   request=malloc(MAX_URL+60+strlen(searchkey_encoded));
329   if(!request)
330     {
331       fprintf(console,"gpgkeys: out of memory\n");
332       ret=KEYSERVER_NO_MEMORY;
333       goto fail;
334     }
335
336   fprintf(output,"NAME %s BEGIN\n",getkey);
337
338   strcpy(request,"http://");
339   strcat(request,opt->host);
340   strcat(request,":");
341   if(opt->port)
342     strcat(request,opt->port);
343   else
344     strcat(request,"11371");
345   strcat(request,opt->path);
346   append_path(request,"/pks/lookup?op=get&options=mr&search=");
347   strcat(request,searchkey_encoded);
348
349   if(opt->action==KS_GETNAME)
350     strcat(request,"&exact=on");
351
352   if(opt->verbose>2)
353     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
354
355   curl_easy_setopt(curl,CURLOPT_URL,request);
356   curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_writer);
357   ctx.stream=output;
358   curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
359
360   res=curl_easy_perform(curl);
361   if(res!=CURLE_OK)
362     {
363       fprintf(console,"gpgkeys: HTTP fetch error %d: %s\n",res,errorbuffer);
364       ret=curl_err_to_gpg_err(res);
365     }
366   else
367     {
368       curl_writer_finalize(&ctx);
369       if(!ctx.flags.done)
370         {
371           fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
372           ret=KEYSERVER_KEY_NOT_FOUND;
373         }
374       else
375         {
376           fprintf(output,"\nNAME %s END\n",getkey);
377           ret=KEYSERVER_OK;
378         }
379     }
380
381  fail:
382   curl_free(searchkey_encoded);
383   free(request);
384
385   if(ret!=KEYSERVER_OK)
386     fprintf(output,"\nNAME %s FAILED %d\n",getkey,ret);
387
388   return ret;
389 }
390
391 static int
392 search_key(const char *searchkey)
393 {
394   CURLcode res;
395   char *request=NULL;
396   char *searchkey_encoded;
397   int ret=KEYSERVER_INTERNAL_ERROR;
398   enum ks_search_type search_type;
399
400   search_type=classify_ks_search(&searchkey);
401
402   if(opt->debug)
403     fprintf(console,"gpgkeys: search type is %d, and key is \"%s\"\n",
404             search_type,searchkey);
405
406   searchkey_encoded=curl_easy_escape(curl,(char *)searchkey,0);
407   if(!searchkey_encoded)
408     {
409       fprintf(console,"gpgkeys: out of memory\n");
410       ret=KEYSERVER_NO_MEMORY;
411       goto fail;
412     }
413
414   request=malloc(MAX_URL+60+strlen(searchkey_encoded));
415   if(!request)
416     {
417       fprintf(console,"gpgkeys: out of memory\n");
418       ret=KEYSERVER_NO_MEMORY;
419       goto fail;
420     }
421
422   fprintf(output,"SEARCH %s BEGIN\n",searchkey);
423
424   strcpy(request,"http://");
425   strcat(request,opt->host);
426   strcat(request,":");
427   if(opt->port)
428     strcat(request,opt->port);
429   else
430     strcat(request,"11371");
431   strcat(request,opt->path);
432   append_path(request,"/pks/lookup?op=index&options=mr&search=");
433
434   /* HKP keyservers like the 0x to be present when searching by
435      keyid */
436   if(search_type==KS_SEARCH_KEYID_SHORT || search_type==KS_SEARCH_KEYID_LONG)
437     strcat(request,"0x");
438
439   strcat(request,searchkey_encoded);
440
441   if(search_type!=KS_SEARCH_SUBSTR)
442     strcat(request,"&exact=on");
443
444   if(opt->verbose>2)
445     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
446
447   curl_easy_setopt(curl,CURLOPT_URL,request);
448   curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_mrindex_writer);
449   curl_easy_setopt(curl,CURLOPT_FILE,output);
450
451   res=curl_easy_perform(curl);
452   if(res!=0)
453     {
454       fprintf(console,"gpgkeys: HTTP search error %d: %s\n",res,errorbuffer);
455       ret=curl_err_to_gpg_err(res);
456     }
457   else
458     {
459       fprintf(output,"\nSEARCH %s END\n",searchkey);
460       ret=KEYSERVER_OK;
461     }
462
463  fail:
464
465   curl_free(searchkey_encoded);
466   free(request);
467
468   if(ret!=KEYSERVER_OK)
469     fprintf(output,"\nSEARCH %s FAILED %d\n",searchkey,ret);
470
471   return ret;
472 }
473
474 void
475 fail_all(struct keylist *keylist,int err)
476 {
477   if(!keylist)
478     return;
479
480   if(opt->action==KS_SEARCH)
481     {
482       fprintf(output,"SEARCH ");
483       while(keylist)
484         {
485           fprintf(output,"%s ",keylist->str);
486           keylist=keylist->next;
487         }
488       fprintf(output,"FAILED %d\n",err);
489     }
490   else
491     while(keylist)
492       {
493         fprintf(output,"KEY %s FAILED %d\n",keylist->str,err);
494         keylist=keylist->next;
495       }
496 }
497
498 static void 
499 show_help (FILE *fp)
500 {
501   fprintf (fp,"-h\thelp\n");
502   fprintf (fp,"-V\tversion\n");
503   fprintf (fp,"-o\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       fputs ("gpgkeys_hkp (GnuPG) " VERSION"\n", stdout);
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(strncasecmp(option,"no-",3)==0)
601             {
602               no=1;
603               start=&option[3];
604             }
605
606           if(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(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,1);
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\n\n",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 }