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