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