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