]> sjero.net Git - wget/blobdiff - src/http.c
[svn] Get rid of ssl_iread/ssl_iwrite. Have the SSL code register its
[wget] / src / http.c
index 3c22bf6cc425b5854d719b642257e8566f14e85b..d7b66a3ae4c61aba5b98e62d5309d44299b6f583 100644 (file)
@@ -7,7 +7,7 @@ This file is part of GNU Wget.
 GNU Wget is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
-(at your option) any later version.
+ (at your option) any later version.
 
 GNU Wget is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -16,7 +16,17 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with Wget; if not, write to the Free Software
-Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
+Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+In addition, as a special exception, the Free Software Foundation
+gives permission to link the code of its release of Wget with the
+OpenSSL project's "OpenSSL" library (or with modified versions of it
+that use the same license as the "OpenSSL" library), and distribute
+the linked executables.  You must obey the GNU General Public License
+in all respects for all of the code used other than "OpenSSL".  If you
+modify this file, you may extend this exception to your version of the
+file, but you are not obligated to do so.  If you do not wish to do
+so, delete this exception statement from your version.  */
 
 #include <config.h>
 
@@ -43,6 +53,9 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #  include <time.h>
 # endif
 #endif
+#ifndef errno
+extern int errno;
+#endif
 
 #include "wget.h"
 #include "utils.h"
@@ -52,7 +65,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "retr.h"
 #include "headers.h"
 #include "connect.h"
-#include "fnmatch.h"
 #include "netrc.h"
 #ifdef HAVE_SSL
 # include "gen_sslfunc.h"
@@ -61,23 +73,25 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #ifdef USE_DIGEST
 # include "gen-md5.h"
 #endif
+#include "convert.h"
 
 extern char *version_string;
+extern LARGE_INT total_downloaded_bytes;
 
-#ifndef errno
-extern int errno;
-#endif
 \f
 static int cookies_loaded_p;
+struct cookie_jar *wget_cookie_jar;
 
 #define TEXTHTML_S "text/html"
+#define TEXTXHTML_S "application/xhtml+xml"
 #define HTTP_ACCEPT "*/*"
 
 /* Some status code validation macros: */
 #define H_20X(x)        (((x) >= 200) && ((x) < 300))
 #define H_PARTIAL(x)    ((x) == HTTP_STATUS_PARTIAL_CONTENTS)
-#define H_REDIRECTED(x) (((x) == HTTP_STATUS_MOVED_PERMANENTLY)        \
-                        || ((x) == HTTP_STATUS_MOVED_TEMPORARILY))
+#define H_REDIRECTED(x) ((x) == HTTP_STATUS_MOVED_PERMANENTLY  \
+                         || (x) == HTTP_STATUS_MOVED_TEMPORARILY \
+                        || (x) == HTTP_STATUS_TEMPORARY_REDIRECT)
 
 /* HTTP/1.0 status codes from RFC1945, provided for reference.  */
 /* Successful 2xx.  */
@@ -92,6 +106,7 @@ static int cookies_loaded_p;
 #define HTTP_STATUS_MOVED_PERMANENTLY  301
 #define HTTP_STATUS_MOVED_TEMPORARILY  302
 #define HTTP_STATUS_NOT_MODIFIED       304
+#define HTTP_STATUS_TEMPORARY_REDIRECT  307
 
 /* Client error 4xx.  */
 #define HTTP_STATUS_BAD_REQUEST                400
@@ -174,39 +189,29 @@ parse_http_status_line (const char *line, const char **reason_phrase_ptr)
 
 /* Send the contents of FILE_NAME to SOCK/SSL.  Make sure that exactly
    PROMISED_SIZE bytes are sent over the wire -- if the file is
-   longer, read only that much; if the file is shorter, pad it with
-   zeros.  */
+   longer, read only that much; if the file is shorter, report an error.  */
 
 static int
-post_file (int sock, void *ssl, const char *file_name, long promised_size)
+post_file (int sock, const char *file_name, long promised_size)
 {
   static char chunk[8192];
   long written = 0;
   int write_error;
   FILE *fp;
 
-  /* Only one of SOCK and SSL may be active at the same time. */
-  assert (sock > -1 || ssl != NULL);
-  assert (sock == -1 || ssl == NULL);
-
   DEBUGP (("[writing POST file %s ... ", file_name));
 
   fp = fopen (file_name, "rb");
   if (!fp)
-    goto pad;
-  while (written < promised_size)
+    return -1;
+  while (!feof (fp) && written < promised_size)
     {
       int towrite;
       int length = fread (chunk, 1, sizeof (chunk), fp);
       if (length == 0)
        break;
       towrite = WMIN (promised_size - written, length);
-#ifdef HAVE_SSL
-      if (ssl)
-       write_error = ssl_iwrite (ssl, chunk, towrite);
-      else
-#endif
-       write_error = iwrite (sock, chunk, towrite);
+      write_error = xwrite (sock, chunk, towrite, -1);
       if (write_error < 0)
        {
          fclose (fp);
@@ -216,29 +221,14 @@ post_file (int sock, void *ssl, const char *file_name, long promised_size)
     }
   fclose (fp);
 
- pad:
+  /* If we've written less than was promised, report a (probably
+     nonsensical) error rather than break the promise.  */
   if (written < promised_size)
     {
-      /* This highly unlikely case can happen only if the file has
-        shrunk under us.  To uphold the promise that exactly
-        promised_size bytes would be delivered, pad the remaining
-        data with zeros.  #### Should we abort instead?  */
-      DEBUGP (("padding %ld bytes ... ", promised_size - written));
-      memset (chunk, '\0', sizeof (chunk));
-      while (written < promised_size)
-       {
-         int towrite = WMIN (promised_size - written, sizeof (chunk));
-#ifdef HAVE_SSL
-         if (ssl)
-           write_error = ssl_iwrite (ssl, chunk, towrite);
-         else
-#endif
-           write_error = iwrite (sock, chunk, towrite);
-         if (write_error < 0)
-           return -1;
-         written += towrite;
-       }
+      errno = EINVAL;
+      return -1;
     }
+
   assert (written == promised_size);
   DEBUGP (("done]\n"));
   return 0;
@@ -334,6 +324,21 @@ http_process_connection (const char *hdr, void *arg)
     *flag = 1;
   return 1;
 }
+
+/* Commit the cookie to the cookie jar. */
+
+int
+http_process_set_cookie (const char *hdr, void *arg)
+{
+  struct url *u = (struct url *)arg;
+
+  /* The jar should have been created by now. */
+  assert (wget_cookie_jar != NULL);
+
+  cookie_handle_set_cookie (wget_cookie_jar, u->host, u->port, u->path, hdr);
+  return 1;
+}
+
 \f
 /* Persistent connections.  Currently, we cache the most recently used
    connection as persistent, provided that the HTTP server agrees to
@@ -345,6 +350,7 @@ http_process_connection (const char *hdr, void *arg)
 
 /* Whether a persistent connection is active. */
 static int pc_active_p;
+
 /* Host and port of currently active persistent connection. */
 static struct address_list *pc_last_host_ip;
 static unsigned short pc_last_port;
@@ -352,12 +358,8 @@ static unsigned short pc_last_port;
 /* File descriptor of the currently active persistent connection. */
 static int pc_last_fd;
 
-#ifdef HAVE_SSL
 /* Whether a ssl handshake has occoured on this connection */
-static int pc_active_ssl;
-/* SSL connection of the currently active persistent connection. */
-static SSL *pc_last_ssl;
-#endif /* HAVE_SSL */
+static int pc_last_ssl_p;
 
 /* Mark the persistent connection as invalid.  This is used by the
    CLOSE_* macros after they forcefully close a registered persistent
@@ -368,9 +370,7 @@ static void
 invalidate_persistent (void)
 {
   pc_active_p = 0;
-#ifdef HAVE_SSL
-  pc_active_ssl = 0;
-#endif /* HAVE_SSL */
+  pc_last_ssl_p = 0;
   if (pc_last_host_ip != NULL)
     {
       address_list_release (pc_last_host_ip);
@@ -387,15 +387,9 @@ invalidate_persistent (void)
 
    If a previous connection was persistent, it is closed. */
 
-#ifdef HAVE_SSL
 static void
-register_persistent (const char *host, unsigned short port, int fd, SSL *ssl)
+register_persistent (const char *host, unsigned short port, int fd, int ssl)
 {
-#else
-static void
-register_persistent (const char *host, unsigned short port, int fd)
-{
-#endif
   if (pc_active_p)
     {
       if (pc_last_fd == fd)
@@ -411,13 +405,7 @@ register_persistent (const char *host, unsigned short port, int fd)
             persistent connection exists, but we then connect to a
             different host, and try to register a persistent
             connection to that one.  */
-#ifdef HAVE_SSL
-         /* The ssl disconnect has to take place before the closing
-             of pc_last_fd.  */
-         if (pc_last_ssl)
-           shutdown_ssl(pc_last_ssl);
-#endif
-         CLOSE (pc_last_fd);
+         xclose (pc_last_fd);
          invalidate_persistent ();
        }
     }
@@ -426,40 +414,22 @@ register_persistent (const char *host, unsigned short port, int fd)
 
   /* This lookup_host cannot fail, because it has the results in the
      cache.  */
-  pc_last_host_ip = lookup_host (host, 1);
+  pc_last_host_ip = lookup_host (host, LH_SILENT);
   assert (pc_last_host_ip != NULL);
 
   pc_last_port = port;
   pc_last_fd = fd;
   pc_active_p = 1;
-#ifdef HAVE_SSL
-  pc_last_ssl = ssl;
-  pc_active_ssl = ssl ? 1 : 0;
-#endif
+  pc_last_ssl_p = ssl;
   DEBUGP (("Registered fd %d for persistent reuse.\n", fd));
 }
 
-#ifdef HAVE_SSL
-# define SHUTDOWN_SSL(ssl) do {                \
-  if (ssl)                             \
-    shutdown_ssl (ssl);                        \
-} while (0)
-#else
-# define SHUTDOWN_SSL(ssl) 
-#endif
-
 /* Return non-zero if a persistent connection is available for
    connecting to HOST:PORT.  */
 
-#ifdef HAVE_SSL
 static int
 persistent_available_p (const char *host, unsigned short port, int ssl)
 {
-#else
-static int
-persistent_available_p (const char *host, unsigned short port)
-{
-#endif
   int success;
   struct address_list *this_host_ip;
 
@@ -471,17 +441,15 @@ persistent_available_p (const char *host, unsigned short port)
   if (port != pc_last_port)
     return 0;
 
-#ifdef HAVE_SSL
   /* Second, a): check if current connection is (not) ssl, too.  This
      test is unlikely to fail because HTTP and HTTPS typicaly use
      different ports.  Yet it is possible, or so I [Christian
      Fraenkel] have been told, to run HTTPS and HTTP simultaneus on
      the same port.  */
-  if (ssl != pc_active_ssl)
+  if (ssl != pc_last_ssl_p)
     return 0;
-#endif /* HAVE_SSL */
 
-  this_host_ip = lookup_host (host, 1);
+  this_host_ip = lookup_host (host, LH_SILENT);
   if (!this_host_ip)
     return 0;
 
@@ -504,11 +472,7 @@ persistent_available_p (const char *host, unsigned short port)
       /* Oops, the socket is no longer open.  Now that we know that,
          let's invalidate the persistent connection before returning
          0.  */
-      CLOSE (pc_last_fd);
-#ifdef HAVE_SSL
-      SHUTDOWN_SSL (pc_last_ssl);
-      pc_last_ssl = NULL;
-#endif
+      xclose (pc_last_fd);
       invalidate_persistent ();
       return 0;
     }
@@ -533,16 +497,14 @@ persistent_available_p (const char *host, unsigned short port)
 #define CLOSE_FINISH(fd) do {                  \
   if (!keep_alive)                             \
     {                                          \
-      SHUTDOWN_SSL (ssl);                      \
-      CLOSE (fd);                              \
+      xclose (fd);                             \
       if (pc_active_p && (fd) == pc_last_fd)   \
        invalidate_persistent ();               \
     }                                          \
 } while (0)
 
 #define CLOSE_INVALIDATE(fd) do {              \
-  SHUTDOWN_SSL (ssl);                          \
-  CLOSE (fd);                                  \
+  xclose (fd);                                 \
   if (pc_active_p && (fd) == pc_last_fd)       \
     invalidate_persistent ();                  \
 } while (0)
@@ -557,7 +519,7 @@ struct http_stat
   char *remote_time;           /* remote time-stamp string */
   char *error;                 /* textual HTTP error */
   int statcode;                        /* status code */
-  long dltime;                 /* time of the download */
+  double dltime;               /* time of the download in msecs */
   int no_truncate;             /* whether truncating the file is
                                   forbidden. */
   const char *referer;         /* value of the referer header. */
@@ -567,9 +529,9 @@ struct http_stat
 static void
 free_hstat (struct http_stat *hs)
 {
-  FREE_MAYBE (hs->newloc);
-  FREE_MAYBE (hs->remote_time);
-  FREE_MAYBE (hs->error);
+  xfree_null (hs->newloc);
+  xfree_null (hs->remote_time);
+  xfree_null (hs->error);
 
   /* Guard against being called twice. */
   hs->newloc = NULL;
@@ -584,7 +546,7 @@ static char *basic_authentication_encode PARAMS ((const char *, const char *,
                                                  const char *));
 static int known_authentication_scheme_p PARAMS ((const char *));
 
-time_t http_atotm PARAMS ((char *));
+time_t http_atotm PARAMS ((const char *));
 
 #define BEGINS_WITH(line, string_constant)                             \
   (!strncasecmp (line, string_constant, sizeof (string_constant) - 1)  \
@@ -623,8 +585,8 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
   struct rbuf rbuf;
 #ifdef HAVE_SSL
   static SSL_CTX *ssl_ctx = NULL;
-  SSL *ssl = NULL;
 #endif
+  int using_ssl = 0;
   char *cookies = NULL;
 
   /* Whether this connection will be kept alive after the HTTP request
@@ -720,36 +682,33 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
 
   /* First: establish the connection.  */
   if (inhibit_keep_alive
-      ||
-#ifndef HAVE_SSL
-      !persistent_available_p (conn->host, conn->port)
+      || !persistent_available_p (conn->host, conn->port,
+#ifdef HAVE_SSL
+                                 u->scheme == SCHEME_HTTPS
 #else
-      !persistent_available_p (conn->host, conn->port,
-                              u->scheme == SCHEME_HTTPS)
-#endif /* HAVE_SSL */
-      )
+                                 0
+#endif
+                                 ))
     {
-      struct address_list *al = lookup_host (conn->host, 0);
-      if (!al)
+      sock = connect_to_host (conn->host, conn->port);
+      if (sock == E_HOST)
        return HOSTERR;
-      set_connection_host_name (conn->host);
-      sock = connect_to_many (al, conn->port, 0);
-      set_connection_host_name (NULL);
-      address_list_release (al);
-
-      if (sock < 0)
-       return errno == ECONNREFUSED ? CONREFUSED : CONERROR;
+      else if (sock < 0)
+       return CONNECT_ERROR (errno);
 
 #ifdef HAVE_SSL
      if (conn->scheme == SCHEME_HTTPS)
-       if (connect_ssl (&ssl, ssl_ctx,sock) != 0)
-        {
-          logputs (LOG_VERBOSE, "\n");
-          logprintf (LOG_NOTQUIET, _("Unable to establish SSL connection.\n"));
-          ssl_printerrors ();
-          CLOSE (sock);
-          return CONSSLERR;
-        }
+       {
+        if (!connect_ssl (sock, ssl_ctx))
+          {
+            logputs (LOG_VERBOSE, "\n");
+            logprintf (LOG_NOTQUIET,
+                       _("Unable to establish SSL connection.\n"));
+            xclose (sock);
+            return CONSSLERR;
+          }
+        using_ssl = 1;
+       }
 #endif /* HAVE_SSL */
     }
   else
@@ -759,9 +718,7 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
       /* #### pc_last_fd should be accessed through an accessor
          function.  */
       sock = pc_last_fd;
-#ifdef HAVE_SSL
-      ssl = pc_last_ssl;
-#endif /* HAVE_SSL */
+      using_ssl = pc_last_ssl_p;
       DEBUGP (("Reusing fd %d.\n", sock));
     }
 
@@ -891,13 +848,13 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
     request_keep_alive = NULL;
 
   if (opt.cookies)
-    cookies = build_cookies_request (u->host, u->port, u->path,
+    cookies = cookie_header (wget_cookie_jar, u->host, u->port, u->path,
 #ifdef HAVE_SSL
-                                    u->scheme == SCHEME_HTTPS
+                            u->scheme == SCHEME_HTTPS
 #else
-                                    0
+                            0
 #endif
-                                    );
+                            );
 
   if (opt.post_data || opt.post_file_name)
     {
@@ -976,42 +933,23 @@ Accept: %s\r\n\
   DEBUGP (("---request begin---\n%s", request));
 
   /* Free the temporary memory.  */
-  FREE_MAYBE (wwwauth);
-  FREE_MAYBE (proxyauth);
-  FREE_MAYBE (cookies);
+  xfree_null (wwwauth);
+  xfree_null (proxyauth);
+  xfree_null (cookies);
   xfree (full_path);
 
   /* Send the request to server.  */
-#ifdef HAVE_SSL
-  if (conn->scheme == SCHEME_HTTPS)
-    write_error = ssl_iwrite (ssl, request, strlen (request));
-  else
-#endif
-    write_error = iwrite (sock, request, strlen (request));
+  write_error = xwrite (sock, request, strlen (request), -1);
 
   if (write_error >= 0)
     {
       if (opt.post_data)
        {
          DEBUGP (("[POST data: %s]\n", opt.post_data));
-#ifdef HAVE_SSL
-         if (conn->scheme == SCHEME_HTTPS)
-           write_error = ssl_iwrite (ssl, opt.post_data, post_data_size);
-         else
-#endif
-           write_error = iwrite (sock, opt.post_data, post_data_size);
-       }
-      else if (opt.post_file_name)
-       {
-#ifdef HAVE_SSL
-         if (conn->scheme == SCHEME_HTTPS)
-           write_error = post_file (-1, ssl, opt.post_file_name,
-                                    post_data_size);
-         else
-#endif
-           write_error = post_file (sock, NULL, opt.post_file_name,
-                                    post_data_size);
+         write_error = xwrite (sock, opt.post_data, post_data_size, -1);
        }
+      else if (opt.post_file_name && post_data_size != 0)
+       write_error = post_file (sock, opt.post_file_name, post_data_size);
     }
   DEBUGP (("---request end---\n"));
 
@@ -1031,12 +969,6 @@ Accept: %s\r\n\
 
   /* Before reading anything, initialize the rbuf.  */
   rbuf_initialize (&rbuf, sock);
-#ifdef HAVE_SSL
-  if (conn->scheme == SCHEME_HTTPS)
-    rbuf.ssl = ssl;
-  else
-    rbuf.ssl = NULL;
-#endif /* HAVE_SSL */
   all_headers = NULL;
   all_length = 0;
   /* Header-fetching loop.  */
@@ -1068,8 +1000,8 @@ Accept: %s\r\n\
          logputs (LOG_VERBOSE, "\n");
          logputs (LOG_NOTQUIET, _("End of file while parsing headers.\n"));
          xfree (hdr);
-         FREE_MAYBE (type);
-         FREE_MAYBE (all_headers);
+         xfree_null (type);
+         xfree_null (all_headers);
          CLOSE_INVALIDATE (sock);
          return HEOF;
        }
@@ -1079,8 +1011,8 @@ Accept: %s\r\n\
          logprintf (LOG_NOTQUIET, _("Read error (%s) in headers.\n"),
                     strerror (errno));
          xfree (hdr);
-         FREE_MAYBE (type);
-         FREE_MAYBE (all_headers);
+         xfree_null (type);
+         xfree_null (all_headers);
          CLOSE_INVALIDATE (sock);
          return HERR;
        }
@@ -1123,7 +1055,7 @@ Accept: %s\r\n\
            hs->error = xstrdup (error);
 
          if ((statcode != -1)
-#ifdef DEBUG
+#ifdef ENABLE_DEBUG
              && !opt.debug
 #endif
              )
@@ -1168,7 +1100,7 @@ Accept: %s\r\n\
          goto done_header;
       /* Try getting cookies. */
       if (opt.cookies)
-       if (header_process (hdr, "Set-Cookie", set_cookie_header_cb, u))
+       if (header_process (hdr, "Set-Cookie", http_process_set_cookie, u))
          goto done_header;
       /* Try getting www-authentication.  */
       if (!authenticate_h)
@@ -1230,17 +1162,13 @@ Accept: %s\r\n\
   if (keep_alive)
     /* The server has promised that it will not close the connection
        when we're done.  This means that we can register it.  */
-#ifndef HAVE_SSL
-    register_persistent (conn->host, conn->port, sock);
-#else
-    register_persistent (conn->host, conn->port, sock, ssl);
-#endif /* HAVE_SSL */
+    register_persistent (conn->host, conn->port, sock, using_ssl);
 
   if ((statcode == HTTP_STATUS_UNAUTHORIZED)
       && authenticate_h)
     {
       /* Authorization is required.  */
-      FREE_MAYBE (type);
+      xfree_null (type);
       type = NULL;
       free_hstat (hs);
       CLOSE_INVALIDATE (sock); /* would be CLOSE_FINISH, but there
@@ -1302,8 +1230,8 @@ Accept: %s\r\n\
                     hs->newloc ? _(" [following]") : "");
          CLOSE_INVALIDATE (sock);      /* would be CLOSE_FINISH, but there
                                           might be more bytes in the body. */
-         FREE_MAYBE (type);
-         FREE_MAYBE (all_headers);
+         xfree_null (type);
+         xfree_null (all_headers);
          return NEWLOCATION;
        }
     }
@@ -1311,7 +1239,9 @@ Accept: %s\r\n\
   /* If content-type is not given, assume text/html.  This is because
      of the multitude of broken CGI's that "forget" to generate the
      content-type.  */
-  if (!type || 0 == strncasecmp (type, TEXTHTML_S, strlen (TEXTHTML_S)))
+  if (!type ||
+        0 == strncasecmp (type, TEXTHTML_S, strlen (TEXTHTML_S)) ||
+        0 == strncasecmp (type, TEXTXHTML_S, strlen (TEXTXHTML_S)))
     *dt |= TEXTHTML;
   else
     *dt &= ~TEXTHTML;
@@ -1323,9 +1253,9 @@ Accept: %s\r\n\
     {
       char*  last_period_in_local_filename = strrchr(*hs->local_file, '.');
 
-      if (last_period_in_local_filename == NULL ||
-         !(strcasecmp(last_period_in_local_filename, ".htm") == EQ ||
-           strcasecmp(last_period_in_local_filename, ".html") == EQ))
+      if (last_period_in_local_filename == NULL
+         || !(0 == strcasecmp (last_period_in_local_filename, ".htm")
+              || 0 == strcasecmp (last_period_in_local_filename, ".html")))
        {
          size_t  local_filename_len = strlen(*hs->local_file);
          
@@ -1371,8 +1301,8 @@ Accept: %s\r\n\
              hs->res = 0;
              /* Mark as successfully retrieved. */
              *dt |= RETROKF;
-             FREE_MAYBE (type);
-             FREE_MAYBE (all_headers);
+             xfree_null (type);
+             xfree_null (all_headers);
              CLOSE_INVALIDATE (sock);  /* would be CLOSE_FINISH, but there
                                           might be more bytes in the body. */
              return RETRUNNEEDED;
@@ -1386,8 +1316,8 @@ Accept: %s\r\n\
 \n\
 Continued download failed on this file, which conflicts with `-c'.\n\
 Refusing to truncate existing file `%s'.\n\n"), *hs->local_file);
-             FREE_MAYBE (type);
-             FREE_MAYBE (all_headers);
+             xfree_null (type);
+             xfree_null (all_headers);
              CLOSE_INVALIDATE (sock);
              return CONTNOTSUPPORTED;
            }
@@ -1402,8 +1332,8 @@ Refusing to truncate existing file `%s'.\n\n"), *hs->local_file);
     {
       /* This means the whole request was somehow misunderstood by the
         server.  Bail out.  */
-      FREE_MAYBE (type);
-      FREE_MAYBE (all_headers);
+      xfree_null (type);
+      xfree_null (all_headers);
       CLOSE_INVALIDATE (sock);
       return RANGEERR;
     }
@@ -1442,7 +1372,7 @@ Refusing to truncate existing file `%s'.\n\n"), *hs->local_file);
            logputs (LOG_VERBOSE, "\n");
        }
     }
-  FREE_MAYBE (type);
+  xfree_null (type);
   type = NULL;                 /* We don't need it any more.  */
 
   /* Return if we have no intention of further downloading.  */
@@ -1451,8 +1381,8 @@ Refusing to truncate existing file `%s'.\n\n"), *hs->local_file);
       /* In case the caller cares to look...  */
       hs->len = 0L;
       hs->res = 0;
-      FREE_MAYBE (type);
-      FREE_MAYBE (all_headers);
+      xfree_null (type);
+      xfree_null (all_headers);
       CLOSE_INVALIDATE (sock); /* would be CLOSE_FINISH, but there
                                   might be more bytes in the body. */
       return RETRFINISHED;
@@ -1470,7 +1400,7 @@ Refusing to truncate existing file `%s'.\n\n"), *hs->local_file);
          logprintf (LOG_NOTQUIET, "%s: %s\n", *hs->local_file, strerror (errno));
          CLOSE_INVALIDATE (sock); /* would be CLOSE_FINISH, but there
                                      might be more bytes in the body. */
-         FREE_MAYBE (all_headers);
+         xfree_null (all_headers);
          return FOPENERR;
        }
     }
@@ -1490,8 +1420,12 @@ Refusing to truncate existing file `%s'.\n\n"), *hs->local_file);
 
          #### A possible solution to this would be to remember the
         file position in the output document and to seek to that
-        position, instead of rewinding.  */
-      if (!hs->restval && global_download_count == 0)
+        position, instead of rewinding.
+
+         We don't truncate stdout, since that breaks
+        "wget -O - [...] >> foo".
+      */
+      if (!hs->restval && global_download_count == 0 && opt.dfp != stdout)
        {
          /* This will silently fail for streams that don't correspond
             to regular files, but that's OK.  */
@@ -1530,7 +1464,7 @@ Refusing to truncate existing file `%s'.\n\n"), *hs->local_file);
     if (flush_res == EOF)
       hs->res = -2;
   }
-  FREE_MAYBE (all_headers);
+  xfree_null (all_headers);
   if (hs->res == -2)
     return FWRITEERR;
   return RETRFINISHED;
@@ -1558,10 +1492,15 @@ http_loop (struct url *u, char **newloc, char **local_file, const char *referer,
   /* This used to be done in main(), but it's a better idea to do it
      here so that we don't go through the hoops if we're just using
      FTP or whatever. */
-  if (opt.cookies && opt.cookies_input && !cookies_loaded_p)
+  if (opt.cookies)
     {
-      load_cookies (opt.cookies_input);
-      cookies_loaded_p = 1;
+      if (!wget_cookie_jar)
+       wget_cookie_jar = cookie_jar_new ();
+      if (opt.cookies_input && !cookies_loaded_p)
+       {
+         cookie_jar_load (wget_cookie_jar, opt.cookies_input);
+         cookies_loaded_p = 1;
+       }
     }
 
   *newloc = NULL;
@@ -1577,12 +1516,12 @@ http_loop (struct url *u, char **newloc, char **local_file, const char *referer,
     hstat.local_file = local_file;
   else if (local_file)
     {
-      *local_file = url_filename (u);
+      *local_file = url_file_name (u);
       hstat.local_file = local_file;
     }
   else
     {
-      dummy = url_filename (u);
+      dummy = url_file_name (u);
       hstat.local_file = &dummy;
     }
 
@@ -1610,14 +1549,14 @@ File `%s' already there, will not retrieve.\n"), *hstat.local_file);
       if (has_html_suffix_p (*hstat.local_file))
        *dt |= TEXTHTML;
 
-      FREE_MAYBE (dummy);
+      xfree_null (dummy);
       return RETROK;
     }
 
   use_ts = 0;
   if (opt.timestamping)
     {
-      boolean  local_dot_orig_file_exists = FALSE;
+      int local_dot_orig_file_exists = 0;
 
       if (opt.backup_converted)
        /* If -K is specified, we'll act on the assumption that it was specified
@@ -1644,7 +1583,7 @@ File `%s' already there, will not retrieve.\n"), *hstat.local_file);
          /* Try to stat() the .orig file. */
          if (stat (filename_plus_orig_suffix, &st) == 0)
            {
-             local_dot_orig_file_exists = TRUE;
+             local_dot_orig_file_exists = 1;
              local_filename = filename_plus_orig_suffix;
            }
        }      
@@ -1762,11 +1701,11 @@ File `%s' already there, will not retrieve.\n"), *hstat.local_file);
          printwhat (count, opt.ntry);
          continue;
          break;
-       case HOSTERR: case CONREFUSED: case PROXERR: case AUTHFAILED: 
+       case HOSTERR: case CONIMPOSSIBLE: case PROXERR: case AUTHFAILED: 
        case SSLERRCTXCREATE: case CONTNOTSUPPORTED:
          /* Fatal errors just return from the function.  */
          free_hstat (&hstat);
-         FREE_MAYBE (dummy);
+         xfree_null (dummy);
          return err;
          break;
        case FWRITEERR: case FOPENERR:
@@ -1775,7 +1714,7 @@ File `%s' already there, will not retrieve.\n"), *hstat.local_file);
          logprintf (LOG_NOTQUIET, _("Cannot write to `%s' (%s).\n"),
                     *hstat.local_file, strerror (errno));
          free_hstat (&hstat);
-         FREE_MAYBE (dummy);
+         xfree_null (dummy);
          return err;
          break;
        case CONSSLERR:
@@ -1783,7 +1722,7 @@ File `%s' already there, will not retrieve.\n"), *hstat.local_file);
          logputs (LOG_VERBOSE, "\n");
          logprintf (LOG_NOTQUIET, _("Unable to establish SSL connection.\n"));
          free_hstat (&hstat);
-         FREE_MAYBE (dummy);
+         xfree_null (dummy);
          return err;
          break;
        case NEWLOCATION:
@@ -1794,17 +1733,17 @@ File `%s' already there, will not retrieve.\n"), *hstat.local_file);
                         _("ERROR: Redirection (%d) without location.\n"),
                         hstat.statcode);
              free_hstat (&hstat);
-             FREE_MAYBE (dummy);
+             xfree_null (dummy);
              return WRONGCODE;
            }
          free_hstat (&hstat);
-         FREE_MAYBE (dummy);
+         xfree_null (dummy);
          return NEWLOCATION;
          break;
        case RETRUNNEEDED:
          /* The file was already fully retrieved. */
          free_hstat (&hstat);
-         FREE_MAYBE (dummy);
+         xfree_null (dummy);
          return RETROK;
          break;
        case RETRFINISHED:
@@ -1827,7 +1766,7 @@ File `%s' already there, will not retrieve.\n"), *hstat.local_file);
                     tms, hstat.statcode, hstat.error);
          logputs (LOG_VERBOSE, "\n");
          free_hstat (&hstat);
-         FREE_MAYBE (dummy);
+         xfree_null (dummy);
          return WRONGCODE;
        }
 
@@ -1871,7 +1810,7 @@ Last-modified header invalid -- time-stamp ignored.\n"));
 Server file no newer than local file `%s' -- not retrieving.\n\n"),
                             local_filename);
                  free_hstat (&hstat);
-                 FREE_MAYBE (dummy);
+                 xfree_null (dummy);
                  return RETROK;
                }
              else if (tml >= tmr)
@@ -1909,7 +1848,7 @@ The sizes do not match (local %ld) -- retrieving.\n"), local_size);
       if (opt.spider)
        {
          logprintf (LOG_NOTQUIET, "%d %s\n\n", hstat.statcode, hstat.error);
-         FREE_MAYBE (dummy);
+         xfree_null (dummy);
          return RETROK;
        }
 
@@ -1927,7 +1866,7 @@ The sizes do not match (local %ld) -- retrieving.\n"), local_size);
                         tms, u->url, hstat.len, hstat.contlen, locf, count);
            }
          ++opt.numurls;
-         downloaded_increase (hstat.len);
+         total_downloaded_bytes += hstat.len;
 
          /* Remember that we downloaded the file for later ".orig" code. */
          if (*dt & ADDED_HTML_EXTENSION)
@@ -1936,7 +1875,7 @@ The sizes do not match (local %ld) -- retrieving.\n"), local_size);
            downloaded_file(FILE_DOWNLOADED_NORMALLY, locf);
 
          free_hstat (&hstat);
-         FREE_MAYBE (dummy);
+         xfree_null (dummy);
          return RETROK;
        }
       else if (hstat.res == 0) /* No read error */
@@ -1954,7 +1893,7 @@ The sizes do not match (local %ld) -- retrieving.\n"), local_size);
                             tms, u->url, hstat.len, locf, count);
                }
              ++opt.numurls;
-             downloaded_increase (hstat.len);
+             total_downloaded_bytes += hstat.len;
 
              /* Remember that we downloaded the file for later ".orig" code. */
              if (*dt & ADDED_HTML_EXTENSION)
@@ -1963,7 +1902,7 @@ The sizes do not match (local %ld) -- retrieving.\n"), local_size);
                downloaded_file(FILE_DOWNLOADED_NORMALLY, locf);
              
              free_hstat (&hstat);
-             FREE_MAYBE (dummy);
+             xfree_null (dummy);
              return RETROK;
            }
          else if (hstat.len < hstat.contlen) /* meaning we lost the
@@ -1985,7 +1924,7 @@ The sizes do not match (local %ld) -- retrieving.\n"), local_size);
                         "%s URL:%s [%ld/%ld] -> \"%s\" [%d]\n",
                         tms, u->url, hstat.len, hstat.contlen, locf, count);
              ++opt.numurls;
-             downloaded_increase (hstat.len);
+             total_downloaded_bytes += hstat.len;
 
              /* Remember that we downloaded the file for later ".orig" code. */
              if (*dt & ADDED_HTML_EXTENSION)
@@ -1994,7 +1933,7 @@ The sizes do not match (local %ld) -- retrieving.\n"), local_size);
                downloaded_file(FILE_DOWNLOADED_NORMALLY, locf);
              
              free_hstat (&hstat);
-             FREE_MAYBE (dummy);
+             xfree_null (dummy);
              return RETROK;
            }
          else                  /* the same, but not accepted */
@@ -2155,7 +2094,7 @@ check_end (const char *p)
    it is not assigned to the FSF.  So I stuck it with strptime.  */
 
 time_t
-http_atotm (char *time_string)
+http_atotm (const char *time_string)
 {
   /* NOTE: Solaris strptime man page claims that %n and %t match white
      space, but that's not universally available.  Instead, we simply
@@ -2190,7 +2129,7 @@ http_atotm (char *time_string)
      GNU strptime does not have this problem because it recognizes
      both international and local dates.  */
 
-  for (i = 0; i < ARRAY_SIZE (time_formats); i++)
+  for (i = 0; i < countof (time_formats); i++)
     if (check_end (strptime (time_string, time_formats[i], &t)))
       return mktime_from_utc (&t);
 
@@ -2301,7 +2240,7 @@ extract_header_attr (const char *au, const char *attr_name, char **ret)
        ;
       if (!*ep)
        return -1;
-      FREE_MAYBE (*ret);
+      xfree_null (*ret);
       *ret = strdupdelim (cp, ep);
       return ep - au + 1;
     }
@@ -2320,8 +2259,8 @@ dump_hash (unsigned char *buf, const unsigned char *hash)
 
   for (i = 0; i < MD5_HASHLEN; i++, hash++)
     {
-      *buf++ = XDIGIT_TO_xchar (*hash >> 4);
-      *buf++ = XDIGIT_TO_xchar (*hash & 0xf);
+      *buf++ = XNUM_TO_digit (*hash >> 4);
+      *buf++ = XNUM_TO_digit (*hash & 0xf);
     }
   *buf = '\0';
 }
@@ -2352,15 +2291,15 @@ digest_authentication_encode (const char *au, const char *user,
       int i;
 
       au += skip_lws (au);
-      for (i = 0; i < ARRAY_SIZE (options); i++)
+      for (i = 0; i < countof (options); i++)
        {
          int skip = extract_header_attr (au, options[i].name,
                                          options[i].variable);
          if (skip < 0)
            {
-             FREE_MAYBE (realm);
-             FREE_MAYBE (opaque);
-             FREE_MAYBE (nonce);
+             xfree_null (realm);
+             xfree_null (opaque);
+             xfree_null (nonce);
              return NULL;
            }
          else if (skip)
@@ -2369,7 +2308,7 @@ digest_authentication_encode (const char *au, const char *user,
              break;
            }
        }
-      if (i == ARRAY_SIZE (options))
+      if (i == countof (options))
        {
          while (*au && *au != '=')
            au++;
@@ -2393,9 +2332,9 @@ digest_authentication_encode (const char *au, const char *user,
     }
   if (!realm || !nonce || !user || !passwd || !path || !method)
     {
-      FREE_MAYBE (realm);
-      FREE_MAYBE (opaque);
-      FREE_MAYBE (nonce);
+      xfree_null (realm);
+      xfree_null (opaque);
+      xfree_null (nonce);
       return NULL;
     }