Minor fixes
[gnupg.git] / keyserver / gpgkeys_hkp.c
1 /* gpgkeys_hkp.c - talk to an HKP keyserver
2  * Copyright (C) 2001, 2002, 2003, 2004 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 <string.h>
24 #include <ctype.h>
25 #include <stdlib.h>
26 #include <errno.h>
27 #include <unistd.h>
28 #ifdef HAVE_GETOPT_H
29 #include <getopt.h>
30 #endif
31 #define INCLUDED_BY_MAIN_MODULE 1
32 #include "util.h"
33 #include "http.h"
34 #include "keyserver.h"
35 #include "ksutil.h"
36
37 extern char *optarg;
38 extern int optind;
39
40 #define GET    0
41 #define SEND   1
42 #define SEARCH 2
43 #define MAX_LINE 80
44
45 static int verbose=0,include_revoked=0,include_disabled=0;
46 static unsigned int http_flags=0;
47 static char host[80]={'\0'},proxy[80]={'\0'},port[10]={'\0'};
48 static FILE *input=NULL,*output=NULL,*console=NULL;
49
50 #define BEGIN "-----BEGIN PGP PUBLIC KEY BLOCK-----"
51 #define END   "-----END PGP PUBLIC KEY BLOCK-----"
52
53 struct keylist
54 {
55   char str[MAX_LINE];
56   struct keylist *next;
57 };
58
59 #ifdef __riscos__
60 #define HTTP_PROXY_ENV           "GnuPG$HttpProxy"
61 #else
62 #define HTTP_PROXY_ENV           "http_proxy"
63 #endif
64
65 int
66 urlencode_filter( void *opaque, int control,
67                   IOBUF a, byte *buf, size_t *ret_len)
68 {
69     size_t size = *ret_len;
70     int rc=0;
71
72     if( control == IOBUFCTRL_FLUSH ) {
73         const byte *p;
74         for(p=buf; size; p++, size-- ) {
75             if( isalnum(*p) || *p == '-' )
76                 iobuf_put( a, *p );
77             else if( *p == ' ' )
78                 iobuf_put( a, '+' );
79             else {
80                 char numbuf[5];
81                 sprintf(numbuf, "%%%02X", *p );
82                 iobuf_writestr(a, numbuf );
83             }
84         }
85     }
86     else if( control == IOBUFCTRL_DESC )
87         *(char**)buf = "urlencode_filter";
88     return rc;
89 }
90
91 int
92 send_key(int *eof)
93 {
94   int rc,begin=0,end=0,ret=KEYSERVER_INTERNAL_ERROR;
95   char keyid[17];
96   char *request;
97   struct http_context hd;
98   unsigned int status;
99   IOBUF temp = iobuf_temp();
100   char line[MAX_LINE];
101
102   memset(&hd,0,sizeof(hd));
103
104   request=malloc(strlen(host)+100);
105   if(!request)
106     {
107       fprintf(console,"gpgkeys: out of memory\n");
108       return KEYSERVER_NO_MEMORY;
109     }
110
111   iobuf_push_filter(temp,urlencode_filter,NULL);
112
113   /* Read and throw away input until we see the BEGIN */
114
115   while(fgets(line,MAX_LINE,input)!=NULL)
116     if(sscanf(line,"KEY %16s BEGIN\n",keyid)==1)
117       {
118         begin=1;
119         break;
120       }
121
122   if(!begin)
123     {
124       /* i.e. eof before the KEY BEGIN was found.  This isn't an
125          error. */
126       *eof=1;
127       ret=KEYSERVER_OK;
128       goto fail;
129     }
130
131   /* Now slurp up everything until we see the END */
132
133   while(fgets(line,MAX_LINE,input))
134     if(sscanf(line,"KEY %16s END\n",keyid)==1)
135       {
136         end=1;
137         break;
138       }
139     else
140       if(iobuf_writestr(temp,line))
141         {
142           fprintf(console,"gpgkeys: internal iobuf error\n");
143           goto fail;
144         }
145
146   if(!end)
147     {
148       fprintf(console,"gpgkeys: no KEY %s END found\n",keyid);
149       *eof=1;
150       ret=KEYSERVER_KEY_INCOMPLETE;
151       goto fail;
152     }
153
154   iobuf_flush_temp(temp);
155
156   sprintf(request,"hkp://%s%s%s/pks/add",host,port[0]?":":"",port[0]?port:"");
157
158   if(verbose>2)
159     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
160
161   rc=http_open(&hd,HTTP_REQ_POST,request,http_flags,proxy[0]?proxy:NULL);
162   if(rc)
163     {
164       fprintf(console,"gpgkeys: unable to connect to `%s'\n",host);
165       goto fail;
166     }
167
168   /* Some keyservers require this Content-Type (e.g. CryptoEx). */
169   iobuf_writestr(hd.fp_write,
170                  "Content-Type: application/x-www-form-urlencoded\r\n");
171
172   sprintf(request,"Content-Length: %u\r\n",
173           (unsigned)iobuf_get_temp_length(temp)+9);
174   iobuf_writestr(hd.fp_write,request);
175
176   http_start_data(&hd);
177
178   iobuf_writestr(hd.fp_write,"keytext=");
179   iobuf_write(hd.fp_write,
180               iobuf_get_temp_buffer(temp),iobuf_get_temp_length(temp));
181   iobuf_put(hd.fp_write,'\n');
182
183   rc=http_wait_response(&hd,&status);
184   if(rc)
185     {
186       fprintf(console,"gpgkeys: error sending to `%s': %s\n",
187               host,g10_errstr(rc));
188       goto fail;
189     }
190
191   if((status/100)!=2)
192     {
193       fprintf(console,"gpgkeys: remote server returned error %d\n",status);
194       goto fail;
195     }
196
197   fprintf(output,"KEY %s SENT\n",keyid);
198
199   ret=KEYSERVER_OK;
200
201  fail:
202   free(request);
203   iobuf_close(temp);
204   http_close(&hd);
205
206   if(ret!=0 && begin)
207     fprintf(output,"KEY %s FAILED %d\n",keyid,ret);
208
209   return ret;
210 }
211
212 int
213 get_key(char *getkey)
214 {
215   int rc,gotit=0;
216   char search[29];
217   char *request;
218   struct http_context hd;
219
220   /* Build the search string.  HKP only uses the short key IDs. */
221
222   if(strncmp(getkey,"0x",2)==0)
223     getkey+=2;
224
225   if(strlen(getkey)==32)
226     {
227       fprintf(console,
228               "gpgkeys: HKP keyservers do not support v3 fingerprints\n");
229       fprintf(output,"KEY 0x%s BEGIN\n",getkey);
230       fprintf(output,"KEY 0x%s FAILED %d\n",getkey,KEYSERVER_NOT_SUPPORTED);
231       return KEYSERVER_NOT_SUPPORTED;
232     }
233
234  if(strlen(getkey)>8)
235     {
236       char *offset=&getkey[strlen(getkey)-8];
237
238       /* fingerprint or long key id.  Take the last 8 characters and
239          treat it like a short key id */
240
241       sprintf(search,"0x%.8s",offset);
242     }
243  else
244    {
245       /* short key id */
246
247       sprintf(search,"0x%.8s",getkey);
248     }
249
250   fprintf(output,"KEY 0x%s BEGIN\n",getkey);
251
252   request=malloc(strlen(host)+100);
253   if(!request)
254     {
255       fprintf(console,"gpgkeys: out of memory\n");
256       return KEYSERVER_NO_MEMORY;
257     }
258
259   sprintf(request,"hkp://%s%s%s/pks/lookup?op=get&options=mr&search=%s",
260           host,port[0]?":":"",port[0]?port:"", search);
261
262   if(verbose>2)
263     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
264
265   rc=http_open_document(&hd,request,http_flags,proxy[0]?proxy:NULL);
266   if(rc!=0)
267     {
268       fprintf(console,"gpgkeys: HKP fetch error: %s\n",
269               rc==G10ERR_NETWORK?strerror(errno):g10_errstr(rc));
270       fprintf(output,"KEY 0x%s FAILED %d\n",getkey,
271             rc==G10ERR_NETWORK?KEYSERVER_UNREACHABLE:KEYSERVER_INTERNAL_ERROR);
272     }
273   else
274     {
275       unsigned int maxlen=1024,buflen;
276       byte *line=NULL;
277
278       while(iobuf_read_line(hd.fp_read,&line,&buflen,&maxlen))
279         {
280           maxlen=1024;
281
282           if(gotit)
283             {
284               fputs (line, output);
285               if(strncmp(line,END,strlen(END))==0)
286                 break;
287             }
288           else
289             if(strncmp(line,BEGIN,strlen(BEGIN))==0)
290               {
291                 fputs (line,output);
292                 gotit=1;
293               }
294         }
295
296       if(gotit)
297         fprintf(output,"KEY 0x%s END\n",getkey);
298       else
299         {
300           fprintf(console,"gpgkeys: key %s not found on keyserver\n",getkey);
301           fprintf(output,"KEY 0x%s FAILED %d\n",
302                   getkey,KEYSERVER_KEY_NOT_FOUND);
303         }
304
305       m_free(line);
306     }
307
308   free(request);
309
310   return KEYSERVER_OK;
311 }
312
313 /* Remove anything <between brackets> and de-urlencode in place.  Note
314    that this requires all brackets to be closed on the same line.  It
315    also means that the result is never larger than the input. */
316 void
317 dehtmlize(char *line)
318 {
319   int parsedindex=0;
320   char *parsed=line;
321
322   while(*line!='\0')
323     {
324       switch(*line)
325         {
326         case '<':
327           while(*line!='>' && *line!='\0')
328             line++;
329
330           if(*line!='\0')
331             line++;
332           break;
333
334         case '&':
335           if((*(line+1)!='\0' && ascii_tolower(*(line+1))=='l') &&
336              (*(line+2)!='\0' && ascii_tolower(*(line+2))=='t') &&
337              (*(line+3)!='\0' && *(line+3)==';'))
338             {
339               parsed[parsedindex++]='<';
340               line+=4;
341               break;
342             }
343           else if((*(line+1)!='\0' && ascii_tolower(*(line+1))=='g') &&
344                   (*(line+2)!='\0' && ascii_tolower(*(line+2))=='t') &&
345                   (*(line+3)!='\0' && *(line+3)==';'))
346             {
347               parsed[parsedindex++]='>';
348               line+=4;
349               break;
350             }
351           else if((*(line+1)!='\0' && ascii_tolower(*(line+1))=='a') &&
352                   (*(line+2)!='\0' && ascii_tolower(*(line+2))=='m') &&
353                   (*(line+3)!='\0' && ascii_tolower(*(line+3))=='p') &&
354                   (*(line+4)!='\0' && *(line+4)==';'))
355             {
356               parsed[parsedindex++]='&';
357               line+=5;
358               break;
359             }
360           else if((*(line+1)!='\0' && ascii_tolower(*(line+1))=='q') &&
361                   (*(line+2)!='\0' && ascii_tolower(*(line+2))=='u') &&
362                   (*(line+3)!='\0' && ascii_tolower(*(line+3))=='o') &&
363                   (*(line+4)!='\0' && ascii_tolower(*(line+4))=='t') &&
364                   (*(line+5)!='\0' && *(line+5)==';'))
365             {
366               parsed[parsedindex++]='"';
367               line+=6;
368               break;
369             }
370
371         default:
372           parsed[parsedindex++]=*line;
373           line++;
374           break;
375         }
376     }
377
378   parsed[parsedindex]='\0';
379
380   /* Chop off any trailing whitespace.  Note that the HKP servers have
381      \r\n as line endings, and the NAI HKP servers have just \n. */
382
383   if(parsedindex>0)
384     {
385       parsedindex--;
386       while(isspace(((unsigned char *)parsed)[parsedindex]))
387         {
388           parsed[parsedindex]='\0';
389           if(parsedindex==0)
390             break;
391           parsedindex--;
392         }
393     }
394 }
395
396 int
397 write_quoted(IOBUF a, const char *buf, char delim)
398 {
399   while(*buf)
400     {
401       if(*buf==delim)
402         {
403           char quoted[5];
404           sprintf(quoted,"%%%02X",delim);
405           if(iobuf_writestr(a,quoted))
406             return -1;
407         }
408       else if(*buf=='%')
409         {
410           if(iobuf_writestr(a,"%25"))
411             return -1;
412         }
413       else
414         {
415           if(iobuf_writebyte(a,*buf))
416             return -1;
417         }
418
419       buf++;
420     }
421
422   return 0;
423 }
424
425 /* 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; */
426
427 /* Luckily enough, both the HKP server and NAI HKP interface to their
428    LDAP server are close enough in output so the same function can
429    parse them both. */
430
431 int
432 parse_hkp_index(IOBUF buffer,char *line)
433 {
434   int ret=0;
435
436   /* printf("Open %d, LINE: `%s'\n",open,line); */
437
438   dehtmlize(line);
439
440   /* printf("Now open %d, LINE: `%s'\n",open,line); */
441
442   if(line[0]=='\0')
443     return 0;
444   else if(ascii_strncasecmp(line,"pub",3)==0)
445     {
446       char *tok,*keyid,*uid=NULL,number[15];
447       int bits=0,type=0,disabled=0,revoked=0;
448       u32 createtime=0;
449
450       line+=3;
451
452       if(*line=='-')
453         {
454           disabled=1;
455           if(!include_disabled)
456             return 0;
457         }
458
459       line++;
460
461       tok=strsep(&line,"/");
462       if(tok==NULL || strlen(tok)==0)
463         return ret;
464
465       if(tok[strlen(tok)-1]=='R')
466         type=1;
467       else if(tok[strlen(tok)-1]=='D')
468         type=17;
469
470       bits=atoi(tok);
471
472       keyid=strsep(&line," ");
473
474       tok=strsep(&line," ");
475       if(tok!=NULL)
476         {
477           char *temp=tok;
478
479           /* The date parser wants '-' instead of '/', so... */
480           while(*temp!='\0')
481             {
482               if(*temp=='/')
483                 *temp='-';
484
485               temp++;
486             }
487
488           createtime=scan_isodatestr(tok);
489         }
490
491       if(line!=NULL)
492         {
493           while(*line==' ' && *line!='\0')
494             line++;
495
496           if(*line!='\0')
497             {
498               if(strncmp(line,"*** KEY REVOKED ***",19)==0)
499                 {
500                   revoked=1;
501                   if(!include_revoked)
502                     return 0;
503                 }
504               else
505                 uid=line;
506             }
507         }
508
509       if(keyid)
510         {
511           iobuf_writestr(buffer,"pub:");
512
513           write_quoted(buffer,keyid,':');
514
515           iobuf_writestr(buffer,":");
516
517           if(type)
518             {
519               sprintf(number,"%d",type);
520               write_quoted(buffer,number,':');
521             }
522
523           iobuf_writestr(buffer,":");
524
525           if(bits)
526             {
527               sprintf(number,"%d",bits);
528               write_quoted(buffer,number,':');
529             }
530
531           iobuf_writestr(buffer,":");
532
533           if(createtime)
534             {
535               sprintf(number,"%d",createtime);
536               write_quoted(buffer,number,':');
537             }
538
539           iobuf_writestr(buffer,"::");
540
541           if(revoked)
542             write_quoted(buffer,"r",':');
543
544           if(disabled)
545             write_quoted(buffer,"d",':');
546
547           if(uid)
548             {
549               iobuf_writestr(buffer,"\nuid:");
550               write_quoted(buffer,uid,':');
551             }
552
553           iobuf_writestr(buffer,"\n");
554
555           ret=1;
556         }
557     }
558   else if(ascii_strncasecmp(line,"   ",3)==0)
559     {
560       while(*line==' ' && *line!='\0')
561         line++;
562
563       if(*line!='\0')
564         {
565           iobuf_writestr(buffer,"uid:");
566           write_quoted(buffer,line,':');
567           iobuf_writestr(buffer,"\n");
568         }
569     }
570
571 #if 0
572   else if(open)
573     {
574       /* Try and catch some bastardization of HKP.  If we don't have
575          certain unchanging landmarks, we can't reliably parse the
576          response.  This only complains about problems within the key
577          section itself.  Headers and footers should not matter. */
578
579       fprintf(console,"gpgkeys: this keyserver does not support searching\n");
580       ret=-1;
581     }
582 #endif
583
584   return ret;
585 }
586
587 void
588 handle_old_hkp_index(IOBUF inp)
589 {
590   int ret,rc,count=0;
591   unsigned int buflen;
592   byte *line=NULL;
593   IOBUF buffer=iobuf_temp();
594
595   do
596     {
597       unsigned int maxlen=1024;
598
599       /* This is a judgement call.  Is it better to slurp up all the
600          results before prompting the user?  On the one hand, it
601          probably makes the keyserver happier to not be blocked on
602          sending for a long time while the user picks a key.  On the
603          other hand, it might be nice for the server to be able to
604          stop sending before a large search result page is
605          complete. */
606
607       rc=iobuf_read_line(inp,&line,&buflen,&maxlen);
608
609       ret=parse_hkp_index(buffer,line);
610       if(ret==-1)
611         break;
612
613       if(rc!=0)
614         count+=ret;
615     }
616   while(rc!=0);
617
618   m_free(line);
619
620   if(ret>-1)
621     fprintf(output,"info:1:%d\n%s",count,iobuf_get_temp_buffer(buffer));
622
623   iobuf_close(buffer);
624 }
625
626 int
627 search_key(char *searchkey)
628 {
629   int max=0,len=0,ret=KEYSERVER_INTERNAL_ERROR,rc;
630   struct http_context hd;
631   char *search=NULL,*request=NULL;
632   unsigned char *skey=(unsigned char*) searchkey;
633
634   fprintf(output,"SEARCH %s BEGIN\n",searchkey);
635
636   /* Build the search string.  It's going to need url-encoding. */
637
638   while(*skey!='\0')
639     {
640       if(max-len<3)
641         {
642           max+=100;
643           search=realloc(search,max+1); /* Note +1 for \0 */
644           if (!search)
645             {
646               fprintf(console,"gpgkeys: out of memory\n");
647               ret=KEYSERVER_NO_MEMORY;
648               goto fail;
649             }
650         }
651
652       if(isalnum(*skey) || *skey=='-')
653         search[len++]=*skey;
654       else if(*skey==' ')
655         search[len++]='+';
656       else
657         {
658           sprintf(&search[len],"%%%02X",*skey);
659           len+=3;
660         }
661
662       skey++;
663     }
664
665   if(!search)
666     {
667       fprintf(console,"gpgkeys: corrupt input?\n");
668       return -1;
669     }
670
671   search[len]='\0';
672
673   request=malloc(strlen(host)+100+strlen(search));
674   if(!request)
675     {
676       fprintf(console,"gpgkeys: out of memory\n");
677       ret=KEYSERVER_NO_MEMORY;
678       goto fail;
679     }
680
681   sprintf(request,"hkp://%s%s%s/pks/lookup?op=index&options=mr&search=%s",
682           host,port[0]?":":"",port[0]?port:"",search);
683
684   if(verbose>2)
685     fprintf(console,"gpgkeys: HTTP URL is `%s'\n",request);
686
687   rc=http_open_document(&hd,request,http_flags,proxy[0]?proxy:NULL);
688   if(rc)
689     {
690       fprintf(console,"gpgkeys: can't search keyserver `%s': %s\n",
691               host,rc==G10ERR_NETWORK?strerror(errno):g10_errstr(rc));
692     }
693   else
694     {
695       unsigned int maxlen=1024,buflen;
696       byte *line=NULL;
697
698       /* Is it a pksd that knows how to handle machine-readable
699          format? */
700
701       rc=iobuf_read_line(hd.fp_read,&line,&buflen,&maxlen);
702       if(line[0]=='<')
703         handle_old_hkp_index(hd.fp_read);
704       else
705         do
706           {
707             fprintf(output,"%s",line);
708             maxlen=1024;
709             rc=iobuf_read_line(hd.fp_read,&line,&buflen,&maxlen);
710           }
711         while(rc!=0);
712
713       m_free(line);
714
715       http_close(&hd);
716
717       fprintf(output,"SEARCH %s END\n",searchkey);
718
719       ret=KEYSERVER_OK;
720     }
721
722  fail:
723
724   free(request);
725   free(search);
726
727   if(ret!=KEYSERVER_OK)
728     fprintf(output,"SEARCH %s FAILED %d\n",searchkey,ret);
729
730   return ret;
731 }
732
733 void
734 fail_all(struct keylist *keylist,int action,int err)
735 {
736   if(!keylist)
737     return;
738
739   if(action==SEARCH)
740     {
741       fprintf(output,"SEARCH ");
742       while(keylist)
743         {
744           fprintf(output,"%s ",keylist->str);
745           keylist=keylist->next;
746         }
747       fprintf(output,"FAILED %d\n",err);
748     }
749   else
750     while(keylist)
751       {
752         fprintf(output,"KEY %s FAILED %d\n",keylist->str,err);
753         keylist=keylist->next;
754       }
755 }
756
757 static void 
758 show_help (FILE *fp)
759 {
760   fprintf (fp,"-h\thelp\n");
761   fprintf (fp,"-V\tversion\n");
762   fprintf (fp,"-o\toutput to this file\n");
763 }
764
765 int
766 main(int argc,char *argv[])
767 {
768   int arg,action=-1,ret=KEYSERVER_INTERNAL_ERROR;
769   char line[MAX_LINE];
770   int failed=0;
771   struct keylist *keylist=NULL,*keyptr=NULL;
772   unsigned int timeout=DEFAULT_KEYSERVER_TIMEOUT;
773
774   console=stderr;
775
776   /* Kludge to implement standard GNU options.  */
777   if (argc > 1 && !strcmp (argv[1], "--version"))
778     {
779       fputs ("gpgkeys_ldap (GnuPG) " VERSION"\n", stdout);
780       return 0;
781     }
782   else if (argc > 1 && !strcmp (argv[1], "--help"))
783     {
784       show_help (stdout);
785       return 0;
786     }
787
788   while((arg=getopt(argc,argv,"hVo:"))!=-1)
789     switch(arg)
790       {
791       default:
792       case 'h':
793         show_help (console);
794         return KEYSERVER_OK;
795
796       case 'V':
797         fprintf(stdout,"%d\n%s\n",KEYSERVER_PROTO_VERSION,VERSION);
798         return KEYSERVER_OK;
799
800       case 'o':
801         output=fopen(optarg,"w");
802         if(output==NULL)
803           {
804             fprintf(console,"gpgkeys: Cannot open output file `%s': %s\n",
805                     optarg,strerror(errno));
806             return KEYSERVER_INTERNAL_ERROR;
807           }
808
809         break;
810       }
811
812   if(argc>optind)
813     {
814       input=fopen(argv[optind],"r");
815       if(input==NULL)
816         {
817           fprintf(console,"gpgkeys: Cannot open input file `%s': %s\n",
818                   argv[optind],strerror(errno));
819           return KEYSERVER_INTERNAL_ERROR;
820         }
821     }
822
823   if(input==NULL)
824     input=stdin;
825
826   if(output==NULL)
827     output=stdout;
828
829   /* Get the command and info block */
830
831   while(fgets(line,MAX_LINE,input)!=NULL)
832     {
833       int version;
834       char commandstr[7];
835       char optionstr[110];
836       char hash;
837
838       if(line[0]=='\n')
839         break;
840
841       if(sscanf(line,"%c",&hash)==1 && hash=='#')
842         continue;
843
844       if(sscanf(line,"COMMAND %6s\n",commandstr)==1)
845         {
846           commandstr[6]='\0';
847
848           if(strcasecmp(commandstr,"get")==0)
849             action=GET;
850           else if(strcasecmp(commandstr,"send")==0)
851             action=SEND;
852           else if(strcasecmp(commandstr,"search")==0)
853             action=SEARCH;
854
855           continue;
856         }
857
858       if(sscanf(line,"HOST %79s\n",host)==1)
859         {
860           host[79]='\0';
861           continue;
862         }
863
864       if(sscanf(line,"PORT %9s\n",port)==1)
865         {
866           port[9]='\0';
867           continue;
868         }
869
870       if(sscanf(line,"VERSION %d\n",&version)==1)
871         {
872           if(version!=KEYSERVER_PROTO_VERSION)
873             {
874               ret=KEYSERVER_VERSION_ERROR;
875               goto fail;
876             }
877
878           continue;
879         }
880
881       if(sscanf(line,"OPTION %109s\n",optionstr)==1)
882         {
883           int no=0;
884           char *start=&optionstr[0];
885
886           optionstr[109]='\0';
887
888           if(strncasecmp(optionstr,"no-",3)==0)
889             {
890               no=1;
891               start=&optionstr[3];
892             }
893
894           if(strcasecmp(start,"verbose")==0)
895             {
896               if(no)
897                 verbose--;
898               else
899                 verbose++;
900             }
901           else if(strcasecmp(start,"include-revoked")==0)
902             {
903               if(no)
904                 include_revoked=0;
905               else
906                 include_revoked=1;
907             }
908           else if(strcasecmp(start,"include-disabled")==0)
909             {
910               if(no)
911                 include_disabled=0;
912               else
913                 include_disabled=1;
914             }
915           else if(strncasecmp(start,"http-proxy",10)==0)
916             {
917               if(no)
918                 proxy[0]='\0';
919               else if(start[10]=='=')
920                 {
921                   strncpy(proxy,&start[11],79);
922                   proxy[79]='\0';
923                 }
924               else if(start[10]=='\0')
925                 {
926                   char *http_proxy=getenv(HTTP_PROXY_ENV);
927                   if(http_proxy)
928                     {
929                       strncpy(proxy,http_proxy,79);
930                       proxy[79]='\0';
931                     }
932                 }
933             }
934           else if(strcasecmp(start,"broken-http-proxy")==0)
935             {
936               if(no)
937                 http_flags&=~HTTP_FLAG_NO_SHUTDOWN;
938               else
939                 http_flags|=HTTP_FLAG_NO_SHUTDOWN;
940             }
941           else if(strcasecmp(start,"try-dns-srv")==0)
942             {
943               if(no)
944                 http_flags&=~HTTP_FLAG_TRY_SRV;
945               else
946                 http_flags|=HTTP_FLAG_TRY_SRV;
947             }
948           else if(strncasecmp(start,"timeout",7)==0)
949             {
950               if(no)
951                 timeout=0;
952               else
953                 timeout=atoi(&start[8]);
954             }
955
956           continue;
957         }
958     }
959
960   if(timeout && register_timeout()==-1)
961     {
962       fprintf(console,"gpgkeys: unable to register timeout handler\n");
963       return KEYSERVER_INTERNAL_ERROR;
964     }
965
966   /* By suggested convention, if the user gives a :port, then disable
967      SRV. */
968   if(port[0])
969     http_flags&=~HTTP_FLAG_TRY_SRV;
970
971   /* If it's a GET or a SEARCH, the next thing to come in is the
972      keyids.  If it's a SEND, then there are no keyids. */
973
974   if(action==SEND)
975     while(fgets(line,MAX_LINE,input)!=NULL && line[0]!='\n');
976   else if(action==GET || action==SEARCH)
977     {
978       for(;;)
979         {
980           struct keylist *work;
981
982           if(fgets(line,MAX_LINE,input)==NULL)
983             break;
984           else
985             {
986               if(line[0]=='\n' || line[0]=='\0')
987                 break;
988
989               work=malloc(sizeof(struct keylist));
990               if(work==NULL)
991                 {
992                   fprintf(console,"gpgkeys: out of memory while "
993                           "building key list\n");
994                   ret=KEYSERVER_NO_MEMORY;
995                   goto fail;
996                 }
997
998               strcpy(work->str,line);
999
1000               /* Trim the trailing \n */
1001               work->str[strlen(line)-1]='\0';
1002
1003               work->next=NULL;
1004
1005               /* Always attach at the end to keep the list in proper
1006                  order for searching */
1007               if(keylist==NULL)
1008                 keylist=work;
1009               else
1010                 keyptr->next=work;
1011
1012               keyptr=work;
1013             }
1014         }
1015     }
1016   else
1017     {
1018       fprintf(console,"gpgkeys: no keyserver command specified\n");
1019       goto fail;
1020     }
1021
1022   /* Send the response */
1023
1024   fprintf(output,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
1025   fprintf(output,"PROGRAM %s\n\n",VERSION);
1026
1027   if(verbose>1)
1028     {
1029       fprintf(console,"Host:\t\t%s\n",host);
1030       if(port[0])
1031         fprintf(console,"Port:\t\t%s\n",port);
1032       fprintf(console,"Command:\t%s\n",action==GET?"GET":
1033               action==SEND?"SEND":"SEARCH");
1034     }
1035
1036 #if 0
1037   if(verbose>1)
1038     {
1039       vals=ldap_get_values(ldap,res,"software");
1040       if(vals!=NULL)
1041         {
1042           fprintf(console,"Server: \t%s\n",vals[0]);
1043           ldap_value_free(vals);
1044         }
1045
1046       vals=ldap_get_values(ldap,res,"version");
1047       if(vals!=NULL)
1048         {
1049           fprintf(console,"Version:\t%s\n",vals[0]);
1050           ldap_value_free(vals);
1051         }
1052     }
1053 #endif
1054
1055   switch(action)
1056     {
1057     case GET:
1058       keyptr=keylist;
1059
1060       while(keyptr!=NULL)
1061         {
1062           set_timeout(timeout);
1063
1064           if(get_key(keyptr->str)!=KEYSERVER_OK)
1065             failed++;
1066
1067           keyptr=keyptr->next;
1068         }
1069       break;
1070
1071     case SEND:
1072       {
1073         int eof=0;
1074
1075         do
1076           {
1077             set_timeout(timeout);
1078
1079             if(send_key(&eof)!=KEYSERVER_OK)
1080               failed++;
1081           }
1082         while(!eof);
1083       }
1084       break;
1085
1086     case SEARCH:
1087       {
1088         char *searchkey=NULL;
1089         int len=0;
1090
1091         set_timeout(timeout);
1092
1093         /* To search, we stick a space in between each key to search
1094            for. */
1095
1096         keyptr=keylist;
1097         while(keyptr!=NULL)
1098           {
1099             len+=strlen(keyptr->str)+1;
1100             keyptr=keyptr->next;
1101           }
1102
1103         searchkey=malloc(len+1);
1104         if(searchkey==NULL)
1105           {
1106             ret=KEYSERVER_NO_MEMORY;
1107             fail_all(keylist,action,KEYSERVER_NO_MEMORY);
1108             goto fail;
1109           }
1110
1111         searchkey[0]='\0';
1112
1113         keyptr=keylist;
1114         while(keyptr!=NULL)
1115           {
1116             strcat(searchkey,keyptr->str);
1117             strcat(searchkey," ");
1118             keyptr=keyptr->next;
1119           }
1120
1121         /* Nail that last space */
1122         if(*searchkey)
1123           searchkey[strlen(searchkey)-1]='\0';
1124
1125         if(search_key(searchkey)!=KEYSERVER_OK)
1126           failed++;
1127
1128         free(searchkey);
1129       }
1130
1131       break;
1132     }
1133
1134   if(!failed)
1135     ret=KEYSERVER_OK;
1136
1137  fail:
1138   while(keylist!=NULL)
1139     {
1140       struct keylist *current=keylist;
1141       keylist=keylist->next;
1142       free(current);
1143     }
1144
1145   if(input!=stdin)
1146     fclose(input);
1147
1148   if(output!=stdout)
1149     fclose(output);
1150
1151   return ret;
1152 }