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