Fix SF bug 468948 & 451295: urllib2 authentication problems

Fix contributed by Jeffrey C. Ollie.

I haven't tested the fix because the situation is non-trivial to
reproduce.

The basic solution is to get rid of the __current_realm attribute of
authentication handlers.  Instead, prevent infinite retries by
checking for the presence of an Authenticate: header in the request
object that exactly matches the Authenticate: header that would be
added.

The problem prevent authentication from working correctly in the
presence of retries.

Ollie mentioned that digest authentication has the same problem and I
applied the same solution there.
diff --git a/Lib/urllib2.py b/Lib/urllib2.py
index 3f01554..ffd73b8 100644
--- a/Lib/urllib2.py
+++ b/Lib/urllib2.py
@@ -578,11 +578,6 @@
             password_mgr = HTTPPasswordMgr()
         self.passwd = password_mgr
         self.add_password = self.passwd.add_password
-        self.__current_realm = None
-        # if __current_realm is not None, then the server must have
-        # refused our name/password and is asking for authorization
-        # again.  must be careful to set it to None on successful
-        # return.
 
     def http_error_auth_reqed(self, authreq, host, req, headers):
         # XXX could be multiple headers
@@ -595,26 +590,20 @@
                     return self.retry_http_basic_auth(host, req, realm)
 
     def retry_http_basic_auth(self, host, req, realm):
-        if self.__current_realm is None:
-            self.__current_realm = realm
-        else:
-            self.__current_realm = realm
-            return None
         user,pw = self.passwd.find_user_password(realm, host)
         if pw:
             raw = "%s:%s" % (user, pw)
-            auth = base64.encodestring(raw).strip()
-            req.add_header(self.header, 'Basic %s' % auth)
-            resp = self.parent.open(req)
-            self.__current_realm = None
-            return resp
+            auth = 'Basic %s' % base64.encodestring(raw).strip()
+            if req.headers.get(self.auth_header, None) == auth:
+                return None
+            req.add_header(self.auth_header, auth)
+            return self.parent.open(req)
         else:
-            self.__current_realm = None
             return None
 
 class HTTPBasicAuthHandler(AbstractBasicAuthHandler, BaseHandler):
 
-    header = 'Authorization'
+    auth_header = 'Authorization'
 
     def http_error_401(self, req, fp, code, msg, headers):
         host = urlparse.urlparse(req.get_full_url())[1]
@@ -624,7 +613,7 @@
 
 class ProxyBasicAuthHandler(AbstractBasicAuthHandler, BaseHandler):
 
-    header = 'Proxy-Authorization'
+    auth_header = 'Proxy-Authorization'
 
     def http_error_407(self, req, fp, code, msg, headers):
         host = req.get_host()
@@ -639,10 +628,9 @@
             passwd = HTTPPasswordMgr()
         self.passwd = passwd
         self.add_password = self.passwd.add_password
-        self.__current_realm = None
 
     def http_error_auth_reqed(self, authreq, host, req, headers):
-        authreq = headers.get(self.header, None)
+        authreq = headers.get(self.auth_header, None)
         if authreq:
             kind = authreq.split()[0]
             if kind == 'Digest':
@@ -653,9 +641,11 @@
         chal = parse_keqv_list(parse_http_list(challenge))
         auth = self.get_authorization(req, chal)
         if auth:
-            req.add_header(self.header, 'Digest %s' % auth)
+            auth_val = 'Digest %s' % auth
+            if req.headers.get(self.auth_header, None) == auth_val:
+                return None
+            req.add_header(self.auth_header, auth_val)
             resp = self.parent.open(req)
-            self.__current_realm = None
             return resp
 
     def get_authorization(self, req, chal):
@@ -669,12 +659,6 @@
         except KeyError:
             return None
 
-        if self.__current_realm is None:
-            self.__current_realm = realm
-        else:
-            self.__current_realm = realm
-            return None
-
         H, KD = self.get_algorithm_impls(algorithm)
         if H is None:
             return None