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