diff --git a/netwerk/protocol/http/public/nsIHTTPChannel.idl b/netwerk/protocol/http/public/nsIHTTPChannel.idl index e71b916a3e34..770f78d7b24b 100644 --- a/netwerk/protocol/http/public/nsIHTTPChannel.idl +++ b/netwerk/protocol/http/public/nsIHTTPChannel.idl @@ -89,6 +89,8 @@ interface nsIHTTPChannel : nsIChannel attribute PRBool authTriedWithPrehost; + attribute string authRealm; + /* We need a way to switch off gzip, etc. compression on the fly so that SaveAs would work as expected. Eventually this needs to diff --git a/netwerk/protocol/http/src/nsAuthEngine.cpp b/netwerk/protocol/http/src/nsAuthEngine.cpp index ea12316823d5..c5075d7e749d 100644 --- a/netwerk/protocol/http/src/nsAuthEngine.cpp +++ b/netwerk/protocol/http/src/nsAuthEngine.cpp @@ -78,6 +78,11 @@ nsAuthEngine::GetAuthString(nsIURI* i_URI, char** o_AuthString) // mAuthList may have been cleared from logout if (!mAuthList) return NS_OK; + PRUint32 count=0; + (void)mAuthList->Count(&count); + if (count<=0) + return NS_OK; // not found + nsXPIDLCString host; rv = i_URI->GetHost(getter_Copies(host)); if (NS_FAILED(rv)) return rv; @@ -98,10 +103,6 @@ nsAuthEngine::GetAuthString(nsIURI* i_URI, char** o_AuthString) char *lastSlash = PL_strrchr(dir, '/'); if (lastSlash) lastSlash[1] = '\0'; - PRUint32 count=0; - (void)mAuthList->Count(&count); - if (count<=0) - return NS_OK; // not found for (PRInt32 i = count-1; i>=0; --i) { nsAuth* auth = (nsAuth*)mAuthList->ElementAt(i); @@ -167,6 +168,7 @@ nsAuthEngine::GetAuthString(nsIURI* i_URI, char** o_AuthString) nsresult nsAuthEngine::SetAuth(nsIURI* i_URI, const char* i_AuthString, + const char* i_Realm, PRBool bProxyAuth) { nsresult rv; @@ -209,7 +211,7 @@ nsAuthEngine::SetAuth(nsIURI* i_URI, CRTFREEIF(unescaped_AuthString); return rv; } - nsAuth* auth = new nsAuth(i_URI, unescaped_AuthString); + nsAuth* auth = new nsAuth(i_URI, unescaped_AuthString, nsnull, nsnull, i_Realm); CRTFREEIF(unescaped_AuthString); if (!auth) return NS_ERROR_OUT_OF_MEMORY; @@ -274,5 +276,52 @@ nsAuthEngine::SetProxyAuthString(const char* host, rv = mIOService->NewURI(spec.GetBuffer(), nsnull, getter_AddRefs(uri)); if (NS_FAILED(rv)) return rv; - return SetAuth(uri, i_AuthString, PR_TRUE); + return SetAuth(uri, i_AuthString, nsnull, PR_TRUE); +} + +nsresult +nsAuthEngine::GetAuthStringForRealm(nsIURI* i_URI, const char* i_Realm, char** o_AuthString) +{ + nsresult rv = NS_OK; + if (!o_AuthString) + return NS_ERROR_NULL_POINTER; + if (!i_Realm) + return NS_ERROR_NULL_POINTER; + *o_AuthString = nsnull; + // list may have been cleared by logout... + if (!mAuthList) + return NS_OK; + + PRUint32 count = 0; + mAuthList->Count(&count); + if (count <= 0) + return NS_OK; // not found + + nsXPIDLCString host; + rv = i_URI->GetHost(getter_Copies(host)); + if (NS_FAILED(rv)) return rv; + + PRInt32 port; + rv = i_URI->GetPort(&port); + if (NS_FAILED(rv)) return rv; + + for (PRInt32 i = count-1; i>=0; --i) + { + nsAuth* auth = (nsAuth*) mAuthList->ElementAt(i); + + nsXPIDLCString authHost; + PRInt32 authPort; + + auth->uri->GetHost(getter_Copies(authHost)); + auth->uri->GetPort(&authPort); + + if ((0 == nsCRT::strcasecmp(authHost, host)) && + (0 == nsCRT::strcasecmp(auth->realm, i_Realm)) && + (port == authPort)) + { + *o_AuthString = nsCRT::strdup(auth->encodedString); + return (!*o_AuthString) ? NS_ERROR_OUT_OF_MEMORY : NS_OK; + } + } + return NS_OK; // not found } diff --git a/netwerk/protocol/http/src/nsAuthEngine.h b/netwerk/protocol/http/src/nsAuthEngine.h index ce1d07958a4c..203de39e4f8d 100644 --- a/netwerk/protocol/http/src/nsAuthEngine.h +++ b/netwerk/protocol/http/src/nsAuthEngine.h @@ -63,6 +63,16 @@ public: PRInt32 port, const char* i_AuthString); + // Get an auth string for a particular host/port/realm + NS_IMETHOD GetAuthStringForRealm(nsIURI* i_URI, + const char* i_Realm, + char** o_AuthString); + + // Set an auth string for a particular host/port/realm + NS_IMETHOD SetAuthStringForRealm(nsIURI* i_URI, + const char* i_Realm, + const char* i_AuthString); + /* Expire all existing auth list entries including proxy auths. */ @@ -72,6 +82,7 @@ protected: NS_IMETHOD SetAuth(nsIURI* i_URI, const char* i_AuthString, + const char* i_Realm = nsnull, PRBool bProxyAuth = PR_FALSE); nsCOMPtr mAuthList; @@ -89,4 +100,10 @@ nsAuthEngine::SetAuthString(nsIURI* i_URI, const char* i_AuthString) return SetAuth(i_URI, i_AuthString); } +inline nsresult +nsAuthEngine::SetAuthStringForRealm(nsIURI* i_URI, const char* i_Realm, const char* i_AuthString) +{ + return SetAuth(i_URI, i_AuthString, i_Realm); +} + #endif /* _nsAuthEngine_h_ */ diff --git a/netwerk/protocol/http/src/nsHTTPChannel.cpp b/netwerk/protocol/http/src/nsHTTPChannel.cpp index 4247ac5c8723..fb8a0f53b21c 100644 --- a/netwerk/protocol/http/src/nsHTTPChannel.cpp +++ b/netwerk/protocol/http/src/nsHTTPChannel.cpp @@ -93,6 +93,7 @@ nsHTTPChannel::nsHTTPChannel(nsIURI* i_URL, nsHTTPHandler* i_Handler): mFiredOpenOnStartRequest(PR_FALSE), mFiredOpenOnStopRequest (PR_FALSE), mAuthTriedWithPrehost(PR_FALSE), + mAuthRealm(nsnull), mProxy(0), mProxyPort(-1), mProxyType(nsnull), @@ -139,6 +140,7 @@ nsHTTPChannel::~nsHTTPChannel() mPrompter = null_nsCOMPtr(); mResponseContext = null_nsCOMPtr(); mLoadGroup = null_nsCOMPtr(); + CRTFREEIF(mAuthRealm); CRTFREEIF(mProxy); CRTFREEIF(mProxyType); } @@ -847,6 +849,27 @@ nsHTTPChannel::GetAuthTriedWithPrehost(PRBool* oTried) return NS_ERROR_NULL_POINTER; } +NS_IMETHODIMP +nsHTTPChannel::SetAuthRealm(const char* aAuthRealm) +{ + CRTFREEIF(mAuthRealm); + if (aAuthRealm) + mAuthRealm = nsCRT::strdup(aAuthRealm); + return NS_OK; +} + +NS_IMETHODIMP +nsHTTPChannel::GetAuthRealm(char** aAuthRealm) +{ + if (aAuthRealm) + { + *aAuthRealm = nsCRT::strdup(mAuthRealm); + return (*aAuthRealm) ? NS_OK : NS_ERROR_OUT_OF_MEMORY; + } + else + return NS_ERROR_NULL_POINTER; +} + // nsIInterfaceRequestor method NS_IMETHODIMP nsHTTPChannel::GetInterface(const nsIID &anIID, void **aResult ) @@ -2029,7 +2052,7 @@ nsHTTPChannel::Authenticate(const char *iChallenge, PRBool iProxyAuth) if (NS_FAILED(rv)) return rv; //flush out existing records of this URI in authengine- - nsAuthEngine* pEngine; + nsAuthEngine* pEngine = nsnull; if (NS_SUCCEEDED(mHandler->GetAuthEngine(&pEngine))) { rv = pEngine->SetAuthString(mURI, 0); @@ -2067,6 +2090,7 @@ nsHTTPChannel::Authenticate(const char *iChallenge, PRBool iProxyAuth) nsXPIDLCString authString; nsCAutoString authLine; nsCOMPtr auth; + nsCAutoString authRealm; // multiple www-auth headers case // go thru each to see if we support that. @@ -2123,8 +2147,6 @@ nsHTTPChannel::Authenticate(const char *iChallenge, PRBool iProxyAuth) Throw a modal dialog box asking for username, password. Prefill (!?!) */ - if (!mPrompter) - return NS_ERROR_FAILURE; PRBool retval = PR_FALSE; //TODO localize it! @@ -2152,6 +2174,9 @@ nsHTTPChannel::Authenticate(const char *iChallenge, PRBool iProxyAuth) if (realm != end) { message.AppendWithConversion(realm, end - realm); foundRealm = PR_TRUE; + + // Remember this realm; we will set it as an attribute of the new channel. + authRealm.Assign(realm, end - realm); } } @@ -2159,44 +2184,74 @@ nsHTTPChannel::Authenticate(const char *iChallenge, PRBool iProxyAuth) if (!foundRealm) message.AppendWithConversion(authLine.GetBuffer()); - // get the hostname - nsXPIDLCString hostname; - if (NS_SUCCEEDED(mURI->GetHost(getter_Copies(hostname)))) + // but, if we did find a realm and this is the first time trying to + // authenticate this channel (indicated by mAuthRealm == NULL), then + // lookup the realm in the auth engine and try to authenticate using + // the response. + else if (!mAuthRealm) { - // TODO localize - message.AppendWithConversion(" at "); - message.AppendWithConversion(hostname); + // Get the authentication string for the realm. If not found, then + // authString will be NULL, and we will be forced to prompt. + if (pEngine) + pEngine->GetAuthStringForRealm(mURI, authRealm, getter_Copies(authString)); + +#ifdef DEBUG_darin + fprintf(stderr, "\n>>>>> Authentication for Realm: [realm=%s, auth=%s]\n\n", (const char*) authRealm, (const char*) authString); +#endif } - // Get url - nsXPIDLCString urlCString; - mURI->GetPrePath(getter_Copies(urlCString)); - - nsAutoString prePath; // XXX i18n - CopyASCIItoUCS2(nsLiteralCString( - NS_STATIC_CAST(const char*, urlCString)), prePath); - rv = mPrompter->PromptUsernameAndPassword(nsnull, - message.GetUnicode(), - prePath.GetUnicode(), - nsIPrompt::SAVE_PASSWORD_PERMANENTLY, - getter_Copies(userBuf), - getter_Copies(passwdBuf), - &retval); - if (NS_FAILED(rv) || !retval) // let it go on if we cancelled auth... + // Skip prompting if we already have an authentication string. + if (!authString || !*authString) + { + // Delay this check until we absolutely would need the prompter + if (!mPrompter) { + NS_WARNING("Failed to prompt for username/password: nsHTTPChannel::mPrompter == NULL"); + return NS_ERROR_FAILURE; + } + + // get the hostname + nsXPIDLCString hostname; + if (NS_SUCCEEDED(mURI->GetHost(getter_Copies(hostname)))) + { + // TODO localize + message.AppendWithConversion(" at "); + message.AppendWithConversion(hostname); + } + + // Get url + nsXPIDLCString urlCString; + mURI->GetPrePath(getter_Copies(urlCString)); + + nsAutoString prePath; // XXX i18n + CopyASCIItoUCS2(nsLiteralCString( + NS_STATIC_CAST(const char*, urlCString)), prePath); + rv = mPrompter->PromptUsernameAndPassword(nsnull, + message.GetUnicode(), + prePath.GetUnicode(), + nsIPrompt::SAVE_PASSWORD_PERMANENTLY, + getter_Copies(userBuf), + getter_Copies(passwdBuf), + &retval); + if (NS_FAILED(rv) || !retval) // let it go on if we cancelled auth... + return rv; + } + } + + // Skip authentication if we already have an authentication string. + if (!authString || !*authString) + { + if (!userBuf && interactionType == nsIAuthenticator::INTERACTION_STANDARD) { + /* can't proceed without at least a username, can we? */ + return NS_ERROR_FAILURE; + } + + if (NS_FAILED(rv) || + NS_FAILED(rv = auth->Authenticate(mURI, "http", authLine.GetBuffer(), + userBuf, passwdBuf, mPrompter, + getter_Copies(authString)))) return rv; } - if (!userBuf && interactionType == nsIAuthenticator::INTERACTION_STANDARD) { - /* can't proceed without at least a username, can we? */ - return NS_ERROR_FAILURE; - } - - if (NS_FAILED(rv) || - NS_FAILED(rv = auth->Authenticate(mURI, "http", authLine.GetBuffer(), - userBuf, passwdBuf, mPrompter, - getter_Copies(authString)))) - return rv; - #if defined(DEBUG_shaver) || defined(DEBUG_gagan) fprintf(stderr, "Auth string: %s\n", (const char *)authString); #endif @@ -2230,6 +2285,10 @@ nsHTTPChannel::Authenticate(const char *iChallenge, PRBool iProxyAuth) // Let it know that we have already tried prehost stuff... httpChannel->SetAuthTriedWithPrehost(PR_TRUE); + // Let it know that we are trying to access a realm + if (authRealm) + httpChannel->SetAuthRealm(authRealm); + if (mResponseDataListener) { // Fire the new request... @@ -2315,10 +2374,21 @@ nsHTTPChannel::ProcessStatusCode(void) { rv = GetRequestHeader(nsHTTPAtoms::Authorization, getter_Copies(authString)); - pEngine->SetAuthString(mURI, authString); +#ifdef DEBUG_darin + fprintf(stderr, "\n>>>>> Auth Accepted!! [realm=%s, auth=%s]\n\n", mAuthRealm, (const char*) authString); +#endif + if (mAuthRealm) + pEngine->SetAuthStringForRealm(mURI, mAuthRealm, authString); + else + pEngine->SetAuthString(mURI, authString); } else // clear out entry from single signon and our cache. { +#ifdef DEBUG_darin + rv = GetRequestHeader(nsHTTPAtoms::Authorization, + getter_Copies(authString)); + fprintf(stderr, "\n>>>>> Auth Rejected!! [realm=%s, auth=%s]\n\n", mAuthRealm, (const char*) authString); +#endif pEngine->SetAuthString(mURI, 0); NS_WITH_SERVICE(nsIWalletService, walletService, diff --git a/netwerk/protocol/http/src/nsHTTPChannel.h b/netwerk/protocol/http/src/nsHTTPChannel.h index c7b133b16cd7..6acb820d80a3 100644 --- a/netwerk/protocol/http/src/nsHTTPChannel.h +++ b/netwerk/protocol/http/src/nsHTTPChannel.h @@ -181,6 +181,7 @@ protected: And so we need to throw a dialog box! */ PRBool mAuthTriedWithPrehost; + char* mAuthRealm; char* mProxy; PRInt32 mProxyPort;