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