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