Add finger support to dirmngr.
[gnupg.git] / common / http.c
index b50b6b8..7df8457 100644 (file)
@@ -161,13 +161,25 @@ 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);
 
 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,
@@ -176,21 +188,21 @@ static es_cookie_io_functions_t cookie_functions =
     cookie_close
   };
 
-struct cookie_s 
+struct cookie_s
 {
-  /* File descriptor or -1 if already closed. */
-  int fd;
+  /* Socket object or NULL if already closed. */
+  my_socket_t sock;
 
   /* TLS session context or NULL if not used. */
-  gnutls_session_t tls_session; 
+  gnutls_session_t tls_session;
 
   /* The remaining content length and a flag telling whether to use
      the content length.  */
-  longcounter_t content_length;  
+  longcounter_t content_length;
   unsigned int content_length_valid:1;
 
   /* Flag to communicate with the close handler. */
-  unsigned int keep_socket:1; 
+  unsigned int keep_socket:1;
 };
 typedef struct cookie_s *cookie_t;
 
@@ -210,10 +222,10 @@ typedef struct header_s *header_t;
 
 
 /* Our handle context. */
-struct http_context_s 
+struct http_context_s
 {
   unsigned int status_code;
-  int sock;
+  my_socket_t sock;
   unsigned int in_data:1;
   unsigned int is_http_0_9:1;
   estream_t fp_read;
@@ -258,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),
@@ -279,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
@@ -290,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+/";
@@ -308,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];
@@ -336,23 +419,23 @@ 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, 
+_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))
@@ -362,7 +445,6 @@ _http_open (http_t *r_hd, http_req_t reqtype, const char *url,
   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;
@@ -370,11 +452,10 @@ _http_open (http_t *r_hd, http_req_t reqtype, const char *url,
   err = _http_parse_uri (&hd->uri, url, 0, errsource);
   if (!err)
     err = send_request (hd, auth, proxy, srvtag, headers, errsource);
-  
+
   if (err)
     {
-      if (!hd->fp_read && !hd->fp_write && hd->sock != -1)
-        sock_close (hd->sock);
+      my_socket_unref (hd->sock);
       if (hd->fp_read)
         es_fclose (hd->fp_read);
       if (hd->fp_write)
@@ -387,6 +468,105 @@ _http_open (http_t *r_hd, http_req_t reqtype, const char *url,
 }
 
 
+/* 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 = es_onclose (hd->fp_read, 1, fp_onclose_notification, hd);
+
+ leave:
+  if (err)
+    {
+      if (hd->fp_read)
+        es_fclose (hd->fp_read);
+      if (hd->fp_write)
+        es_fclose (hd->fp_write);
+      my_socket_unref (hd->sock);
+      xfree (hd);
+    }
+  else
+    *r_hd = hd;
+  return err;
+}
+
+
+
+
 void
 http_start_data (http_t hd)
 {
@@ -408,14 +588,14 @@ _http_wait_response (http_t hd, gpg_err_source_t errsource)
   cookie_t cookie;
 
   /* Make sure that we are in the data. */
-  http_start_data (hd);        
+  http_start_data (hd);
 
-  /* Close the write stream but keep the socket open.  */
+  /* 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);
 
-  cookie->keep_socket = 1;
   es_fclose (hd->fp_write);
   hd->fp_write = NULL;
   /* The close has released the cookie and thus we better set it to NULL.  */
@@ -425,14 +605,14 @@ _http_wait_response (http_t hd, gpg_err_source_t errsource)
      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, 1);
+    shutdown (hd->sock->fd, 1);
   hd->in_data = 0;
 
   /* 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->fd = hd->sock;
+  cookie->sock = my_socket_ref (hd->sock);
   if (hd->uri->use_tls)
     cookie->tls_session = hd->tls_context;
 
@@ -440,12 +620,18 @@ _http_wait_response (http_t hd, gpg_err_source_t errsource)
   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);
       hd->read_cookie = NULL;
-      return gpg_err_make (errsource, gpg_err_code_from_syserror ());
+      return err;
     }
 
   err = parse_response (hd);
+
+  if (!err)
+    err = es_onclose (hd->fp_read, 1, fp_onclose_notification, hd);
+
   return err;
 }
 
@@ -455,7 +641,7 @@ _http_wait_response (http_t hd, gpg_err_source_t errsource)
    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, 
+_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)
@@ -480,8 +666,15 @@ 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)
     es_fclose (hd->fp_read);
   if (hd->fp_write)
@@ -577,6 +770,7 @@ do_parse_uri (parsed_uri_t uri, int only_local_part, int no_scheme_check)
   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)
@@ -614,14 +808,9 @@ do_parse_uri (parsed_uri_t uri, int only_local_part, int no_scheme_check)
 
       p = p2;
 
-      /* Find the hostname */
-      if (*p != '/')
-       return 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;
 
@@ -659,6 +848,15 @@ do_parse_uri (parsed_uri_t uri, int only_local_part, int no_scheme_check)
            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 */
@@ -678,7 +876,7 @@ do_parse_uri (parsed_uri_t uri, int only_local_part, int no_scheme_check)
     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. */
@@ -888,7 +1086,8 @@ send_request (http_t hd, const char *auth,
   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)
@@ -902,10 +1101,11 @@ send_request (http_t hd, const char *auth,
 
   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;
@@ -932,32 +1132,42 @@ send_request (http_t hd, const char *auth,
             }
         }
 
-      hd->sock = connect_server (*uri->host ? uri->host : "localhost",
-                                uri->port ? uri->port : 80,
-                                 hd->flags, srvtag);
+      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, srvtag);
-      save_errno = errno;
+      sock = connect_server (server, port, hd->flags, srvtag, &hnf);
     }
 
-  if (hd->sock == -1)
+  if (sock == -1)
+    {
+      xfree (proxy_authstr);
+      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, (save_errno 
-                                       ? gpg_err_code_from_errno (save_errno)
-                                       : GPG_ERR_NOT_FOUND));
+      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);
@@ -987,7 +1197,7 @@ send_request (http_t hd, const char *auth,
   if (auth || hd->uri->auth)
     {
       char *myauth;
-      
+
       if (auth)
         {
           myauth = xtrystrdup (auth);
@@ -1015,14 +1225,14 @@ send_request (http_t hd, const char *auth,
           return gpg_err_make (errsource, gpg_err_code_from_syserror ());
         }
     }
-  
+
   p = build_rel_path (hd->uri);
   if (!p)
     return gpg_err_make (errsource, gpg_err_code_from_syserror ());
 
   if (http_proxy && *http_proxy)
     {
-      request = es_asprintf 
+      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" :
@@ -1034,13 +1244,13 @@ send_request (http_t hd, const char *auth,
   else
     {
       char portstr[35];
-        
+
       if (port == 80)
         *portstr = 0;
       else
         snprintf (portstr, sizeof portstr, ":%u", port);
 
-      request = es_asprintf 
+      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" :
@@ -1069,7 +1279,7 @@ send_request (http_t hd, const char *auth,
         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;
@@ -1078,6 +1288,7 @@ send_request (http_t hd, const char *auth,
     if (!hd->fp_write)
       {
         err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+        my_socket_unref (cookie->sock);
         xfree (cookie);
         hd->write_cookie = NULL;
       }
@@ -1099,7 +1310,7 @@ send_request (http_t hd, const char *auth,
         }
     }
   }
-  
+
  leave:
   es_free (request);
   xfree (authstr);
@@ -1228,7 +1439,7 @@ store_header (http_t hd, char *line)
   while (*p == ' ' || *p == '\t')
     p++;
   value = p;
-  
+
   for (h=hd->headers; h; h = h->next)
     if ( !strcmp (h->name, line) )
       break;
@@ -1469,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;
@@ -1483,6 +1694,7 @@ connect_server (const char *server, unsigned short port,
   /* Not currently using the flags */
   (void)flags;
 
+  *r_host_not_found = 0;
 #ifdef HAVE_W32_SYSTEM
 
 #ifndef HTTP_NO_WSASTARTUP
@@ -1494,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 )
        {
@@ -1504,9 +1716,9 @@ 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 (!my_connect (sock,(struct sockaddr *)&addr,sizeof(addr)) )
        return sock;
@@ -1575,7 +1787,7 @@ connect_server (const char *server, unsigned short port,
               errno = save_errno;
               return -1;
             }
-          
+
           if (my_connect (sock, ai->ai_addr, ai->ai_addrlen))
             last_errno = errno;
           else
@@ -1608,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)
        {
@@ -1655,6 +1867,8 @@ 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);
       gpg_err_set_errno (last_errno);
@@ -1675,7 +1889,7 @@ write_server (int sock, const char *data, size_t length)
     {
 #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);
@@ -1739,7 +1953,7 @@ 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;
               my_select (0, NULL, NULL, NULL, &tv);
@@ -1758,12 +1972,12 @@ cookie_read (void *cookie, void *buffer, size_t size)
       do
         {
 #ifdef HAVE_PTH
-          nread = pth_read (c->fd, buffer, size);
+          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);
@@ -1774,7 +1988,7 @@ cookie_read (void *cookie, void *buffer, size_t size)
       if (nread < c->content_length)
         c->content_length -= nread;
       else
-        c->content_length = 0;          
+        c->content_length = 0;
     }
 
   return nread;
@@ -1793,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)
@@ -1801,7 +2015,7 @@ 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;
                   my_select (0, NULL, NULL, NULL, &tv);
@@ -1819,7 +2033,7 @@ 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) )
         {
           gpg_err_set_errno (EIO);
           nwritten = -1;
@@ -1844,28 +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;
 }
 
 
-
 \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 */
@@ -1938,7 +2153,7 @@ main (int argc, char **argv)
   http_register_tls_callback (verify_callback);
 #endif /*HTTP_USE_GNUTLS*/
 
-  rc = http_parse_uri (&uri, *argv, 0);
+  rc = http_parse_uri (&uri, *argv, 1);
   if (rc)
     {
       log_error ("`%s': %s\n", *argv, gpg_strerror (rc));
@@ -1946,35 +2161,41 @@ main (int argc, char **argv)
     }
 
   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, 0, 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));
@@ -2010,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:
 */