Add finger support to dirmngr.
[gnupg.git] / common / http.c
index 96e2a9e..7df8457 100644 (file)
@@ -1,6 +1,6 @@
 /* http.c  -  HTTP protocol handler
- * Copyright (C) 1999, 2001, 2002, 2003, 2004,
- *               2006 Free Software Foundation, Inc.
+ * Copyright (C) 1999, 2001, 2002, 2003, 2004, 2006, 2009, 2010,
+ *               2011 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
 /* Simple HTTP client implementation.  We try to keep the code as
    self-contained as possible.  There are some contraints however:
 
+  - estream is required.  We now require estream because it provides a
+    very useful and portable asprintf implementation and the fopencookie
+    function.
   - stpcpy is required
   - fixme: list other requirements.
 
 
-  - With HTTP_USE_ESTREAM defined, all I/O is done through estream.
   - With HTTP_USE_GNUTLS support for https is provided (this also
     requires estream).
   - With HTTP_NO_WSASTARTUP the socket initialization is not done
 # include <netdb.h>
 #endif /*!HAVE_W32_SYSTEM*/
 
+#ifdef WITHOUT_GNU_PTH /* Give the Makefile a chance to build without Pth.  */
+# undef HAVE_PTH
+# undef USE_GNU_PTH
+#endif
+
+#ifdef HAVE_PTH
+# include <pth.h>
+#endif
+
+
 #ifdef HTTP_USE_GNUTLS
 # include <gnutls/gnutls.h>
 /* For non-understandable reasons GNUTLS dropped the _t suffix from
@@ -75,13 +87,13 @@ typedef gnutls_transport_ptr gnutls_transport_ptr_t;
 #include "i18n.h"
 #include "http.h"
 #ifdef USE_DNS_SRV
-#include "srv.h"
+# include "srv.h"
 #else /*!USE_DNS_SRV*/
-/* If we are not compiling with SRV record support we provide stub
-   data structures. */
-#ifndef MAXDNAME
-#define MAXDNAME 1025
-#endif
+  /* If we are not compiling with SRV record support we provide stub
+     data structures. */
+# ifndef MAXDNAME
+#  define MAXDNAME 1025
+# endif
 struct srventry
 {
   unsigned short priority;
@@ -93,6 +105,16 @@ struct srventry
 #endif/*!USE_DNS_SRV*/
 
 
+#ifdef HAVE_PTH
+# define my_select(a,b,c,d,e)  pth_select ((a), (b), (c), (d), (e))
+# define my_connect(a,b,c)     pth_connect ((a), (b), (c))
+# define my_accept(a,b,c)      pth_accept ((a), (b), (c))
+#else
+# define my_select(a,b,c,d,e)  select ((a), (b), (c), (d), (e))
+# define my_connect(a,b,c)     connect ((a), (b), (c))
+# define my_accept(a,b,c)      accept ((a), (b), (c))
+#endif
+
 #ifdef HAVE_W32_SYSTEM
 #define sock_close(a)  closesocket(a)
 #else
@@ -102,6 +124,9 @@ struct srventry
 #ifndef EAGAIN
 #define EAGAIN  EWOULDBLOCK
 #endif
+#ifndef INADDR_NONE  /* Slowaris is missing that.  */
+#define INADDR_NONE  ((unsigned long)(-1))
+#endif /*INADDR_NONE*/
 
 #define HTTP_PROXY_ENV           "http_proxy"
 #define MAX_LINELEN 20000  /* Max. length of a HTTP header line. */
@@ -110,38 +135,51 @@ struct srventry
                         "01234567890@"                 \
                         "!\"#$%&'()*+,-./:;<=>?[\\]^_{|}~"
 
-/* Define a prefix to map stream functions to the estream library. */
-#ifdef HTTP_USE_ESTREAM
-#define P_ES(a)  es_ ## a
+/* A long counter type.  */
+#ifdef HAVE_STRTOULL
+typedef unsigned long long longcounter_t;
+# define counter_strtoul(a) strtoull ((a), NULL, 10)
 #else
-#define P_ES(a)  a
+typedef unsigned long longcounter_t;
+# define counter_strtoul(a) strtoul ((a), NULL, 10)
 #endif
+
 #ifndef HTTP_USE_GNUTLS
 typedef void * gnutls_session_t;
 #endif
-#if defined(HTTP_USE_GNUTLS) && !defined(HTTP_USE_ESTREAM)
-#error Use of GNUTLS also requires support for Estream
-#endif
 
-static gpg_error_t do_parse_uri (parsed_uri_t uri, int only_local_part);
+static gpg_err_code_t do_parse_uri (parsed_uri_t uri, int only_local_part,
+                                    int no_scheme_check);
 static int remove_escapes (char *string);
 static int insert_escapes (char *buffer, const char *string,
                            const char *special);
 static uri_tuple_t parse_tuple (char *string);
-static gpg_error_t send_request (http_t hd,
-                                 const char *auth, const char *proxy);
+static gpg_error_t send_request (http_t hd, const char *auth,const char *proxy,
+                                const char *srvtag,strlist_t headers,
+                                 gpg_err_source_t errsource);
 static char *build_rel_path (parsed_uri_t uri);
 static gpg_error_t parse_response (http_t hd);
 
 static int connect_server (const char *server, unsigned short port,
-                           unsigned int flags, const char *srvtag);
+                           unsigned int flags, const char *srvtag,
+                           int *r_host_not_found);
 static gpg_error_t write_server (int sock, const char *data, size_t length);
 
-#ifdef HTTP_USE_ESTREAM
 static ssize_t cookie_read (void *cookie, void *buffer, size_t size);
 static ssize_t cookie_write (void *cookie, const void *buffer, size_t size);
 static int cookie_close (void *cookie);
 
+
+/* A socket object used to a allow ref counting of sockets.  */
+struct my_socket_s
+{
+  int fd;       /* The actual socket - shall never be -1.  */
+  int refcount; /* Number of references to this socket.  */
+};
+typedef struct my_socket_s *my_socket_t;
+
+
+/* Cookie function structure and cookie object.  */
 static es_cookie_io_functions_t cookie_functions =
   {
     cookie_read,
@@ -150,16 +188,24 @@ static es_cookie_io_functions_t cookie_functions =
     cookie_close
   };
 
-struct cookie_s 
+struct cookie_s
 {
-  int fd;  /* File descriptor or -1 if already closed. */
-  gnutls_session_t tls_session;  /* TLS session context or NULL if not used. */
-  int keep_socket; /* Flag to communicate with teh close handler. */
+  /* Socket object or NULL if already closed. */
+  my_socket_t sock;
+
+  /* TLS session context or NULL if not used. */
+  gnutls_session_t tls_session;
+
+  /* The remaining content length and a flag telling whether to use
+     the content length.  */
+  longcounter_t content_length;
+  unsigned int content_length_valid:1;
+
+  /* Flag to communicate with the close handler. */
+  unsigned int keep_socket:1;
 };
 typedef struct cookie_s *cookie_t;
 
-#endif /*HTTP_USE_ESTREAM*/
-
 #ifdef HTTP_USE_GNUTLS
 static gpg_error_t (*tls_callback) (http_t, gnutls_session_t, int);
 #endif /*HTTP_USE_GNUTLS*/
@@ -176,21 +222,17 @@ typedef struct header_s *header_t;
 
 
 /* Our handle context. */
-struct http_context_s 
+struct http_context_s
 {
   unsigned int status_code;
-  int sock;
-  int in_data;
-#ifdef HTTP_USE_ESTREAM
+  my_socket_t sock;
+  unsigned int in_data:1;
+  unsigned int is_http_0_9:1;
   estream_t fp_read;
   estream_t fp_write;
   void *write_cookie;
-#else /*!HTTP_USE_ESTREAM*/
-  FILE *fp_read;
-  FILE *fp_write;
-#endif /*!HTTP_USE_ESTREAM*/
+  void *read_cookie;
   void *tls_context;
-  int is_http_0_9;
   parsed_uri_t uri;
   http_req_t req_type;
   char *buffer;          /* Line buffer. */
@@ -228,14 +270,14 @@ init_sockets (void)
   if (initialized)
     return;
 
-  if ( WSAStartup( MAKEWORD (REQ_WINSOCK_MINOR, REQ_WINSOCK_MAJOR), &wsdata ) ) 
+  if ( WSAStartup( MAKEWORD (REQ_WINSOCK_MINOR, REQ_WINSOCK_MAJOR), &wsdata ) )
     {
-      log_error ("error initializing socket library: ec=%d\n", 
+      log_error ("error initializing socket library: ec=%d\n",
                  (int)WSAGetLastError () );
       return;
     }
-  if ( LOBYTE(wsdata.wVersion) != REQ_WINSOCK_MAJOR  
-       || HIBYTE(wsdata.wVersion) != REQ_WINSOCK_MINOR ) 
+  if ( LOBYTE(wsdata.wVersion) != REQ_WINSOCK_MAJOR
+       || HIBYTE(wsdata.wVersion) != REQ_WINSOCK_MINOR )
     {
       log_error ("socket library version is %x.%x - but %d.%d needed\n",
                  LOBYTE(wsdata.wVersion), HIBYTE(wsdata.wVersion),
@@ -249,6 +291,77 @@ init_sockets (void)
 #endif /*HAVE_W32_SYSTEM && !HTTP_NO_WSASTARTUP*/
 
 
+/* Create a new socket object.  Returns NULL and closes FD if not
+   enough memory is available.  */
+static my_socket_t
+my_socket_new (int fd)
+{
+  my_socket_t so;
+
+  so = xtrymalloc (sizeof *so);
+  if (!so)
+    {
+      int save_errno = errno;
+      sock_close (fd);
+      gpg_err_set_errno (save_errno);
+      return NULL;
+    }
+  so->fd = fd;
+  so->refcount = 1;
+  /* log_debug ("my_socket_new(%d): object %p for fd %d created\n", */
+  /*            lnr, so, so->fd); */
+  return so;
+}
+/* #define my_socket_new(a) _my_socket_new ((a),__LINE__) */
+
+/* Bump up the reference counter for the socket object SO.  */
+static my_socket_t
+my_socket_ref (my_socket_t so)
+{
+  so->refcount++;
+  /* log_debug ("my_socket_ref(%d): object %p for fd %d refcount now %d\n", */
+  /*            lnr, so, so->fd, so->refcount); */
+  return so;
+}
+/* #define my_socket_ref(a) _my_socket_ref ((a),__LINE__) */
+
+/* Bump down the reference counter for the socket object SO.  If SO
+   has no more references, close the socket and release the
+   object.  */
+static void
+my_socket_unref (my_socket_t so)
+{
+  if (so)
+    {
+      so->refcount--;
+      /* log_debug ("my_socket_unref(%d): object %p for fd %d ref now %d\n", */
+      /*            lnr, so, so->fd, so->refcount); */
+      if (!so->refcount)
+        {
+          sock_close (so->fd);
+          xfree (so);
+        }
+    }
+}
+/* #define my_socket_unref(a) _my_socket_unref ((a),__LINE__) */
+
+
+/* This notification function is called by estream whenever stream is
+   closed.  Its purpose is to mark the the closing in the handle so
+   that a http_close won't accidentally close the estream.  The function
+   http_close removes this notification so that it won't be called if
+   http_close was used before an es_fclose.  */
+static void
+fp_onclose_notification (estream_t stream, void *opaque)
+{
+  http_t hd = opaque;
+
+  if (hd->fp_read && hd->fp_read == stream)
+    hd->fp_read = NULL;
+  else if (hd->fp_write && hd->fp_write == stream)
+    hd->fp_write = NULL;
+}
+
 
 /*
  * Helper function to create an HTTP header with hex encoded data.  A
@@ -260,7 +373,7 @@ static char *
 make_header_line (const char *prefix, const char *suffix,
                    const void *data, size_t len )
 {
-  static unsigned char bintoasc[] = 
+  static unsigned char bintoasc[] =
     "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
     "abcdefghijklmnopqrstuvwxyz"
     "0123456789+/";
@@ -278,7 +391,7 @@ make_header_line (const char *prefix, const char *suffix,
       *p++ = bintoasc[(((s[1]<<2)&074)|((s[2]>>6)&03))&077];
       *p++ = bintoasc[s[2]&077];
     }
-  if ( len == 2 ) 
+  if ( len == 2 )
     {
       *p++ = bintoasc[(s[0] >> 2) & 077];
       *p++ = bintoasc[(((s[0] <<4)&060)|((s[1] >> 4)&017))&077];
@@ -306,49 +419,144 @@ http_register_tls_callback ( gpg_error_t (*cb) (http_t, void *, int) )
   tls_callback = (gpg_error_t (*) (http_t, gnutls_session_t, int))cb;
 #else
   (void)cb;
-#endif  
+#endif
 }
 
 
 
 /* Start a HTTP retrieval and return on success in R_HD a context
    pointer for completing the the request and to wait for the
-   response. */
+   response.  */
 gpg_error_t
-http_open (http_t *r_hd, http_req_t reqtype, const char *url, 
-           const char *auth, unsigned int flags, const char *proxy,
-           void *tls_context)
+_http_open (http_t *r_hd, http_req_t reqtype, const char *url,
+            const char *auth, unsigned int flags, const char *proxy,
+            void *tls_context, const char *srvtag, strlist_t headers,
+            gpg_err_source_t errsource)
 {
   gpg_error_t err;
   http_t hd;
-  
+
   *r_hd = NULL;
 
   if (!(reqtype == HTTP_REQ_GET || reqtype == HTTP_REQ_POST))
-    return gpg_error (GPG_ERR_INV_ARG);
+    return gpg_err_make (errsource, GPG_ERR_INV_ARG);
 
   /* Create the handle. */
   hd = xtrycalloc (1, sizeof *hd);
   if (!hd)
     return gpg_error_from_syserror ();
-  hd->sock = -1;
   hd->req_type = reqtype;
   hd->flags = flags;
   hd->tls_context = tls_context;
 
-  err = http_parse_uri (&hd->uri, url);
+  err = _http_parse_uri (&hd->uri, url, 0, errsource);
+  if (!err)
+    err = send_request (hd, auth, proxy, srvtag, headers, errsource);
+
+  if (err)
+    {
+      my_socket_unref (hd->sock);
+      if (hd->fp_read)
+        es_fclose (hd->fp_read);
+      if (hd->fp_write)
+        es_fclose (hd->fp_write);
+      xfree (hd);
+    }
+  else
+    *r_hd = hd;
+  return err;
+}
+
+
+/* This function is useful to connect to a generic TCP service using
+   this http abstraction layer.  This has the advantage of providing
+   service tags and an estream interface.  */
+gpg_error_t
+_http_raw_connect (http_t *r_hd, const char *server, unsigned short port,
+                   unsigned int flags, const char *srvtag,
+                   gpg_err_source_t errsource)
+{
+  gpg_error_t err = 0;
+  int sock;
+  http_t hd;
+  cookie_t cookie;
+  int hnf;
+
+  *r_hd = NULL;
+
+  /* Create the handle. */
+  hd = xtrycalloc (1, sizeof *hd);
+  if (!hd)
+    return gpg_error_from_syserror ();
+  hd->req_type = HTTP_REQ_OPAQUE;
+  hd->flags = flags;
+
+  /* Connect.  */
+  sock = connect_server (server, port, hd->flags, srvtag, &hnf);
+  if (sock == -1)
+    {
+      err = gpg_err_make (errsource, (hnf? GPG_ERR_UNKNOWN_HOST
+                                      :gpg_err_code_from_syserror ()));
+      xfree (hd);
+      return err;
+    }
+  hd->sock = my_socket_new (sock);
+  if (!hd->sock)
+    {
+      err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+      xfree (hd);
+      return err;
+    }
+
+  /* Setup estreams for reading and writing.  */
+  cookie = xtrycalloc (1, sizeof *cookie);
+  if (!cookie)
+    {
+      err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+      goto leave;
+    }
+  cookie->sock = my_socket_ref (hd->sock);
+  hd->fp_write = es_fopencookie (cookie, "w", cookie_functions);
+  if (!hd->fp_write)
+    {
+      err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+      my_socket_unref (cookie->sock);
+      xfree (cookie);
+      goto leave;
+    }
+  hd->write_cookie = cookie; /* Cookie now owned by FP_WRITE.  */
+
+  cookie = xtrycalloc (1, sizeof *cookie);
+  if (!cookie)
+    {
+      err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+      goto leave;
+    }
+  cookie->sock = my_socket_ref (hd->sock);
+  hd->fp_read = es_fopencookie (cookie, "r", cookie_functions);
+  if (!hd->fp_read)
+    {
+      err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+      my_socket_unref (cookie->sock);
+      xfree (cookie);
+      goto leave;
+    }
+  hd->read_cookie = cookie; /* Cookie now owned by FP_READ.  */
+
+  /* Register close notification to interlock the use of es_fclose in
+     http_close and in user code.  */
+  err = es_onclose (hd->fp_write, 1, fp_onclose_notification, hd);
   if (!err)
-    err = send_request (hd, auth, proxy);
-  
+    err = es_onclose (hd->fp_read, 1, fp_onclose_notification, hd);
+
+ leave:
   if (err)
     {
-      if (!hd->fp_read && !hd->fp_write && hd->sock != -1)
-        sock_close (hd->sock);
       if (hd->fp_read)
-        P_ES(fclose) (hd->fp_read);
+        es_fclose (hd->fp_read);
       if (hd->fp_write)
-        P_ES(fclose) (hd->fp_write);
-      http_release_parsed_uri (hd->uri);
+        es_fclose (hd->fp_write);
+      my_socket_unref (hd->sock);
       xfree (hd);
     }
   else
@@ -357,95 +565,73 @@ http_open (http_t *r_hd, http_req_t reqtype, const char *url,
 }
 
 
+
+
 void
 http_start_data (http_t hd)
 {
   if (!hd->in_data)
     {
-#ifdef HTTP_USE_ESTREAM
       es_fputs ("\r\n", hd->fp_write);
       es_fflush (hd->fp_write);
-#else
-      fflush (hd->fp_write);
-      write_server (hd->sock, "\r\n", 2);
-#endif
       hd->in_data = 1;
     }
   else
-    P_ES(fflush) (hd->fp_write);
+    es_fflush (hd->fp_write);
 }
 
 
 gpg_error_t
-http_wait_response (http_t hd)
+_http_wait_response (http_t hd, gpg_err_source_t errsource)
 {
   gpg_error_t err;
+  cookie_t cookie;
 
   /* Make sure that we are in the data. */
-  http_start_data (hd);        
+  http_start_data (hd);
 
-  /* We dup the socket, to cope with the fact that fclose closes the
-     underlying socket. In TLS mode we don't do that because we can't
-     close the socket gnutls is working on; instead we make sure that
-     the fclose won't close the socket in this case. */
-#ifdef HTTP_USE_ESTREAM
-  if (hd->write_cookie)
-    {
-      /* The write cookie is only set in the TLS case. */
-      cookie_t cookie = hd->write_cookie;
-      cookie->keep_socket = 1;
-    }
-  else
-#endif /*HTTP_USE_ESTREAM*/
-    {
-#ifdef HAVE_W32_SYSTEM
-      HANDLE handle = (HANDLE)hd->sock;
-      if (!DuplicateHandle (GetCurrentProcess(), handle,
-                           GetCurrentProcess(), &handle, 0,
-                           TRUE, DUPLICATE_SAME_ACCESS ))
-       return gpg_error_from_syserror ();
-      hd->sock = (int)handle;
-#else
-      hd->sock = dup (hd->sock);
-#endif
-      if (hd->sock == -1)
-        return gpg_error_from_syserror ();
-    }
-  P_ES(fclose) (hd->fp_write);
+  /* Close the write stream.  Note that the reference counted socket
+     object keeps the actual system socket open.  */
+  cookie = hd->write_cookie;
+  if (!cookie)
+    return gpg_err_make (errsource, GPG_ERR_INTERNAL);
+
+  es_fclose (hd->fp_write);
   hd->fp_write = NULL;
-#ifdef HTTP_USE_ESTREAM
+  /* The close has released the cookie and thus we better set it to NULL.  */
   hd->write_cookie = NULL;
-#endif
 
-  if (!(hd->flags & HTTP_FLAG_NO_SHUTDOWN))
-    shutdown (hd->sock, 1);
+  /* Shutdown one end of the socket is desired.  As per HTTP/1.0 this
+     is not required but some very old servers (e.g. the original pksd
+     key server didn't worked without it.  */
+  if ((hd->flags & HTTP_FLAG_SHUTDOWN))
+    shutdown (hd->sock->fd, 1);
   hd->in_data = 0;
 
-#ifdef HTTP_USE_ESTREAM
-  {
-    cookie_t cookie;
-
-    cookie = xtrycalloc (1, sizeof *cookie);
-    if (!cookie)
-      return gpg_error_from_syserror ();
-    cookie->fd = hd->sock;
-    if (hd->uri->use_tls)
-      cookie->tls_session = hd->tls_context;
+  /* Create a new cookie and a stream for reading.  */
+  cookie = xtrycalloc (1, sizeof *cookie);
+  if (!cookie)
+    return gpg_err_make (errsource, gpg_err_code_from_syserror ());
+  cookie->sock = my_socket_ref (hd->sock);
+  if (hd->uri->use_tls)
+    cookie->tls_session = hd->tls_context;
 
-    hd->fp_read = es_fopencookie (cookie, "r", cookie_functions);
-    if (!hd->fp_read)
-      {
-        xfree (cookie);
-        return gpg_error_from_syserror ();
-      }
-  }
-#else /*!HTTP_USE_ESTREAM*/
-  hd->fp_read = fdopen (hd->sock, "r");
+  hd->read_cookie = cookie;
+  hd->fp_read = es_fopencookie (cookie, "r", cookie_functions);
   if (!hd->fp_read)
-    return gpg_error_from_syserror ();
-#endif /*!HTTP_USE_ESTREAM*/
+    {
+      err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+      my_socket_unref (cookie->sock);
+      xfree (cookie);
+      hd->read_cookie = NULL;
+      return err;
+    }
 
   err = parse_response (hd);
+
+  if (!err)
+    err = es_onclose (hd->fp_read, 1, fp_onclose_notification, hd);
+
   return err;
 }
 
@@ -455,18 +641,19 @@ http_wait_response (http_t hd)
    be used as an HTTP proxy and any enabled $http_proxy gets
    ignored. */
 gpg_error_t
-http_open_document (http_t *r_hd, const char *document, 
-                    const char *auth, unsigned int flags, const char *proxy,
-                    void *tls_context)
+_http_open_document (http_t *r_hd, const char *document,
+                     const char *auth, unsigned int flags, const char *proxy,
+                     void *tls_context, const char *srvtag, strlist_t headers,
+                     gpg_err_source_t errsource)
 {
   gpg_error_t err;
 
-  err = http_open (r_hd, HTTP_REQ_GET, document, auth, flags,
-                   proxy, tls_context);
+  err = _http_open (r_hd, HTTP_REQ_GET, document, auth, flags,
+                    proxy, tls_context, srvtag, headers, errsource);
   if (err)
     return err;
 
-  err = http_wait_response (*r_hd);
+  err = _http_wait_response (*r_hd, errsource);
   if (err)
     http_close (*r_hd, 0);
 
@@ -479,12 +666,19 @@ http_close (http_t hd, int keep_read_stream)
 {
   if (!hd)
     return;
-  if (!hd->fp_read && !hd->fp_write && hd->sock != -1)
-    sock_close (hd->sock);
+
+  /* First remove the close notifications for the streams.  */
+  if (hd->fp_read)
+    es_onclose (hd->fp_read, 0, fp_onclose_notification, hd);
+  if (hd->fp_write)
+    es_onclose (hd->fp_write, 0, fp_onclose_notification, hd);
+
+  /* Now we can close the streams.  */
+  my_socket_unref (hd->sock);
   if (hd->fp_read && !keep_read_stream)
-    P_ES(fclose) (hd->fp_read);
+    es_fclose (hd->fp_read);
   if (hd->fp_write)
-    P_ES(fclose) (hd->fp_write);
+    es_fclose (hd->fp_write);
   http_release_parsed_uri (hd->uri);
   while (hd->headers)
     {
@@ -498,29 +692,18 @@ http_close (http_t hd, int keep_read_stream)
 }
 
 
-#ifdef HTTP_USE_ESTREAM
 estream_t
 http_get_read_ptr (http_t hd)
 {
   return hd?hd->fp_read:NULL;
 }
+
 estream_t
 http_get_write_ptr (http_t hd)
 {
   return hd?hd->fp_write:NULL;
 }
-#else /*!HTTP_USE_ESTREAM*/
-FILE *
-http_get_read_ptr (http_t hd)
-{
-  return hd?hd->fp_read:NULL;
-}
-FILE *
-http_get_write_ptr (http_t hd)
-{
-  return hd?hd->fp_write:NULL;
-}
-#endif /*!HTTP_USE_ESTREAM*/
+
 unsigned int
 http_get_status_code (http_t hd)
 {
@@ -531,15 +714,27 @@ http_get_status_code (http_t hd)
 \f
 /*
  * Parse an URI and put the result into the newly allocated RET_URI.
- * The caller must always use release_parsed_uri() to releases the
- * resources (even on error).
+ * On success the caller must use release_parsed_uri() to releases the
+ * resources.  If NO_SCHEME_CHECK is set, the function tries to parse
+ * the URL in the same way it would do for an HTTP style URI.
  */
 gpg_error_t
-http_parse_uri (parsed_uri_t * ret_uri, const char *uri)
+_http_parse_uri (parsed_uri_t *ret_uri, const char *uri,
+                 int no_scheme_check, gpg_err_source_t errsource)
 {
-  *ret_uri = xcalloc (1, sizeof **ret_uri + strlen (uri));
+  gpg_err_code_t ec;
+
+  *ret_uri = xtrycalloc (1, sizeof **ret_uri + strlen (uri));
+  if (!*ret_uri)
+    return gpg_err_make (errsource, gpg_err_code_from_syserror ());
   strcpy ((*ret_uri)->buffer, uri);
-  return do_parse_uri (*ret_uri, 0);
+  ec = do_parse_uri (*ret_uri, 0, no_scheme_check);
+  if (ec)
+    {
+      xfree (*ret_uri);
+      *ret_uri = NULL;
+    }
+  return gpg_err_make (errsource, ec);
 }
 
 void
@@ -559,8 +754,8 @@ http_release_parsed_uri (parsed_uri_t uri)
 }
 
 
-static gpg_error_t
-do_parse_uri (parsed_uri_t uri, int only_local_part)
+static gpg_err_code_t
+do_parse_uri (parsed_uri_t uri, int only_local_part, int no_scheme_check)
 {
   uri_tuple_t *tail;
   char *p, *p2, *p3, *pp;
@@ -574,42 +769,48 @@ do_parse_uri (parsed_uri_t uri, int only_local_part)
   uri->port = 0;
   uri->params = uri->query = NULL;
   uri->use_tls = 0;
+  uri->is_http = 0;
+  uri->opaque = 0;
 
   /* A quick validity check. */
   if (strspn (p, VALID_URI_CHARS) != n)
-    return gpg_error (GPG_ERR_BAD_URI);        /* Invalid characters found. */
+    return GPG_ERR_BAD_URI;    /* Invalid characters found. */
 
   if (!only_local_part)
     {
       /* Find the scheme. */
       if (!(p2 = strchr (p, ':')) || p2 == p)
-       return gpg_error (GPG_ERR_BAD_URI); /* No scheme. */
+       return GPG_ERR_BAD_URI; /* No scheme. */
       *p2++ = 0;
       for (pp=p; *pp; pp++)
        *pp = tolower (*(unsigned char*)pp);
       uri->scheme = p;
       if (!strcmp (uri->scheme, "http"))
-        uri->port = 80;
+        {
+          uri->port = 80;
+          uri->is_http = 1;
+        }
+      else if (!strcmp (uri->scheme, "hkp"))
+        {
+          uri->port = 11371;
+          uri->is_http = 1;
+        }
 #ifdef HTTP_USE_GNUTLS
-      else if (!strcmp (uri->scheme, "https"))
+      else if (!strcmp (uri->scheme, "https") || !strcmp (uri->scheme,"hkps"))
         {
           uri->port = 443;
+          uri->is_http = 1;
           uri->use_tls = 1;
         }
 #endif
-      else
-       return gpg_error (GPG_ERR_INV_URI); /* Unsupported scheme */
+      else if (!no_scheme_check)
+       return GPG_ERR_INV_URI; /* Unsupported scheme */
 
       p = p2;
 
-      /* Find the hostname */
-      if (*p != '/')
-       return gpg_error (GPG_ERR_INV_URI); /* Does not start with a slash. */
-
-      p++;
-      if (*p == '/') /* There seems to be a hostname. */
-       { 
-         p++;
+      if (*p == '/' && p[1] == '/' ) /* There seems to be a hostname. */
+       {
+          p += 2;
          if ((p2 = strchr (p, '/')))
            *p2++ = 0;
 
@@ -623,20 +824,39 @@ do_parse_uri (parsed_uri_t uri, int only_local_part)
 
           for (pp=p; *pp; pp++)
             *pp = tolower (*(unsigned char*)pp);
-         uri->host = p;
+
+         /* Handle an IPv6 literal */
+         if( *p == '[' && (p3=strchr( p, ']' )) )
+           {
+             *p3++ = '\0';
+             /* worst case, uri->host should have length 0, points to \0 */
+             uri->host = p + 1;
+             p = p3;
+           }
+         else
+           uri->host = p;
+
          if ((p3 = strchr (p, ':')))
            {
-             *p3++ = 0;
+             *p3++ = '\0';
              uri->port = atoi (p3);
            }
 
-         uri->host = p;
          if ((n = remove_escapes (uri->host)) < 0)
-           return gpg_error (GPG_ERR_BAD_URI);
-         if (n != strlen (p))
-           return gpg_error (GPG_ERR_BAD_URI); /* Hostname incudes a Nul. */
+           return GPG_ERR_BAD_URI;
+         if (n != strlen (uri->host))
+           return GPG_ERR_BAD_URI;     /* Hostname incudes a Nul. */
          p = p2 ? p2 : NULL;
        }
+      else if (uri->is_http)
+       return GPG_ERR_INV_URI; /* No Leading double slash for HTTP.  */
+      else
+        {
+          uri->opaque = 1;
+          uri->path = p;
+          return 0;
+        }
+
     } /* End global URI part. */
 
   /* Parse the pathname part */
@@ -651,12 +871,12 @@ do_parse_uri (parsed_uri_t uri, int only_local_part)
 
   uri->path = p;
   if ((n = remove_escapes (p)) < 0)
-    return gpg_error (GPG_ERR_BAD_URI);
+    return GPG_ERR_BAD_URI;
   if (n != strlen (p))
-    return gpg_error (GPG_ERR_BAD_URI);        /* Path includes a Nul. */
+    return GPG_ERR_BAD_URI;    /* Path includes a Nul. */
   p = p2 ? p2 : NULL;
 
-  if (!p || !*p)       
+  if (!p || !*p)
     return 0; /* We don't have a query string.  Okay. */
 
   /* Now parse the query string. */
@@ -668,7 +888,7 @@ do_parse_uri (parsed_uri_t uri, int only_local_part)
       if ((p2 = strchr (p, '&')))
        *p2++ = 0;
       if (!(elem = parse_tuple (p)))
-       return gpg_error (GPG_ERR_BAD_URI);
+       return GPG_ERR_BAD_URI;
       *tail = elem;
       tail = &elem->next;
 
@@ -730,14 +950,14 @@ remove_escapes (char *string)
 }
 
 
-static int
-insert_escapes (char *buffer, const char *string,
-               const char *special)
+static size_t
+escape_data (char *buffer, const void *data, size_t datalen,
+             const char *special)
 {
-  const unsigned char *s = (const unsigned char*)string;
-  int n = 0;
+  const unsigned char *s;
+  size_t n = 0;
 
-  for (; *s; s++)
+  for (s = data; datalen; s++, datalen--)
     {
       if (strchr (VALID_URI_CHARS, *s) && !strchr (special, *s))
        {
@@ -749,7 +969,7 @@ insert_escapes (char *buffer, const char *string,
        {
          if (buffer)
            {
-             sprintf (buffer, "%%%02X", *s);
+             snprintf (buffer, 4, "%%%02X", *s);
              buffer += 3;
            }
          n += 3;
@@ -759,6 +979,14 @@ insert_escapes (char *buffer, const char *string,
 }
 
 
+static int
+insert_escapes (char *buffer, const char *string,
+               const char *special)
+{
+  return escape_data (buffer, string, strlen (string), special);
+}
+
+
 /* Allocate a new string from STRING using standard HTTP escaping as
    well as escaping of characters given in SPECIALS.  A common pattern
    for SPECIALS is "%;?&=". However it depends on the needs, for
@@ -780,6 +1008,27 @@ http_escape_string (const char *string, const char *specials)
   return buf;
 }
 
+/* Allocate a new string from {DATA,DATALEN} using standard HTTP
+   escaping as well as escaping of characters given in SPECIALS.  A
+   common pattern for SPECIALS is "%;?&=".  However it depends on the
+   needs, for example "+" and "/: often needs to be escaped too.
+   Returns NULL on failure and sets ERRNO. */
+char *
+http_escape_data (const void *data, size_t datalen, const char *specials)
+{
+  int n;
+  char *buf;
+
+  n = escape_data (NULL, data, datalen, specials);
+  buf = xtrymalloc (n+1);
+  if (buf)
+    {
+      escape_data (buf, data, datalen, specials);
+      buf[n] = 0;
+    }
+  return buf;
+}
+
 
 
 static uri_tuple_t
@@ -825,7 +1074,9 @@ parse_tuple (char *string)
  * Returns 0 if the request was successful
  */
 static gpg_error_t
-send_request (http_t hd, const char *auth, const char *proxy)
+send_request (http_t hd, const char *auth,
+             const char *proxy, const char *srvtag, strlist_t headers,
+              gpg_err_source_t errsource)
 {
   gnutls_session_t tls_session;
   gpg_error_t err;
@@ -835,13 +1086,14 @@ send_request (http_t hd, const char *auth, const char *proxy)
   const char *http_proxy = NULL;
   char *proxy_authstr = NULL;
   char *authstr = NULL;
-  int save_errno;
+  int sock;
+  int hnf;
 
   tls_session = hd->tls_context;
   if (hd->uri->use_tls && !tls_session)
     {
       log_error ("TLS requested but no GNUTLS context provided\n");
-      return gpg_error (GPG_ERR_INTERNAL);
+      return gpg_err_make (errsource, GPG_ERR_INTERNAL);
     }
 
   server = *hd->uri->host ? hd->uri->host : "localhost";
@@ -849,22 +1101,21 @@ send_request (http_t hd, const char *auth, const char *proxy)
 
   if ( (proxy && *proxy)
        || ( (hd->flags & HTTP_FLAG_TRY_PROXY)
-            && (http_proxy = getenv (HTTP_PROXY_ENV)) 
+            && (http_proxy = getenv (HTTP_PROXY_ENV))
             && *http_proxy ))
     {
       parsed_uri_t uri;
+      int save_errno;
 
       if (proxy)
        http_proxy = proxy;
 
-      err = http_parse_uri (&uri, http_proxy);
+      err = _http_parse_uri (&uri, http_proxy, 0, errsource);
       if (err)
        {
          log_error ("invalid HTTP proxy (%s): %s\n",
                     http_proxy, gpg_strerror (err));
-         http_release_parsed_uri (uri);
-         return gpg_error (GPG_ERR_CONFIGURATION);
-
+         return gpg_err_make (errsource, GPG_ERR_CONFIGURATION);
        }
 
       if (uri->auth)
@@ -875,38 +1126,48 @@ send_request (http_t hd, const char *auth, const char *proxy)
                                             uri->auth, strlen(uri->auth));
           if (!proxy_authstr)
             {
-              err = gpg_error_from_syserror ();
+              err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
               http_release_parsed_uri (uri);
               return err;
             }
         }
 
-      hd->sock = connect_server (*uri->host ? uri->host : "localhost",
-                                uri->port ? uri->port : 80,
-                                 hd->flags, hd->uri->scheme);
+      sock = connect_server (*uri->host ? uri->host : "localhost",
+                             uri->port ? uri->port : 80,
+                             hd->flags, srvtag, &hnf);
       save_errno = errno;
       http_release_parsed_uri (uri);
+      if (sock == -1)
+        gpg_err_set_errno (save_errno);
     }
   else
     {
-      hd->sock = connect_server (server, port, hd->flags, hd->uri->scheme);
-      save_errno = errno;
+      sock = connect_server (server, port, hd->flags, srvtag, &hnf);
     }
 
-  if (hd->sock == -1)
+  if (sock == -1)
     {
       xfree (proxy_authstr);
-      return (save_errno 
-              ? gpg_error_from_errno (save_errno)
-              : gpg_error (GPG_ERR_NOT_FOUND));
+      return gpg_err_make (errsource, (hnf? GPG_ERR_UNKNOWN_HOST
+                                       : gpg_err_code_from_syserror ()));
     }
+  hd->sock = my_socket_new (sock);
+  if (!hd->sock)
+    {
+      xfree (proxy_authstr);
+      return gpg_err_make (errsource, gpg_err_code_from_syserror ());
+    }
+
+
 
 #ifdef HTTP_USE_GNUTLS
   if (hd->uri->use_tls)
     {
       int rc;
 
-      gnutls_transport_set_ptr (tls_session, (gnutls_transport_ptr_t)hd->sock);
+      my_socket_ref (hd->sock);
+      gnutls_transport_set_ptr (tls_session,
+                                (gnutls_transport_ptr_t)(hd->sock->fd));
       do
         {
           rc = gnutls_handshake (tls_session);
@@ -916,7 +1177,7 @@ send_request (http_t hd, const char *auth, const char *proxy)
         {
           log_info ("TLS handshake failed: %s\n", gnutls_strerror (rc));
           xfree (proxy_authstr);
-          return gpg_error (GPG_ERR_NETWORK);
+          return gpg_err_make (errsource, GPG_ERR_NETWORK);
         }
 
       if (tls_callback)
@@ -936,14 +1197,14 @@ send_request (http_t hd, const char *auth, const char *proxy)
   if (auth || hd->uri->auth)
     {
       char *myauth;
-      
+
       if (auth)
         {
           myauth = xtrystrdup (auth);
           if (!myauth)
             {
               xfree (proxy_authstr);
-              return gpg_error_from_syserror ();
+              return gpg_err_make (errsource, gpg_err_code_from_syserror ());
             }
           remove_escapes (myauth);
         }
@@ -961,58 +1222,52 @@ send_request (http_t hd, const char *auth, const char *proxy)
       if (!authstr)
         {
           xfree (proxy_authstr);
-          return gpg_error_from_syserror ();
+          return gpg_err_make (errsource, gpg_err_code_from_syserror ());
         }
     }
-  
+
   p = build_rel_path (hd->uri);
   if (!p)
-    return gpg_error_from_syserror ();
-
-  request = xtrymalloc (2 * strlen (server) 
-                        + strlen (p)
-                        + (authstr?strlen(authstr):0)
-                        + (proxy_authstr?strlen(proxy_authstr):0)
-                        + 100);
-  if (!request)
-    {
-      err = gpg_error_from_syserror ();
-      xfree (p);
-      xfree (authstr);
-      xfree (proxy_authstr);
-      return err;
-    }
+    return gpg_err_make (errsource, gpg_err_code_from_syserror ());
 
   if (http_proxy && *http_proxy)
     {
-      sprintf (request, "%s http://%s:%hu%s%s HTTP/1.0\r\n%s%s",
-              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,
-              authstr ? authstr : "",
-               proxy_authstr ? proxy_authstr : "");
+      request = es_asprintf
+        ("%s http://%s:%hu%s%s HTTP/1.0\r\n%s%s",
+         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,
+         authstr ? authstr : "",
+         proxy_authstr ? proxy_authstr : "");
     }
   else
     {
       char portstr[35];
-        
+
       if (port == 80)
         *portstr = 0;
       else
-        sprintf (portstr, ":%u", port);
+        snprintf (portstr, sizeof portstr, ":%u", port);
 
-      sprintf (request, "%s %s%s HTTP/1.0\r\nHost: %s%s\r\n%s",
-              hd->req_type == HTTP_REQ_GET ? "GET" :
-              hd->req_type == HTTP_REQ_HEAD ? "HEAD" :
-              hd->req_type == HTTP_REQ_POST ? "POST" : "OOPS",
-              *p == '/' ? "" : "/", p, server, portstr,
-               authstr? authstr:"");
+      request = es_asprintf
+        ("%s %s%s HTTP/1.0\r\nHost: %s%s\r\n%s",
+         hd->req_type == HTTP_REQ_GET ? "GET" :
+         hd->req_type == HTTP_REQ_HEAD ? "HEAD" :
+         hd->req_type == HTTP_REQ_POST ? "POST" : "OOPS",
+         *p == '/' ? "" : "/", p, server, portstr,
+         authstr? authstr:"");
     }
   xfree (p);
+  if (!request)
+    {
+      err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+      xfree (authstr);
+      xfree (proxy_authstr);
+      return err;
+    }
 
 
-#ifdef HTTP_USE_ESTREAM
   /* First setup estream so that we can write even the first line
      using estream.  This is also required for the sake of gnutls. */
   {
@@ -1021,44 +1276,43 @@ send_request (http_t hd, const char *auth, const char *proxy)
     cookie = xtrycalloc (1, sizeof *cookie);
     if (!cookie)
       {
-        err = gpg_error_from_syserror ();
+        err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
         goto leave;
       }
-    cookie->fd = hd->sock;
+    cookie->sock = my_socket_ref (hd->sock);
+    hd->write_cookie = cookie;
     if (hd->uri->use_tls)
-      {
-        cookie->tls_session = tls_session;
-        hd->write_cookie = cookie;
-      }
+      cookie->tls_session = tls_session;
 
     hd->fp_write = es_fopencookie (cookie, "w", cookie_functions);
     if (!hd->fp_write)
       {
+        err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+        my_socket_unref (cookie->sock);
         xfree (cookie);
-        err = gpg_error_from_syserror ();
+        hd->write_cookie = NULL;
       }
     else if (es_fputs (request, hd->fp_write) || es_fflush (hd->fp_write))
-      err = gpg_error_from_syserror ();
+      err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
     else
       err = 0;
-  }
 
- leave:
-
-#else /*!HTTP_USE_ESTREAM*/
-  /* We send out the start of the request through our own send
-     function and only then assign a stdio stream.  This allows for
-     better error reporting that through standard stdio means. */
-  err = write_server (hd->sock, request, strlen (request));
   if (!err)
     {
-      hd->fp_write = fdopen (hd->sock, "w");
-      if (!hd->fp_write)
-        err = gpg_error_from_syserror ();
+      for (;headers; headers=headers->next)
+        {
+          if ((es_fputs (headers->d, hd->fp_write) || es_fflush (hd->fp_write))
+              || (es_fputs("\r\n",hd->fp_write) || es_fflush(hd->fp_write)))
+            {
+              err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+              break;
+            }
+        }
     }
-#endif /*!HTTP_USE_ESTREAM*/
+  }
 
-  xfree (request);
+ leave:
+  es_free (request);
   xfree (authstr);
   xfree (proxy_authstr);
 
@@ -1118,97 +1372,6 @@ build_rel_path (parsed_uri_t uri)
 }
 
 
-
-/*
-   Same as fgets() but if the buffer is too short a larger one will be
-   allocated up to some limit *MAX_LENGTH.  A line is considered a
-   byte stream ending in a LF.  Returns the length of the line. EOF is
-   indicated by a line of length zero. The last LF may be missing due
-   to an EOF.  If MAX_LENGTH is zero on return, the line has been
-   truncated.  If the returned buffer is NULL, not enough memory was
-   enable to increase it, the return value will also be 0 and some
-   bytes might have been lost which should be no problem becuase
-   out-of-memory is pretty fatal for most applications.
-
-   If a line has been truncated, the file pointer is internally moved
-   forward to the end of the line.
-
-   Note: The returned buffer is allocated with enough extra space to
-   append a CR,LF,Nul
- */
-static size_t
-my_read_line (
-#ifdef HTTP_USE_ESTREAM
-              estream_t fp,
-#else
-              FILE *fp,
-#endif
-              char **addr_of_buffer,
-              size_t *length_of_buffer, size_t *max_length)
-{
-  int c;
-  char *buffer = *addr_of_buffer;
-  size_t length = *length_of_buffer;
-  size_t nbytes = 0;
-  size_t maxlen = *max_length;
-  char *p;
-
-  if (!buffer) /* Must allocate a new buffer. */
-    {          
-      length = 256;
-      buffer = xtrymalloc (length);
-      *addr_of_buffer = buffer;
-      if (!buffer)
-       {
-         *length_of_buffer = *max_length = 0;
-         return 0;
-       }
-      *length_of_buffer = length;
-    }
-
-  length -= 3; /* Reserve 3 bytes (cr,lf,eol). */
-  p = buffer;
-  while ((c = P_ES(getc) (fp)) != EOF)
-    {
-      if (nbytes == length) /* Increase the buffer. */
-       {                       
-         if (length > maxlen) /* Limit reached. */
-           {
-             /* Skip the rest of the line. */
-             while (c != '\n' && (c = P_ES(getc) (fp)) != EOF)
-               ;
-             *p++ = '\n'; /* Always append a LF (we reserved some space). */
-             nbytes++;
-             *max_length = 0; /* Indicate truncation */
-             break; /*(the while loop)*/
-           }
-         length += 3; /* Adjust for the reserved bytes. */
-         length += length < 1024 ? 256 : 1024;
-         *addr_of_buffer = xtryrealloc (buffer, length);
-         if (!*addr_of_buffer)
-           {
-             int save_errno = errno;
-             xfree (buffer);
-             *length_of_buffer = *max_length = 0;
-             errno = save_errno;
-             return 0;
-           }
-         buffer = *addr_of_buffer;
-         *length_of_buffer = length;
-         length -= 3; /* And re-adjust for the reservation. */
-         p = buffer + nbytes;
-       }
-      *p++ = c;
-      nbytes++;
-      if (c == '\n')
-       break;
-    }
-  *p = 0; /* Make sure the line is a string. */
-
-  return nbytes;
-}
-
-
 /* Transform a header name into a standard capitalized format; e.g.
    "Content-Type".  Conversion stops at the colon.  As usual we don't
    use the localized versions of ctype.h. */
@@ -1236,7 +1399,7 @@ capitalize_header_name (char *name)
 /* Store an HTTP header line in LINE away.  Line continuation is
    supported as well as merging of headers with the same name. This
    function may modify LINE. */
-static gpg_error_t
+static gpg_err_code_t
 store_header (http_t hd, char *line)
 {
   size_t n;
@@ -1251,17 +1414,17 @@ store_header (http_t hd, char *line)
         line[--n] = 0;
     }
   if (!n)  /* we are never called to hit this. */
-    return gpg_error (GPG_ERR_BUG);
+    return GPG_ERR_BUG;
   if (*line == ' ' || *line == '\t')
     {
       /* Continuation. This won't happen too often as it is not
          recommended.  We use a straightforward implementaion. */
       if (!hd->headers)
-        return gpg_error (GPG_ERR_PROTOCOL_VIOLATION);
+        return GPG_ERR_PROTOCOL_VIOLATION;
       n += strlen (hd->headers->value);
       p = xtrymalloc (n+1);
       if (!p)
-        return gpg_error_from_syserror ();
+        return gpg_err_code_from_syserror ();
       strcpy (stpcpy (p, hd->headers->value), line);
       xfree (hd->headers->value);
       hd->headers->value = p;
@@ -1271,12 +1434,12 @@ store_header (http_t hd, char *line)
   capitalize_header_name (line);
   p = strchr (line, ':');
   if (!p)
-    return gpg_error (GPG_ERR_PROTOCOL_VIOLATION);
+    return GPG_ERR_PROTOCOL_VIOLATION;
   *p++ = 0;
   while (*p == ' ' || *p == '\t')
     p++;
   value = p;
-  
+
   for (h=hd->headers; h; h = h->next)
     if ( !strcmp (h->name, line) )
       break;
@@ -1286,7 +1449,7 @@ store_header (http_t hd, char *line)
          it is a comma separated list and merge them.  */
       p = xtrymalloc (strlen (h->value) + 1 + strlen (value)+ 1);
       if (!p)
-        return gpg_error_from_syserror ();
+        return gpg_err_code_from_syserror ();
       strcpy (stpcpy (stpcpy (p, h->value), ","), value);
       xfree (h->value);
       h->value = p;
@@ -1296,13 +1459,13 @@ store_header (http_t hd, char *line)
   /* Append a new header. */
   h = xtrymalloc (sizeof *h + strlen (line));
   if (!h)
-    return gpg_error_from_syserror ();
+    return gpg_err_code_from_syserror ();
   strcpy (h->name, line);
   h->value = xtrymalloc (strlen (value)+1);
   if (!h->value)
     {
       xfree (h);
-      return gpg_error_from_syserror ();
+      return gpg_err_code_from_syserror ();
     }
   strcpy (h->value, value);
   h->next = hd->headers;
@@ -1316,9 +1479,7 @@ store_header (http_t hd, char *line)
    is valid as along as HD has not been closed and no othe request has
    been send. If the header was not found, NULL is returned.  Name
    must be canonicalized, that is the first letter of each dash
-   delimited part must be uppercase and all other letters lowercase.
-   Note that the context must have been opened with the
-   HTTP_FLAG_NEED_HEADER. */
+   delimited part must be uppercase and all other letters lowercase.  */
 const char *
 http_get_header (http_t hd, const char *name)
 {
@@ -1336,11 +1497,13 @@ http_get_header (http_t hd, const char *name)
  * Parse the response from a server.
  * Returns: Errorcode and sets some files in the handle
  */
-static gpg_error_t
+static gpg_err_code_t
 parse_response (http_t hd)
 {
   char *line, *p, *p2;
   size_t maxlen, len;
+  cookie_t cookie = hd->read_cookie;
+  const char *s;
 
   /* Delete old header lines.  */
   while (hd->headers)
@@ -1355,15 +1518,16 @@ parse_response (http_t hd)
   do
     {
       maxlen = MAX_LINELEN;
-      len = my_read_line (hd->fp_read, &hd->buffer, &hd->buffer_size, &maxlen);
+      len = es_read_line (hd->fp_read, &hd->buffer, &hd->buffer_size, &maxlen);
       line = hd->buffer;
       if (!line)
-       return gpg_error_from_syserror (); /* Out of core. */
+       return gpg_err_code_from_syserror (); /* Out of core. */
       if (!maxlen)
-       return gpg_error (GPG_ERR_TRUNCATED); /* Line has been truncated. */
+       return GPG_ERR_TRUNCATED; /* Line has been truncated. */
       if (!len)
-       return gpg_error (GPG_ERR_EOF);
-      if ( (hd->flags & HTTP_FLAG_LOG_RESP) )
+       return GPG_ERR_EOF;
+
+      if ((hd->flags & HTTP_FLAG_LOG_RESP))
         log_info ("RESP: `%.*s'\n",
                   (int)strlen(line)-(*line&&line[1]?2:0),line);
     }
@@ -1399,28 +1563,39 @@ parse_response (http_t hd)
   do
     {
       maxlen = MAX_LINELEN;
-      len = my_read_line (hd->fp_read, &hd->buffer, &hd->buffer_size, &maxlen);
+      len = es_read_line (hd->fp_read, &hd->buffer, &hd->buffer_size, &maxlen);
       line = hd->buffer;
       if (!line)
-       return gpg_error_from_syserror (); /* Out of core. */
+       return gpg_err_code_from_syserror (); /* Out of core. */
       /* Note, that we can silently ignore truncated lines. */
       if (!len)
-       return gpg_error (GPG_ERR_EOF);
+       return GPG_ERR_EOF;
       /* Trim line endings of empty lines. */
       if ((*line == '\r' && line[1] == '\n') || *line == '\n')
        *line = 0;
-      if ( (hd->flags & HTTP_FLAG_LOG_RESP) )
+      if ((hd->flags & HTTP_FLAG_LOG_RESP))
         log_info ("RESP: `%.*s'\n",
                   (int)strlen(line)-(*line&&line[1]?2:0),line);
-      if ( (hd->flags & HTTP_FLAG_NEED_HEADER) && *line )
+      if (*line)
         {
-          gpg_error_t err = store_header (hd, line);
-          if (err)
-            return err;
+          gpg_err_code_t ec = store_header (hd, line);
+          if (ec)
+            return ec;
         }
     }
   while (len && *line);
 
+  cookie->content_length_valid = 0;
+  if (!(hd->flags & HTTP_FLAG_IGNORE_CL))
+    {
+      s = http_get_header (hd, "Content-Length");
+      if (s)
+        {
+          cookie->content_length_valid = 1;
+          cookie->content_length = counter_strtoul (s);
+        }
+    }
+
   return 0;
 }
 
@@ -1467,14 +1642,14 @@ start_server ()
       FD_ZERO (&rfds);
       FD_SET (fd, &rfds);
 
-      if (select (fd + 1, &rfds, NULL, NULL, NULL) <= 0)
+      if (my_select (fd + 1, &rfds, NULL, NULL, NULL) <= 0)
        continue;               /* ignore any errors */
 
       if (!FD_ISSET (fd, &rfds))
        continue;
 
       addrlen = sizeof peer;
-      client = accept (fd, (struct sockaddr *) &peer, &addrlen);
+      client = my_accept (fd, (struct sockaddr *) &peer, &addrlen);
       if (client == -1)
        continue;               /* oops */
 
@@ -1505,7 +1680,7 @@ start_server ()
    error.  ERRNO is set on error. */
 static int
 connect_server (const char *server, unsigned short port,
-                unsigned int flags, const char *srvtag)
+                unsigned int flags, const char *srvtag, int *r_host_not_found)
 {
   int sock = -1;
   int srvcount = 0;
@@ -1513,9 +1688,14 @@ connect_server (const char *server, unsigned short port,
   int srv, connected;
   int last_errno = 0;
   struct srventry *serverlist = NULL;
-
 #ifdef HAVE_W32_SYSTEM
   unsigned long inaddr;
+#endif
+  /* Not currently using the flags */
+  (void)flags;
+
+  *r_host_not_found = 0;
+#ifdef HAVE_W32_SYSTEM
 
 #ifndef HTTP_NO_WSASTARTUP
   init_sockets ();
@@ -1526,9 +1706,9 @@ connect_server (const char *server, unsigned short port,
   if ( inaddr != INADDR_NONE )
     {
       struct sockaddr_in addr;
-      
+
       memset(&addr,0,sizeof(addr));
-      
+
       sock = socket(AF_INET,SOCK_STREAM,0);
       if ( sock==INVALID_SOCKET )
        {
@@ -1536,11 +1716,11 @@ connect_server (const char *server, unsigned short port,
          return -1;
        }
 
-      addr.sin_family = AF_INET; 
+      addr.sin_family = AF_INET;
       addr.sin_port = htons(port);
-      memcpy (&addr.sin_addr,&inaddr,sizeof(inaddr));      
+      memcpy (&addr.sin_addr,&inaddr,sizeof(inaddr));
 
-      if (!connect (sock,(struct sockaddr *)&addr,sizeof(addr)) )
+      if (!my_connect (sock,(struct sockaddr *)&addr,sizeof(addr)) )
        return sock;
       sock_close(sock);
       return -1;
@@ -1549,7 +1729,7 @@ connect_server (const char *server, unsigned short port,
 
 #ifdef USE_DNS_SRV
   /* Do the SRV thing */
-  if ((flags & HTTP_FLAG_TRY_SRV) && srvtag)
+  if (srvtag)
     {
       /* We're using SRV, so append the tags. */
       if (1+strlen (srvtag) + 6 + strlen (server) + 1 <= MAXDNAME)
@@ -1561,6 +1741,9 @@ connect_server (const char *server, unsigned short port,
          srvcount = getsrv (srvname, &serverlist);
        }
     }
+#else
+  (void)flags;
+  (void)srvtag;
 #endif /*USE_DNS_SRV*/
 
   if (!serverlist)
@@ -1583,7 +1766,7 @@ connect_server (const char *server, unsigned short port,
       struct addrinfo hints, *res, *ai;
       char portstr[35];
 
-      sprintf (portstr, "%hu", port);
+      snprintf (portstr, sizeof portstr, "%hu", port);
       memset (&hints, 0, sizeof (hints));
       hints.ai_socktype = SOCK_STREAM;
       if (getaddrinfo (serverlist[srv].target, portstr, &hints, &res))
@@ -1604,8 +1787,8 @@ connect_server (const char *server, unsigned short port,
               errno = save_errno;
               return -1;
             }
-          
-          if (connect (sock, ai->ai_addr, ai->ai_addrlen))
+
+          if (my_connect (sock, ai->ai_addr, ai->ai_addrlen))
             last_errno = errno;
           else
             connected = 1;
@@ -1637,7 +1820,7 @@ connect_server (const char *server, unsigned short port,
           xfree (serverlist);
           return -1;
         }
-      
+
       addr.sin_family = host->h_addrtype;
       if (addr.sin_family != AF_INET)
        {
@@ -1659,7 +1842,7 @@ connect_server (const char *server, unsigned short port,
       for (i = 0; host->h_addr_list[i] && !connected; i++)
         {
           memcpy (&addr.sin_addr, host->h_addr_list[i], host->h_length);
-          if (connect (sock, (struct sockaddr *) &addr, sizeof (addr)))
+          if (my_connect (sock, (struct sockaddr *) &addr, sizeof (addr)))
             last_errno = errno;
           else
             {
@@ -1684,9 +1867,11 @@ connect_server (const char *server, unsigned short port,
                  server,
                  hostfound? strerror (last_errno):"host not found");
 #endif
+      if (!hostfound)
+        *r_host_not_found = 1;
       if (sock != -1)
        sock_close (sock);
-      errno = last_errno;
+      gpg_err_set_errno (last_errno);
       return -1;
     }
   return sock;
@@ -1697,21 +1882,24 @@ static gpg_error_t
 write_server (int sock, const char *data, size_t length)
 {
   int nleft;
+  int nwritten;
 
   nleft = length;
   while (nleft > 0)
     {
-#ifdef HAVE_W32_SYSTEM
-      int nwritten;
-      
+#if defined(HAVE_W32_SYSTEM) && !defined(HAVE_PTH)
       nwritten = send (sock, data, nleft, 0);
-      if ( nwritten == SOCKET_ERROR ) 
+      if ( nwritten == SOCKET_ERROR )
         {
           log_info ("network write failed: ec=%d\n", (int)WSAGetLastError ());
           return gpg_error (GPG_ERR_NETWORK);
         }
-#else /*!HAVE_W32_SYSTEM*/
-      int nwritten = write (sock, data, nleft);
+#else /*!HAVE_W32_SYSTEM || HAVE_PTH*/
+# ifdef HAVE_PTH
+      nwritten = pth_write (sock, data, nleft);
+# else
+      nwritten = write (sock, data, nleft);
+# endif
       if (nwritten == -1)
        {
          if (errno == EINTR)
@@ -1722,13 +1910,13 @@ write_server (int sock, const char *data, size_t length)
 
              tv.tv_sec = 0;
              tv.tv_usec = 50000;
-             select (0, NULL, NULL, NULL, &tv);
+             my_select (0, NULL, NULL, NULL, &tv);
              continue;
            }
          log_info ("network write failed: %s\n", strerror (errno));
          return gpg_error_from_syserror ();
        }
-#endif /*!HAVE_W32_SYSTEM*/
+#endif /*!HAVE_W32_SYSTEM || HAVE_PTH*/
       nleft -= nwritten;
       data += nwritten;
     }
@@ -1738,7 +1926,6 @@ write_server (int sock, const char *data, size_t length)
 
 
 \f
-#ifdef HTTP_USE_ESTREAM
 /* Read handler for estream.  */
 static ssize_t
 cookie_read (void *cookie, void *buffer, size_t size)
@@ -1746,6 +1933,14 @@ cookie_read (void *cookie, void *buffer, size_t size)
   cookie_t c = cookie;
   int nread;
 
+  if (c->content_length_valid)
+    {
+      if (!c->content_length)
+        return 0; /* EOF */
+      if (c->content_length < size)
+        size = c->content_length;
+    }
+
 #ifdef HTTP_USE_GNUTLS
   if (c->tls_session)
     {
@@ -1758,16 +1953,16 @@ cookie_read (void *cookie, void *buffer, size_t size)
           if (nread == GNUTLS_E_AGAIN)
             {
               struct timeval tv;
-              
+
               tv.tv_sec = 0;
               tv.tv_usec = 50000;
-              select (0, NULL, NULL, NULL, &tv);
+              my_select (0, NULL, NULL, NULL, &tv);
               goto again;
             }
           if (nread == GNUTLS_E_REHANDSHAKE)
             goto again; /* A client is allowed to just ignore this request. */
           log_info ("TLS network read failed: %s\n", gnutls_strerror (nread));
-          errno = EIO;
+          gpg_err_set_errno (EIO);
           return -1;
         }
     }
@@ -1776,16 +1971,26 @@ cookie_read (void *cookie, void *buffer, size_t size)
     {
       do
         {
-#ifdef HAVE_W32_SYSTEM
+#ifdef HAVE_PTH
+          nread = pth_read (c->sock->fd, buffer, size);
+#elif defined(HAVE_W32_SYSTEM)
           /* Under Windows we need to use recv for a socket.  */
-          nread = recv (c->fd, buffer, size, 0);
-#else          
-          nread = read (c->fd, buffer, size);
+          nread = recv (c->sock->fd, buffer, size, 0);
+#else
+          nread = read (c->sock->fd, buffer, size);
 #endif
         }
       while (nread == -1 && errno == EINTR);
     }
 
+  if (c->content_length_valid && nread > 0)
+    {
+      if (nread < c->content_length)
+        c->content_length -= nread;
+      else
+        c->content_length = 0;
+    }
+
   return nread;
 }
 
@@ -1802,7 +2007,7 @@ cookie_write (void *cookie, const void *buffer, size_t size)
       int nleft = size;
       while (nleft > 0)
         {
-          nwritten = gnutls_record_send (c->tls_session, buffer, nleft); 
+          nwritten = gnutls_record_send (c->tls_session, buffer, nleft);
           if (nwritten <= 0)
             {
               if (nwritten == GNUTLS_E_INTERRUPTED)
@@ -1810,15 +2015,15 @@ cookie_write (void *cookie, const void *buffer, size_t size)
               if (nwritten == GNUTLS_E_AGAIN)
                 {
                   struct timeval tv;
-                  
+
                   tv.tv_sec = 0;
                   tv.tv_usec = 50000;
-                  select (0, NULL, NULL, NULL, &tv);
+                  my_select (0, NULL, NULL, NULL, &tv);
                   continue;
                 }
               log_info ("TLS network write failed: %s\n",
                         gnutls_strerror (nwritten));
-              errno = EIO;
+              gpg_err_set_errno (EIO);
               return -1;
             }
           nleft -= nwritten;
@@ -1828,9 +2033,9 @@ cookie_write (void *cookie, const void *buffer, size_t size)
   else
 #endif /*HTTP_USE_GNUTLS*/
     {
-      if ( write_server (c->fd, buffer, size) )
+      if ( write_server (c->sock->fd, buffer, size) )
         {
-          errno = EIO;
+          gpg_err_set_errno (EIO);
           nwritten = -1;
         }
       else
@@ -1853,29 +2058,29 @@ cookie_close (void *cookie)
   if (c->tls_session && !c->keep_socket)
     {
       gnutls_bye (c->tls_session, GNUTLS_SHUT_RDWR);
+      my_socket_unref (c->sock);
     }
 #endif /*HTTP_USE_GNUTLS*/
-  if (c->fd != -1 && !c->keep_socket)
-    sock_close (c->fd);
+  if (c->sock && !c->keep_socket)
+    my_socket_unref (c->sock);
 
   xfree (c);
   return 0;
 }
-#endif /*HTTP_USE_ESTREAM*/
-
 
 
 \f
 /**** Test code ****/
 #ifdef TEST
 
+#ifdef HTTP_USE_GNUTLS
 static gpg_error_t
 verify_callback (http_t hd, void *tls_context, int reserved)
 {
   log_info ("verification of certificates skipped\n");
   return 0;
 }
-
+#endif /*HTTP_USE_GNUTLS*/
 
 
 /* static void */
@@ -1899,9 +2104,7 @@ main (int argc, char **argv)
 #endif /*HTTP_USE_GNUTLS*/
   header_t hdr;
 
-#ifdef HTTP_USE_ESTREAM
   es_init ();
-#endif
   log_set_prefix ("http-test", 1 | 4);
   if (argc == 1)
     {
@@ -1950,46 +2153,49 @@ main (int argc, char **argv)
   http_register_tls_callback (verify_callback);
 #endif /*HTTP_USE_GNUTLS*/
 
-  rc = http_parse_uri (&uri, *argv);
+  rc = http_parse_uri (&uri, *argv, 1);
   if (rc)
     {
       log_error ("`%s': %s\n", *argv, gpg_strerror (rc));
-      http_release_parsed_uri (uri);
       return 1;
     }
 
   printf ("Scheme: %s\n", uri->scheme);
-  printf ("Host  : %s\n", uri->host);
-  printf ("Port  : %u\n", uri->port);
-  printf ("Path  : %s\n", uri->path);
-  for (r = uri->params; r; r = r->next)
-    {
-      printf ("Params: %s", r->name);
-      if (!r->no_value)
-       {
-         printf ("=%s", r->value);
-         if (strlen (r->value) != r->valuelen)
-           printf (" [real length=%d]", (int) r->valuelen);
-       }
-      putchar ('\n');
-    }
-  for (r = uri->query; r; r = r->next)
+  if (uri->opaque)
+    printf ("Value : %s\n", uri->path);
+  else
     {
-      printf ("Query : %s", r->name);
-      if (!r->no_value)
-       {
-         printf ("=%s", r->value);
-         if (strlen (r->value) != r->valuelen)
-           printf (" [real length=%d]", (int) r->valuelen);
-       }
-      putchar ('\n');
+      printf ("Auth  : %s\n", uri->auth? uri->auth:"[none]");
+      printf ("Host  : %s\n", uri->host);
+      printf ("Port  : %u\n", uri->port);
+      printf ("Path  : %s\n", uri->path);
+      for (r = uri->params; r; r = r->next)
+        {
+          printf ("Params: %s", r->name);
+          if (!r->no_value)
+            {
+              printf ("=%s", r->value);
+              if (strlen (r->value) != r->valuelen)
+                printf (" [real length=%d]", (int) r->valuelen);
+            }
+          putchar ('\n');
+        }
+      for (r = uri->query; r; r = r->next)
+        {
+          printf ("Query : %s", r->name);
+          if (!r->no_value)
+            {
+              printf ("=%s", r->value);
+              if (strlen (r->value) != r->valuelen)
+                printf (" [real length=%d]", (int) r->valuelen);
+            }
+          putchar ('\n');
+        }
     }
   http_release_parsed_uri (uri);
   uri = NULL;
 
-  rc = http_open_document (&hd, *argv, NULL, 
-                           HTTP_FLAG_NO_SHUTDOWN | HTTP_FLAG_NEED_HEADER,
-                           NULL, tls_session);
+  rc = http_open_document (&hd, *argv, NULL, 0, NULL, tls_session, NULL, NULL);
   if (rc)
     {
       log_error ("can't get `%s': %s\n", *argv, gpg_strerror (rc));
@@ -2002,7 +2208,7 @@ main (int argc, char **argv)
   switch (http_get_status_code (hd))
     {
     case 200:
-      while ((c = P_ES(getc) (http_get_read_ptr (hd))) != EOF)
+      while ((c = es_getc (http_get_read_ptr (hd))) != EOF)
         putchar (c);
       break;
     case 301:
@@ -2025,6 +2231,6 @@ main (int argc, char **argv)
 
 /*
 Local Variables:
-compile-command: "gcc -I.. -I../gl -DTEST -DHAVE_CONFIG_H -Wall -O2 -g -o http-test http.c -L. -lcommon -L../jnlib -ljnlib -lgcrypt -lpth -lgnutls"
+compile-command: "gcc -I.. -I../gl -DTEST -DHAVE_CONFIG_H -Wall -O2 -g -o http-test http.c -L. -lcommon -lgcrypt -lpth -lgnutls"
 End:
 */