]> sjero.net Git - wget/blobdiff - src/http.c
[svn] Committed a bunch of different tweaks of mine.
[wget] / src / http.c
index 07621acc5971e6a1e5f4b6238b98a38016cff19c..8c36a255be52b5cd1de31c8335bcaada6cd7173c 100644 (file)
@@ -239,18 +239,13 @@ static int
 http_process_type (const char *hdr, void *arg)
 {
   char **result = (char **)arg;
-  char *p;
-
-  p = strrchr (hdr, ';');
-  if (p)
-    {
-      int len = p - hdr;
-      *result = (char *)xmalloc (len + 1);
-      memcpy (*result, hdr, len);
-      (*result)[len] = '\0';
-    }
-  else
-    *result = xstrdup (hdr);
+  /* Locate P on `;' or the terminating zero, whichever comes first. */
+  const char *p = strchr (hdr, ';');
+  if (!p)
+    p = hdr + strlen (hdr);
+  while (p > hdr && ISSPACE (*(p - 1)))
+    --p;
+  *result = strdupdelim (hdr, p);
   return 1;
 }
 
@@ -264,27 +259,28 @@ http_process_connection (const char *hdr, void *arg)
   return 1;
 }
 \f
-/* Persistent connections (pc).  Currently, we cache the most recently
-   used connection as persistent, provided that the HTTP server agrees
-   to make it such.  The persistence data is stored in the variables
+/* Persistent connections.  Currently, we cache the most recently used
+   connection as persistent, provided that the HTTP server agrees to
+   make it such.  The persistence data is stored in the variables
    below.  Ideally, it would be in a structure, and it should be
    possible to cache an arbitrary fixed number of these connections.
 
    I think the code is quite easy to extend in that direction.  */
 
-/* Whether the persistent connection is active. */
+/* Whether a persistent connection is active. */
 static int pc_active_p;
 
-/* Host and port of the last persistent connection. */
+/* Host and port of currently active persistent connection. */
 static unsigned char pc_last_host[4];
 static unsigned short pc_last_port;
 
-/* File descriptor of the last persistent connection. */
+/* File descriptor of the currently active persistent connection. */
 static int pc_last_fd;
 
 /* Mark the persistent connection as invalid.  This is used by the
    CLOSE_* macros after they forcefully close a registered persistent
-   connection.  */
+   connection.  This does not close the file descriptor -- it is left
+   to the caller to do that.  (Maybe it should, though.)  */
 
 static void
 invalidate_persistent (void)
@@ -343,16 +339,28 @@ static int
 persistent_available_p (const char *host, unsigned short port)
 {
   unsigned char this_host[4];
+  /* First, check whether a persistent connection is active at all.  */
   if (!pc_active_p)
     return 0;
+  /* Second, check if the active connection pertains to the correct
+     (HOST, PORT) ordered pair.  */
   if (port != pc_last_port)
     return 0;
   if (!store_hostaddress (this_host, host))
     return 0;
   if (memcmp (pc_last_host, this_host, 4))
     return 0;
+  /* Third: check whether the connection is still open.  This is
+     important because most server implement a liberal (short) timeout
+     on persistent connections.  Wget can of course always reconnect
+     if the connection doesn't work out, but it's nicer to know in
+     advance.  This test is a logical followup of the first test, but
+     is "expensive" and therefore placed at the end of the list.  */
   if (!test_socket_open (pc_last_fd))
     {
+      /* 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);
       invalidate_persistent ();
       return 0;
@@ -422,6 +430,11 @@ static int known_authentication_scheme_p PARAMS ((const char *));
 
 static time_t http_atotm PARAMS ((char *));
 
+#define BEGINS_WITH(line, string_constant)                             \
+  (!strncasecmp (line, string_constant, sizeof (string_constant) - 1)  \
+   && (ISSPACE (line[sizeof (string_constant) - 1])                    \
+       || !line[sizeof (string_constant) - 1]))
+
 /* Retrieve a document through HTTP protocol.  It recognizes status
    code, and correctly handles redirections.  It closes the network
    socket.  If it receives an error from the functions below it, it
@@ -444,9 +457,8 @@ gethttp (struct urlinfo *u, struct http_stat *hs, int *dt)
   char *authenticate_h;
   char *proxyauth;
   char *all_headers;
-  char *host_port;
+  char *port_maybe;
   char *request_keep_alive;
-  int host_port_len;
   int sock, hcount, num_written, all_length, remport, statcode;
   long contlen, contrange;
   struct urlinfo *ou;
@@ -474,11 +486,13 @@ gethttp (struct urlinfo *u, struct http_stat *hs, int *dt)
   authenticate_h = 0;
   auth_tried_already = 0;
 
-  inhibit_keep_alive = (u->proxy != NULL);
+  inhibit_keep_alive = (!opt.http_keep_alive || u->proxy != NULL);
 
  again:
   /* We need to come back here when the initial attempt to retrieve
-     without authorization header fails.  */
+     without authorization header fails.  (Expected to happen at least
+     for the Digest authorization scheme.)  */
+
   keep_alive = 0;
   http_keep_alive_1 = http_keep_alive_2 = 0;
 
@@ -589,10 +603,37 @@ gethttp (struct urlinfo *u, struct http_stat *hs, int *dt)
   passwd = passwd ? passwd : opt.http_passwd;
 
   wwwauth = NULL;
-  if (authenticate_h && user && passwd)
+  if (user && passwd)
     {
-      wwwauth = create_authorization_line (authenticate_h, user, passwd,
-                                          command, ou->path);
+      if (!authenticate_h)
+       {
+         /* We have the username and the password, but haven't tried
+            any authorization yet.  Let's see if the "Basic" method
+            works.  If not, we'll come back here and construct a
+            proper authorization method with the right challenges.
+
+            If we didn't employ this kind of logic, every URL that
+            requires authorization would have to be processed twice,
+            which is very suboptimal and generates a bunch of false
+            "unauthorized" errors in the server log.
+
+            #### But this logic also has a serious problem when used
+            with stronger authentications: we *first* transmit the
+            username and the password in clear text, and *then*
+            attempt a stronger authentication scheme.  That cannot be
+            right!  We are only fortunate that almost everyone still
+            uses the `Basic' scheme anyway.
+
+            There should be an option to prevent this from happening,
+            for those who use strong authentication schemes and value
+            their passwords.  */
+         wwwauth = basic_authentication_encode (user, passwd, "Authorization");
+       }
+      else
+       {
+         wwwauth = create_authorization_line (authenticate_h, user, passwd,
+                                              command, ou->path);
+       }
     }
 
   proxyauth = NULL;
@@ -623,15 +664,12 @@ gethttp (struct urlinfo *u, struct http_stat *hs, int *dt)
   remhost = ou->host;
   remport = ou->port;
 
-  if (remport == 80)
+  /* String of the form :PORT.  Used only for non-standard ports. */
+  port_maybe = NULL;
+  if (remport != 80)
     {
-      host_port = NULL;
-      host_port_len = 0;
-    }
-  else
-    {
-      host_port = (char *)alloca (numdigit (remport) + 2);
-      host_port_len = sprintf (host_port, ":%d", remport);
+      port_maybe = (char *)alloca (numdigit (remport) + 2);
+      sprintf (port_maybe, ":%d", remport);
     }
 
   if (!inhibit_keep_alive)
@@ -642,7 +680,8 @@ gethttp (struct urlinfo *u, struct http_stat *hs, int *dt)
   /* Allocate the memory for the request.  */
   request = (char *)alloca (strlen (command) + strlen (path)
                            + strlen (useragent)
-                           + strlen (remhost) + host_port_len
+                           + strlen (remhost)
+                           + (port_maybe ? strlen (port_maybe) : 0)
                            + strlen (HTTP_ACCEPT)
                            + (request_keep_alive
                               ? strlen (request_keep_alive) : 0)
@@ -661,7 +700,7 @@ Host: %s%s\r\n\
 Accept: %s\r\n\
 %s%s%s%s%s%s%s\r\n",
           command, path, useragent, remhost,
-          host_port ? host_port : "",
+          port_maybe ? port_maybe : "",
           HTTP_ACCEPT,
           request_keep_alive ? request_keep_alive : "",
           referer ? referer : "",
@@ -894,6 +933,7 @@ Accept: %s\r\n\
        {
          /* If we have tried it already, then there is not point
             retrying it.  */
+       failed:
          logputs (LOG_NOTQUIET, _("Authorization failed.\n"));
          free (authenticate_h);
          return AUTHFAILED;
@@ -904,6 +944,13 @@ Accept: %s\r\n\
          logputs (LOG_NOTQUIET, _("Unknown authentication scheme.\n"));
          return AUTHFAILED;
        }
+      else if (BEGINS_WITH (authenticate_h, "Basic"))
+       {
+         /* The authentication scheme is basic, the one we try by
+             default, and it failed.  There's no sense in trying
+             again.  */
+         goto failed;
+       }
       else
        {
          auth_tried_already = 1;
@@ -1563,7 +1610,7 @@ mktime_from_utc (struct tm *t)
    "^ *(GMT|[+-][0-9]|$)", 0 otherwise.  P being NULL (a valid result of
    strptime()) is considered a failure and 0 is returned.  */
 static int
-check_end (char *p)
+check_end (const char *p)
 {
   if (!p)
     return 0;
@@ -1911,7 +1958,7 @@ username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"",
 #endif /* USE_DIGEST */
 
 
-#define HACK_O_MATIC(line, string_constant)                            \
+#define BEGINS_WITH(line, string_constant)                             \
   (!strncasecmp (line, string_constant, sizeof (string_constant) - 1)  \
    && (ISSPACE (line[sizeof (string_constant) - 1])                    \
        || !line[sizeof (string_constant) - 1]))
@@ -1919,12 +1966,12 @@ username=\"%s\", realm=\"%s\", nonce=\"%s\", uri=\"%s\", response=\"%s\"",
 static int
 known_authentication_scheme_p (const char *au)
 {
-  return HACK_O_MATIC (au, "Basic")
-    || HACK_O_MATIC (au, "Digest")
-    || HACK_O_MATIC (au, "NTLM");
+  return BEGINS_WITH (au, "Basic")
+    || BEGINS_WITH (au, "Digest")
+    || BEGINS_WITH (au, "NTLM");
 }
 
-#undef HACK_O_MATIC
+#undef BEGINS_WITH
 
 /* Create the HTTP authorization request header.  When the
    `WWW-Authenticate' response header is seen, according to the