httpfilters/httpfilterauthentication/Src/HttpFilterDigestAuthentication.cpp
author Pat Downey <patd@symbian.org>
Wed, 01 Sep 2010 12:21:21 +0100
branchRCL_3
changeset 20 a0da872af3fa
parent 19 c0c2f28ace9c
permissions -rw-r--r--
Revert incorrect RCL_3 drop: Revision: 201029 Kit: 201035

/*
* 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
		// coverity [CHECKED_RETURN]
        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;
    }