use of EXTSEP_S
[gnupg.git] / g10 / hkp.c
1 /* hkp.c  -  Horowitz Keyserver Protocol
2  * Copyright (C) 1998, 1999, 2000, 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 <stdlib.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <errno.h>
27 #include <assert.h>
28
29 #include "errors.h"
30 #include "util.h"
31 #include "ttyio.h"
32 #include "i18n.h"
33 #include "options.h"
34 #include "filter.h"
35 #include "http.h"
36 #include "main.h"
37 #include "keyserver-internal.h"
38
39 static int urlencode_filter( void *opaque, int control,
40                              IOBUF a, byte *buf, size_t *ret_len);
41
42 /****************
43  * Try to import the key with KEYID from a keyserver but ask the user
44  * before doing so.
45  * Returns: 0 the key was successfully imported
46  *          -1 key not found on server or user does not want to
47  *             import the key
48  *          or other error codes.
49  */
50 int
51 hkp_ask_import( KEYDB_SEARCH_DESC *desc, void *stats_handle)
52 {
53     struct http_context hd;
54     char *request;
55     int rc;
56     unsigned int hflags = opt.keyserver_options.honor_http_proxy? HTTP_FLAG_TRY_PROXY : 0;
57     u32 key[2];
58
59     if(desc->mode==KEYDB_SEARCH_MODE_FPR20)
60       keyid_from_fingerprint(desc->u.fpr,MAX_FINGERPRINT_LEN,key);
61     else if(desc->mode==KEYDB_SEARCH_MODE_LONG_KID ||
62             desc->mode==KEYDB_SEARCH_MODE_SHORT_KID)
63       {
64         key[0]=desc->u.kid[0];
65         key[1]=desc->u.kid[1];
66       }
67     else
68       return -1; /* HKP does not support v3 fingerprints */
69
70     if(opt.keyserver_options.verbose)
71       log_info(_("requesting key %08lX from %s\n"),
72                (ulong)key[1],opt.keyserver_uri);
73
74     request = m_alloc( strlen( opt.keyserver_host ) + 100 );
75     /* hkp does not accept the long keyid - we should really write a
76      * nicer one :-)
77      * FIXME: request binary mode - need to pass no_armor mode
78      * down to the import function.  Marc told that there is such a
79      * binary mode ... how?
80      */
81
82     if(opt.keyserver_options.broken_http_proxy)
83       hflags |= HTTP_FLAG_NO_SHUTDOWN;
84
85     sprintf(request,"x-hkp://%s%s%s/pks/lookup?op=get&search=0x%08lX",
86             opt.keyserver_host,
87             opt.keyserver_port?":":"",
88             opt.keyserver_port?opt.keyserver_port:"",
89             (ulong)key[1] );
90
91   if(opt.keyserver_options.verbose>2)
92     log_info("request is \"%s\"\n",request);
93
94     rc = http_open_document( &hd, request, hflags );
95     if( rc ) {
96         log_info(_("can't get key from keyserver: %s\n"),
97                         rc == G10ERR_NETWORK? strerror(errno)
98                                             : g10_errstr(rc) );
99     }
100     else {
101       rc = import_keys_stream( hd.fp_read, 0, stats_handle,
102                                opt.keyserver_options.import_options);
103         http_close( &hd );
104     }
105
106     m_free( request );
107     return rc;
108 }
109
110 int
111 hkp_export( STRLIST users )
112 {
113     int rc;
114     armor_filter_context_t afx;
115     IOBUF temp = iobuf_temp();
116     struct http_context hd;
117     char *request;
118     unsigned int status;
119     unsigned int hflags = opt.keyserver_options.honor_http_proxy? HTTP_FLAG_TRY_PROXY : 0;
120
121     iobuf_push_filter( temp, urlencode_filter, NULL );
122
123     memset( &afx, 0, sizeof afx);
124     afx.what = 1;
125     iobuf_push_filter( temp, armor_filter, &afx );
126
127     rc = export_pubkeys_stream( temp, users,
128                                 opt.keyserver_options.export_options );
129     if( rc == -1 ) {
130         iobuf_close(temp);
131         return 0;
132     }
133
134     iobuf_flush_temp( temp );
135
136     request = m_alloc( strlen( opt.keyserver_host ) + 100 );
137
138     if(opt.keyserver_options.broken_http_proxy)
139       hflags |= HTTP_FLAG_NO_SHUTDOWN;
140
141     sprintf( request, "x-hkp://%s%s%s/pks/add",
142              opt.keyserver_host,
143              opt.keyserver_port?":":"",
144              opt.keyserver_port?opt.keyserver_port:"");
145
146   if(opt.keyserver_options.verbose>2)
147     log_info("request is \"%s\"\n",request);
148
149     rc = http_open( &hd, HTTP_REQ_POST, request , hflags );
150     if( rc ) {
151         log_error(_("can't connect to `%s': %s\n"),
152                    opt.keyserver_host,
153                         rc == G10ERR_NETWORK? strerror(errno)
154                                             : g10_errstr(rc) );
155         iobuf_close(temp);
156         m_free( request );
157         return rc;
158     }
159
160     sprintf( request, "Content-Length: %u\n",
161                       (unsigned)iobuf_get_temp_length(temp) + 9 );
162     iobuf_writestr( hd.fp_write, request );
163     m_free( request );
164     http_start_data( &hd );
165
166     iobuf_writestr( hd.fp_write, "keytext=" );
167     iobuf_write( hd.fp_write, iobuf_get_temp_buffer(temp),
168                               iobuf_get_temp_length(temp) );
169     iobuf_put( hd.fp_write, '\n' );
170     iobuf_flush_temp( temp );
171     iobuf_close(temp);
172
173     rc = http_wait_response( &hd, &status );
174     if( rc ) {
175         log_error(_("error sending to `%s': %s\n"),
176                    opt.keyserver_host, g10_errstr(rc) );
177     }
178     else {
179       #if 1
180         if( opt.verbose ) {
181             int c;
182             while( (c=iobuf_get(hd.fp_read)) != EOF )
183               if ( c >= 32 && c < 127 )
184                 putchar( c );
185               else
186                 putchar ( '?' );
187         }
188       #endif
189         if( (status/100) == 2 )
190             log_info(_("success sending to `%s' (status=%u)\n"),
191                                         opt.keyserver_host, status  );
192         else
193             log_error(_("failed sending to `%s': status=%u\n"),
194                                         opt.keyserver_host, status  );
195     }
196     http_close( &hd );
197     return rc;
198 }
199
200 static int
201 urlencode_filter( void *opaque, int control,
202                   IOBUF a, byte *buf, size_t *ret_len)
203 {
204     size_t size = *ret_len;
205     int rc=0;
206
207     if( control == IOBUFCTRL_FLUSH ) {
208         const byte *p;
209         for(p=buf; size; p++, size-- ) {
210             if( isalnum(*p) || *p == '-' )
211                 iobuf_put( a, *p );
212             else if( *p == ' ' )
213                 iobuf_put( a, '+' );
214             else {
215                 char numbuf[5];
216                 sprintf(numbuf, "%%%02X", *p );
217                 iobuf_writestr(a, numbuf );
218             }
219         }
220     }
221     else if( control == IOBUFCTRL_DESC )
222         *(char**)buf = "urlencode_filter";
223     return rc;
224 }
225
226 static int
227 write_quoted(IOBUF a, const char *buf, char delim)
228 {
229   char quoted[5];
230
231   sprintf(quoted,"\\x%02X",delim);
232
233   while(*buf)
234     {
235       if(*buf==delim)
236         {
237           if(iobuf_writestr(a,quoted))
238             return -1;
239         }
240       else if(*buf=='\\')
241         {
242           if(iobuf_writestr(a,"\\x5c"))
243             return -1;
244         }
245       else
246         {
247           if(iobuf_writebyte(a,*buf))
248             return -1;
249         }
250
251       buf++;
252     }
253
254   return 0;
255 }
256
257 /* 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; */
258
259 /* Luckily enough, both the HKP server and NAI HKP interface to their
260    LDAP server are close enough in output so the same function can
261    parse them both. */
262
263 static int 
264 parse_hkp_index(IOBUF buffer,char *line)
265 {
266   static int open=0,revoked=0;
267   static char *key=NULL;
268 #ifdef __riscos__
269   static char *uid=NULL;
270 #else
271   static unsigned char *uid=NULL;
272 #endif
273   static u32 bits,createtime;
274   int ret=0;
275
276   /* printf("Open %d, LINE: %s, uid: %s\n",open,line,uid); */
277
278   /* Try and catch some bastardization of HKP.  If we don't have
279      certain unchanging landmarks, we can't reliably parse the
280      response. */
281   if(open && ascii_memcasecmp(line,"</pre>",6)!=0 &&
282      ascii_memcasecmp(line,"pub  ",5)!=0 &&
283      ascii_memcasecmp(line,"     ",5)!=0)
284     {
285       m_free(key);
286       m_free(uid);
287       log_error(_("this keyserver is not fully HKP compatible\n"));
288       return -1;
289     }
290
291   /* For multiple UIDs */
292   if(open && uid!=NULL)
293     {
294       ret=0;
295
296       if(!(revoked && !opt.keyserver_options.include_revoked))
297         {
298           char intstr[11];
299
300           if(key)
301             write_quoted(buffer,key,':');
302           iobuf_writestr(buffer,":");
303           write_quoted(buffer,uid,':');
304           iobuf_writestr(buffer,":");
305           iobuf_writestr(buffer,revoked?"1:":":");
306           sprintf(intstr,"%u",createtime);
307           write_quoted(buffer,intstr,':');
308           iobuf_writestr(buffer,"::::");
309           sprintf(intstr,"%u",bits);
310           write_quoted(buffer,intstr,':');
311           iobuf_writestr(buffer,"\n");
312
313           ret=1;
314         }
315
316       if(strncmp(line,"     ",5)!=0)
317         {
318           revoked=0;
319           m_free(key);
320           m_free(uid);
321           uid=NULL;
322           open=0;
323         }
324     }
325
326   if(ascii_memcasecmp(line,"pub  ",5)==0)
327     {
328       char *tok,*temp;
329
330       open=1;
331
332       line+=4;
333
334       tok=strsep(&line,"/");
335       if(tok==NULL)
336         return ret;
337
338       bits=atoi(tok);
339
340       tok=strsep(&line,">");
341       if(tok==NULL)
342         return ret;
343
344       tok=strsep(&line,"<");
345       if(tok==NULL)
346         return ret;
347
348       key=m_strdup(tok);
349
350       tok=strsep(&line," ");
351       if(tok==NULL)
352         return ret;
353
354       tok=strsep(&line," ");
355       if(tok==NULL)
356         return ret;
357   
358       /* The date parser wants '-' instead of '/', so... */
359       temp=tok;
360       while(*temp!='\0')
361         {
362           if(*temp=='/')
363             *temp='-';
364
365           temp++;
366         }
367
368       createtime=scan_isodatestr(tok);
369     }
370
371   if(open)
372     {
373       int uidindex=0;
374
375       if(line==NULL)
376         {
377           uid=m_strdup("Key index corrupted");
378           return ret;
379         }
380
381       /* All that's left is the user name.  Strip off anything
382          <between brackets> and de-urlencode it. */
383
384       while(*line==' ' && *line!='\0')
385         line++;
386
387       if(strncmp(line,"*** KEY REVOKED ***",19)==0)
388         {
389           revoked=1;
390           return ret;
391         }
392
393       uid=m_alloc(strlen(line)+1);
394
395       while(*line!='\0')
396         {
397           switch(*line)
398             {
399             case '<':
400               while(*line!='>' && *line!='\0')
401                 line++;
402
403               if(*line!='\0')
404                 line++;
405               break;
406
407             case '&':
408               if((*(line+1)!='\0' && tolower(*(line+1))=='l') &&
409                  (*(line+2)!='\0' && tolower(*(line+2))=='t') &&
410                  (*(line+3)!='\0' && *(line+3)==';'))
411                 {
412                   uid[uidindex++]='<';
413                   line+=4;
414                   break;
415                 }
416               else if((*(line+1)!='\0' && tolower(*(line+1))=='g') &&
417                  (*(line+2)!='\0' && tolower(*(line+2))=='t') &&
418                  (*(line+3)!='\0' && *(line+3)==';'))
419                 {
420                   uid[uidindex++]='>';
421                   line+=4;
422                   break;
423                 }
424               else if((*(line+1)!='\0' && tolower(*(line+1))=='a') &&
425                  (*(line+2)!='\0' && tolower(*(line+2))=='m') &&
426                  (*(line+3)!='\0' && tolower(*(line+3))=='p') &&
427                  (*(line+4)!='\0' && *(line+4)==';'))
428                 {
429                   uid[uidindex++]='&';
430                   line+=5;
431                   break;
432                 }
433
434             default:
435               uid[uidindex++]=*line;
436               line++;
437               break;
438             }
439         }
440
441       uid[uidindex]='\0';
442
443       /* Chop off the trailing \r, \n, or both. This is fussy as the
444          true HKP servers have \r\n, and the NAI HKP servers have just
445          \n. */
446
447       if(isspace(uid[uidindex-1]))
448         uid[uidindex-1]='\0';
449
450       if(isspace(uid[uidindex-2]))
451         uid[uidindex-2]='\0';
452     }
453
454   return ret;
455 }
456
457 int hkp_search(STRLIST tokens)
458 {
459   int rc=0,len=0,max,first=1;
460   unsigned int maxlen=1024,buflen=0;
461 #ifndef __riscos__
462   unsigned char *searchstr=NULL,*searchurl;
463   unsigned char *request;
464 #else
465   char *searchstr=NULL,*searchurl;
466   char *request;
467 #endif
468   struct http_context hd;
469   unsigned int hflags=opt.keyserver_options.honor_http_proxy?HTTP_FLAG_TRY_PROXY:0;
470   byte *line=NULL;
471
472   /* Glue the tokens together to make a search string */
473
474   for(;tokens;tokens=tokens->next)
475     {
476       len+=strlen(tokens->d)+1;
477
478       searchstr=m_realloc(searchstr,len+1);
479       if(first)
480         {
481           searchstr[0]='\0';
482           first=0;
483         }
484
485       strcat(searchstr,tokens->d);
486       strcat(searchstr," ");
487     }
488
489   if(len<=1)
490     {
491       m_free(searchstr);
492       return 0;
493     }
494
495   searchstr[len-1]='\0';
496
497   log_info(_("searching for \"%s\" from HKP server %s\n"),
498            searchstr,opt.keyserver_host);
499
500   /* Now make it url-ish */
501
502   max=0;
503   len=0;
504   searchurl=NULL;
505   request=searchstr;
506
507   while(*request!='\0')
508     {
509       if(max-len<3)
510         {
511           max+=100;
512           searchurl=m_realloc(searchurl,max+1); /* Note +1 for \0 */
513         }
514
515       if(isalnum(*request) || *request=='-')
516         searchurl[len++]=*request;
517       else if(*request==' ')
518         searchurl[len++]='+';
519       else
520         {
521           sprintf(&searchurl[len],"%%%02X",*request);
522           len+=3;
523         }
524
525       request++;
526     }
527
528   searchurl[len]='\0';
529
530   request=m_alloc(strlen(opt.keyserver_host) + 100 + strlen(searchurl));
531
532   if(opt.keyserver_options.broken_http_proxy)
533     hflags |= HTTP_FLAG_NO_SHUTDOWN;
534
535   sprintf(request,"x-hkp://%s%s%s/pks/lookup?op=index&search=%s",
536           opt.keyserver_host,
537           opt.keyserver_port?":":"",
538           opt.keyserver_port?opt.keyserver_port:"",
539           searchurl);
540
541   if(opt.keyserver_options.verbose>2)
542     log_info("request is \"%s\"\n",request);
543
544   rc=http_open_document(&hd,request,hflags);
545   if(rc)
546     {
547       log_error(_("can't search keyserver: %s\n"),
548                rc==G10ERR_NETWORK?strerror(errno):g10_errstr(rc));
549     }
550   else
551     {
552       IOBUF buffer;
553       int count=1;
554       int ret;
555
556       buffer=iobuf_temp();
557
558       rc=1;
559       while(rc!=0)
560         {
561           /* This is a judgement call.  Is it better to slurp up all
562              the results before prompting the user?  On the one hand,
563              it probably makes the keyserver happier to not be blocked
564              on sending for a long time while the user picks a key.
565              On the other hand, it might be nice for the server to be
566              able to stop sending before a large search result page is
567              complete. */
568
569           rc=iobuf_read_line(hd.fp_read,&line,&buflen,&maxlen);
570
571           ret=parse_hkp_index(buffer,line);
572           if(ret==-1)
573             break;
574
575           if(rc!=0)
576             count+=ret;
577         }
578
579       http_close(&hd);
580
581       count--;
582
583       if(ret>-1)
584         keyserver_search_prompt(buffer,count,searchstr);
585
586       iobuf_close(buffer);
587       m_free(line);
588     }
589
590   m_free(request);
591   m_free(searchurl);
592   m_free(searchstr);
593
594   return rc;
595 }