Index: lib/http.c
===================================================================
RCS file: /repository/curl/lib/http.c,v
retrieving revision 1.204
diff -u -r1.204 http.c
--- lib/http.c	14 Mar 2004 18:15:04 -0000	1.204
+++ lib/http.c	30 Mar 2004 01:19:28 -0000
@@ -188,23 +188,23 @@
   }
 }
 
-/*
+/**
  * Setup the authentication headers for the host/proxy and the correct
- * authentication method.
+ * authentication method.  @p conn->data->state.authdone set to TRUE
+ * when authentication is done.
+ *
+ * @param conn all information about the current connection
  */
-
 static CURLcode http_auth_headers(struct connectdata *conn,
                                   char *request,
-                                  char *path,
-                                  bool *ready) /* set TRUE when the auth phase
-                                           is done and ready to do the *actual*
-                                           request */
+                                  char *path)
 {
   CURLcode result = CURLE_OK;
   struct SessionHandle *data = conn->data;
   char *auth=NULL;
 
-  *ready = FALSE; /* default is no */
+  curlassert(data);
+  data->state.authdone = FALSE; /* default is no */
 
   if(!data->state.authstage) {
     if(conn->bits.httpproxy && conn->bits.proxy_user_passwd)
@@ -212,7 +212,7 @@
     else if(conn->bits.user_passwd)
       Curl_http_auth_stage(data, 401);
     else {
-      *ready = TRUE;
+      data->state.authdone = TRUE;
       return CURLE_OK; /* no authentication with no user or password */
     }
   }
@@ -229,7 +229,7 @@
 #ifdef USE_SSLEAY
       if(data->state.authwant == CURLAUTH_NTLM) {
         auth=(char *)"NTLM";
-        result = Curl_output_ntlm(conn, TRUE, ready);
+        result = Curl_output_ntlm(conn, TRUE);
         if(result)
           return result;
       }
@@ -244,7 +244,7 @@
           if(result)
             return result;
         }
-        *ready = TRUE;
+        data->state.authdone = TRUE;
         /* Switch to web authentication after proxy authentication is done */
         Curl_http_auth_stage(data, 401);
       }
@@ -262,14 +262,14 @@
         result = Curl_output_negotiate(conn);
         if (result)
           return result;
-        *ready = TRUE;
+        data->state.authdone = TRUE;
       }
       else
 #endif
 #ifdef USE_SSLEAY
       if(data->state.authwant == CURLAUTH_NTLM) {
         auth=(char *)"NTLM";
-        result = Curl_output_ntlm(conn, FALSE, ready);
+        result = Curl_output_ntlm(conn, FALSE);
         if(result)
           return result;
       }
@@ -284,7 +284,7 @@
                                       (unsigned char *)path);
           if(result)
             return result;
-          *ready = TRUE;
+          data->state.authdone = TRUE;
         }
         else if(data->state.authwant == CURLAUTH_BASIC) {/* Basic */
           if(conn->bits.user_passwd &&
@@ -295,7 +295,7 @@
               return result;
           }
           /* basic is always ready */
-          *ready = TRUE;
+          data->state.authdone = TRUE;
         }
       }
       if(auth)
@@ -304,7 +304,7 @@
     }
   }
   else
-    *ready = TRUE;
+    data->state.authdone = TRUE;
 
   return result;
 }
@@ -438,6 +438,83 @@
   return CURLE_OK;
 }
 
+/**
+ * determine whether an http response has gotten us into an
+ * error state or not.
+ *
+ * @param conn all information about the current connection
+ *
+ * @retval 0 communications should continue
+ *
+ * @retval 1 communications should not continue
+ */
+int Curl_http_should_fail(struct connectdata *conn)
+{
+  struct SessionHandle *data;
+  struct Curl_transfer_keeper *k;
+
+  curlassert(conn);
+  data = conn->data;
+  curlassert(data);
+
+  /*
+  ** For readability
+  */
+  k = &conn->keep;
+
+  /*
+  ** If we haven't been asked to fail on error,
+  ** don't fail.
+  */
+  if (!data->set.http_fail_on_error)
+    return 0;
+
+  /*
+  ** Any code < 400 is never terminal.
+  */
+  if (k->httpcode < 400)
+    return 0;
+
+  /*
+  ** Any code >= 400 that's not 401 or 407 is always
+  ** a terminal error
+  */
+  if ((k->httpcode != 401) &&
+      (k->httpcode != 407))
+    return 1;
+
+  /*
+  ** All we have left to deal with is 401 and 407
+  */
+  curlassert((k->httpcode == 401) || (k->httpcode == 407));
+
+  /*
+  ** Examine the current authentication state to see if this
+  ** is an error.  The idea is for this function to get
+  ** called after processing all the headers in a response
+  ** message.  So, if we've been to asked to authenticate a
+  ** particular stage, and we've done it, we're OK.  But, if
+  ** we're already completely authenticated, it's not OK to
+  ** get another 401 or 407.
+  **
+  ** It is possible for authentication to go stale such that
+  ** the client needs to reauthenticate.  Once that info is
+  ** available, use it here.
+  */
+  infof(data,"%s: authstage = %d\n",__FUNCTION__,data->state.authstage);
+  infof(data,"%s: httpcode = %d\n",__FUNCTION__,k->httpcode);
+  infof(data,"%s: authdone = %d\n",__FUNCTION__,data->state.authdone);
+
+  if (data->state.authstage &&
+      (data->state.authstage == k->httpcode))
+    return data->state.authdone;
+
+  /*
+  ** Either we're not authenticating, or we're supposed to
+  ** be authenticating something else.  This is an error.
+  */
+  return 1;
+}
 
 /* fread() emulation to provide POST and/or request data */
 static size_t readmoredata(char *buffer,
@@ -760,9 +837,6 @@
   infof(data, "Establish HTTP proxy tunnel to %s:%d\n", hostname, remote_port);
 
   do {
-    bool auth; /* we don't really have to know when the auth phase is done,
-                  but this variable will be set to true then */
-
     if(conn->newurl) {
       /* This only happens if we've looped here due to authentication reasons,
          and we don't really use the newly cloned URL here then. Just free()
@@ -776,7 +850,7 @@
       return CURLE_OUT_OF_MEMORY;
 
     /* Setup the proxy-authorization header, if any */
-    result = http_auth_headers(conn, (char *)"CONNECT", host_port, &auth);
+    result = http_auth_headers(conn, (char *)"CONNECT", host_port);
     if(CURLE_OK == result) {
 
       /* OK, now send the connect request to the proxy */
@@ -1066,7 +1140,6 @@
   const char *te = ""; /* tranfer-encoding */
   char *ptr;
   char *request;
-  bool authdone=TRUE; /* if the authentication phase is done */
 
   if(!conn->proto.http) {
     /* Only allocate this struct if we don't already have it! */
@@ -1105,7 +1178,7 @@
   }
 
   /* setup the authentication headers */
-  result = http_auth_headers(conn, request, ppath, &authdone);
+  result = http_auth_headers(conn, request, ppath);
   if(result)
     return result;
 
@@ -1535,8 +1608,8 @@
         /* setup variables for the upcoming transfer */
         result = Curl_Transfer(conn, FIRSTSOCKET, -1, TRUE,
                                &http->readbytecount,
-                               authdone?FIRSTSOCKET:-1,
-                               authdone?&http->writebytecount:NULL);
+                               data->state.authdone?FIRSTSOCKET:-1,
+                               data->state.authdone?&http->writebytecount:NULL);
       if(result) {
         Curl_formclean(http->sendit); /* free that whole lot */
         return result;
@@ -1574,8 +1647,8 @@
         /* prepare for transfer */
         result = Curl_Transfer(conn, FIRSTSOCKET, -1, TRUE,
                                &http->readbytecount,
-                               authdone?FIRSTSOCKET:-1,
-                               authdone?&http->writebytecount:NULL);
+                               data->state.authdone?FIRSTSOCKET:-1,
+                               data->state.authdone?&http->writebytecount:NULL);
       if(result)
         return result;
       break;
@@ -1606,7 +1679,7 @@
 
       if(data->set.postfields) {
 
-        if(authdone && (postsize < (100*1024))) {
+        if(data->state.authdone && (postsize < (100*1024))) {
           /* If we're not done with the authentication phase, we don't expect
              to actually send off any data yet. Hence, we delay the sending of
              the body until we receive that friendly 100-continue response */
@@ -1642,7 +1715,7 @@
           /* set the upload size to the progress meter */
           Curl_pgrsSetUploadSize(data, http->postsize);
 
-          if(!authdone && !checkheaders(data, "Expect:")) {
+          if(!data->state.authdone && !checkheaders(data, "Expect:")) {
             /* if not disabled explicitly we add a Expect: 100-continue to the
                headers which actually speeds up post operations (as there is
                one packet coming back from the web server) */
Index: lib/http.h
===================================================================
RCS file: /repository/curl/lib/http.h,v
retrieving revision 1.20
diff -u -r1.20 http.h
--- lib/http.h	7 Jan 2004 09:19:35 -0000	1.20
+++ lib/http.h	30 Mar 2004 01:19:28 -0000
@@ -42,9 +42,13 @@
 void Curl_httpchunk_init(struct connectdata *conn);
 CHUNKcode Curl_httpchunk_read(struct connectdata *conn, char *datap,
                               ssize_t length, ssize_t *wrote);
+
+/* These functions are in http.c */
 void Curl_http_auth_stage(struct SessionHandle *data, int stage);
 CURLcode Curl_http_auth(struct connectdata *conn,
                         int httpcode, char *header);
 void Curl_http_auth_act(struct connectdata *conn);
+
+int Curl_http_should_fail(struct connectdata *conn);
 #endif
 #endif
Index: lib/http_ntlm.c
===================================================================
RCS file: /repository/curl/lib/http_ntlm.c,v
retrieving revision 1.29
diff -u -r1.29 http_ntlm.c
--- lib/http_ntlm.c	22 Mar 2004 13:51:46 -0000	1.29
+++ lib/http_ntlm.c	30 Mar 2004 01:19:28 -0000
@@ -277,8 +277,7 @@
 
 /* this is for creating ntlm header output */
 CURLcode Curl_output_ntlm(struct connectdata *conn,
-                          bool proxy,
-                          bool *ready)
+                          bool proxy)
 {
   const char *domain=""; /* empty */
   const char *host=""; /* empty */
@@ -300,7 +299,9 @@
   /* point to the correct struct with this */
   struct ntlmdata *ntlm;
 
-  *ready = FALSE;
+  curlassert(conn);
+  curlassert(conn->data);
+  conn->data->state.authdone = FALSE;
 
   if(proxy) {
     allocuserpwd = &conn->allocptr.proxyuserpwd;
@@ -562,7 +563,7 @@
       return CURLE_OUT_OF_MEMORY; /* FIX TODO */
 
     ntlm->state = NTLMSTATE_TYPE3; /* we sent a type-3 */
-    *ready = TRUE;
+    conn->data->state.authdone = TRUE;
 
     /* Switch to web authentication after proxy authentication is done */
     if (proxy)
@@ -577,7 +578,7 @@
       free(*allocuserpwd);
       *allocuserpwd=NULL;
     }
-    *ready = TRUE;
+    conn->data->state.authdone = TRUE;
     break;
   }
 
Index: lib/http_ntlm.h
===================================================================
RCS file: /repository/curl/lib/http_ntlm.h,v
retrieving revision 1.6
diff -u -r1.6 http_ntlm.h
--- lib/http_ntlm.h	7 Jan 2004 09:19:35 -0000	1.6
+++ lib/http_ntlm.h	30 Mar 2004 01:19:28 -0000
@@ -36,7 +36,7 @@
 CURLntlm Curl_input_ntlm(struct connectdata *conn, bool proxy, char *header);
 
 /* this is for creating ntlm header output */
-CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy, bool *ready);
+CURLcode Curl_output_ntlm(struct connectdata *conn, bool proxy);
 
 void Curl_ntlm_cleanup(struct SessionHandle *data);
 
Index: lib/transfer.c
===================================================================
RCS file: /repository/curl/lib/transfer.c,v
retrieving revision 1.213
diff -u -r1.213 transfer.c
--- lib/transfer.c	22 Mar 2004 22:38:12 -0000	1.213
+++ lib/transfer.c	30 Mar 2004 01:19:31 -0000
@@ -576,9 +576,21 @@
                 data->info.httpcode = k->httpcode;
                 data->info.httpversion = k->httpversion;
 
-                /* 404 -> URL not found! */
+                /*
+                ** This code executes as part of processing
+                ** the header.  As a result, it's not
+                ** totally clear how to interpret the
+                ** response code yet as that depends on what
+                ** other headers may be present.  401 and
+                ** 407 may be errors, but may be OK
+                ** depending on how authentication is
+                ** working.  Other codes are definitely
+                ** errors, so give up here.
+                */
                 if (data->set.http_fail_on_error &&
-                    (k->httpcode >= 400)) {
+                    (k->httpcode >= 400) &&
+                    (k->httpcode != 401) &&
+                    (k->httpcode != 407)) {
                   /* If we have been told to fail hard on HTTP-errors,
                      here is the check for that: */
                   /* serious error, go home! */
@@ -873,6 +885,16 @@
 
         }                       /* end if header mode */
 
+        /*
+        ** Now that all of the headers have been parsed, see
+        ** if we should give up and return an error.
+        */
+        if (Curl_http_should_fail(conn)) {
+          failf (data, "The requested URL returned error: %d",
+                 k->httpcode);
+          return CURLE_HTTP_RETURNED_ERROR;
+        }
+
         /* This is not an 'else if' since it may be a rest from the header
            parsing, where the beginning of the buffer is headers and the end
            is non-headers. */
Index: lib/urldata.h
===================================================================
RCS file: /repository/curl/lib/urldata.h,v
retrieving revision 1.207
diff -u -r1.207 urldata.h
--- lib/urldata.h	25 Mar 2004 13:37:19 -0000	1.207
+++ lib/urldata.h	30 Mar 2004 01:19:35 -0000
@@ -719,6 +719,8 @@
                      depending on authstage) */
   long authavail; /* what the server reports */
 
+  bool authdone; /* TRUE when the auth phase is done and ready
+                    to do the *actual* request */
 #ifdef USE_ARES
   ares_channel areschannel; /* for name resolves */
 #endif

