Updated from latest NewPG project
[gnupg.git] / util / http.c
index 4082092..fa3d512 100644 (file)
@@ -1,5 +1,5 @@
 /* http.c  -  HTTP protocol handler
- *     Copyright (C) 1999 Free Software Foundation, Inc.
+ *     Copyright (C) 1999, 2001 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
 #include <string.h>
 #include <ctype.h>
 #include <errno.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/time.h>
-#include <arpa/inet.h>
-#include <netinet/in.h>
-#include <netdb.h>
+
+#ifdef __MINGW32__
+ #include <windows.h>
+#else
+ #include <unistd.h>
+ #include <sys/types.h>
+ #include <sys/socket.h>
+ #include <sys/time.h>
+ #include <time.h>
+ #include <netinet/in.h>
+ #include <arpa/inet.h>
+ #include <netdb.h>
+#endif
 
 #include "util.h"
 #include "iobuf.h"
 
 #include "http.h"
 
+#ifdef __riscos__
+  #define HTTP_PROXY_ENV           "GnuPG$HttpProxy"
+  #define HTTP_PROXY_ENV_PRINTABLE "<GnuPG$HttpProxy>"
+#else
+  #define HTTP_PROXY_ENV           "http_proxy"
+  #define HTTP_PROXY_ENV_PRINTABLE "$http_proxy"
+#endif
+
+#ifdef __MINGW32__
+#define sock_close(a)  closesocket(a)
+#else
+#define sock_close(a)  close(a)
+#endif
+
 #define MAX_LINELEN 20000  /* max. length of a HTTP line */
 #define VALID_URI_CHARS "abcdefghijklmnopqrstuvwxyz"   \
                        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"   \
@@ -61,7 +81,39 @@ static byte *build_rel_path( PARSED_URI uri );
 static int parse_response( HTTP_HD hd );
 
 static int connect_server( const char *server, ushort port );
-static int write_server( int socket, const char *data, size_t length );
+static int write_server( int sock, const char *data, size_t length );
+
+#ifdef __MINGW32__
+static void
+deinit_sockets (void)
+{
+    WSACleanup();
+}
+
+static void
+init_sockets (void)
+{
+    static int initialized;
+    static WSADATA wsdata;
+
+    if (initialized)
+        return;
+
+    if( WSAStartup( 0x0101, &wsdata ) ) {
+        log_error ("error initializing socket library: ec=%d\n", 
+                    (int)WSAGetLastError () );
+        return;
+    }
+    if( wsdata.wVersion < 0x0001 ) {
+        log_error ("socket library version is %x.%x - but 1.1 needed\n",
+                   LOBYTE(wsdata.wVersion), HIBYTE(wsdata.wVersion));
+        WSACleanup();
+        return;
+    }
+    atexit ( deinit_sockets );
+    initialized = 1;
+}
+#endif /*__MINGW32__*/
 
 
 int
@@ -70,28 +122,29 @@ http_open( HTTP_HD hd, HTTP_REQ_TYPE reqtype, const char *url,
 {
     int rc;
 
-    if( flags || !(reqtype == HTTP_REQ_GET || reqtype == HTTP_REQ_POST) )
+    if( !(reqtype == HTTP_REQ_GET || reqtype == HTTP_REQ_POST) )
        return G10ERR_INV_ARG;
 
     /* initialize the handle */
     memset( hd, 0, sizeof *hd );
-    hd->socket = -1;
+    hd->sock = -1;
     hd->initialized = 1;
     hd->req_type = reqtype;
+    hd->flags = flags;
 
     rc = parse_uri( &hd->uri, url );
     if( !rc ) {
        rc = send_request( hd );
        if( !rc ) {
-           hd->fp_write = iobuf_fdopen( hd->socket , "w" );
+           hd->fp_write = iobuf_sockopen( hd->sock , "w" );
            if( hd->fp_write )
                return 0;
            rc = G10ERR_GENERAL;
        }
     }
 
-    if( !hd->fp_read && !hd->fp_write && hd->socket != -1 )
-       close( hd->socket );
+    if( !hd->fp_read && !hd->fp_write && hd->sock != -1 )
+       sock_close( hd->sock );
     iobuf_close( hd->fp_read );
     iobuf_close( hd->fp_write);
     release_parsed_uri( hd->uri );
@@ -104,8 +157,9 @@ http_open( HTTP_HD hd, HTTP_REQ_TYPE reqtype, const char *url,
 void
 http_start_data( HTTP_HD hd )
 {
+    iobuf_flush ( hd->fp_write );
     if( !hd->in_data ) {
-       iobuf_put( hd->fp_write, '\n' );
+        write_server (hd->sock, "\r\n", 2);
        hd->in_data = 1;
     }
 }
@@ -117,17 +171,20 @@ http_wait_response( HTTP_HD hd, unsigned int *ret_status )
     int rc;
 
     http_start_data( hd ); /* make sure that we are in the data */
-    iobuf_flush( hd->fp_write );
 
-    hd->socket = dup( hd->socket );
-    if( hd->socket == -1 )
+  #if 0
+    hd->sock = dup( hd->sock ); 
+    if( hd->sock == -1 )
        return G10ERR_GENERAL;
-    iobuf_close( hd->fp_write );
+  #endif
+    iobuf_ioctl (hd->fp_write, 1, 1, NULL); /* keep the socket open */
+    iobuf_close (hd->fp_write);
     hd->fp_write = NULL;
-    shutdown( hd->socket, 1 );
+    if ( !(hd->flags & HTTP_FLAG_NO_SHUTDOWN) )
+        shutdown( hd->sock, 1 );
     hd->in_data = 0;
 
-    hd->fp_read = iobuf_fdopen( hd->socket , "r" );
+    hd->fp_read = iobuf_sockopen( hd->sock , "r" );
     if( !hd->fp_read )
        return G10ERR_GENERAL;
 
@@ -144,10 +201,7 @@ http_open_document( HTTP_HD hd, const char *document, unsigned int flags )
 {
     int rc;
 
-    if( flags )
-       return G10ERR_INV_ARG;
-
-    rc = http_open( hd, HTTP_REQ_GET, document, 0 );
+    rc = http_open( hd, HTTP_REQ_GET, document, flags );
     if( rc )
        return rc;
 
@@ -166,8 +220,8 @@ http_close( HTTP_HD hd )
 {
     if( !hd || !hd->initialized )
        return;
-    if( !hd->fp_read && !hd->fp_write && hd->socket != -1 )
-       close( hd->socket );
+    if( !hd->fp_read && !hd->fp_write && hd->sock != -1 )
+       sock_close( hd->sock );
     iobuf_close( hd->fp_read );
     iobuf_close( hd->fp_write );
     release_parsed_uri( hd->uri );
@@ -230,10 +284,11 @@ do_parse_uri( PARSED_URI uri, int only_local_part )
        *p2++ = 0;
        strlwr( p );
        uri->scheme = p;
+        uri->port = 80;
        if( !strcmp( uri->scheme, "http" ) )
            ;
        else if( !strcmp( uri->scheme, "x-hkp" ) ) /* same as HTTP */
-           ;
+           uri->port = 11371;
        else
            return G10ERR_INVALID_URI; /* Unsupported scheme */
 
@@ -254,8 +309,7 @@ do_parse_uri( PARSED_URI uri, int only_local_part )
                *p3++ = 0;
                uri->port = atoi( p3 );
            }
-           else
-              uri->port = 80;
+
            uri->host = p;
            if( (n = remove_escapes( uri->host )) < 0 )
                return G10ERR_BAD_URI;
@@ -269,7 +323,7 @@ do_parse_uri( PARSED_URI uri, int only_local_part )
     if( !p || !*p ) /* we don't have a path */
        return 0; /* and this is okay */
 
-    /* fixme: here we have to check params */
+    /* todo: here we have to check params */
 
     /* do we have a query part */
     if( (p2 = strchr( p, '?' )) )
@@ -423,24 +477,51 @@ send_request( HTTP_HD hd )
     byte *request, *p;
     ushort port;
     int rc;
+    const char *http_proxy = NULL;
 
     server = *hd->uri->host? hd->uri->host : "localhost";
     port   = hd->uri->port?  hd->uri->port : 80;
 
-    hd->socket = connect_server( server, port );
-    if( hd->socket == -1 )
+    if( (hd->flags & HTTP_FLAG_TRY_PROXY)
+       && (http_proxy = getenv( HTTP_PROXY_ENV )) ) {
+       PARSED_URI uri;
+
+       rc = parse_uri( &uri, http_proxy );
+       if (rc) {
+           log_error("invalid " HTTP_PROXY_ENV_PRINTABLE ": %s\n",
+                      g10_errstr(rc));
+           release_parsed_uri( uri );
+           return G10ERR_NETWORK;
+       }
+       hd->sock = connect_server( *uri->host? uri->host : "localhost",
+                                   uri->port? uri->port : 80 );
+       release_parsed_uri( uri );
+    }
+    else
+       hd->sock = connect_server( server, port );
+
+    if( hd->sock == -1 )
        return G10ERR_NETWORK;
 
     p = build_rel_path( hd->uri );
-    request = m_alloc( strlen(p) + 20 );
-    sprintf( request, "%s %s%s HTTP/1.0\r\n",
+    request = m_alloc( strlen(server) + strlen(p) + 50 );
+    if( http_proxy ) {
+       sprintf( request, "%s http://%s:%hu%s%s HTTP/1.0\r\n",
+                         hd->req_type == HTTP_REQ_GET ? "GET" :
+                         hd->req_type == HTTP_REQ_HEAD? "HEAD":
+                         hd->req_type == HTTP_REQ_POST? "POST": "OOPS",
+                         server, port,  *p == '/'? "":"/", p );
+    }
+    else {
+       sprintf( request, "%s %s%s HTTP/1.0\r\n",
                          hd->req_type == HTTP_REQ_GET ? "GET" :
                          hd->req_type == HTTP_REQ_HEAD? "HEAD":
                          hd->req_type == HTTP_REQ_POST? "POST": "OOPS",
                                                  *p == '/'? "":"/", p );
+    }
     m_free(p);
 
-    rc = write_server( hd->socket, request, strlen(request) );
+    rc = write_server( hd->sock, request, strlen(request) );
     m_free( request );
 
     return rc;
@@ -462,7 +543,7 @@ build_rel_path( PARSED_URI uri )
 
     /* count the needed space */
     n = insert_escapes( NULL, uri->path, "%;?&" );
-    /* fixme: add params */
+    /* todo: build params */
     for( r=uri->query; r; r = r->next ) {
        n++; /* '?'/'&' */
        n += insert_escapes( NULL, r->name, "%;?&=" );
@@ -475,13 +556,13 @@ build_rel_path( PARSED_URI uri )
     p = rel_path = m_alloc( n );
     n = insert_escapes( p, uri->path, "%;?&" );
     p += n;
-    /* fixme: add params */
+    /* todo: add params */
     for( r=uri->query; r; r = r->next ) {
        *p++ = r == uri->query? '?':'&';
        n = insert_escapes( p, r->name, "%;?&=" );
        p += n;
        *p++ = '=';
-       /* fixme: use valuelen */
+       /* todo: use valuelen */
        n = insert_escapes( p, r->value, "%;?&=" );
        p += n;
     }
@@ -525,7 +606,7 @@ parse_response( HTTP_HD hd )
     if( !p2 )
        return 0; /* assume http 0.9 */
     p = p2;
-    /* fixme: add HTTP version number check here */
+    /* todo: add HTTP version number check here */
     if( (p2 = strpbrk( p, " \t" ) ) )
        *p2++ = 0;
     if( !isdigit(p[0]) || !isdigit(p[1]) || !isdigit(p[2]) || p[3] ) {
@@ -578,13 +659,13 @@ start_server()
 
     if( bind( fd, (struct sockaddr *)&mya, sizeof(mya)) ) {
        log_error("bind to port 11371 failed: %s\n", strerror(errno) );
-       close( fd );
+       sock_close( fd );
        return -1;
     }
 
     if( listen( fd, 5 ) ) {
        log_error("listen failed: %s\n", strerror(errno) );
-       close( fd );
+       sock_close( fd );
        return -1;
     }
 
@@ -617,7 +698,7 @@ start_server()
            fclose(fp);
            exit(0);
        }
-       close( client );
+       sock_close( client );
     }
 
 
@@ -630,39 +711,113 @@ start_server()
 static int
 connect_server( const char *server, ushort port )
 {
-    struct sockaddr_in addr;
-    struct hostent *host;
-    int sd;
+  int sock,i=0;
+  struct sockaddr_in addr;
+  struct hostent *host=NULL;
+  unsigned long l;
 
-    addr.sin_family = AF_INET;
-    addr.sin_port = htons(port);
-    host = gethostbyname((char*)server);
-    if( !host )
-        return -1;
+  memset(&addr,0,sizeof(addr));
 
-    addr.sin_addr = *(struct in_addr*)host->h_addr;
+  addr.sin_family = AF_INET;
+  addr.sin_port = htons(port);
 
-    sd = socket(AF_INET, SOCK_STREAM, 0);
-    if( sd == -1 )
-       return -1;
+#ifdef __MINGW32__
+  init_sockets ();
+
+  if((sock=socket(AF_INET,SOCK_STREAM,0))==INVALID_SOCKET)
+    {
+      log_error("error creating socket: ec=%d\n",(int)WSAGetLastError());
+      return -1;
+    }
+#else
+  if((sock=socket(AF_INET,SOCK_STREAM,0))==-1)
+    {
+      log_error("error creating socket\n");
+      return -1;
+    }
+#endif
 
-    if( connect( sd, (struct sockaddr *)&addr, sizeof addr) == -1 ) {
-       close(sd);
+#ifdef __MINGW32__
+  /* Win32 gethostbyname doesn't handle IP addresses internally, so we
+     try inet_addr first on that platform only. */
+  if((l=inet_addr(server))==SOCKET_ERROR)
+#endif
+    if((host=gethostbyname(server))==NULL)
+      {
+#ifdef __MINGW32__
+       log_error("%s: host not found: ec=%d\n",server,(int)WSAGetLastError());
+#else
+       log_error("%s: host not found\n",server);
+#endif
+       sock_close(sock);
        return -1;
+      }
+
+  if(host)
+    {
+      if(host->h_addrtype != AF_INET)
+       {
+         log_error ("%s: unknown address family\n", server);
+         sock_close(sock);
+         return -1;
+        }
+
+      if(host->h_length != 4 )
+       {
+         log_error ("%s: illegal address length\n", server);
+         sock_close(sock);
+         return -1;
+       }
+
+      /* Try all A records until one responds. */
+      while(host->h_addr_list[i])
+       {
+         memcpy(&addr.sin_addr,host->h_addr_list[i],host->h_length);
+
+         if(connect(sock,(struct sockaddr *)&addr,sizeof(addr))==0)
+           break;
+
+         i++;
+       }
+
+      if(host->h_addr_list[i]==0)
+       {
+         sock_close(sock);
+         return -1;
+       }
+    }
+  else
+    {
+      memcpy(&addr.sin_addr,&l,sizeof(l));
+
+      if(connect(sock,(struct sockaddr *)&addr,sizeof(addr))!=0)
+       {
+         sock_close(sock);
+         return -1;
+       }
     }
 
-    return sd;
+    return sock;
 }
 
 
 static int
-write_server( int socket, const char *data, size_t length )
+write_server( int sock, const char *data, size_t length )
 {
-    int nleft, nwritten;
+    int nleft;
 
     nleft = length;
     while( nleft > 0 ) {
-       nwritten = write( socket, data, nleft );
+      #ifdef __MINGW32__  
+        int nwritten;
+
+        nwritten = send (sock, data, nleft, 0);
+        if ( nwritten == SOCKET_ERROR ) {
+           log_info ("write failed: ec=%d\n", (int)WSAGetLastError ());
+           return G10ERR_NETWORK;
+        }
+      #else
+       int nwritten = write( sock, data, nleft );
        if( nwritten == -1 ) {
            if( errno == EINTR )
                continue;
@@ -677,6 +832,7 @@ write_server( int socket, const char *data, size_t length )
            log_info("write failed: %s\n", strerror(errno));
            return G10ERR_NETWORK;
        }
+      #endif
        nleft -=nwritten;
        data += nwritten;
     }
@@ -684,7 +840,6 @@ write_server( int socket, const char *data, size_t length )
     return 0;
 }
 
-
 /**** Test code ****/
 #ifdef TEST