* gpgkeys_hkp.c (srv_replace): New function to transform a SRV
[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, 2008,
3  *               2009 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 #ifdef USE_DNS_SRV
47 #include "srv.h"
48 #endif
49 #include "compat.h"
50 #include "keyserver.h"
51 #include "ksutil.h"
52
53 extern char *optarg;
54 extern int optind;
55
56 static FILE *input,*output,*console;
57 static CURL *curl;
58 static struct ks_options *opt;
59 static char errorbuffer[CURL_ERROR_SIZE];
60 static char *proto,*port;
61
62 static size_t
63 curl_mrindex_writer(const void *ptr,size_t size,size_t nmemb,void *stream)
64 {
65   static int checked=0,swallow=0;
66
67   if(!checked)
68     {
69       /* If the document begins with a '<', assume it's a HTML
70          response, which we don't support.  Discard the whole message
71          body.  GPG can handle it, but this is an optimization to deal
72          with it on this side of the pipe.  */
73       const char *buf=ptr;
74       if(buf[0]=='<')
75         swallow=1;
76
77       checked=1;
78     }
79
80   if(swallow || fwrite(ptr,size,nmemb,stream)==nmemb)
81     return size*nmemb;
82   else
83     return 0;
84 }
85
86 /* Append but avoid creating a double slash // in the path. */
87 static char *
88 append_path(char *dest,const char *src)
89 {
90   size_t n=strlen(dest);
91
92   if(src[0]=='/' && n>0 && dest[n-1]=='/')
93     dest[n-1]='\0';
94
95   return strcat(dest,src);
96 }
97
98 int
99 send_key(int *eof)
100 {
101   CURLcode res;
102   char request[MAX_URL+15];
103   int begin=0,end=0,ret=KEYSERVER_INTERNAL_ERROR;
104   char keyid[17],state[6];
105   char line[MAX_LINE];
106   char *key=NULL,*encoded_key=NULL;
107   size_t keysize=1;
108
109   key=malloc(1);
110   if(!key)
111     {
112       fprintf(console,"gpgkeys: unable to allocate memory for key\n");
113       ret=KEYSERVER_NO_MEMORY;
114       goto fail;
115     }
116
117   key[0]='\0';
118
119   /* Read and throw away input until we see the BEGIN */
120
121   while(fgets(line,MAX_LINE,input)!=NULL)
122     if(sscanf(line,"KEY%*[ ]%16s%*[ ]%5s\n",keyid,state)==2
123        && strcmp(state,"BEGIN")==0)
124       {
125         begin=1;
126         break;
127       }
128
129   if(!begin)
130     {
131       /* i.e. eof before the KEY BEGIN was found.  This isn't an
132          error. */
133       *eof=1;
134       ret=KEYSERVER_OK;
135       goto fail;
136     }
137
138   /* Now slurp up everything until we see the END */
139
140   while(fgets(line,MAX_LINE,input))
141     if(sscanf(line,"KEY%*[ ]%16s%*[ ]%3s\n",keyid,state)==2
142        && strcmp(state,"END")==0)
143       {
144         end=1;
145         break;
146       }
147     else
148       {
149         char *tempkey;
150         keysize+=strlen(line);
151         tempkey=realloc(key,keysize);
152         if(tempkey==NULL)
153           {
154             fprintf(console,"gpgkeys: unable to reallocate for key\n");
155             ret=KEYSERVER_NO_MEMORY;
156             goto fail;
157           }
158         else
159           key=tempkey;
160
161         strcat(key,line);
162       }
163
164   if(!end)
165     {
166       fprintf(console,"gpgkeys: no KEY %s END found\n",keyid);
167       *eof=1;
168       ret=KEYSERVER_KEY_INCOMPLETE;
169       goto fail;
170     }
171
172   encoded_key=curl_easy_escape(curl,key,keysize);
173   if(!encoded_key)
174     {
175       fprintf(console,"gpgkeys: out of memory\n");
176       ret=KEYSERVER_NO_MEMORY;
177       goto fail;
178     }
179
180   free(key);
181
182   key=malloc(8+strlen(encoded_key)+1);
183   if(!key)
184     {
185       fprintf(console,"gpgkeys: out of memory\n");
186       ret=KEYSERVER_NO_MEMORY;
187       goto fail;
188     }
189
190   strcpy(key,"keytext=");
191   strcat(key,encoded_key);
192
193   strcpy(request,proto);
194   strcat(request,"://");
195   strcat(request,opt->host);
196   strcat(request,":");
197   strcat(request,port);
198   strcat(request,opt->path);
199   /* request is MAX_URL+15 bytes long - MAX_URL covers the whole URL,
200      including any supplied path.  The 15 covers /pks/add. */
201   append_path(request,"/pks/add");
202
203   if(opt->verbose>2)
204     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
205
206   curl_easy_setopt(curl,CURLOPT_URL,request);
207   curl_easy_setopt(curl,CURLOPT_POST,1L);
208   curl_easy_setopt(curl,CURLOPT_POSTFIELDS,key);
209   curl_easy_setopt(curl,CURLOPT_FAILONERROR,1L);
210
211   res=curl_easy_perform(curl);
212   if(res!=0)
213     {
214       fprintf(console,"gpgkeys: HTTP post error %d: %s\n",res,errorbuffer);
215       ret=curl_err_to_gpg_err(res);
216       goto fail;
217     }
218   else
219     fprintf(output,"\nKEY %s SENT\n",keyid);
220
221   ret=KEYSERVER_OK;
222
223  fail:
224   free(key);
225   curl_free(encoded_key);
226
227   if(ret!=0 && begin)
228     fprintf(output,"KEY %s FAILED %d\n",keyid,ret);
229
230   return ret;
231 }
232
233 static int
234 get_key(char *getkey)
235 {
236   CURLcode res;
237   char request[MAX_URL+60];
238   char *offset;
239   struct curl_writer_ctx ctx;
240
241   memset(&ctx,0,sizeof(ctx));
242
243   /* Build the search string.  HKP only uses the short key IDs. */
244
245   if(strncmp(getkey,"0x",2)==0)
246     getkey+=2;
247
248   fprintf(output,"KEY 0x%s BEGIN\n",getkey);
249
250   if(strlen(getkey)==32)
251     {
252       fprintf(console,
253               "gpgkeys: HKP keyservers do not support v3 fingerprints\n");
254       fprintf(output,"KEY 0x%s FAILED %d\n",getkey,KEYSERVER_NOT_SUPPORTED);
255       return KEYSERVER_NOT_SUPPORTED;
256     }
257
258   strcpy(request,proto);
259   strcat(request,"://");
260   strcat(request,opt->host);
261   strcat(request,":");
262   strcat(request,port);
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,proto);
338   strcat(request,"://");
339   strcat(request,opt->host);
340   strcat(request,":");
341   strcat(request,port);
342   strcat(request,opt->path);
343   append_path(request,"/pks/lookup?op=get&options=mr&search=");
344   strcat(request,searchkey_encoded);
345
346   if(opt->action==KS_GETNAME)
347     strcat(request,"&exact=on");
348
349   if(opt->verbose>2)
350     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
351
352   curl_easy_setopt(curl,CURLOPT_URL,request);
353   curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_writer);
354   ctx.stream=output;
355   curl_easy_setopt(curl,CURLOPT_FILE,&ctx);
356
357   res=curl_easy_perform(curl);
358   if(res!=CURLE_OK)
359     {
360       fprintf(console,"gpgkeys: HTTP fetch error %d: %s\n",res,errorbuffer);
361       ret=curl_err_to_gpg_err(res);
362     }
363   else
364     {
365       curl_writer_finalize(&ctx);
366       if(!ctx.flags.done)
367         {
368           fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
369           ret=KEYSERVER_KEY_NOT_FOUND;
370         }
371       else
372         {
373           fprintf(output,"\nNAME %s END\n",getkey);
374           ret=KEYSERVER_OK;
375         }
376     }
377
378  fail:
379   curl_free(searchkey_encoded);
380   free(request);
381
382   if(ret!=KEYSERVER_OK)
383     fprintf(output,"\nNAME %s FAILED %d\n",getkey,ret);
384
385   return ret;
386 }
387
388 static int
389 search_key(const char *searchkey)
390 {
391   CURLcode res;
392   char *request=NULL;
393   char *searchkey_encoded;
394   int ret=KEYSERVER_INTERNAL_ERROR;
395   enum ks_search_type search_type;
396
397   search_type=classify_ks_search(&searchkey);
398
399   if(opt->debug)
400     fprintf(console,"gpgkeys: search type is %d, and key is \"%s\"\n",
401             search_type,searchkey);
402
403   searchkey_encoded=curl_easy_escape(curl,(char *)searchkey,0);
404   if(!searchkey_encoded)
405     {
406       fprintf(console,"gpgkeys: out of memory\n");
407       ret=KEYSERVER_NO_MEMORY;
408       goto fail;
409     }
410
411   request=malloc(MAX_URL+60+strlen(searchkey_encoded));
412   if(!request)
413     {
414       fprintf(console,"gpgkeys: out of memory\n");
415       ret=KEYSERVER_NO_MEMORY;
416       goto fail;
417     }
418
419   fprintf(output,"SEARCH %s BEGIN\n",searchkey);
420
421   strcpy(request,proto);
422   strcat(request,"://");
423   strcat(request,opt->host);
424   strcat(request,":");
425   strcat(request,port);
426   strcat(request,opt->path);
427   append_path(request,"/pks/lookup?op=index&options=mr&search=");
428
429   /* HKP keyservers like the 0x to be present when searching by
430      keyid */
431   if(search_type==KS_SEARCH_KEYID_SHORT || search_type==KS_SEARCH_KEYID_LONG)
432     strcat(request,"0x");
433
434   strcat(request,searchkey_encoded);
435
436   if(search_type!=KS_SEARCH_SUBSTR)
437     strcat(request,"&exact=on");
438
439   if(opt->verbose>2)
440     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
441
442   curl_easy_setopt(curl,CURLOPT_URL,request);
443   curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,curl_mrindex_writer);
444   curl_easy_setopt(curl,CURLOPT_FILE,output);
445
446   res=curl_easy_perform(curl);
447   if(res!=0)
448     {
449       fprintf(console,"gpgkeys: HTTP search error %d: %s\n",res,errorbuffer);
450       ret=curl_err_to_gpg_err(res);
451     }
452   else
453     {
454       fprintf(output,"\nSEARCH %s END\n",searchkey);
455       ret=KEYSERVER_OK;
456     }
457
458  fail:
459
460   curl_free(searchkey_encoded);
461   free(request);
462
463   if(ret!=KEYSERVER_OK)
464     fprintf(output,"\nSEARCH %s FAILED %d\n",searchkey,ret);
465
466   return ret;
467 }
468
469 void
470 fail_all(struct keylist *keylist,int err)
471 {
472   if(!keylist)
473     return;
474
475   if(opt->action==KS_SEARCH)
476     {
477       fprintf(output,"SEARCH ");
478       while(keylist)
479         {
480           fprintf(output,"%s ",keylist->str);
481           keylist=keylist->next;
482         }
483       fprintf(output,"FAILED %d\n",err);
484     }
485   else
486     while(keylist)
487       {
488         fprintf(output,"KEY %s FAILED %d\n",keylist->str,err);
489         keylist=keylist->next;
490       }
491 }
492
493 /* If there is a SRV record, take the highest ranked possibility.
494    This is a hack, as we don't proceed downwards. */
495 static void
496 srv_replace(void)
497 {
498 #ifdef USE_DNS_SRV
499   struct srventry *srvlist=NULL;
500   int srvcount;
501
502   if(1+strlen(opt->scheme)+6+strlen(opt->host)+1<=MAXDNAME)
503     {
504       char srvname[MAXDNAME];
505
506       strcpy(srvname,"_");
507       strcat(srvname,opt->scheme);
508       strcat(srvname,"._tcp.");
509       strcat(srvname,opt->host);
510       srvcount=getsrv(srvname,&srvlist);
511     }
512
513   if(srvlist)
514     {
515       char *newname,*newport;
516
517       newname=strdup(srvlist->target);
518       newport=malloc(MAX_PORT);
519       if(newname && newport)
520         {
521           free(opt->host);
522           free(opt->port);
523           opt->host=newname;
524           snprintf(newport,MAX_PORT,"%u",srvlist->port);
525           opt->port=newport;
526         }
527       else
528         {
529           free(newname);
530           free(newport);
531         }
532     }
533 #endif
534 }
535
536 static void 
537 show_help (FILE *fp)
538 {
539   fprintf (fp,"-h, --help\thelp\n");
540   fprintf (fp,"-V\t\tmachine readable version\n");
541   fprintf (fp,"--version\thuman readable version\n");
542   fprintf (fp,"-o\t\toutput to this file\n");
543 }
544
545 int
546 main(int argc,char *argv[])
547 {
548   int arg,ret=KEYSERVER_INTERNAL_ERROR,try_srv=1;
549   char line[MAX_LINE];
550   int failed=0;
551   struct keylist *keylist=NULL,*keyptr=NULL;
552   char *proxy=NULL;
553
554   console=stderr;
555
556   /* Kludge to implement standard GNU options.  */
557   if (argc > 1 && !strcmp (argv[1], "--version"))
558     {
559       printf ("gpgkeys_hkp (GnuPG) %s\n", VERSION);
560       printf ("Uses: %s\n", curl_version());
561       return 0;
562     }
563   else if (argc > 1 && !strcmp (argv[1], "--help"))
564     {
565       show_help (stdout);
566       return 0;
567     }
568
569   while((arg=getopt(argc,argv,"hVo:"))!=-1)
570     switch(arg)
571       {
572       default:
573       case 'h':
574         show_help (console);
575         return KEYSERVER_OK;
576
577       case 'V':
578         fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
579         return KEYSERVER_OK;
580
581       case 'o':
582         output=fopen(optarg,"w");
583         if(output==NULL)
584           {
585             fprintf(console,"gpgkeys: Cannot open output file `%s': %s\n",
586                     optarg,strerror(errno));
587             return KEYSERVER_INTERNAL_ERROR;
588           }
589
590         break;
591       }
592
593   if(argc>optind)
594     {
595       input=fopen(argv[optind],"r");
596       if(input==NULL)
597         {
598           fprintf(console,"gpgkeys: Cannot open input file `%s': %s\n",
599                   argv[optind],strerror(errno));
600           return KEYSERVER_INTERNAL_ERROR;
601         }
602     }
603
604   if(input==NULL)
605     input=stdin;
606
607   if(output==NULL)
608     output=stdout;
609
610   opt=init_ks_options();
611   if(!opt)
612     return KEYSERVER_NO_MEMORY;
613
614   /* Get the command and info block */
615
616   while(fgets(line,MAX_LINE,input)!=NULL)
617     {
618       int err;
619       char option[MAX_OPTION+1];
620
621       if(line[0]=='\n')
622         break;
623
624       err=parse_ks_options(line,opt);
625       if(err>0)
626         {
627           ret=err;
628           goto fail;
629         }
630       else if(err==0)
631         continue;
632
633       if(sscanf(line,"OPTION %" MKSTRING(MAX_OPTION) "s\n",option)==1)
634         {
635           int no=0;
636           char *start=&option[0];
637
638           option[MAX_OPTION]='\0';
639
640           if(ascii_strncasecmp(option,"no-",3)==0)
641             {
642               no=1;
643               start=&option[3];
644             }
645
646           if(ascii_strncasecmp(start,"http-proxy",10)==0)
647             {
648               if(no)
649                 {
650                   free(proxy);
651                   proxy=strdup("");
652                 }
653               else if(start[10]=='=')
654                 {
655                   if(strlen(&start[11])<MAX_PROXY)
656                     {
657                       free(proxy);
658                       proxy=strdup(&start[11]);
659                     }
660                 }
661             }
662           else if(ascii_strcasecmp(start,"try-dns-srv")==0)
663             {
664               if(no)
665                 try_srv=0;
666               else
667                 try_srv=1;
668             }
669
670           continue;
671         }
672     }
673
674
675   if(!opt->scheme)
676     {
677       fprintf(console,"gpgkeys: no scheme supplied!\n");
678       ret=KEYSERVER_SCHEME_NOT_FOUND;
679       goto fail;
680     }
681
682   if(ascii_strcasecmp(opt->scheme,"hkps")==0)
683     {
684       proto="https";
685       port="443";
686     }
687   else
688     {
689       proto="http";
690       port="11371";
691     }
692
693   if(!opt->host)
694     {
695       fprintf(console,"gpgkeys: no keyserver host provided\n");
696       goto fail;
697     }
698
699   if(opt->timeout && register_timeout()==-1)
700     {
701       fprintf(console,"gpgkeys: unable to register timeout handler\n");
702       return KEYSERVER_INTERNAL_ERROR;
703     }
704
705   curl_global_init(CURL_GLOBAL_DEFAULT);
706   curl=curl_easy_init();
707   if(!curl)
708     {
709       fprintf(console,"gpgkeys: unable to initialize curl\n");
710       ret=KEYSERVER_INTERNAL_ERROR;
711       goto fail;
712     }
713
714   /* If the user gives a :port, then disable SRV.  The semantics of a
715      specified port and SRV do not play well together. */
716   if(opt->port)
717     port=opt->port;
718   else if(try_srv)
719     {
720 #ifdef HAVE_LIBCURL
721       /* We're using libcurl, so fake SRV support via our wrapper.
722          This isn't as good as true SRV support, as we do not try all
723          possible targets at one particular level and work our way
724          down the list, but it's better than nothing. */
725       srv_replace();
726 #else
727       /* We're using our internal curl shim, so we can use its (true)
728          SRV support.  Obviously, CURLOPT_SRVTAG_GPG_HACK isn't a real
729          libcurl option.  It's specific to our shim. */
730       curl_easy_setopt(curl,CURLOPT_SRVTAG_GPG_HACK,opt->scheme);
731 #endif
732     }
733
734   curl_easy_setopt(curl,CURLOPT_ERRORBUFFER,errorbuffer);
735
736   if(opt->auth)
737     curl_easy_setopt(curl,CURLOPT_USERPWD,opt->auth);
738
739   if(opt->debug)
740     {
741       fprintf(console,"gpgkeys: curl version = %s\n",curl_version());
742       curl_easy_setopt(curl,CURLOPT_STDERR,console);
743       curl_easy_setopt(curl,CURLOPT_VERBOSE,1L);
744     }
745
746   curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,(long)opt->flags.check_cert);
747   curl_easy_setopt(curl,CURLOPT_CAINFO,opt->ca_cert_file);
748
749   if(proxy)
750     curl_easy_setopt(curl,CURLOPT_PROXY,proxy);
751
752   /* If it's a GET or a SEARCH, the next thing to come in is the
753      keyids.  If it's a SEND, then there are no keyids. */
754
755   if(opt->action==KS_SEND)
756     while(fgets(line,MAX_LINE,input)!=NULL && line[0]!='\n');
757   else if(opt->action==KS_GET
758           || opt->action==KS_GETNAME || opt->action==KS_SEARCH)
759     {
760       for(;;)
761         {
762           struct keylist *work;
763
764           if(fgets(line,MAX_LINE,input)==NULL)
765             break;
766           else
767             {
768               if(line[0]=='\n' || line[0]=='\0')
769                 break;
770
771               work=malloc(sizeof(struct keylist));
772               if(work==NULL)
773                 {
774                   fprintf(console,"gpgkeys: out of memory while "
775                           "building key list\n");
776                   ret=KEYSERVER_NO_MEMORY;
777                   goto fail;
778                 }
779
780               strcpy(work->str,line);
781
782               /* Trim the trailing \n */
783               work->str[strlen(line)-1]='\0';
784
785               work->next=NULL;
786
787               /* Always attach at the end to keep the list in proper
788                  order for searching */
789               if(keylist==NULL)
790                 keylist=work;
791               else
792                 keyptr->next=work;
793
794               keyptr=work;
795             }
796         }
797     }
798   else
799     {
800       fprintf(console,"gpgkeys: no keyserver command specified\n");
801       goto fail;
802     }
803
804   /* Send the response */
805
806   fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
807   fprintf(output,"PROGRAM %s %s\n\n",VERSION,curl_version());
808
809   if(opt->verbose>1)
810     {
811       fprintf(console,"Host:\t\t%s\n",opt->host);
812       if(opt->port)
813         fprintf(console,"Port:\t\t%s\n",opt->port);
814       if(strcmp(opt->path,"/")!=0)
815         fprintf(console,"Path:\t\t%s\n",opt->path);
816       fprintf(console,"Command:\t%s\n",ks_action_to_string(opt->action));
817     }
818
819   if(opt->action==KS_GET)
820     {
821       keyptr=keylist;
822
823       while(keyptr!=NULL)
824         {
825           set_timeout(opt->timeout);
826
827           if(get_key(keyptr->str)!=KEYSERVER_OK)
828             failed++;
829
830           keyptr=keyptr->next;
831         }
832     }
833   else if(opt->action==KS_GETNAME)
834     {
835       keyptr=keylist;
836
837       while(keyptr!=NULL)
838         {
839           set_timeout(opt->timeout);
840
841           if(get_name(keyptr->str)!=KEYSERVER_OK)
842             failed++;
843
844           keyptr=keyptr->next;
845         }
846     }
847   else if(opt->action==KS_SEND)
848     {
849       int eof=0;
850
851       do
852         {
853           set_timeout(opt->timeout);
854
855           if(send_key(&eof)!=KEYSERVER_OK)
856             failed++;
857         }
858       while(!eof);
859     }
860   else if(opt->action==KS_SEARCH)
861     {
862       char *searchkey=NULL;
863       int len=0;
864
865       set_timeout(opt->timeout);
866
867       /* To search, we stick a space in between each key to search
868          for. */
869
870       keyptr=keylist;
871       while(keyptr!=NULL)
872         {
873           len+=strlen(keyptr->str)+1;
874           keyptr=keyptr->next;
875         }
876
877       searchkey=malloc(len+1);
878       if(searchkey==NULL)
879         {
880           ret=KEYSERVER_NO_MEMORY;
881           fail_all(keylist,KEYSERVER_NO_MEMORY);
882           goto fail;
883         }
884
885       searchkey[0]='\0';
886
887       keyptr=keylist;
888       while(keyptr!=NULL)
889         {
890           strcat(searchkey,keyptr->str);
891           strcat(searchkey," ");
892           keyptr=keyptr->next;
893         }
894
895       /* Nail that last space */
896       if(*searchkey)
897         searchkey[strlen(searchkey)-1]='\0';
898
899       if(search_key(searchkey)!=KEYSERVER_OK)
900         failed++;
901
902       free(searchkey);
903     }
904   else
905     abort();
906
907   if(!failed)
908     ret=KEYSERVER_OK;
909
910  fail:
911   while(keylist!=NULL)
912     {
913       struct keylist *current=keylist;
914       keylist=keylist->next;
915       free(current);
916     }
917
918   if(input!=stdin)
919     fclose(input);
920
921   if(output!=stdout)
922     fclose(output);
923
924   free_ks_options(opt);
925
926   if(curl)
927     curl_easy_cleanup(curl);
928
929   free(proxy);
930
931   return ret;
932 }