See ChangeLog: Thu May 20 14:04:08 CEST 1999 Werner Koch
[gnupg.git] / util / http.c
1 /* http.c  -  HTTP protocol handler
2  *      Copyright (C) 1999 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 <stdarg.h>
25 #include <string.h>
26 #include <ctype.h>
27 #include <errno.h>
28 #include <unistd.h>
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <sys/time.h>
32 #include <time.h>
33 #include <netinet/in.h>
34 #include <arpa/inet.h>
35 #include <netdb.h>
36
37 #include "util.h"
38 #include "iobuf.h"
39 #include "i18n.h"
40
41 #include "http.h"
42
43 #define MAX_LINELEN 20000  /* max. length of a HTTP line */
44 #define VALID_URI_CHARS "abcdefghijklmnopqrstuvwxyz"   \
45                         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"   \
46                         "01234567890@"                 \
47                         "!\"#$%&'()*+,-./:;<=>?[\\]^_{|}~"
48
49 #ifndef EAGAIN
50   #define EAGAIN  EWOULDBLOCK
51 #endif
52
53 static int parse_uri( PARSED_URI *ret_uri, const char *uri );
54 static void release_parsed_uri( PARSED_URI uri );
55 static int do_parse_uri( PARSED_URI uri, int only_local_part );
56 static int remove_escapes( byte *string );
57 static int insert_escapes( byte *buffer, const byte *string,
58                                          const byte *special );
59 static URI_TUPLE parse_tuple( byte *string );
60 static int send_request( HTTP_HD hd );
61 static byte *build_rel_path( PARSED_URI uri );
62 static int parse_response( HTTP_HD hd );
63
64 static int connect_server( const char *server, ushort port );
65 static int write_server( int sock, const char *data, size_t length );
66
67
68 int
69 http_open( HTTP_HD hd, HTTP_REQ_TYPE reqtype, const char *url,
70                                               unsigned int flags )
71 {
72     int rc;
73
74     if( flags || !(reqtype == HTTP_REQ_GET || reqtype == HTTP_REQ_POST) )
75         return G10ERR_INV_ARG;
76
77     /* initialize the handle */
78     memset( hd, 0, sizeof *hd );
79     hd->sock = -1;
80     hd->initialized = 1;
81     hd->req_type = reqtype;
82
83     rc = parse_uri( &hd->uri, url );
84     if( !rc ) {
85         rc = send_request( hd );
86         if( !rc ) {
87             hd->fp_write = iobuf_fdopen( hd->sock , "w" );
88             if( hd->fp_write )
89                 return 0;
90             rc = G10ERR_GENERAL;
91         }
92     }
93
94     if( !hd->fp_read && !hd->fp_write && hd->sock != -1 )
95         close( hd->sock );
96     iobuf_close( hd->fp_read );
97     iobuf_close( hd->fp_write);
98     release_parsed_uri( hd->uri );
99     hd->initialized = 0;
100
101     return rc;
102 }
103
104
105 void
106 http_start_data( HTTP_HD hd )
107 {
108     if( !hd->in_data ) {
109         iobuf_put( hd->fp_write, '\n' );
110         hd->in_data = 1;
111     }
112 }
113
114
115 int
116 http_wait_response( HTTP_HD hd, unsigned int *ret_status )
117 {
118     int rc;
119
120     http_start_data( hd ); /* make sure that we are in the data */
121     iobuf_flush( hd->fp_write );
122
123     hd->sock = dup( hd->sock );
124     if( hd->sock == -1 )
125         return G10ERR_GENERAL;
126     iobuf_close( hd->fp_write );
127     hd->fp_write = NULL;
128     shutdown( hd->sock, 1 );
129     hd->in_data = 0;
130
131     hd->fp_read = iobuf_fdopen( hd->sock , "r" );
132     if( !hd->fp_read )
133         return G10ERR_GENERAL;
134
135     rc = parse_response( hd );
136     if( !rc && ret_status )
137         *ret_status = hd->status_code;
138
139     return rc;
140 }
141
142
143 int
144 http_open_document( HTTP_HD hd, const char *document, unsigned int flags )
145 {
146     int rc;
147
148     if( flags )
149         return G10ERR_INV_ARG;
150
151     rc = http_open( hd, HTTP_REQ_GET, document, 0 );
152     if( rc )
153         return rc;
154
155     rc = http_wait_response( hd, NULL );
156     if( rc )
157         http_close( hd );
158
159     return rc;
160 }
161
162
163
164
165 void
166 http_close( HTTP_HD hd )
167 {
168     if( !hd || !hd->initialized )
169         return;
170     if( !hd->fp_read && !hd->fp_write && hd->sock != -1 )
171         close( hd->sock );
172     iobuf_close( hd->fp_read );
173     iobuf_close( hd->fp_write );
174     release_parsed_uri( hd->uri );
175     m_free( hd->buffer );
176     hd->initialized = 0;
177 }
178
179
180
181 /****************
182  * Parse an URI and put the result into the newly allocated ret_uri.
183  * The caller must always use release_parsed_uri to releases the
184  * resources (even on an error).
185  */
186 static int
187 parse_uri( PARSED_URI *ret_uri, const char *uri )
188 {
189    *ret_uri = m_alloc_clear( sizeof(**ret_uri) + strlen(uri) );
190    strcpy( (*ret_uri)->buffer, uri );
191    return do_parse_uri( *ret_uri, 0 );
192 }
193
194 static void
195 release_parsed_uri( PARSED_URI uri )
196 {
197     if( uri )
198     {
199         URI_TUPLE r, r2;
200
201         for( r = uri->query; r; r = r2 ) {
202             r2 = r->next;
203             m_free( r );
204         }
205         m_free( uri );
206     }
207 }
208
209 static int
210 do_parse_uri( PARSED_URI uri, int only_local_part )
211 {
212     URI_TUPLE *tail;
213     char *p, *p2, *p3;
214     int n;
215
216     p = uri->buffer;
217     n = strlen( uri->buffer );
218     /* initialize all fields to an empty string or an empty list */
219     uri->scheme = uri->host = uri->path = p + n;
220     uri->port = 0;
221     uri->params = uri->query = NULL;
222
223     /* a quick validity check */
224     if( strspn( p, VALID_URI_CHARS) != n )
225         return G10ERR_BAD_URI; /* invalid characters found */
226
227     if( !only_local_part ) {
228         /* find the scheme */
229         if( !(p2 = strchr( p, ':' ) ) || p2 == p )
230            return G10ERR_BAD_URI; /* No scheme */
231         *p2++ = 0;
232         strlwr( p );
233         uri->scheme = p;
234         if( !strcmp( uri->scheme, "http" ) )
235             ;
236         else if( !strcmp( uri->scheme, "x-hkp" ) ) /* same as HTTP */
237             ;
238         else
239             return G10ERR_INVALID_URI; /* Unsupported scheme */
240
241         p = p2;
242
243         /* find the hostname */
244         if( *p != '/' )
245             return G10ERR_INVALID_URI; /* does not start with a slash */
246
247         p++;
248         if( *p == '/' ) {  /* there seems to be a hostname */
249             p++;
250             if( (p2 = strchr(p, '/')) )
251                 *p2++ = 0;
252             strlwr( p );
253             uri->host = p;
254             if( (p3=strchr( p, ':' )) ) {
255                 *p3++ = 0;
256                 uri->port = atoi( p3 );
257             }
258             else
259                uri->port = 80;
260             uri->host = p;
261             if( (n = remove_escapes( uri->host )) < 0 )
262                 return G10ERR_BAD_URI;
263             if( n != strlen( p ) )
264                 return G10ERR_BAD_URI; /* hostname with a Nul in it */
265             p = p2 ? p2 : NULL;
266         }
267     } /* end global URI part */
268
269     /* parse the pathname part */
270     if( !p || !*p ) /* we don't have a path */
271         return 0; /* and this is okay */
272
273     /* todo: here we have to check params */
274
275     /* do we have a query part */
276     if( (p2 = strchr( p, '?' )) )
277         *p2++ = 0;
278
279     uri->path = p;
280     if( (n = remove_escapes( p )) < 0 )
281         return G10ERR_BAD_URI;
282     if( n != strlen( p ) )
283         return G10ERR_BAD_URI; /* path with a Nul in it */
284     p = p2 ? p2 : NULL;
285
286     if( !p || !*p ) /* we don't have a query string */
287         return 0;   /* okay */
288
289     /* now parse the query string */
290     tail = &uri->query;
291     for(;;) {
292         URI_TUPLE elem;
293
294         if( (p2 = strchr( p, '&' )) )
295             *p2++ = 0;
296         if( !(elem = parse_tuple( p )) )
297             return G10ERR_BAD_URI;
298         *tail = elem;
299         tail = &elem->next;
300
301         if( !p2 )
302            break; /* ready */
303         p = p2;
304     }
305
306     return 0;
307 }
308
309
310
311 /****************
312  * Remove all %xx escapes; this is done inplace.
313  * Returns: new length of the string.
314  */
315 static int
316 remove_escapes( byte *string )
317 {
318     int n = 0;
319     byte *p, *s;
320
321     for(p=s=string; *s ; s++ ) {
322         if( *s == '%' ) {
323             if( s[1] && s[2] && isxdigit(s[1]) && isxdigit(s[2]) ) {
324                 s++;
325                 *p  = *s >= '0' && *s <= '9' ? *s - '0' :
326                       *s >= 'A' && *s <= 'F' ? *s - 'A' + 10 : *s - 'a' + 10 ;
327                 *p <<= 4;
328                 s++;
329                 *p |= *s >= '0' && *s <= '9' ? *s - '0' :
330                       *s >= 'A' && *s <= 'F' ? *s - 'A' + 10 : *s - 'a' + 10 ;
331                 p++;
332                 n++;
333             }
334             else {
335                 *p++ = *s++;
336                 if( *s )
337                    *p++ = *s++;
338                 if( *s )
339                    *p++ = *s++;
340                 if( *s )
341                    *p = 0;
342                 return -1; /* bad URI */
343             }
344         }
345         else
346         {
347             *p++ = *s;
348             n++;
349         }
350     }
351     *p = 0; /* always keep a string terminator */
352     return n;
353 }
354
355
356 static int
357 insert_escapes( byte *buffer, const byte *string, const byte *special )
358 {
359     int n = 0;
360
361     for( ; *string; string++ ) {
362         if( strchr( VALID_URI_CHARS, *string )
363             && !strchr( special, *string ) )  {
364             if( buffer )
365                 *buffer++ = *string;
366             n++;
367         }
368         else {
369             if( buffer ) {
370                 sprintf( buffer, "%02X", *string );
371                 buffer += 3;
372             }
373             n += 3;
374         }
375     }
376     return n;
377 }
378
379
380
381
382
383 static URI_TUPLE
384 parse_tuple( byte *string )
385 {
386     byte *p = string;
387     byte *p2;
388     int n;
389     URI_TUPLE tuple;
390
391     if( (p2 = strchr( p, '=' )) )
392         *p2++ = 0;
393     if( (n = remove_escapes( p )) < 0 )
394         return NULL; /* bad URI */
395     if( n != strlen( p ) )
396        return NULL; /* name with a Nul in it */
397     tuple = m_alloc_clear( sizeof *tuple );
398     tuple->name = p;
399     if( !p2 )  {
400         /* we have only the name, so we assume an empty value string */
401         tuple->value = p + strlen(p);
402         tuple->valuelen = 0;
403     }
404     else { /* name and value */
405         if( (n = remove_escapes( p2 )) < 0 ) {
406             m_free( tuple );
407             return NULL; /* bad URI */
408         }
409         tuple->value = p2;
410         tuple->valuelen = n;
411     }
412     return tuple;
413 }
414
415
416 /****************
417  * Send a HTTP request to the server
418  * Returns 0 if the request was successful
419  */
420 static int
421 send_request( HTTP_HD hd )
422 {
423     const byte *server;
424     byte *request, *p;
425     ushort port;
426     int rc;
427
428     server = *hd->uri->host? hd->uri->host : "localhost";
429     port   = hd->uri->port?  hd->uri->port : 80;
430
431     hd->sock = connect_server( server, port );
432     if( hd->sock == -1 )
433         return G10ERR_NETWORK;
434
435     p = build_rel_path( hd->uri );
436     request = m_alloc( strlen(p) + 20 );
437     sprintf( request, "%s %s%s HTTP/1.0\r\n",
438                           hd->req_type == HTTP_REQ_GET ? "GET" :
439                           hd->req_type == HTTP_REQ_HEAD? "HEAD":
440                           hd->req_type == HTTP_REQ_POST? "POST": "OOPS",
441                                                   *p == '/'? "":"/", p );
442     m_free(p);
443
444     rc = write_server( hd->sock, request, strlen(request) );
445     m_free( request );
446
447     return rc;
448 }
449
450
451
452
453 /****************
454  * Build the relative path from the parsed URI.
455  * Minimal implementation.
456  */
457 static byte*
458 build_rel_path( PARSED_URI uri )
459 {
460     URI_TUPLE r;
461     byte *rel_path, *p;
462     int n;
463
464     /* count the needed space */
465     n = insert_escapes( NULL, uri->path, "%;?&" );
466     /* todo: build params */
467     for( r=uri->query; r; r = r->next ) {
468         n++; /* '?'/'&' */
469         n += insert_escapes( NULL, r->name, "%;?&=" );
470         n++; /* '='*/
471         n += insert_escapes( NULL, r->value, "%;?&=" );
472     }
473     n++;
474
475     /* now  allocate and copy */
476     p = rel_path = m_alloc( n );
477     n = insert_escapes( p, uri->path, "%;?&" );
478     p += n;
479     /* todo: add params */
480     for( r=uri->query; r; r = r->next ) {
481         *p++ = r == uri->query? '?':'&';
482         n = insert_escapes( p, r->name, "%;?&=" );
483         p += n;
484         *p++ = '=';
485         /* todo: use valuelen */
486         n = insert_escapes( p, r->value, "%;?&=" );
487         p += n;
488     }
489     *p = 0;
490     return rel_path;
491 }
492
493
494
495 /***********************
496  * Parse the response from a server.
497  * Returns: errorcode and sets some fileds in the handle
498  */
499 static int
500 parse_response( HTTP_HD hd )
501 {
502     byte *line, *p, *p2;
503     unsigned maxlen, len;
504
505     /* Wait for the status line */
506     do {
507         maxlen = MAX_LINELEN;
508         len = iobuf_read_line( hd->fp_read, &hd->buffer,
509                                             &hd->buffer_size, &maxlen );
510         line = hd->buffer;
511         if( !maxlen )
512             return -1; /* line has been truncated */
513         if( !len )
514             return -1; /* eof */
515     } while( !*line  );
516
517     if( (p = strchr( line, '/')) )
518         *p++ = 0;
519     if( !p || strcmp( line, "HTTP" ) )
520         return 0; /* assume http 0.9 */
521
522     if( (p2 = strpbrk( p, " \t" ) ) ) {
523         *p2++ = 0;
524         p2 += strspn( p2, " \t" );
525     }
526     if( !p2 )
527         return 0; /* assume http 0.9 */
528     p = p2;
529     /* todo: add HTTP version number check here */
530     if( (p2 = strpbrk( p, " \t" ) ) )
531         *p2++ = 0;
532     if( !isdigit(p[0]) || !isdigit(p[1]) || !isdigit(p[2]) || p[3] ) {
533          /* malformed HTTP statuscode - assume HTTP 0.9 */
534         hd->is_http_0_9 = 1;
535         hd->status_code = 200;
536         return 0;
537     }
538     hd->status_code = atoi( p );
539
540     /* skip all the header lines and wait for the empty line */
541     do {
542         maxlen = MAX_LINELEN;
543         len = iobuf_read_line( hd->fp_read, &hd->buffer,
544                                &hd->buffer_size, &maxlen );
545         line = hd->buffer;
546         /* we ignore truncated lines */
547         if( !len )
548             return -1; /* eof */
549         /* time lineendings */
550         if( (*line == '\r' && line[1] == '\n') || *line == '\n' )
551             *line = 0;
552     } while( len && *line  );
553
554     return 0;
555 }
556
557 #if 0
558 static int
559 start_server()
560 {
561     struct sockaddr_in mya;
562     struct sockaddr_in peer;
563     int fd, client;
564     fd_set rfds;
565     int addrlen;
566     int i;
567
568     if( (fd=socket(AF_INET,SOCK_STREAM, 0)) == -1 ) {
569         log_error("socket() failed: %s\n", strerror(errno));
570         return -1;
571     }
572     i = 1;
573     if( setsockopt( fd, SOL_SOCKET, SO_REUSEADDR, (byte*)&i, sizeof(i) ) )
574         log_info("setsockopt(SO_REUSEADDR) failed: %s\n", strerror(errno) );
575
576     mya.sin_family=AF_INET;
577     memset(&mya.sin_addr, 0, sizeof(mya.sin_addr));
578     mya.sin_port=htons(11371);
579
580     if( bind( fd, (struct sockaddr *)&mya, sizeof(mya)) ) {
581         log_error("bind to port 11371 failed: %s\n", strerror(errno) );
582         close( fd );
583         return -1;
584     }
585
586     if( listen( fd, 5 ) ) {
587         log_error("listen failed: %s\n", strerror(errno) );
588         close( fd );
589         return -1;
590     }
591
592     for(;;) {
593         FD_ZERO(&rfds);
594         FD_SET( fd, &rfds );
595
596         if( select( fd+1, &rfds, NULL, NULL, NULL) <= 0 )
597             continue; /* ignore any errors */
598
599         if( !FD_ISSET( fd, &rfds ) )
600             continue;
601
602         addrlen = sizeof peer;
603         client = accept( fd, (struct sockaddr *)&peer, &addrlen);
604         if( client == -1 )
605             continue; /* oops */
606
607         log_info("connect from %s\n", inet_ntoa( peer.sin_addr ) );
608
609         fflush(stdout);
610         fflush(stderr);
611         if( !fork() ) {
612             int c;
613             FILE *fp;
614
615             fp = fdopen( client , "r" );
616             while( (c=getc(fp)) != EOF )
617                 putchar(c);
618             fclose(fp);
619             exit(0);
620         }
621         close( client );
622     }
623
624
625     return 0;
626 }
627 #endif
628
629
630
631 static int
632 connect_server( const char *server, ushort port )
633 {
634     struct sockaddr_in addr;
635     struct hostent *host;
636     int sd;
637
638     addr.sin_family = AF_INET;
639     addr.sin_port = htons(port);
640     host = gethostbyname((char*)server);
641     if( !host )
642          return -1;
643
644     addr.sin_addr = *(struct in_addr*)host->h_addr;
645
646     sd = socket(AF_INET, SOCK_STREAM, 0);
647     if( sd == -1 )
648         return -1;
649
650     if( connect( sd, (struct sockaddr *)&addr, sizeof addr) == -1 ) {
651         close(sd);
652         return -1;
653     }
654
655     return sd;
656 }
657
658
659 static int
660 write_server( int sock, const char *data, size_t length )
661 {
662     int nleft, nwritten;
663
664     nleft = length;
665     while( nleft > 0 ) {
666         nwritten = write( sock, data, nleft );
667         if( nwritten == -1 ) {
668             if( errno == EINTR )
669                 continue;
670             if( errno == EAGAIN ) {
671                 struct timeval tv;
672
673                 tv.tv_sec =  0;
674                 tv.tv_usec = 50000;
675                 select(0, NULL, NULL, NULL, &tv);
676                 continue;
677             }
678             log_info("write failed: %s\n", strerror(errno));
679             return G10ERR_NETWORK;
680         }
681         nleft -=nwritten;
682         data += nwritten;
683     }
684
685     return 0;
686 }
687
688
689 /**** Test code ****/
690 #ifdef TEST
691
692 int
693 main(int argc, char **argv)
694 {
695     int rc;
696     PARSED_URI uri;
697     URI_TUPLE r;
698     struct http_context hd;
699     int c;
700
701     log_set_name("http-test");
702     if( argc == 1 ) {
703         start_server();
704         return 0;
705     }
706
707     if( argc != 2 ) {
708         fprintf(stderr,"usage: http-test uri\n");
709         return 1;
710     }
711     argc--; argv++;
712
713     rc = parse_uri( &uri, *argv );
714     if( rc ) {
715         log_error("`%s': %s\n", *argv, g10_errstr(rc));
716         release_parsed_uri( uri );
717         return 1;
718     }
719
720     printf("Scheme: %s\n", uri->scheme );
721     printf("Host  : %s\n", uri->host );
722     printf("Port  : %u\n", uri->port );
723     printf("Path  : %s\n", uri->path );
724     for( r=uri->params; r; r = r->next ) {
725         printf("Params: %s=%s", r->name, r->value );
726         if( strlen( r->value ) != r->valuelen )
727             printf(" [real length=%d]", (int)r->valuelen );
728         putchar('\n');
729     }
730     for( r=uri->query; r; r = r->next ) {
731         printf("Query : %s=%s", r->name, r->value );
732         if( strlen( r->value ) != r->valuelen )
733             printf(" [real length=%d]", (int)r->valuelen );
734         putchar('\n');
735     }
736     release_parsed_uri( uri ); uri = NULL;
737
738     rc = http_open_document( &hd, *argv, 0 );
739     if( rc ) {
740         log_error("can't get `%s': %s\n", *argv, g10_errstr(rc));
741         return 1;
742     }
743     log_info("open_http_document succeeded; status=%u\n", hd.status_code );
744     while( (c=iobuf_get( hd.fp_read)) != -1 )
745         putchar(c);
746     http_close( &hd );
747     return 0;
748 }
749 #endif /*TEST*/