httpfilters/httpfilterauthentication/Src/HttpFilterAuthentication.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 31 Aug 2010 15:44:10 +0300
branchRCL_3
changeset 19 c0c2f28ace9c
parent 8 73e95e652591
child 20 a0da872af3fa
permissions -rw-r--r--
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:  Authentication filter
*
*/



// INCLUDE FILES
#include <e32std.h>
#include <e32math.h>
#include <uri8.h>
#include <http/rhttpsession.h>
#include <http/rhttptransaction.h>
#include <http/rhttpheaders.h>
#include <httpstringconstants.h>
#include <tconvbase64.h>

// User Includes
#include "HttpFilterAuthentication.h"
#include "httpfiltercommonstringsext.h"
#include "httperr.h"
#include "HttpFilterAuthenticationLogger.h"

// EXTERNAL DATA STRUCTURES

// EXTERNAL FUNCTION PROTOTYPES

// CONSTANTS

// MACROS

// LOCAL CONSTANTS AND MACROS

// MODULE DATA STRUCTURES

// LOCAL FUNCTION PROTOTYPES

// FORWARD DECLARATIONS

// ============================= LOCAL FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// PanicAuthenticationFilter
// Panic
// Returns: void
// -----------------------------------------------------------------------------
//
void PanicAuthenticationFilter( TInt aErr = 0 )
    {
    User::Panic( _L( "HTTP FILTER AUTHENTICATION" ), aErr );
    }


// ============================ MEMBER FUNCTIONS ===============================

// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::CHttpFilterAuthentication
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CHttpFilterAuthentication::CHttpFilterAuthentication( RHTTPSession* aSession, TBool aIsProxy )
    {
    __ASSERT_DEBUG( aSession != NULL, PanicAuthenticationFilter() );
    iIsProxy = aIsProxy;
    iSession = aSession;
    iStringPool = ( *aSession ).StringPool();

   
    }


// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CHttpFilterAuthentication::ConstructL( RHTTPSession aSession )
    {

    // Register the filter. We only care about 401 status codes where
    // there's a WWW-Authenticate header (which the RFC says MUST be
    // present). If we don't fire (e.g. due to a missing header) then
    // the transaction will fail if we ignore it, which is what we
    // want.

    // Register for submit events, to extract credentials from the URI
    // and to pre-supply credentials for basic
    // authentication. Registered as EStatusCodeHandler+1 so that it
    // isn't offered submit events originating from the 401 handler
    // (which is harmless but slow and confusing)
    
     iSession->StringPool().OpenL( HttpFilterCommonStringsExt::GetTable() );

    if ( iIsProxy )
        {
        iHttpStringAuthorization = HTTP::EProxyAuthorization;
        iHttpStringAuthenticate = HTTP::EProxyAuthenticate;
        iStatusCode = HTTPStatus::EProxyAuthenticationRequired;   // 407
        iRealmStr = iStringPool.StringF( HttpFilterCommonStringsExt::EProxyRealm, HttpFilterCommonStringsExt::GetTable() );
        iUsernameStr = iStringPool.StringF( HttpFilterCommonStringsExt::EProxyUsername, HttpFilterCommonStringsExt::GetTable() );
        iPasswordStr = iStringPool.StringF( HttpFilterCommonStringsExt::EProxyPassword, HttpFilterCommonStringsExt::GetTable() );
        }

    else
        {
        iHttpStringAuthorization = HTTP::EAuthorization;
        iHttpStringAuthenticate = HTTP::EWWWAuthenticate;
        iStatusCode = HTTPStatus::EUnauthorized;       // 401
        iRealmStr = iStringPool.StringF( HTTP::ERealm, RHTTPSession::GetTable() );
        iUsernameStr = iStringPool.StringF( HTTP::EUsername, RHTTPSession::GetTable() );
        iPasswordStr = iStringPool.StringF( HTTP::EPassword, RHTTPSession::GetTable() );
        }
        
    aSession.FilterCollection().AddFilterL( *this,
                                            THTTPEvent::ESubmit,
                                            RStringF(),
                                            KAnyStatusCode,
                                            EStatusCodeHandler + 1,
                                            iStringPool.StringF( HTTP::EAuthentication, RHTTPSession::GetTable() ) );

    // And for 401 status codes, for obvious reasons.
    aSession.FilterCollection().AddFilterL( *this,
                                            THTTPEvent::EGotResponseHeaders,
                                            iStringPool.StringF( iHttpStringAuthenticate, RHTTPSession::GetTable() ),
                                            iStatusCode,
                                            EStatusCodeHandler,
                                            iStringPool.StringF( HTTP::EAuthentication, RHTTPSession::GetTable() ) );

    }

// Destructor
CHttpFilterAuthentication::~CHttpFilterAuthentication()
    {
    TInt credCount = iCredentials.Count();

    for ( TInt ii = 0; ii < credCount; ++ii )
        {
        TCredentials& cred = iCredentials[ ii ];

        // Close all owned strings now while we can get a string pool handle.
        iStringPool.String( cred.iUser ).Close();
        iStringPool.String( cred.iPassword ).Close();
        iStringPool.StringF( cred.iURI ).Close();
        iStringPool.String( cred.iRealm ).Close();
        }

    iCredentials.Close();
    }

// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::InstallFilterL
// Two-phased constructor. This function replaces NewL.
// -----------------------------------------------------------------------------
//
CHttpFilterAuthentication* CHttpFilterAuthentication::InstallFilterL( TAny* aAuthenticationParams )
    {
    __ASSERT_DEBUG( aAuthenticationParams != NULL, PanicAuthenticationFilter() );
    TAuthenticationParams* authParams = REINTERPRET_CAST( TAuthenticationParams*, aAuthenticationParams );
    __ASSERT_DEBUG( authParams->iSession != NULL, PanicAuthenticationFilter() );
    CHttpFilterAuthentication* filter = new ( ELeave ) CHttpFilterAuthentication( authParams->iSession, authParams->iIsProxy );
    CleanupStack::PushL( filter );
    filter->ConstructL( *( authParams->iSession ) );
    CleanupStack::Pop( filter );
    return filter;
    }

// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::MHFLoad
// Called when the filter is being added to the session's filter queue.
// -----------------------------------------------------------------------------
//
void CHttpFilterAuthentication::MHFLoad( RHTTPSession, THTTPFilterHandle )
    {
    ++iLoadCount;
    }

// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::MHFUnload
// Called when the filter is being removed from a session's filter queue.
// -----------------------------------------------------------------------------
//
void CHttpFilterAuthentication::MHFUnload( RHTTPSession /*aSession*/, THTTPFilterHandle /*aFilterHandler*/ )
    {
    __ASSERT_DEBUG( iLoadCount >= 0, PanicAuthenticationFilter() );

    if ( --iLoadCount )
        {
        return ;
        }

    delete this;
    }


// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::MHFRunL
// Process a transaction event.
// -----------------------------------------------------------------------------
//
void CHttpFilterAuthentication::MHFRunL( RHTTPTransaction aTransaction,
        const THTTPEvent& aEvent )
    {
    switch ( aEvent.iStatus )
        {

        case THTTPEvent::ESubmit:
            {
            SubmitL( aTransaction );
            break;
            }

        case THTTPEvent::EGotResponseHeaders:
            {
            HandleTransactionL( aTransaction );
            break;
            }

        default:
            {
            __ASSERT_DEBUG( EFalse, PanicAuthenticationFilter() );
            break;
            }
        }
    }

// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::MHFRunError
// Process an error that occured while processing the transaction.
// -----------------------------------------------------------------------------
//
TInt CHttpFilterAuthentication::MHFRunError( TInt /*aError*/,
        RHTTPTransaction /*aTransaction*/,
        const THTTPEvent& /*aEvent*/ )
    {
    // aTransaction.Fail(); This causes a crash in Symbian code
    return KErrNone;
    }

// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::MHFSessionRunL
// Process a session event.
// -----------------------------------------------------------------------------
//
void CHttpFilterAuthentication::MHFSessionRunL( const THTTPSessionEvent& /*aEvent*/ )
    {}

// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::MHFSessionRunError
// Called when MHFRunL leaves from a session event.
// -----------------------------------------------------------------------------
//
TInt CHttpFilterAuthentication::MHFSessionRunError( TInt aError, const THTTPSessionEvent& /*aEvent*/ )
    {
    return aError;
    }


// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::HandleTransactionL
// Provides a central point for all Tranaction handling.
// -----------------------------------------------------------------------------
//
void CHttpFilterAuthentication::HandleTransactionL( RHTTPTransaction aTransaction )
    {
    AUTHLOGGER_ENTERFN( "CHttpFilterAuthentication::HandleTransactionL" );

    // 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 realmVal;
    TInt cred = KErrNotFound;

    if ( aTransaction.PropertySet().Property( iRealmStr, realmVal ) )
        {
        if ( iIsProxy )
            {
            cred = FindCredentialsForRealm( realmVal );
            }

        else
            {
            cred = FindCredentials( realmVal, aTransaction.Request().URI() );
            }
        }

    if ( cred != KErrNotFound )
        {
        RemoveCredentialsFromList( cred );
        }

    RStringF wwwAuthHeader = iStringPool.StringF( iHttpStringAuthenticate, RHTTPSession::GetTable() );
    RHTTPHeaders headers( aTransaction.Response().GetHeaderCollection() );

    if ( iIsProxy )
        {
        AddProxyRealmToTransactionL( aTransaction );
        }

    else
        {
        TInt headerPart = FindHeaderPartToUseL( aTransaction );

        if ( headerPart == KErrNotFound )
            {
			aTransaction.Cancel();
			aTransaction.SendEventL(KErrHttpDecodeUnknownAuthScheme, THTTPEvent::EIncoming, THTTPFilterHandle::ECurrentFilter);
			return;
            }

        THTTPHdrVal authTypeParam;

        if ( headers.GetField( wwwAuthHeader, headerPart, authTypeParam ) == KErrNone &&
             headers.GetParam( wwwAuthHeader, iRealmStr, realmVal, headerPart ) == KErrNone )
            {
            TInt cred = FindCredentialsForRealm( realmVal.Str(), EFalse );

            if( cred != KErrNotFound )
                // We have already met with this realm. Check that it came from the same host.
                {
                const TUriC8& uri = aTransaction.Request().URI();
                TUriParser8 uriCred;

                uriCred.Parse( iStringPool.StringF(iCredentials[cred].iURI).DesC() );

                if( !uriCred.Extract( EUriHost ).CompareF( uri.Extract( EUriHost ) ) )
                    // the host and the realm are the same as previously
                    // we most probably authenticated to this server
                    // use the previously sent credentials
                    {
                    aTransaction.PropertySet().RemoveProperty( iRealmStr );
                    aTransaction.PropertySet().RemoveProperty( iUsernameStr );
                    aTransaction.PropertySet().RemoveProperty( iPasswordStr );

                    aTransaction.PropertySet().SetPropertyL( iRealmStr, realmVal );

                    SetPropertyL( aTransaction, iUsernameStr, iStringPool.String( iCredentials[ cred ].iUser).DesC() );
                    SetPropertyL( aTransaction, iPasswordStr, iStringPool.String( iCredentials[ cred ].iPassword).DesC() );

                    // 'stale' is used in digest authentication only. But we can
                    // take its advantage here as well. Because we are working
                    // in the same protection space as before thus we know 
                    // the uname/pwd -> don't need to ask it again from user,
                    // or show dialog the authentication failed.
                    SetPropertyL( aTransaction, 
                                  iSession->StringPool().StringF( HTTP::EStale, 
                                                                  RHTTPSession::GetTable() ),
                                  KNullDesC8 );
                    }
                }
            else
                {
                aTransaction.PropertySet().RemoveProperty( iRealmStr );
                aTransaction.PropertySet().SetPropertyL( iRealmStr, realmVal );

                SetBasicProperty( aTransaction );
                }
            }
        }
    AUTHLOGGER_LEAVEFN( "CHttpFilterAuthentication::HandleTransactionL" );
    }


// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::SubmitL
// Function to handle Submit events. We need to remove any username and password from the URI.
// -----------------------------------------------------------------------------
//
void CHttpFilterAuthentication::SubmitL( RHTTPTransaction aTransaction )
    {
    // From RFC 2617:
    // A client SHOULD assume that all paths at or deeper than the depth of
    // the last symbolic element in the path field of the Request-URI also
    // are within the protection space specified by the Basic realm value of
    // the current challenge. A client MAY preemptively send the
    // corresponding Authorization header with requests for resources in
    // that space without receipt of another challenge from the server.

    // First time the transaction is submitted, we check if the credentials are appended to the url,
    // remove it from the url and add it to the transaction's properties.
    // Then we check if there are credentials in the transaction's properties,
    // and add it to the credentials list.
    // Last thing is to lookup the list for credentials for:
    //     WWWAuthenticate: match the URI.
    //     ProxyAuthenticate: match the realm.
    TInt cred = KErrNotFound;
    // Get credentials from URI, only for wwwAuthenticate

    // we don't need this property anymore
    aTransaction.PropertySet().RemoveProperty( iStringPool.StringF( HTTP::EBasic,
                                               RHTTPSession::GetTable() ) );

    if ( !iIsProxy )
        {
        GetCredentialsFromURIL( aTransaction );
        }

    // If there are credentials in the transaction properties, add them to the list
    GetCredentialsFromPropertiesL( aTransaction );

    if ( iIsProxy )
        {
        THTTPHdrVal realmVal;

        if ( aTransaction.PropertySet().Property( iRealmStr, realmVal ) )
            {
            cred = FindCredentialsForRealm( realmVal.Str() );
            }

        else
            {
            cred = FindCredentialsForProxy();
            }
        }

    else
        {
        // If we have credentials for this URL, add them
        cred = FindCredentialsForURI( aTransaction.Request().URI() );
        }

    if ( cred != KErrNotFound )
        {
        EncodeBasicAuthL( iStringPool.String( iCredentials[ cred ].iUser ),
                          iStringPool.String( iCredentials[ cred ].iPassword ),
                          aTransaction );

        if( !iIsProxy )
            {
            SetPropertyL( aTransaction, iUsernameStr, iStringPool.String( iCredentials[ cred ].iUser ).DesC() );
            SetPropertyL( aTransaction, iPasswordStr, iStringPool.String( iCredentials[ cred ].iPassword ).DesC() );
            SetPropertyL( aTransaction, iRealmStr, iStringPool.String( iCredentials[ cred ].iRealm ).DesC() );
            }
        }
    }

// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::FindCredentials
// Search list for matching credentials.
// -----------------------------------------------------------------------------
//
TInt CHttpFilterAuthentication::FindCredentials( const RString& aRealm,
        const TUriC8& aURI )
    {
    TInt count = iCredentials.Count();

    for ( TInt cred = 0; cred < count; ++cred )
        {
        if ( iCredentials[ cred ].iRealm == aRealm )
            {
            TUriParser8 parser;
            parser.Parse( iStringPool.StringF( iCredentials[ cred ].iURI ).DesC() );

            if ( !parser.Compare(aURI, EUriScheme) &&
                 !parser.Compare( aURI, EUriHost ) &&
                 ( ( !parser.Compare( aURI, EUriPort ) )
                   || ( !parser.IsPresent( EUriPort ) &&
                        !aURI.IsPresent( EUriPort ) ) ) )
                {
                return cred;
                }
            }
        }
    return KErrNotFound;
    }

// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::FindCredentialsForURI
// Searches list for credentials that can be used with this URI
// -----------------------------------------------------------------------------
//
TInt CHttpFilterAuthentication::FindCredentialsForURI( 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.
	TInt count = iCredentials.Count();
	for (TInt cred = 0; cred < count; ++cred)
		{
		const TDesC8& transDes = aURI.UriDes();
		const TDesC8& credDes = 
			iStringPool.StringF(iCredentials[cred].iURI).DesC();
		if (transDes.Length() >= credDes.Length())
			{
			// The URI is long enough, which is a good start.
			if (transDes.Left(credDes.Length()).Compare(credDes) == 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 (transDes.Length() == credDes.Length())
                {
					return cred;
                }
				else if (transDes[credDes.Length()] == '/')
                {
					return cred;
                }
				}
			}
		}
	return KErrNotFound;
    }

// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::FindCredentialsForRealm
// Search list for matching credentials for a given realm.
// -----------------------------------------------------------------------------
//
TInt CHttpFilterAuthentication::FindCredentialsForRealm( const RString& aRealm,
                                                         TBool aCheckProxy )
    {
    RStringF proxyName;
    TInt count = iCredentials.Count();

    if( aCheckProxy )
        {
        TBool ret = GetProxyName( proxyName );

        if ( ret )
            {
            for ( TInt cred = 0; cred < count; ++cred )
                {
                if ( iCredentials[ cred ].iRealm == aRealm && iCredentials[ cred ].iURI == proxyName )
                    {
                    return cred;
                    }
                }
            }
        }
    else
        {
        for ( TInt cred = 0; cred < count; ++cred )
            {
            if ( iCredentials[ cred ].iRealm == aRealm )
                {
                return cred;
                }
            }
        }
    AUTHLOGGER_LEAVEFN( "CHttpFilterAuthentication::FindCredentialsForRealm" );
    return KErrNotFound;
    }


// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::FindCredentialsForProxy
// Search list for matching credentials based on proxy used.
// -----------------------------------------------------------------------------
//
TInt CHttpFilterAuthentication::FindCredentialsForProxy()
    {
    RStringF proxyName;
    TInt cred = KErrNotFound;
    TInt count = iCredentials.Count();
    TBool ret = GetProxyName( proxyName );

    if ( ret )
        {
        for ( cred = 0; cred < count; ++cred )
            {
            if ( iCredentials[ cred ].iURI == proxyName )
                {
                return cred;
                }
            }
        }

    return KErrNotFound;
    }


// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::EncodeBasicAuthL
// Set basic auth header.
// -----------------------------------------------------------------------------
//
void CHttpFilterAuthentication::EncodeBasicAuthL( const RString& aUsername,
        const RString& aPW,
        RHTTPTransaction& aTransaction )
    {
    AUTHLOGGER_ENTERFN( "CHttpFilterAuthentication::EncodeBasicAuthL" );

    // Standard, plain-text HTTP - Base 64 the name and password
    TBase64 codec;
    HBufC8* nameAndPW = HBufC8::NewMaxLC( aUsername.DesC().Length() + aPW.DesC().Length() + 1 );
    _LIT8( KBasicAuthFormatString, "%S:%S" );
    nameAndPW->Des().Format( KBasicAuthFormatString, &aUsername.DesC(), &aPW.DesC() );

    // Conservatively allocate a buffer twice as big as the unencoded
    // buffer for the encoded string.
    HBufC8* encoded = HBufC8::NewMaxLC( nameAndPW->Des().Length() * 2 );
    TPtr8 encodedPtr( encoded->Des() );
    codec.Encode( *nameAndPW, encodedPtr );
    HBufC8* authHeaderField = HBufC8::NewLC( encodedPtr.Length() + 1 +
                              iStringPool.StringF( HTTP::EBasic, RHTTPSession::GetTable() ).DesC().Length() );
    authHeaderField->Des().Copy( iStringPool.StringF( HTTP::EBasic, RHTTPSession::GetTable() ).DesC() );
    _LIT8( KSpace, " " );
    authHeaderField->Des().Append( KSpace );
    authHeaderField->Des().Append( encoded->Des() );

    RString encodedString = iStringPool.OpenStringL( *authHeaderField );
    CleanupStack::PopAndDestroy( 3 ); // authHeaderField, encoded, nameAndPW

    CleanupClosePushL( encodedString );

    RHTTPHeaders requestHeaders( aTransaction.Request().GetHeaderCollection() );

    requestHeaders.RemoveField( iStringPool.StringF( iHttpStringAuthorization, RHTTPSession::GetTable() ) );

    requestHeaders.SetFieldL( iStringPool.StringF( iHttpStringAuthorization, RHTTPSession::GetTable() ),
                              THTTPHdrVal( encodedString ) );

    CleanupStack::PopAndDestroy(); // encodedString
    AUTHLOGGER_LEAVEFN( "CHttpFilterAuthentication::EncodeBasicAuthL" );
    }

// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::FindHeaderPartToUseL
// Find the Authentication header.
// -----------------------------------------------------------------------------
//
TInt CHttpFilterAuthentication::FindHeaderPartToUseL( RHTTPTransaction aTransaction )
    {
    AUTHLOGGER_ENTERFN( "CHttpFilterAuthentication::FindHeaderPartToUseL" );

    // There may be several different authentication fields. We need
    // to chose the strongest one we understnad. Currently, we only
    // support 2, Basic and Digest, and Digest is the strongest
    // assuming there is a qop that we can accept. Therefore, we keep
    // track of the last good basic one we found, and return the
    // moment we find an acceptable digest one.

    // While we're here, we check that all desired fields are there.

    RStringF wwwAuthenticate = iStringPool.StringF( iHttpStringAuthenticate, RHTTPSession::GetTable() );
    TInt lastGoodBasic = 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 [check_return]
				// coverity [unchecked_value]
        headers.GetField( wwwAuthenticate, ii, fieldVal );

        switch ( fieldVal.StrF().Index( RHTTPSession::GetTable() ) )
            {

            case HTTP::EBasic:

            if ( headers.GetParam( wwwAuthenticate, iRealmStr, hdrVal, ii ) == KErrNone )
                lastGoodBasic = ii;

            break;

            default:
            // We don't understand this, whatever it is. Ignore it.
            break;
            }
        }
    AUTHLOGGER_LEAVEFN( "CHttpFilterAuthentication::FindHeaderPartToUseL" );
    return lastGoodBasic;
    }

// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::GetCredentialsFromURIL
// Find credentials from the transaction's URI.
// -----------------------------------------------------------------------------
//
void CHttpFilterAuthentication::GetCredentialsFromURIL( RHTTPTransaction& aTransaction )
    {
    AUTHLOGGER_ENTERFN( "CHttpFilterAuthentication::GetCredentialsFromURIL" );

    const TUriC8 & uri = aTransaction.Request().URI();

    if ( uri.IsPresent( EUriUserinfo ) )
        {
        const TPtrC8 userInfo = uri.Extract( EUriUserinfo );
        TInt colonPos = userInfo.Locate( ':' );

        if ( colonPos == KErrNotFound )
            colonPos = userInfo.Length();

        if ( colonPos )
            {
            // We have a username
            TPtrC8 username = userInfo.Left( colonPos );
            SetPropertyL( aTransaction, iUsernameStr, username );
            }

        if ( colonPos != userInfo.Length() )
            {
            // We have a password
            TPtrC8 password = userInfo.Mid( colonPos + 1 );
            SetPropertyL( aTransaction, iPasswordStr, password );
            }

        if ( colonPos && uri.IsPresent( EUriHost ) )
            {
            // Get the realm only if there is a username
            TPtrC8 realm = uri.Extract( EUriHost );
            SetPropertyL( aTransaction, iRealmStr, realm );
            }

        // Now remove the username and password from the URI
        CUri8* newUri = CUri8::NewLC( uri );

        newUri->RemoveComponentL( EUriUserinfo );

        aTransaction.Request().SetURIL( newUri->Uri() );

        CleanupStack::PopAndDestroy( newUri ); // newUri
        }
    AUTHLOGGER_LEAVEFN( "CHttpFilterAuthentication::GetCredentialsFromURIL" );
    }

// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::GetCredentialsFromPropertiesL
// Find credentials from the transaction's properties.
// -----------------------------------------------------------------------------
//
void CHttpFilterAuthentication::GetCredentialsFromPropertiesL( RHTTPTransaction& aTransaction )
    {
    AUTHLOGGER_ENTERFN( "CHttpFilterAuthentication::GetCredentialsFromPropertiesL" );

    THTTPHdrVal usernameVal;
    THTTPHdrVal passwordVal;
    THTTPHdrVal realmVal;
    const TUriC8& uri = aTransaction.Request().URI();
    RHTTPTransactionPropertySet propSet = aTransaction.PropertySet();

    if ( propSet.Property( iUsernameStr, usernameVal ) &&
         propSet.Property( iPasswordStr, passwordVal ) &&
         propSet.Property( iRealmStr, realmVal ) )
        {
        TInt cred;
        RStringF uriStr;

        if ( iIsProxy )
            {
            if ( ( cred = FindCredentialsForRealm( realmVal ) ) != KErrNotFound )
                {
                // Remove old credentials from the list
                RemoveCredentialsFromList( cred );
                }

            if ( GetProxyName( uriStr ) )
                {
                // Add credentials to the list from the transaction's properties
                AddCredentialsToListL( usernameVal.Str().Copy(), passwordVal.Str().Copy(),
                                       realmVal.Str().Copy(), uriStr.Copy() );
                }
            }

        else
            {
            if ( ( cred = FindCredentialsForURI( uri ) ) != KErrNotFound )
                {
                // Remove old credentials from the list
                RemoveCredentialsFromList( cred );
                }

            TPtrC8 uriPathPtr( uri.UriDes() );

            if ( uriPathPtr.LocateReverse( '/' ) > 0 )
                {
                uriPathPtr.Set( uriPathPtr.Left( uriPathPtr.LocateReverse( '/' ) ) );
                }

            uriStr = iStringPool.OpenFStringL( uriPathPtr );
            // Add credentials to the list from the transaction's properties
            AddCredentialsToListL( usernameVal.Str().Copy(), passwordVal.Str().Copy(),
                                   realmVal.Str().Copy(), uriStr );
            }
        }
    AUTHLOGGER_LEAVEFN( "CHttpFilterAuthentication::GetCredentialsFromPropertiesL" );
    }

// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::RemoveCredentialsFromList
// Remove credentials from the list.
// -----------------------------------------------------------------------------
//
void CHttpFilterAuthentication::RemoveCredentialsFromList( TInt aCredId )
    {
    __ASSERT_DEBUG( aCredId >= 0 && aCredId < iCredentials.Count(), PanicAuthenticationFilter() );

    TCredentials& creds = iCredentials[ aCredId ];
    iStringPool.String( creds.iUser ).Close();
    iStringPool.String( creds.iPassword ).Close();
    iStringPool.StringF( creds.iURI ).Close();
    iStringPool.String( creds.iRealm ).Close();
    iCredentials.Remove( aCredId );
    }


// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::AddCredentialsToListL
// Store credentials in the list.
// -----------------------------------------------------------------------------
//
void CHttpFilterAuthentication::AddCredentialsToListL( RString aUsernameStr,
        RString aPasswordStr,
        RString aRealmStr,
        RStringF aUriStr )
    {
    TCredentials newCred;
    newCred.iUser = aUsernameStr;
    newCred.iPassword = aPasswordStr;
    newCred.iRealm = aRealmStr;
    newCred.iURI = aUriStr;
    newCred.iID = iNextID++;
    User::LeaveIfError( iCredentials.Append( newCred ) );
    }


// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::TrimLWSLeft
// Remove white spaces from the left.
// -----------------------------------------------------------------------------
//
void CHttpFilterAuthentication::TrimLWSLeft( TPtrC8& aPtr )
    {
    TInt i;
    _LIT8( KSpaceNewLineTab, " \r\n\t" );
    TInt len = aPtr.Length();

    for ( i = 0; i < len; i++ )
        {
        if ( KSpaceNewLineTab().Locate( aPtr[ i ] ) == KErrNotFound )
            {
            break;
            }
        }

    if ( i == len )
        {
        aPtr.Set( NULL, 0 );
        }

    else
        {
        aPtr.Set( aPtr.Right( len - i ) );
        }
    }

// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::TrimLWSRight
// Remove white spaces from the right.
// -----------------------------------------------------------------------------
//
void CHttpFilterAuthentication::TrimLWSRight( TPtrC8& aPtr )
    {
    TInt i;
    _LIT8( KSpaceNewLineTab, " \r\n\t" );
    TInt len = aPtr.Length();

    for ( i = len - 1; i >= 0; i-- )
        {
        if ( KSpaceNewLineTab().Locate( aPtr[ i ] ) == KErrNotFound )
            {
            break;
            }
        }

    if ( i == -1 )
        {
        aPtr.Set( NULL, 0 );
        }

    else
        {
        aPtr.Set( aPtr.Left( i + 1 ) );
        }
    }

// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::FindCharNotInQoutes
// Find a character in a buffer, but not in a quoted string.
// -----------------------------------------------------------------------------
//
TInt CHttpFilterAuthentication::FindCharNotInQoutes( TPtrC8& aPtr, TUint8 aCharToFind )
    {
    TInt len = aPtr.Length();
    TInt i;

    for ( i = 0; i < len; i++ )
        {
        if ( aPtr[ i ] == aCharToFind )
            {
            return i;
            }

        if ( aPtr[ i ] == '"' )
            {
            for ( ; i < len && i != '"'; i++ )

                ;
            }
        }

    return KErrNotFound;
    }

// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::FindCharNotInQoutes
// Find a substring in a buffer, but not in a quoted string.
// -----------------------------------------------------------------------------
//
TInt CHttpFilterAuthentication::FindCharNotInQoutes( TPtrC8& aPtr, const TPtrC8& aCharsToFind )
    {
    TInt len = aPtr.Length();
    TInt i;

    for ( i = 0; i < len; i++ )
        {
        if ( aCharsToFind.Locate( aPtr[ i ] ) != KErrNotFound )
            return i;

        if ( aPtr[ i ] == '"' )
            {
            for ( ; i < len && aPtr[ i ] != '"'; i++ )

                ;
            }
        }

    return KErrNotFound;
    }


// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::FindNextTokenInHeaderFieldL
// Find a next token in the header.
// -----------------------------------------------------------------------------
//
TInt CHttpFilterAuthentication::FindNextTokenInHeaderFieldL( TPtrC8& aHeaderPtr, TPtrC8& aTokenPtr )
    {
    _LIT8( KCommaSpaceNewLineTab, ", \r\n\t" );
    aTokenPtr.Set( aHeaderPtr );
    TrimLWSLeft( aTokenPtr ); // Skip leading spaces
    TInt leadingSpaces = aHeaderPtr.Length() - aTokenPtr.Length();

    if ( aTokenPtr.Length() == 0 )
        return KErrNotFound;

    TInt i = FindCharNotInQoutes( aTokenPtr, KCommaSpaceNewLineTab() ); // Find separator after leading spaces

    if ( i == KErrNotFound )
        {
        i = aHeaderPtr.Length();
        }

    else
        {
        aTokenPtr.Set( aTokenPtr.Left( i ) );
        i += leadingSpaces; // Add the number of leading spaces
        i++; // Advance beyond separator
        }

    return i;
    }


// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::AddProxyRealmToTransactionL
// Find the proxy realm in the response header and add it to the transaction's properties.
// -----------------------------------------------------------------------------
//
void CHttpFilterAuthentication::AddProxyRealmToTransactionL( RHTTPTransaction aTransaction )
    {
    AUTHLOGGER_ENTERFN( "CHttpFilterAuthentication::AddProxyRealmToTransactionL" );

    RStringF authenticateStr = iStringPool.StringF( HTTP::EProxyAuthenticate, RHTTPSession::GetTable() );
    RHTTPHeaders headers = aTransaction.Response().GetHeaderCollection();
    const TInt parts = headers.FieldPartsL( authenticateStr );
    TInt i;
    TInt offset = 0;
    TInt equalOffset = 0;
    TBool found = EFalse;
    THTTPHdrVal fieldVal;
    TPtrC8 headerPtr;
    TPtrC8 tokenPtr;
    TPtrC8 partTokenPtr;
    RStringF tokenStr;
    RString realmStr;

    for ( i = 0; i < parts; i++ )
        {
        if ( headers.GetField( authenticateStr, i, fieldVal ) == KErrNotFound )
            {
            continue;
            }

        headerPtr.Set( fieldVal.StrF().DesC() );

        while ( ( offset = FindNextTokenInHeaderFieldL( headerPtr, tokenPtr ) ) != KErrNotFound )     // Get next token
            {
            headerPtr.Set( headerPtr.Right( headerPtr.Length() - offset ) ); // Advance headerPtr
            tokenStr = iStringPool.OpenFStringL( tokenPtr );

            switch ( tokenStr.Index( RHTTPSession::GetTable() ) )     // Is it Basic?
                {

                case HTTP::EBasic:
                    {
                    found = ETrue;
                    break;
                    }

                default:
                    {
                    found = EFalse;
                    break;
                    }

                }
            tokenStr.Close();

            if ( !found )
                {
                continue;
                }

            offset = FindNextTokenInHeaderFieldL( headerPtr, tokenPtr ); // Find next token
            headerPtr.Set( headerPtr.Right( headerPtr.Length() - offset ) ); // Advance headerPtr

            equalOffset = FindCharNotInQoutes( tokenPtr, '=' );

            if ( equalOffset < 1 )     // Not the first character
                {
                continue;
                }

            partTokenPtr.Set( tokenPtr.Left( equalOffset ) );
            tokenStr = iStringPool.OpenFStringL( partTokenPtr );

            switch ( tokenStr.Index( RHTTPSession::GetTable() ) )     // Is it realm?
                {

                case HTTP::ERealm:
                    {
                    found = ETrue;
                    break;
                    }

                default:
                    {
                    found = EFalse;
                    break;
                    }

                }
            tokenStr.Close();

            if ( !found )
                {
                continue;
                }

            partTokenPtr.Set( tokenPtr.Right( tokenPtr.Length() - equalOffset - 1 ) );

            if ( partTokenPtr.Length() > 0 )
                {
                realmStr = iStringPool.OpenStringL( partTokenPtr );
                CleanupClosePushL( realmStr );
                aTransaction.PropertySet().RemoveProperty( iRealmStr );
                aTransaction.PropertySet().SetPropertyL( iRealmStr, realmStr );
                CleanupStack::PopAndDestroy(); // realmStr

                SetBasicProperty( aTransaction );
                }

            }
        }
    AUTHLOGGER_LEAVEFN( "CHttpFilterAuthentication::AddProxyRealmToTransactionL" );
    }


// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::GetProxyName
// Get proxy name from the HTTP session.
// -----------------------------------------------------------------------------
//
TBool CHttpFilterAuthentication::GetProxyName( RStringF &aProxyName )
    {
    RHTTPConnectionInfo connInfo = iSession->ConnectionInfo();
    THTTPHdrVal proxyAddrVal;


    TBool ret = connInfo.Property( iStringPool.StringF( HTTP::EProxyAddress,
                                   RHTTPSession::GetTable() ), proxyAddrVal );

    if ( ret )
        aProxyName = proxyAddrVal.StrF();

    return ret;
    }

// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::SetPropertyL
// Set property of the transaction
// -----------------------------------------------------------------------------
//
void CHttpFilterAuthentication::SetPropertyL( RHTTPTransaction& aTransaction,
                                              RStringF aPropertyName, 
                                              const TDesC8& aToken )
    {
    RString tokenStr = iStringPool.OpenStringL( aToken );
    THTTPHdrVal tokenVal = tokenStr;
    CleanupClosePushL( tokenStr );
    aTransaction.PropertySet().SetPropertyL( aPropertyName, tokenVal );
    CleanupStack::PopAndDestroy(); // tokenStr
    }

// -----------------------------------------------------------------------------
// CHttpFilterAuthentication::SetBasicProperty()
// Set 'Basic' in property set indicating that this is a
// basic authentication
// -----------------------------------------------------------------------------
//
void CHttpFilterAuthentication::SetBasicProperty( RHTTPTransaction aTransaction )
    {
    // the value is dummy. The property needs to be there only
    THTTPHdrVal value( 1 ); 
    aTransaction.PropertySet().RemoveProperty( 
    iStringPool.StringF( HTTP::EBasic, 
                         RHTTPSession::GetTable() ) );
    aTransaction.PropertySet().SetPropertyL( 
    iStringPool.StringF( HTTP::EBasic, 
                         RHTTPSession::GetTable() ), 
                         value );
    }

//  End of File