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