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