httpfilters/httpfilterauthentication/Src/HttpFilterDigestAuthentication.cpp
changeset 0 b16258d2340f
child 8 73e95e652591
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/httpfilters/httpfilterauthentication/Src/HttpFilterDigestAuthentication.cpp	Tue Feb 02 01:09:52 2010 +0200
@@ -0,0 +1,1096 @@
+/*
+* Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:  Digest Authentication filter
+*
+*/
+
+
+
+// INCLUDE FILES
+#include <e32std.h>
+#include <e32math.h>
+#include <hash.h>
+#include <http/rhttpsession.h>
+#include <http/rhttptransaction.h>
+#include <http/rhttpheaders.h>
+#include <httpstringconstants.h>
+#include <httperr.h>
+
+// User Includes
+#include "HttpFilterDigestAuthentication.h"
+#include "HttpFilterAuthenticationLogger.h"
+
+// EXTERNAL DATA STRUCTURES
+
+// EXTERNAL FUNCTION PROTOTYPES
+
+// CONSTANTS
+
+// MACROS
+
+// LOCAL CONSTANTS AND MACROS
+
+// Length of a digest hash when represented in hex
+const TInt KHashLength = 32;
+// Length of a digest hash before converting to hex.
+const TInt KRawHashLength = 16;
+// Length of nonce-count
+const TInt KNonceCountLength = 8;
+
+_LIT8( KMd5AlgorithmStr, "MD5" );
+_LIT8( KMd5SessAlgorithmStr, "MD5-sess" );
+_LIT8( KQopAuthStr, "auth" );
+_LIT8( KQopAuthIntStr, "auth-int" );
+_LIT8( KAuthenticationInfoStr, "Authentication-Info" );
+_LIT8( KColon, ":" );
+
+// MODULE DATA STRUCTURES
+
+// LOCAL FUNCTION PROTOTYPES
+
+// FORWARD DECLARATIONS
+
+// ============================= LOCAL FUNCTIONS ===============================
+
+// -----------------------------------------------------------------------------
+// PanicDigestAuthenticationFilter
+// Panic
+// Returns: void
+// -----------------------------------------------------------------------------
+//
+void PanicDigestAuthenticationFilter( TInt aErr = 0 )
+    {
+    User::Panic( _L( "HTTP FILTER DIGEST AUTHENTICATION" ), aErr );
+    }
+
+// ============================ MEMBER FUNCTIONS ===============================
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::CHttpFilterDigestAuthentication
+// C++ default constructor can NOT contain any code, that
+// might leave.
+// -----------------------------------------------------------------------------
+//
+CHttpFilterDigestAuthentication::CHttpFilterDigestAuthentication( RHTTPSession* aSession, TBool aIsProxy )
+    : CHttpFilterAuthentication( aSession, aIsProxy )
+    {
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::ConstructL
+// Symbian 2nd phase constructor can leave.
+// -----------------------------------------------------------------------------
+//
+void CHttpFilterDigestAuthentication::ConstructL( RHTTPSession aSession )
+    {
+    AUTHLOGGER_ENTERFN( "ConstructL" );
+    CHttpFilterAuthentication::ConstructL( aSession );
+    
+    iOpaqueStr = iStringPool.StringF( HTTP::EOpaque, RHTTPSession::GetTable() );
+    iNonceStr = iStringPool.StringF( HTTP::ENonce, RHTTPSession::GetTable() );
+    iQopStr = iStringPool.StringF( HTTP::EQop, RHTTPSession::GetTable() );
+    iStaleStr = iStringPool.StringF( HTTP::EStale, RHTTPSession::GetTable() );
+    
+    iMd5Str = iStringPool.OpenFStringL( KMd5AlgorithmStr );
+    iMd5SessStr = iStringPool.OpenFStringL( KMd5SessAlgorithmStr );
+    iQopAuthStr = iStringPool.OpenFStringL( KQopAuthStr );
+    iAuthInfo = iStringPool.OpenFStringL( KAuthenticationInfoStr );
+
+    aSession.FilterCollection().AddFilterL( *this,
+                                            THTTPEvent::EGotResponseHeaders,
+                                            iAuthInfo,
+                                            HTTPStatus::EOk,
+                                            EStatusCodeHandler + 1,
+                                            iStringPool.StringF( HTTP::EAuthentication, RHTTPSession::GetTable() ) );
+
+    AUTHLOGGER_WRITE( "CMD5::NewL" );
+    iMD5Calculator = CMD5::NewL();
+
+    AUTHLOGGER_LEAVEFN( "ConstructL" );
+    }
+
+// Destructor
+CHttpFilterDigestAuthentication::~CHttpFilterDigestAuthentication()
+    {
+    iQopAuthStr.Close();
+    iMd5SessStr.Close();
+    iMd5Str.Close();
+    iAuthInfo.Close();
+
+    delete iMD5Calculator;
+
+    // just in case...
+    for( TInt i = 0; i < iDCredentials.Count(); ++i )
+        {
+        DRemoveCredentialsFromList( 0 );
+        }
+
+    iDCredentials.Close();
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::InstallFilterL
+// Two-phased constructor. This function replaces NewL.
+// -----------------------------------------------------------------------------
+//
+CHttpFilterDigestAuthentication* CHttpFilterDigestAuthentication::InstallFilterL( TAny* aAuthenticationParams )
+    {
+    AUTHLOGGER_CREATE;
+
+    __ASSERT_DEBUG( aAuthenticationParams != NULL, PanicDigestAuthenticationFilter() );
+    TAuthenticationParams* authParams = REINTERPRET_CAST( TAuthenticationParams*, aAuthenticationParams );
+    __ASSERT_DEBUG( authParams->iSession != NULL, PanicDigestAuthenticationFilter() );
+    CHttpFilterDigestAuthentication* filter = new ( ELeave ) CHttpFilterDigestAuthentication( authParams->iSession, authParams->iIsProxy );
+    CleanupStack::PushL( filter );
+    filter->ConstructL( *( authParams->iSession ) );
+    CleanupStack::Pop( filter );
+    return filter;
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::MHFRunL
+// Process a transaction event.
+// -----------------------------------------------------------------------------
+//
+void CHttpFilterDigestAuthentication::MHFRunL( RHTTPTransaction aTransaction,
+        const THTTPEvent& aEvent )
+    {
+    switch ( aEvent.iStatus )
+        {
+
+        case THTTPEvent::ESubmit:
+            {
+            DSubmitL( aTransaction );
+            break;
+            }
+
+        case THTTPEvent::EGotResponseHeaders:
+            {
+            if( aTransaction.Response().StatusCode() == HTTPStatus::EOk )
+                {
+                ParseAuthenticationInfoL( aTransaction );
+                }
+            else
+                {
+                DHandleTransactionL( aTransaction );
+                }
+            break;
+            }
+
+        default:
+            {
+            __ASSERT_DEBUG( EFalse, PanicDigestAuthenticationFilter() );
+            break;
+            }
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::ParseAuthenticationInfoL
+// -----------------------------------------------------------------------------
+//
+void CHttpFilterDigestAuthentication::ParseAuthenticationInfoL( RHTTPTransaction /*aTransaction*/ )
+    {
+    // TODO: parse and store nc-value
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::HandleTransactionL
+// Provides a central point for all Transaction handling.
+// -----------------------------------------------------------------------------
+//
+void CHttpFilterDigestAuthentication::DHandleTransactionL( RHTTPTransaction aTransaction )
+    {
+    AUTHLOGGER_ENTERFN( "DHandleTransactionL" );
+
+    // Find if we have credentials in the transaction's properties, and remove them from the list
+    // (but not from the properties)
+    // Find if there are WWWAuthenticate or ProxyAuthenticate headers in the request,
+    // and replace the realm property with what came from the request.
+    THTTPHdrVal headerVal;
+    TInt cred = KErrNotFound;
+    TBool doDigest = EFalse;
+    TBool stale( EFalse );
+
+    if ( aTransaction.PropertySet().Property( iRealmStr, headerVal ) )
+        {
+        if ( iIsProxy )
+            {
+            // do nothing this time
+            }
+
+        else
+            {
+            cred = DFindCredentials( headerVal, aTransaction.Request().URI() );
+            }
+        }
+
+    RStringF wwwAuthHeader = iStringPool.StringF( iHttpStringAuthenticate, RHTTPSession::GetTable() );
+    RHTTPHeaders headers( aTransaction.Response().GetHeaderCollection() );
+
+    if ( iIsProxy )
+        {
+        // do nothing this time
+        }
+
+    else
+        {
+        TInt headerPart = DFindHeaderPartToUseL( aTransaction );
+
+        if ( headerPart != KErrNotFound )
+            {
+            THTTPHdrVal authTypeParam;
+
+            if ( headers.GetField( wwwAuthHeader, headerPart, authTypeParam ) == KErrNone )
+                // we need to store realm, opaque, nonce and qop values to
+                // use them when resubmitting the request
+                {
+                THTTPHdrVal algorithmVal;
+                THTTPHdrVal nonceVal;
+                THTTPHdrVal realmVal;
+                THTTPHdrVal opaqueVal;
+                THTTPHdrVal staleVal;
+                TInt algVal( KAlgUnknown );
+                TInt authVal( KQopNone );
+
+                aTransaction.PropertySet().RemoveProperty( iNonceStr );
+                aTransaction.PropertySet().RemoveProperty( iRealmStr );
+                aTransaction.PropertySet().RemoveProperty( iOpaqueStr );
+                aTransaction.PropertySet().RemoveProperty( iQopStr );
+                aTransaction.PropertySet().RemoveProperty( iStaleStr );
+
+                if( !headers.GetParam( wwwAuthHeader, 
+                                      iStringPool.StringF( HTTP::EAlgorithm, 
+                                                      RHTTPSession::GetTable() ), 
+                                      algorithmVal, 
+                                      headerPart ) )
+                    {
+                    if( !algorithmVal.Str().DesC().CompareF( iMd5Str.DesC() ) )
+                        {
+                        algVal = KAlgMd5;
+                        }
+                    else if( !algorithmVal.Str().DesC().CompareF( iMd5SessStr.DesC() ) )
+                        {
+                        algVal = KAlgMd5Sess;
+                        }
+                    }
+
+                authVal = CheckQop( headers, wwwAuthHeader, headerPart );
+
+                headers.GetParam( wwwAuthHeader, iNonceStr, nonceVal, headerPart );
+                headers.GetParam( wwwAuthHeader, iRealmStr, realmVal, headerPart );
+                headers.GetParam( wwwAuthHeader, iOpaqueStr, opaqueVal, headerPart );
+                headers.GetParam( wwwAuthHeader, iStaleStr, staleVal, headerPart );
+
+                // There's only MD5 and MD5-sess algorithm and QoP-Auth support
+                // Realm and Nonce values are mandatory. W/o them digest response is meaningless.
+                if( ( authVal & KQopAuth || authVal == KQopNone )
+                    && nonceVal.Type() == THTTPHdrVal::KStrVal
+                    && realmVal.Type() == THTTPHdrVal::KStrVal )
+                    {
+                    aTransaction.PropertySet().SetPropertyL( iNonceStr, nonceVal );
+                    aTransaction.PropertySet().SetPropertyL( iRealmStr, realmVal );
+                    // Opaque
+                    if( opaqueVal.Type() == THTTPHdrVal::KStrVal )
+                        {
+                        aTransaction.PropertySet().SetPropertyL( iOpaqueStr, opaqueVal );
+                        }
+
+                    // Qop
+                    aTransaction.PropertySet().SetPropertyL( iQopStr, authVal );
+                    // Algorithm
+                    aTransaction.PropertySet().SetPropertyL( iStringPool.StringF( HTTP::EAlgorithm, 
+                                                              RHTTPSession::GetTable() ), 
+                                                              algVal );
+
+                    _LIT8( KTrue, "TRUE" );
+                    if( staleVal.Type() == THTTPHdrVal::KStrVal 
+                        && !staleVal.Str().DesC().CompareF( KTrue ) )
+                        // stale is true -> nonce expired
+                        {
+                        AUTHLOGGER_WRITE( "Stale found" );
+
+                        __ASSERT_DEBUG( cred != KErrNotFound, PanicDigestAuthenticationFilter() );
+
+                        // update nonce value
+                        TDCredentials& creds = iDCredentials[cred];
+                        iStringPool.String( creds.iNonce ).Close();
+                        creds.iNonce = nonceVal.Str().Copy();
+
+                        // indicate to engine that no uname/pwd dialog needed
+                        staleVal.SetInt( 1 );
+                        aTransaction.PropertySet().SetPropertyL( iStaleStr, staleVal );
+                        stale = ETrue;
+                        }
+
+                    doDigest = ETrue;
+                    }
+                }
+            }
+        }
+
+    if ( cred != KErrNotFound && !stale )
+        // if stale is true credentials were ok, 
+        // only the nonce value was expired.
+        {
+        DRemoveCredentialsFromList( cred );
+        }
+
+    if( !doDigest )
+        //try with Basis
+        {
+        CHttpFilterAuthentication::HandleTransactionL( aTransaction );
+        }
+
+    AUTHLOGGER_LEAVEFN( "DHandleTransactionL" );
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::SubmitL
+// -----------------------------------------------------------------------------
+//
+void CHttpFilterDigestAuthentication::DSubmitL( RHTTPTransaction aTransaction )
+    {
+    TInt cred = KErrNotFound;
+    // Get credentials from URI, only for wwwAuthenticate
+
+    // If there are credentials in the transaction properties, add them to the list
+    DGetCredentialsFromPropertiesL( aTransaction );
+
+    // just in case we always remove stale value from properties
+    aTransaction.PropertySet().RemoveProperty( iStaleStr );
+
+    if ( iIsProxy )
+        {
+        // do nothing this time
+        }
+    else
+        {
+        // If we have credentials for this URL, add them
+        cred = DFindCredentialsForURI( aTransaction.Request().URI() );
+        }
+
+    if ( cred != KErrNotFound )
+        {
+        EncodeDigestAuthL( cred, aTransaction );
+        }
+    else
+        // Do Basic
+        {
+        CHttpFilterAuthentication::SubmitL( aTransaction );
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::DFindCredentials
+// Search list for matching credentials.
+// -----------------------------------------------------------------------------
+//
+TInt CHttpFilterDigestAuthentication::DFindCredentials( const RString& aRealm,
+        const TUriC8& aURI )
+    {
+    TInt count = iDCredentials.Count();
+
+    for ( TInt cred = 0; cred < count; ++cred )
+        {
+        if ( iDCredentials[ cred ].iRealm == aRealm )
+            {
+            TUriParser8 parser;
+            parser.Parse( iStringPool.StringF( iDCredentials[ cred ].iURI ).DesC() );
+
+            if ( !parser.Compare( aURI, EUriHost ) &&
+                 ( ( !parser.Compare( aURI, EUriPort ) )
+                   || ( !parser.IsPresent( EUriPort ) &&
+                        !aURI.IsPresent( EUriPort ) ) ) )
+                {
+                AUTHLOGGER_WRITE_FORMAT( "Found cred(3): %d", cred );
+                return cred;
+                }
+            }
+        }
+
+    return KErrNotFound;
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::DFindCredentialsForURI
+// Searches list for credentials that can be used with this URI
+// -----------------------------------------------------------------------------
+//
+TInt CHttpFilterDigestAuthentication::DFindCredentialsForURI( const TUriC8& aURI )
+    {
+    // Check if any of the stored URIs are the beginning of the URI
+    // we're trying now. If they are, we can immediately attempt to
+    // re-use the existing credentials.
+    _LIT8 ( KSchemeEnd, "://" );
+    TInt count = iDCredentials.Count();
+
+    for ( TInt cred = 0; cred < count; ++cred )
+        {
+        const TPtrC8& transDes = aURI.UriDes();
+        const TPtrC8& credDes =
+            iStringPool.StringF( iDCredentials[ cred ].iURI ).DesC();
+        TPtrC8 transNoSchemePtr( transDes );
+        TPtrC8 credNoSchemePtr( credDes );
+
+        if ( transNoSchemePtr.Find( KSchemeEnd() ) > 0 )
+            {
+            transNoSchemePtr.Set( transNoSchemePtr.Mid( transNoSchemePtr.Find( KSchemeEnd ) ) );
+            }
+
+        if ( credNoSchemePtr.Find( KSchemeEnd() ) > 0 )
+            {
+            credNoSchemePtr.Set( credNoSchemePtr.Mid( credNoSchemePtr.Find( KSchemeEnd ) ) );
+            }
+
+        if ( transNoSchemePtr.Length() >= credNoSchemePtr.Length() )
+            {
+            // The URI is long enough, which is a good start.
+
+            if ( transNoSchemePtr.Left( credNoSchemePtr.Length() ).Compare( credNoSchemePtr ) == 0 )
+                {
+                // The descriptors match. Things are looking good. In
+                // the interests of paranoia, if we haven't matched
+                // the entire URI, check that the character after the
+                // match is '/'.
+                if ( transNoSchemePtr.Length() == credNoSchemePtr.Length() )
+                    {
+                    TBuf<256> temp16;
+
+                    temp16.Copy(aURI.UriDes());
+
+
+                    AUTHLOGGER_WRITE_FORMAT( "Found cred(1): %d", cred );
+                    AUTHLOGGER_WRITE_FORMAT8( "uri: %S", &aURI.UriDes() );
+                    return cred;
+                    }
+
+                else if ( transNoSchemePtr[ credNoSchemePtr.Length() ] == '/' )
+                    {
+                    AUTHLOGGER_WRITE_FORMAT( "Found cred(2): %d", cred );
+                    AUTHLOGGER_WRITE_FORMAT8( "uri: %S", &aURI.UriDes() );
+                    return cred;
+                    }
+                }
+            }
+        }
+
+    AUTHLOGGER_WRITE( "cred not found" );
+
+    return KErrNotFound;
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::FindHeaderPartToUseL
+// Find the Authentication header.
+// -----------------------------------------------------------------------------
+//
+TInt CHttpFilterDigestAuthentication::DFindHeaderPartToUseL( RHTTPTransaction aTransaction )
+    {
+    // While we're here, we check that all desired fields are there.
+    RStringF wwwAuthenticate = iStringPool.StringF( iHttpStringAuthenticate, RHTTPSession::GetTable() );
+    TInt lastGoodDigest = KErrNotFound;
+    RHTTPHeaders headers = aTransaction.Response().GetHeaderCollection();
+    const TInt parts = headers.FieldPartsL( wwwAuthenticate );
+
+    for ( TInt ii = 0; ii < parts; ii++ )
+        {
+        THTTPHdrVal fieldVal; // The name of the current field.
+        THTTPHdrVal hdrVal; //A scratch hdrVal
+        headers.GetField( wwwAuthenticate, ii, fieldVal );
+
+        switch ( fieldVal.StrF().Index( RHTTPSession::GetTable() ) )
+            {
+            case HTTP::EDigest:
+                {
+                if ( headers.GetParam( wwwAuthenticate, iRealmStr, hdrVal, ii ) == KErrNone )
+                    {
+                    lastGoodDigest = ii;
+                    }
+                }
+                break;
+
+            default:
+                // We don't understand this, whatever it is. Ignore it.
+                break;
+            }
+        }
+
+    return lastGoodDigest;
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::DGetCredentialsFromPropertiesL
+// Find credentials from the transaction's properties.
+// -----------------------------------------------------------------------------
+//
+void CHttpFilterDigestAuthentication::DGetCredentialsFromPropertiesL( RHTTPTransaction& aTransaction )
+    {
+    THTTPHdrVal usernameVal;
+    THTTPHdrVal passwordVal;
+    THTTPHdrVal realmVal;
+    THTTPHdrVal nonceVal;
+    THTTPHdrVal staleVal;
+    const TUriC8& uri = aTransaction.Request().URI();
+    RHTTPTransactionPropertySet propSet = aTransaction.PropertySet();
+
+    if ( propSet.Property( iUsernameStr, usernameVal ) &&
+         propSet.Property( iPasswordStr, passwordVal ) &&
+         propSet.Property( iRealmStr, realmVal ) && 
+         propSet.Property( iNonceStr, nonceVal ) && 
+         !propSet.Property( iStaleStr, staleVal ) )
+        // if stale is TRUE we don't need to update credential
+        {
+        TInt cred;
+        RStringF uriStr;
+
+        if ( iIsProxy )
+            {
+            // do nothing this time
+            }
+        else
+            {
+            TInt pushCounter( 0 );
+            if ( ( cred = DFindCredentialsForURI( uri ) ) != KErrNotFound )
+                {
+                // Remove old credentials from the list
+                DRemoveCredentialsFromList( cred );
+                }
+
+            TPtrC8 uriPathPtr( uri.UriDes() );
+
+            if ( uriPathPtr.LocateReverse( '/' ) > 0 )
+                {
+                uriPathPtr.Set( uriPathPtr.Left( uriPathPtr.LocateReverse( '/' ) ) );
+                }
+
+            uriStr = iStringPool.OpenFStringL( uriPathPtr );
+            CleanupClosePushL<RStringF>( uriStr );
+            ++pushCounter;
+
+            THTTPHdrVal opaqueVal;
+            RString opaqueStr;
+
+            if( !propSet.Property( iOpaqueStr, opaqueVal ) )
+                // this isn't a error case
+                {
+                opaqueStr = iStringPool.OpenStringL( KNullDesC8 );
+                CleanupClosePushL<RString>( opaqueStr );
+                ++pushCounter;
+                }
+            else
+                {
+                opaqueStr = opaqueVal;
+                }
+
+            THTTPHdrVal qopVal;
+
+            if( !propSet.Property( iQopStr, qopVal ) )
+                // this isn't a error case
+                {
+                qopVal.SetInt( KQopNone );
+                }
+
+
+            THTTPHdrVal algVal;
+            if( !propSet.Property( iStringPool.StringF( HTTP::EAlgorithm, 
+                                               RHTTPSession::GetTable() ), algVal) )
+                {
+                algVal.SetInt( KAlgUnknown );
+                }
+
+            // Add credentials to the list from the transaction's properties
+            DAddCredentialsToListL( usernameVal.Str().Copy(), 
+                                    passwordVal.Str().Copy(),
+                                    realmVal.Str().Copy(), 
+                                    uriStr.Copy(), 
+                                    opaqueStr.Copy(), 
+                                    nonceVal.Str().Copy(),
+                                    qopVal.Int(),
+                                    algVal.Int()
+                                    );
+
+            // this is not the traditional way to pop and destroy elements
+            // but in this case this is the simplest one.
+            CleanupStack::PopAndDestroy( pushCounter ); // [opaqueVal,]uriStr
+            }
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::DRemoveCredentialsFromList
+// Remove credentials from the list.
+// -----------------------------------------------------------------------------
+//
+void CHttpFilterDigestAuthentication::DRemoveCredentialsFromList( TInt aCredId )
+    {
+    AUTHLOGGER_WRITE_FORMAT( "DRemoveCredentialsFromList: %d", aCredId );
+
+    __ASSERT_DEBUG( aCredId >= 0 && aCredId < iDCredentials.Count(), PanicDigestAuthenticationFilter() );
+
+    TDCredentials& creds = iDCredentials[ aCredId ];
+    iStringPool.String( creds.iUser ).Close();
+    iStringPool.String( creds.iPassword ).Close();
+    iStringPool.StringF( creds.iURI ).Close();
+    iStringPool.String( creds.iRealm ).Close();
+    iStringPool.String( creds.iOpaque ).Close();
+    iStringPool.String( creds.iNonce ).Close();
+    iDCredentials.Remove( aCredId );
+    }
+
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::DAddCredentialsToListL
+// Store credentials in the list.
+// -----------------------------------------------------------------------------
+//
+void CHttpFilterDigestAuthentication::DAddCredentialsToListL( RString aUsernameStr,
+        RString aPasswordStr,
+        RString aRealmStr,
+        RStringF aUriStr,
+        RString aOpaque,
+        RString aNonce,
+        TInt aQop,
+        TInt aAuthAlg )
+    {
+    TDCredentials newCred;
+    newCred.iUser = aUsernameStr;
+    newCred.iPassword = aPasswordStr;
+    newCred.iRealm = aRealmStr;
+    newCred.iURI = aUriStr;
+    newCred.iOpaque = aOpaque;
+    newCred.iNonce = aNonce;
+    newCred.iQop = aQop;
+    newCred.iAlgorithm = aAuthAlg;
+    newCred.iNc = 0;
+    User::LeaveIfError( iDCredentials.Append( newCred ) );
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::EncodeDAuthL
+// Encodes digest authentication
+// -----------------------------------------------------------------------------
+//
+void CHttpFilterDigestAuthentication::EncodeDigestAuthL( TInt aCred, RHTTPTransaction aTransaction )
+    {
+    AUTHLOGGER_ENTERFN( "EncodeDigestAuthL" );
+
+	// For now, only support RFC2069 format. The digest is
+	// MD5(H(A1):nonce:H(A2))
+	TBuf8<KHashLength> hash;
+    TBuf8<KNonceCountLength> nonceCount;
+
+    TDCredentials& cred = iDCredentials[aCred];
+
+    THTTPHdrVal nonce( iStringPool.String( cred.iNonce ) );
+    THTTPHdrVal realm( iStringPool.String( cred.iRealm ) );
+    THTTPHdrVal qop;
+    THTTPHdrVal uname( iStringPool.String( cred.iUser ) );
+    THTTPHdrVal password( iStringPool.String( cred.iPassword ) );
+    THTTPHdrVal opaque( iStringPool.String( cred.iOpaque ) );
+
+    if( cred.iQop & KQopAuth )
+        // recent release supports only qop 'auth'.
+        {
+        qop.SetStrF( iQopAuthStr );
+        }
+
+	THTTPHdrVal requestUri;
+    // we get url from request and modify as made in chttpclienthandler.cpp
+    RString uriStr = RequestUriL( aTransaction );
+    CleanupClosePushL<RString>( uriStr );
+    
+    requestUri.SetStr( uriStr );
+
+	HBufC8* stringToHash = NULL;
+	RStringF auth = iStringPool.StringF(HTTP::EAuth,RHTTPSession::GetTable());
+
+	TBuf8<KHashLength> cnonce;
+	if ( cred.iQop != KQopNone )
+		{
+        GenerateCNonce( cnonce );
+
+        // 7 is for colons
+        // 8 for nc-value
+        stringToHash = HBufC8::NewMaxLC( KHashLength +  // H(A1)
+                                         1 +    // ":"
+                                         nonce.Str().DesC().Length() + 
+                                         1 +    // ":"
+                                         8 +    // nc value
+                                         1 +    // ":"
+                                         KHashLength +  // cnonce
+                                         1 +    // ":"
+										 qop.StrF().DesC().Length() +
+                                         1 +    // ":"
+										 KHashLength ); // H(A2)
+		TPtr8 stringMod = stringToHash->Des();
+		stringMod.Zero();
+		HA1L( cred.iAlgorithm, uname, password, realm.Str(), nonce, cnonce, stringMod );
+		stringMod.Append(':');
+		stringMod.Append(nonce.Str().DesC());
+
+        // nc value
+		stringMod.Append(':');
+        ++cred.iNc;
+        // nc value is of 8 chars length and in hexadecimal format
+        nonceCount.NumFixedWidth( cred.iNc, EHex, 8 );
+		stringMod.Append( nonceCount );
+		stringMod.Append(':');
+
+		stringMod.Append(cnonce);
+		stringMod.Append(':');
+		stringMod.Append(auth.DesC());
+		stringMod.Append(':');
+		HA2L(aTransaction.Request().Method(), requestUri.Str(), hash);
+		stringMod.Append(hash);
+		}
+	else
+		{
+		// The response is the hash of H(A1):nonce:H(A2).
+		// Crqeate a buffer for the string to convert into MD5. The
+		// length is 32 characters for each of the hashes, 2
+		// characters for the 2 colons, plus the length of the nonce
+		stringToHash = HBufC8::NewMaxLC(nonce.Str().DesC().Length() + 
+												 2 * KHashLength + 2);
+		TPtr8 stringMod = stringToHash->Des();
+		stringMod.Zero();
+		HA1L( cred.iAlgorithm, uname, password, realm.Str(), nonce, cnonce, stringMod );
+		stringMod.Append(':');
+		stringMod.Append(nonce.Str().DesC());
+		stringMod.Append(':');
+		HA2L(aTransaction.Request().Method(), requestUri.Str(), hash);
+		stringMod.Append(hash);
+		}
+	
+	Hash(*stringToHash, hash);
+	CleanupStack::PopAndDestroy(stringToHash);
+
+	// OK. hash now contains the digest response. Set up the header.
+	RHTTPHeaders requestHeaders(aTransaction.Request().GetHeaderCollection());
+	
+	requestHeaders.RemoveField(iStringPool.StringF(HTTP::EAuthorization,RHTTPSession::GetTable())); 
+
+	requestHeaders.SetFieldL(iStringPool.StringF(HTTP::EAuthorization,RHTTPSession::GetTable()), 
+							 THTTPHdrVal(iStringPool.StringF(HTTP::EDigest,RHTTPSession::GetTable())),
+							 iStringPool.StringF(HTTP::EUsername,RHTTPSession::GetTable()),
+							 uname);
+	requestHeaders.SetFieldL(iStringPool.StringF(HTTP::EAuthorization,RHTTPSession::GetTable()), 
+							 THTTPHdrVal(iStringPool.StringF(HTTP::EDigest,RHTTPSession::GetTable())),
+							 iStringPool.StringF(HTTP::ERealm,RHTTPSession::GetTable()),
+							 realm);
+	requestHeaders.SetFieldL(iStringPool.StringF(HTTP::EAuthorization,RHTTPSession::GetTable()), 
+							 THTTPHdrVal(iStringPool.StringF(HTTP::EDigest,RHTTPSession::GetTable())),
+							 iStringPool.StringF(HTTP::ENonce,RHTTPSession::GetTable()),
+							 nonce);
+	RString hashStr = iStringPool.OpenStringL(hash);
+	CleanupClosePushL<RString>(hashStr);
+
+	THTTPHdrVal hdrVal;
+    hdrVal.SetStr(hashStr);
+	requestHeaders.SetFieldL(iStringPool.StringF(HTTP::EAuthorization,RHTTPSession::GetTable()), 
+							 THTTPHdrVal(iStringPool.StringF(HTTP::EDigest,RHTTPSession::GetTable())),
+							 iStringPool.StringF(HTTP::EResponse,RHTTPSession::GetTable()),
+							 hdrVal);
+
+	requestHeaders.SetFieldL(iStringPool.StringF(HTTP::EAuthorization,RHTTPSession::GetTable()), 
+							 THTTPHdrVal(iStringPool.StringF(HTTP::EDigest,RHTTPSession::GetTable())),
+							 iStringPool.StringF(HTTP::EUri,RHTTPSession::GetTable()),
+							 requestUri);
+	CleanupStack::PopAndDestroy(&hashStr);
+	if ( cred.iQop != KQopNone )
+		{
+        // QOP
+		hdrVal.SetStrF( auth );
+		requestHeaders.SetFieldL(iStringPool.StringF(HTTP::EAuthorization,RHTTPSession::GetTable()), 
+								 THTTPHdrVal(iStringPool.StringF(HTTP::EDigest,RHTTPSession::GetTable())),
+								 iStringPool.StringF(HTTP::EQop,RHTTPSession::GetTable()),
+								 hdrVal);
+		RString cnonceString = iStringPool.OpenStringL(cnonce);
+        // CNonce
+		CleanupClosePushL<RString>( cnonceString );
+		hdrVal.SetStr(cnonceString);
+		requestHeaders.SetFieldL(iStringPool.StringF(HTTP::EAuthorization,RHTTPSession::GetTable()), 
+								 THTTPHdrVal(iStringPool.StringF(HTTP::EDigest,RHTTPSession::GetTable())),
+								 iStringPool.StringF(HTTP::ECnonce,RHTTPSession::GetTable()),
+								 hdrVal);
+		CleanupStack::PopAndDestroy(); //cnonceString
+        // Nonce-count
+        RString nonceStr = iStringPool.OpenStringL( nonceCount );
+        CleanupClosePushL<RString>( nonceStr );
+		hdrVal.SetStr( nonceStr );
+		requestHeaders.SetFieldL(iStringPool.StringF(HTTP::EAuthorization,RHTTPSession::GetTable()), 
+								 THTTPHdrVal(iStringPool.StringF(HTTP::EDigest,RHTTPSession::GetTable())),
+								 iStringPool.StringF(HTTP::ENc,RHTTPSession::GetTable()),
+								 hdrVal);
+        CleanupStack::PopAndDestroy(); // nonceStr
+		}
+	
+	if(  opaque.Str().DesC().Length() )
+		requestHeaders.SetFieldL(iStringPool.StringF(HTTP::EAuthorization,RHTTPSession::GetTable()), 
+								 THTTPHdrVal(iStringPool.StringF(HTTP::EDigest,RHTTPSession::GetTable())),
+								 iStringPool.StringF(HTTP::EOpaque,RHTTPSession::GetTable()),
+								 opaque );	
+
+    if( cred.iAlgorithm == KAlgMd5 )
+        {
+		requestHeaders.SetFieldL(iStringPool.StringF(HTTP::EAuthorization,RHTTPSession::GetTable()), 
+								 THTTPHdrVal(iStringPool.StringF(HTTP::EDigest,RHTTPSession::GetTable())),
+								 iStringPool.StringF(HTTP::EAlgorithm,RHTTPSession::GetTable()),
+								 iMd5Str );
+        }
+    else if( cred.iAlgorithm == KAlgMd5Sess )
+        {
+		requestHeaders.SetFieldL(iStringPool.StringF(HTTP::EAuthorization,RHTTPSession::GetTable()), 
+								 THTTPHdrVal(iStringPool.StringF(HTTP::EDigest,RHTTPSession::GetTable())),
+								 iStringPool.StringF(HTTP::EAlgorithm,RHTTPSession::GetTable()),
+								 iMd5SessStr );
+        }
+
+    // set it again
+    // this may be needed if auth. fails and stale is true.
+    aTransaction.PropertySet().SetPropertyL( iRealmStr, realm );
+    
+    CleanupStack::PopAndDestroy();  // requestUri
+
+    AUTHLOGGER_LEAVEFN( "EncodeDigestAuthL" );
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::HA1L
+// Calculates H(A1)
+// -----------------------------------------------------------------------------
+//
+TInt CHttpFilterDigestAuthentication::HA1L(int aAlgorithm,
+                                           const RString& aUsername,
+                                           const RString& aPW,
+								           const RString& aRealm, 
+                                           const RString& aNonceValue,
+                                           TDesC8& aCNonce,
+                                           TDes8& aResult )
+	{
+    TBuf8<KHashLength> hash;
+
+	HBufC8* a1 = HBufC8::NewMaxL(aUsername.DesC().Length() + 
+								 aPW.DesC().Length() + 
+								 aRealm.DesC().Length() + 2 );
+	TPtr8 a1Mod = a1->Des();
+	a1Mod.Zero();
+	a1Mod.Append( aUsername.DesC() );
+	a1Mod.Append( KColon );
+	a1Mod.Append( aRealm.DesC() );
+	a1Mod.Append( KColon );
+	a1Mod.Append( aPW.DesC() );
+
+	Hash( *a1, hash );
+
+    delete a1;
+    a1 = NULL;
+
+    if( aAlgorithm == KAlgMd5Sess )
+        {
+	    a1 = HBufC8::NewMaxL( KHashLength +
+                               1 +  // ":"
+                               aNonceValue.DesC().Length() +
+                               1 +  // ":"
+							   aCNonce.Length() );
+	    TPtr8 a1Mod = a1->Des();
+	    a1Mod.Zero();
+        a1Mod.Append( hash );
+        a1Mod.Append( KColon );
+        a1Mod.Append( aNonceValue.DesC() );
+        a1Mod.Append( KColon );
+        a1Mod.Append( aCNonce );
+
+	    Hash(*a1, aResult);
+
+	    delete a1;
+        a1 = NULL;
+        }
+    else
+        {
+        aResult.Copy( hash );
+        }
+
+	return KErrNone;
+	}
+
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::HA2L
+// Calculates H(A2)
+// -----------------------------------------------------------------------------
+//
+TInt CHttpFilterDigestAuthentication::HA2L(const RStringF& aMethod, 
+								           const RString& aRequestUri,
+								           TDes8& aResult)
+	{
+	// In the auth qop, a2 is Method:digest-uri-value
+	// Digest-uri-value. We don't support auth-int qop
+	// Allocate enough space for the method, the URI and the colon.
+	TPtrC8 requestUri = aRequestUri.DesC();
+	TPtrC8 method = aMethod.DesC();
+	HBufC8* a2 = HBufC8::NewMaxLC(requestUri.Length() + method.Length() + 1);
+	TPtr8 a2Mod = a2->Des();
+	a2Mod.Zero();
+	a2Mod.Append(method);
+	a2Mod.Append(':');
+	a2Mod.Append(requestUri);
+
+	Hash(*a2, aResult);
+
+	CleanupStack::PopAndDestroy(a2);
+
+	return KErrNone;
+	}
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::Hash
+// Calculates hash value
+// -----------------------------------------------------------------------------
+//
+void CHttpFilterDigestAuthentication::Hash(const TDesC8& aMessage, TDes8& aHash)
+	{
+	// Calculate the 128 bit (16 byte) hash
+	iMD5Calculator->Reset();
+	TPtrC8 hash = iMD5Calculator->Hash( aMessage );
+	// Now print it as a 32 byte hex number
+	aHash.Zero();
+	_LIT8(formatStr, "%02x");
+	for (TInt ii = 0; ii < KRawHashLength; ii++)
+		{
+		TBuf8<2> scratch;
+		scratch.Zero();
+		scratch.Format( formatStr, hash[ii] );
+		aHash.Append( scratch );
+		}
+	}
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::GenerateCNonce
+// Generate unique cnonce value.
+// -----------------------------------------------------------------------------
+//
+void CHttpFilterDigestAuthentication::GenerateCNonce(TDes8& aNonce)
+	{
+	// 'Inspired by' CObexAuthenticator
+
+	// The purpose of the client nonce is to protect against 'chosen
+	// plaintext' attacks where a hostile server tricks the client
+	// into supplying a password using a specific server nonce that
+	// allows an (as yet undiscovered) flaw in MD5 to recover the
+	// password. As such the only important thing about client nonces
+	// is that they change and aren't predictable. See section 4.9 of
+	// RFC2616
+
+	TTime time;
+	time.UniversalTime();
+	TInt64 randomNumber = Math::Rand(iSeed);
+	randomNumber <<= 32;
+	randomNumber += Math::Rand(iSeed);
+	TBuf8<33> key;
+	key.Zero();
+	key.AppendNum(time.Int64(), EHex);
+	key.Append(_L8(":"));
+	key.AppendNum(randomNumber, EHex);
+	
+	Hash(key, aNonce);
+	}
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::RequestUriLC
+// Returns requested URI in form that can be used in uri field.
+// This code comes from 
+// Application-protocols/http/httpclient/chttpclienthandler.cpp.
+// -----------------------------------------------------------------------------
+//
+RString CHttpFilterDigestAuthentication::RequestUriL( RHTTPTransaction& aTransaction )
+    {
+    TBool useProxy( EFalse );
+    THTTPHdrVal useProxyHdr;
+    RStringF proxyUsage = iStringPool.StringF( HTTP::EProxyUsage, RHTTPSession::GetTable() );
+    RHTTPRequest request = aTransaction.Request();
+    const TUriC8& uri = request.URI();
+    RStringF scheme = iStringPool.OpenFStringL( uri.Extract( EUriScheme ) );
+    CleanupClosePushL<RStringF>( scheme );
+    CUri8* uriToUse = CUri8::NewLC( uri );
+
+    if( iSession->ConnectionInfo().Property( proxyUsage, useProxyHdr ) )
+        {
+        __ASSERT_DEBUG( useProxyHdr.Type() == THTTPHdrVal::KStrFVal, 
+                        PanicDigestAuthenticationFilter(-1/*THttpClientPanic::EInvalidProxySetting*/) );
+
+	    // Is a proxy being used?
+        useProxy = ( useProxyHdr.StrF().Index(RHTTPSession::GetTable()) == HTTP::EUseProxy );
+        }
+
+	if( !useProxy || (useProxy && scheme.Index(RHTTPSession::GetTable()) == HTTP::EHTTPS ))
+		{
+		// Not going via a proxy - need to remove the scheme and authority parts
+		uriToUse->RemoveComponentL( EUriScheme );
+		uriToUse->RemoveComponentL( EUriHost );	// this also removes the userinfo + port
+		}
+
+    RString uriStr = iStringPool.OpenStringL( uriToUse->Uri().UriDes()  );
+
+    CleanupStack::PopAndDestroy( 2 );  // schem, uriToUse
+
+    return uriStr;    
+    }
+
+// -----------------------------------------------------------------------------
+// CHttpFilterDigestAuthentication::CheckQop
+//
+// -----------------------------------------------------------------------------
+//
+TInt CHttpFilterDigestAuthentication::CheckQop( RHTTPHeaders& aHeaders,
+                                                RStringF& aWwwAuthHeader,
+                                                TInt aHeaderPart )
+    {
+    THTTPHdrVal qopVal;
+    TInt retVal( KQopNone );
+    
+    if( !aHeaders.GetParam( aWwwAuthHeader, iQopStr, qopVal, aHeaderPart ) )
+        {
+        TPtrC8 qopBuf( qopVal.Str().DesC() );
+        TPtrC8 qopValue;
+        TInt comma;
+
+        do
+            {
+            comma = qopBuf.Locate( ',' );
+            
+            if( comma != KErrNotFound )
+                {
+                qopValue.Set( qopBuf.Left(comma) );
+                }
+            else
+                {
+                qopValue.Set( qopBuf );
+                }
+
+            if( !qopValue.CompareF(iQopAuthStr.DesC()) )
+                {
+                retVal |= KQopAuth;
+                }
+            else if( !qopValue.CompareF(KQopAuthIntStr) )
+                {
+                retVal |= KQopAuthInt;
+                }
+            
+            qopBuf.Set( qopBuf.Right(qopBuf.Length()-comma-1));
+            }while( comma != KErrNotFound );
+        }
+        
+    return retVal;
+    }