* gpgkeys_hkp.c: Include util.h on RISC OS as per Stefan. Include a
[gnupg.git] / keyserver / gpgkeys_hkp.c
1 /* gpgkeys_hkp.c - talk to an HKP keyserver
2  * Copyright (C) 2001, 2002 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdio.h>
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 #include <netinet/in.h>
26 #include <netdb.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <ctype.h>
31 #include <time.h>
32 #include <stdlib.h>
33 #include "keyserver.h"
34
35 #ifdef __riscos__
36 #include "util.h"
37 #endif
38
39 #define GET    0
40 #define SEND   1
41 #define SEARCH 2
42 #define MAX_LINE 80
43
44 int verbose=0,include_disabled=0,include_revoked=0;
45 char *basekeyspacedn=NULL;
46 char host[80]={'\0'};
47 char portstr[10]={'\0'};
48 FILE *input=NULL,*output=NULL,*console=NULL,*server=NULL;
49
50 struct keylist
51 {
52   char str[MAX_LINE];
53   struct keylist *next;
54 };
55
56 #ifndef HAVE_HSTRERROR
57 const char *hstrerror(int err)
58 {
59   if(err<0)
60     return "Resolver internal error";
61
62   switch(err)
63     {
64     case 0:
65       return "Resolver Error 0 (no error)";
66
67     case HOST_NOT_FOUND:
68       return "Unknown host"; /* 1 HOST_NOT_FOUND */
69
70     case TRY_AGAIN:
71       return "Host name lookup failure"; /* 2 TRY_AGAIN */
72
73     case NO_RECOVERY:
74       return "Unknown server error"; /* 3 NO_RECOVERY */
75
76     case NO_ADDRESS:
77       return "No address associated with name"; /* 4 NO_ADDRESS */
78
79     default:
80       return "Unknown resolver error";
81     }
82 }
83 #endif /* !HAVE_HSTRERROR */
84
85 int http_connect(const char *http_host,unsigned short port)
86 {
87   int sock=-1;
88   struct hostent *ent;
89   struct sockaddr_in addr;
90
91   sock=socket(AF_INET,SOCK_STREAM,0);
92   if(sock==-1)
93     {
94       fprintf(console,"gpgkeys: internal socket error: %s\n",strerror(errno));
95       goto fail;
96     }
97
98   ent=gethostbyname(http_host);
99   if(ent==NULL)
100     {
101       fprintf(console,"gpgkeys: DNS error: %s\n",hstrerror(h_errno));
102       goto fail;
103     }
104
105   addr.sin_family=AF_INET;
106   addr.sin_addr.s_addr=*(int *)ent->h_addr_list[0];
107   addr.sin_port=htons(port?port:11371);
108
109   if(connect(sock,(struct sockaddr *)&addr,sizeof(addr))==-1)
110     {
111       fprintf(console,"gpgkeys: unable to contact keyserver: %s\n",
112               strerror(errno));
113       goto fail;
114     }
115
116   server=fdopen(sock,"r+");
117   if(server==NULL)
118     {
119       fprintf(console,"gpgkeys: unable to fdopen socket: %s\n",
120               strerror(errno));
121       goto fail;
122     }
123
124   if(verbose>3)
125     fprintf(console,"gpgkeys: HKP connect to %s:%d\n",
126             http_host,port?port:11371);
127
128   return 0;
129
130  fail:
131   if(sock>-1)
132     close(sock);
133
134   return -1;
135 }
136
137 void http_disconnect(void)
138 {
139   if(verbose>3)
140     fprintf(console,"gpgkeys: HKP disconnect from %s\n",host);
141
142   fclose(server);
143 }
144
145 int http_get(const char *op,const char *search)
146 {
147   fprintf(server,"GET /pks/lookup?op=%s&search=%s HTTP/1.0\n\n",op,search);
148
149   if(verbose>2)
150     fprintf(console,"gpgkeys: HTTP GET /pks/lookup?op=%s&search=%s HTTP/1.0\n",
151             op,search);
152
153   return 0;
154 }
155
156 int http_post(const char *data)
157 {
158   char line[MAX_LINE];
159   int result;
160
161   fprintf(server,
162           "POST /pks/add HTTP/1.0\n"
163           "Content-type: application/x-www-form-urlencoded\n"
164           "Content-Length: %d\n\n%s",strlen(data),data);
165
166   if(verbose>2)
167     fprintf(console,
168             "gpgkeys: HTTP POST /pks/add HTTP/1.0\n"
169             "gpgkeys: Content-type: application/x-www-form-urlencoded\n"
170             "gpgkeys: Content-Length: %d\n\n",strlen(data));
171
172   /* Now wait for a response */
173
174   while(fgets(line,MAX_LINE,server)!=NULL)
175     if(sscanf(line,"HTTP/%*f %d OK",&result)==1)
176       return result;
177
178   return -1;
179 }
180
181 /* Returns 0 on success, -1 on failure, and 1 on eof */
182 int send_key(void)
183 {
184   int err,gotit=0,keylen,maxlen,ret=-1;
185   char keyid[17],line[MAX_LINE],*key;
186
187   key=strdup("keytext=");
188   if(key==NULL)
189     {
190       fprintf(console,"gpgkeys: unable to allocate for key\n");
191       goto fail;
192     }
193
194   maxlen=keylen=strlen(key);
195
196   /* Read and throw away stdin until we see the BEGIN */
197
198   while(fgets(line,MAX_LINE,input)!=NULL)
199     if(sscanf(line,"KEY %16s BEGIN\n",keyid)==1)
200       {
201         gotit=1;
202         break;
203       }
204
205   if(!gotit)
206     {
207       /* i.e. eof before the KEY BEGIN was found */
208       ret=1;
209       goto fail;
210     }
211
212   gotit=0;
213
214   /* Now slurp up everything until we see the END */
215
216   while(fgets(line,MAX_LINE,input)!=NULL)
217     if(sscanf(line,"KEY %16s END\n",keyid)==1)
218       {
219         gotit=1;
220         break;
221       }
222     else
223       {
224         char *c=line;
225
226         while(*c!='\0')
227           {
228             if(maxlen-keylen<4)
229               {
230                 maxlen+=1024;
231                 key=realloc(key,maxlen);
232                 if(key==NULL)
233                   {
234                     fprintf(console,"gpgkeys: unable to reallocate for key\n");
235                     goto fail;
236                   }
237               }
238
239             if(isalnum(*c) || *c=='-')
240               {
241                 key[keylen++]=*c;
242                 key[keylen]='\0';
243               }
244             else if(*c==' ')
245               {
246                 key[keylen++]='+';
247                 key[keylen]='\0';
248               }
249             else
250               {
251                 sprintf(&key[keylen],"%%%02X",*c);
252                 keylen+=3;
253               }
254
255             c++;
256           }
257       }
258
259   if(!gotit)
260     {
261       fprintf(console,"gpgkeys: no KEY %s END found\n",keyid);
262       goto fail;
263     }
264
265   err=http_post(key);
266   if(err!=200)
267     {
268       fprintf(console,"gpgkeys: remote server returned error %d\n",err);
269       goto fail;
270     }
271
272   ret=0;
273
274  fail:
275
276   free(key);
277
278   if(ret!=0)
279     fprintf(output,"KEY %s FAILED\n",keyid);
280
281   return ret;
282 }
283
284 int get_key(char *getkey)
285 {
286   int err,gotit=0;
287   char search[29],line[MAX_LINE];
288
289   /* Build the search string.  HKP only uses the short key IDs. */
290
291   if(strncmp(getkey,"0x",2)==0)
292     getkey+=2;
293
294   if(strlen(getkey)==32)
295     {
296       fprintf(console,
297               "gpgkeys: HKP keyservers do not support v3 fingerprints\n");
298       fprintf(output,"KEY 0x%s BEGIN\n",getkey);
299       fprintf(output,"KEY 0x%s FAILED\n",getkey);
300       return -1;
301     }
302
303  if(strlen(getkey)>8)
304     {
305       char *offset=&getkey[strlen(getkey)-8];
306
307       /* fingerprint or long key id.  Take the last 8 characters and
308          treat it like a short key id */
309
310       sprintf(search,"0x%.8s",offset);
311     }
312  else
313    {
314       /* short key id */
315     
316       sprintf(search,"0x%.8s",getkey);
317     }
318
319   fprintf(output,"KEY 0x%s BEGIN\n",getkey);
320
321   if(verbose>2)
322     fprintf(console,"gpgkeys: HKP fetch for: %s\n",search);
323
324   if(verbose)
325     fprintf(console,"gpgkeys: requesting key 0x%s from hkp://%s%s%s\n",
326             getkey,host,portstr[0]?":":"",portstr[0]?portstr:"");
327
328   err=http_get("get",search);
329   if(err!=0)
330     {
331       fprintf(console,"gpgkeys: HKP fetch error: %s\n",strerror(errno));
332       fprintf(output,"KEY 0x%s FAILED\n",getkey);
333       return -1;
334     }
335
336   while(fgets(line,MAX_LINE,server))
337     {
338       if(gotit)
339         {
340           fprintf(output,line);
341           if(strcmp(line,"-----END PGP PUBLIC KEY BLOCK-----\n")==0)
342             {
343               gotit=0;
344               fprintf(output,"KEY 0x%s END\n",getkey);
345               break;
346             }
347         }
348       else
349         if(strcmp(line,"-----BEGIN PGP PUBLIC KEY BLOCK-----\n")==0)
350           {
351             fprintf(output,line);
352             gotit=1;
353           }
354     }
355
356   return 0;
357 }
358
359 void print_quoted(FILE *stream,char *string,char delim)
360 {
361   while(*string)
362     {
363       if(*string==delim)
364         fprintf(stream,"\\x%02X",*string);
365       else
366         fputc(*string,stream);
367
368       string++;
369     }
370 }
371
372 void append_quoted(char *buffer,char *string,char delim)
373 {
374   while(*buffer)
375     buffer++;
376
377   while(*string)
378     {
379       if(*string==delim)
380         {
381           sprintf(buffer,"\\x%02X",*string);
382           buffer+=4;
383         }
384       else
385         *buffer=*string;
386
387       buffer++;
388       string++;
389     }
390
391   *buffer='\0';
392 }
393
394 unsigned int scan_isodatestr( const char *string )
395 {
396   int year, month, day;
397   struct tm tmbuf;
398   time_t stamp;
399   int i;
400
401   if( strlen(string) != 10 || string[4] != '-' || string[7] != '-' )
402     return 0;
403   for( i=0; i < 4; i++ )
404     if( !isdigit(string[i]) )
405       return 0;
406   if( !isdigit(string[5]) || !isdigit(string[6]) )
407     return 0;
408   if( !isdigit(string[8]) || !isdigit(string[9]) )
409     return 0;
410   year = atoi(string);
411   month = atoi(string+5);
412   day = atoi(string+8);
413   /* some basic checks */
414   if( year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 )
415     return 0;
416   memset( &tmbuf, 0, sizeof tmbuf );
417   tmbuf.tm_mday = day;
418   tmbuf.tm_mon = month-1;
419   tmbuf.tm_year = year - 1900;
420   tmbuf.tm_isdst = -1;
421   stamp = mktime( &tmbuf );
422   if( stamp == (time_t)-1 )
423     return 0;
424   return stamp;
425 }
426
427 /* Remove anything <between brackets> and de-urlencode in place.  Note
428    that this requires all brackets to be closed on the same line.  It
429    also means that the result is never larger than the input. */
430 static void
431 dehtmlize(char *line)
432 {
433   int parsedindex=0;
434   char *parsed=line;
435
436   while(*line!='\0')
437     {
438       switch(*line)
439         {
440         case '<':
441           while(*line!='>' && *line!='\0')
442             line++;
443
444           if(*line!='\0')
445             line++;
446           break;
447
448         case '&':
449           if((*(line+1)!='\0' && tolower(*(line+1))=='l') &&
450              (*(line+2)!='\0' && tolower(*(line+2))=='t') &&
451              (*(line+3)!='\0' && *(line+3)==';'))
452             {
453               parsed[parsedindex++]='<';
454               line+=4;
455               break;
456             }
457           else if((*(line+1)!='\0' && tolower(*(line+1))=='g') &&
458                   (*(line+2)!='\0' && tolower(*(line+2))=='t') &&
459                   (*(line+3)!='\0' && *(line+3)==';'))
460             {
461               parsed[parsedindex++]='>';
462               line+=4;
463               break;
464             }
465           else if((*(line+1)!='\0' && tolower(*(line+1))=='a') &&
466                   (*(line+2)!='\0' && tolower(*(line+2))=='m') &&
467                   (*(line+3)!='\0' && tolower(*(line+3))=='p') &&
468                   (*(line+4)!='\0' && *(line+4)==';'))
469             {
470               parsed[parsedindex++]='&';
471               line+=5;
472               break;
473             }
474
475         default:
476           parsed[parsedindex++]=*line;
477           line++;
478           break;
479         }
480     }
481
482   parsed[parsedindex]='\0';
483
484   /* Chop off any trailing whitespace.  Note that the HKP servers have
485      \r\n as line endings, and the NAI HKP servers have just \n. */
486
487   if(parsedindex>0)
488     {
489       parsedindex--;
490       while(isspace(parsed[parsedindex]))
491         {
492           parsed[parsedindex]='\0';
493           parsedindex--;
494         }
495     }
496 }
497
498 /* pub  2048/<a href="/pks/lookup?op=get&search=0x3CB3B415">3CB3B415</a> 1998/04/03 David M. Shaw &lt;<a href="/pks/lookup?op=get&search=0x3CB3B415">dshaw@jabberwocky.com</a>&gt; */
499
500 /* Luckily enough, both the HKP server and NAI HKP interface to their
501    LDAP server are close enough in output so the same function can
502    parse them both. */
503
504 int parse_hkp_index(char *line,char **buffer)
505 {
506   static int open=0,revoked=0;
507   static char *key=NULL,*uid=NULL,*type=NULL;
508   static unsigned int bits,createtime;
509   int ret=0;
510
511   /* printf("Open %d, LINE: %s, uid: %s\n",open,line,uid); */
512
513   dehtmlize(line);
514
515   /* printf("Now open %d, LINE: \"%s\", uid: %s\n",open,line,uid); */
516
517   /* Try and catch some bastardization of HKP.  If we don't have
518      certain unchanging landmarks, we can't reliably parse the
519      response.  This only complains about problems within the key
520      section itself.  Headers and footers should not matter. */
521   if(open && line[0]!='\0' &&
522      strncasecmp(line,"pub ",4)!=0 &&
523      strncasecmp(line,"    ",4)!=0)
524     {
525       free(key);
526       free(uid);
527       fprintf(console,"gpgkeys: this keyserver is not fully HKP compatible\n");
528       return -1;
529     }
530
531   /* For multiple UIDs */
532   if(open && uid!=NULL)
533     {
534       ret=0;
535
536       if(!(revoked && !include_revoked))
537         {
538           char intstr[11],*buf;
539
540           buf=realloc(*buffer,
541                       (*buffer?strlen(*buffer):0)+
542                       (strlen(key)*4)+
543                       1+
544                       (strlen(uid)*4)
545                       +1
546                       +2
547                       +10
548                       +3
549                       +3
550                       +1
551                       +10
552                       +1
553                       +1
554                       +20);
555
556           if(buf)
557             *buffer=buf;
558           else
559             return -1;
560
561           append_quoted(*buffer,key,':');
562           append_quoted(*buffer,":",0);
563           append_quoted(*buffer,uid,':');
564           append_quoted(*buffer,":",0);
565           append_quoted(*buffer,revoked?"1:":":",0);
566           sprintf(intstr,"%u",createtime);
567           append_quoted(*buffer,intstr,':');
568           append_quoted(*buffer,":::",0);
569           if(type)
570             append_quoted(*buffer,type,':');
571           append_quoted(*buffer,":",0);
572           sprintf(intstr,"%u",bits);
573           append_quoted(*buffer,intstr,':');
574           append_quoted(*buffer,"\n",0);
575
576           ret=1;
577         }
578
579       if(strncmp(line,"    ",4)!=0)
580         {
581           revoked=0;
582           free(key);
583           free(uid);
584           uid=NULL;
585           open=0;
586         }
587     }
588
589   if(strncasecmp(line,"pub ",4)==0)
590     {
591       char *tok,*temp;
592
593       open=1;
594
595       line+=4;
596
597       tok=strsep(&line,"/");
598       if(tok==NULL)
599         return ret;
600
601       if(tok[strlen(tok)-1]=='R')
602         type="RSA";
603       else if(tok[strlen(tok)-1]=='D')
604         type="DSA";
605       else
606         type=NULL;
607
608       bits=atoi(tok);
609
610       tok=strsep(&line," ");
611       if(tok==NULL)
612         {
613           key=strdup("00000000");
614           return ret;
615         }
616
617       key=strdup(tok);
618
619       tok=strsep(&line," ");
620       if(tok==NULL)
621         return ret;
622
623       /* The date parser wants '-' instead of '/', so... */
624       temp=tok;
625       while(*temp!='\0')
626         {
627           if(*temp=='/')
628             *temp='-';
629
630           temp++;
631         }
632
633       createtime=scan_isodatestr(tok);
634     }
635
636   if(open)
637     {
638       if(line==NULL)
639         {
640           uid=strdup("Key index corrupted");
641           return ret;
642         }
643
644       while(*line==' ' && *line!='\0')
645         line++;
646
647       if(*line=='\0')
648         return ret;
649
650       if(strncmp(line,"*** KEY REVOKED ***",19)==0)
651         {
652           revoked=1;
653           return ret;
654         }
655
656       uid=strdup(line);
657     }
658
659   return ret;
660 }
661
662 int search_key(char *searchkey)
663 {
664   int ret=-1,err,count=0;
665   char *search,*request,*buffer=NULL;
666   char line[1024];
667   int max,len;
668
669   fprintf(output,"SEARCH %s BEGIN\n",searchkey);
670
671   /* Build the search string.  It's going to need url-encoding. */
672
673   max=0;
674   len=0;
675   search=NULL;
676   request=searchkey;
677
678   while(*request!='\0')
679     {
680       if(max-len<3)
681         {
682           max+=100;
683           search=realloc(search,max+1); /* Note +1 for \0 */
684         }
685
686       if(isalnum(*request) || *request=='-')
687         search[len++]=*request;
688       else if(*request==' ')
689         search[len++]='+';
690       else
691         {
692           sprintf(&search[len],"%%%02X",*request);
693           len+=3;
694         }
695
696       request++;
697     }
698
699   search[len]='\0';
700
701   if(verbose>2)
702     fprintf(console,"gpgkeys: HKP search for: %s\n",search);
703
704   fprintf(console,("gpgkeys: searching for \"%s\" from HKP server %s\n"),
705           searchkey,host);
706
707   http_get("index",search);
708
709   free(search);
710
711   while(fgets(line,1024,server))
712     {
713       err=parse_hkp_index(line,&buffer);
714       if(err==-1)
715         goto fail;
716
717       count+=err;
718     }
719
720   fprintf(output,"COUNT %d\n%s",count,buffer);
721   /* fprintf(output,"COUNT -1\n%s",buffer); */
722
723   fprintf(output,"SEARCH %s END\n",searchkey);
724
725   ret=0;
726
727  fail:
728   free(buffer);
729
730   return ret;
731 }
732
733 int main(int argc,char *argv[])
734 {
735   int port=0,arg,action=-1,ret=KEYSERVER_INTERNAL_ERROR;
736   char line[MAX_LINE];
737   int version,failed=0;
738   struct keylist *keylist=NULL,*keyptr=NULL;
739
740   console=stderr;
741
742   fprintf(console,
743           "gpgkeys: WARNING: this is an *experimental* HKP interface!\n");
744
745   while((arg=getopt(argc,argv,"ho:"))!=-1)
746     switch(arg)
747       {
748       default:
749       case 'h':
750         fprintf(console,"-h\thelp\n");
751         fprintf(console,"-o\toutput to this file\n");
752         return KEYSERVER_OK;
753
754       case 'o':
755         output=fopen(optarg,"w");
756         if(output==NULL)
757           {
758             fprintf(console,"gpgkeys: Cannot open output file \"%s\": %s\n",
759                     optarg,strerror(errno));
760             return KEYSERVER_INTERNAL_ERROR;
761           }
762
763         break;
764       }
765
766   if(argc>optind)
767     {
768       input=fopen(argv[optind],"r");
769       if(input==NULL)
770         {
771           fprintf(console,"gpgkeys: Cannot open input file \"%s\": %s\n",
772                   argv[optind],strerror(errno));
773           return KEYSERVER_INTERNAL_ERROR;
774         }
775     }
776
777   if(input==NULL)
778     input=stdin;
779
780   if(output==NULL)
781     output=stdout;
782
783   /* Get the command and info block */
784
785   while(fgets(line,MAX_LINE,input)!=NULL)
786     {
787       char commandstr[7];
788       char optionstr[30];
789       char hash;
790
791       if(line[0]=='\n')
792         break;
793
794       if(sscanf(line,"%c",&hash)==1 && hash=='#')
795         continue;
796
797       if(sscanf(line,"COMMAND %6s\n",commandstr)==1)
798         {
799           commandstr[6]='\0';
800
801           if(strcasecmp(commandstr,"get")==0)
802             action=GET;
803           else if(strcasecmp(commandstr,"send")==0)
804             action=SEND;
805           else if(strcasecmp(commandstr,"search")==0)
806             action=SEARCH;
807
808           continue;
809         }
810
811       if(sscanf(line,"HOST %79s\n",host)==1)
812         {
813           host[79]='\0';
814           continue;
815         }
816
817       if(sscanf(line,"PORT %9s\n",portstr)==1)
818         {
819           portstr[9]='\0';
820           port=atoi(portstr);
821           continue;
822         }
823
824       if(sscanf(line,"VERSION %d\n",&version)==1)
825         {
826           if(version!=0)
827             {
828               ret=KEYSERVER_VERSION_ERROR;
829               goto fail;
830             }
831
832           continue;
833         }
834
835       if(sscanf(line,"OPTION %29s\n",optionstr)==1)
836         {
837           int no=0;
838           char *start=&optionstr[0];
839
840           optionstr[29]='\0';
841
842           if(strncasecmp(optionstr,"no-",3)==0)
843             {
844               no=1;
845               start=&optionstr[3];
846             }
847
848           if(strcasecmp(start,"verbose")==0)
849             {
850               if(no)
851                 verbose--;
852               else
853                 verbose++;
854             }
855           else if(strcasecmp(start,"include-disabled")==0)
856             {
857               if(no)
858                 include_disabled=0;
859               else
860                 include_disabled=1;
861             }
862           else if(strcasecmp(start,"include-revoked")==0)
863             {
864               if(no)
865                 include_revoked=0;
866               else
867                 include_revoked=1;
868             }
869           else if(strcasecmp(start,"honor-http-proxy")==0 ||
870                   strcasecmp(start,"broken-http-proxy")==0)
871             {
872               fprintf(stderr,"gpgkeys: HKP does not currently support %s\n",
873                       start);
874             }
875
876           continue;
877         }
878     }
879
880   /* If it's a GET or a SEARCH, the next thing to come in is the
881      keyids.  If it's a SEND, then there are no keyids. */
882
883   if(action==SEND)
884     while(fgets(line,MAX_LINE,input)!=NULL && line[0]!='\n');
885   else if(action==GET || action==SEARCH)
886     {
887       for(;;)
888         {
889           struct keylist *work;
890
891           if(fgets(line,MAX_LINE,input)==NULL)
892             break;
893           else
894             {
895               if(line[0]=='\n')
896                 break;
897
898               work=malloc(sizeof(struct keylist));
899               if(work==NULL)
900                 {
901                   fprintf(console,"gpgkeys: out of memory while "
902                           "building key list\n");
903                   goto fail;
904                 }
905
906               strcpy(work->str,line);
907
908               /* Trim the trailing \n */
909               work->str[strlen(line)-1]='\0';
910
911               work->next=NULL;
912
913               /* Always attach at the end to keep the list in proper
914                  order for searching */
915               if(keylist==NULL)
916                 keylist=work;
917               else
918                 keyptr->next=work;
919
920               keyptr=work;
921             }
922         }
923     }
924   else
925     {
926       fprintf(console,"gpgkeys: no keyserver command specified\n");
927       goto fail;
928     }
929
930   /* Send the response */
931
932   fprintf(output,"VERSION 0\n");
933   fprintf(output,"PROGRAM %s\n\n",VERSION);
934
935   if(verbose>1)
936     {
937       fprintf(console,"Host:\t\t%s\n",host);
938       if(port)
939         fprintf(console,"Port:\t\t%d\n",port);
940       fprintf(console,"Command:\t%s\n",action==GET?"GET":
941               action==SEND?"SEND":"SEARCH");
942     }
943
944 #if 0
945   if(verbose>1)
946     {
947       vals=ldap_get_values(ldap,res,"software");
948       if(vals!=NULL)
949         {
950           fprintf(console,"Server: \t%s\n",vals[0]);
951           ldap_value_free(vals);
952         }
953
954       vals=ldap_get_values(ldap,res,"version");
955       if(vals!=NULL)
956         {
957           fprintf(console,"Version:\t%s\n",vals[0]);
958           ldap_value_free(vals);
959         }
960     }
961 #endif
962
963   switch(action)
964     {
965     case GET:
966       keyptr=keylist;
967
968       while(keyptr!=NULL)
969         {
970           http_connect(host,port);
971
972           if(get_key(keyptr->str)==-1)
973             failed++;
974
975           http_disconnect();
976
977           keyptr=keyptr->next;
978         }
979       break;
980
981     case SEND:
982       {
983         int ret2;
984
985         do
986           {
987             http_connect(host,port);
988             ret2=send_key();
989             if(ret2==-1)
990               failed++;
991             http_disconnect();
992           }
993         while(ret2!=1);
994       }
995       break;
996
997     case SEARCH:
998       {
999         char *searchkey=NULL;
1000         int len=0;
1001
1002         /* To search, we stick a space in between each key to search
1003            for. */
1004
1005         keyptr=keylist;
1006         while(keyptr!=NULL)
1007           {
1008             len+=strlen(keyptr->str)+1;
1009             keyptr=keyptr->next;
1010           }
1011
1012         searchkey=malloc(len+1);
1013         if(searchkey==NULL)
1014           goto fail;
1015
1016         searchkey[0]='\0';
1017
1018         keyptr=keylist;
1019         while(keyptr!=NULL)
1020           {
1021             strcat(searchkey,keyptr->str);
1022             strcat(searchkey," ");
1023             keyptr=keyptr->next;
1024           }
1025
1026         /* Nail that last space */
1027         searchkey[strlen(searchkey)-1]='\0';
1028
1029         http_connect(host,port);
1030
1031         if(search_key(searchkey)==-1)
1032           {
1033             fprintf(output,"SEARCH %s FAILED\n",searchkey);
1034             failed++;
1035           }
1036
1037         http_disconnect();
1038
1039         free(searchkey);
1040       }
1041
1042       break;
1043     }
1044
1045   if(!failed)
1046     ret=KEYSERVER_OK;
1047
1048  fail:
1049
1050   while(keylist!=NULL)
1051     {
1052       struct keylist *current=keylist;
1053       keylist=keylist->next;
1054       free(current);
1055     }
1056
1057   if(input!=stdin)
1058     fclose(input);
1059
1060   if(output!=stdout)
1061     fclose(output);
1062
1063   return ret;
1064 }