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