/*
* Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of the License "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:  Implementation of CHttpCacheManager
*
*/

// INCLUDE FILES
#include "httpcachemanager.h"
#include "HttpCacheHandler.h"
#include "HttpCacheUtil.h"
#include "HttpCacheFileWriteHandler.h"
#include <CenRepNotifyHandler.h>
#include <httpcachemanagerinternalcrkeys.h>
#include <CoreApplicationUIsSDKCRKeys.h>
#include <centralrepository.h>
#include <FeatMgr.h>
#include <http/RHTTPTransaction.h>
#include <SysUtilDomainCRKeys.h>
#include <eikenv.h>
#include <f32file.h>
#include <e32hashtab.h>
#include <mmf/common/mmfcontrollerpluginresolver.h>

// EXTERNAL DATA STRUCTURES

// EXTERNAL FUNCTION PROTOTYPES

// CONSTANTS

// kbyte
const TUint KDefaultCacheSize = 1048576; // 1MB = 1024*1024
const TUint KDefaultOperatorCacheSize = 300*1024; // 300KB
_LIT( KDefaultCacheDrive, "c");
_LIT( KDefaultCacheDir, "c:\\system\\cache\\");
_LIT( KDefaultOperatorCacheDir, "c:\\system\\cache\\");
_LIT( KDefaultVssCacheDir, "c:\\system\\cache\\vss\\");
_LIT( KDefaultIndexFile, "index.dat" );
_LIT( KDefaultOpIndexFile, "index_op.dat" );
_LIT( KDefaultVSSIndexFile, "index_vss.dat" );
_LIT( KIndexFileExtension, ".dat" );
_LIT( KCrashCheck, "browser.val");
_LIT( KValidationFileExtension, ".val" );
_LIT8( KVSSHeaderFieldName, "X-Vodafone-Content" );
_LIT8( KVSSHeaderFieldValue, "Portal" );
// default override string, browser gets 2MB, widgetUI gets 1MB, OVI Store gets 1MB : max 4MB.
_LIT( KDefaultOverrideString, "10008D39;1;2048;C;10282822;1;1024;C;102829A0;1;1024;C" );

// MACROS
#define KUIDBROWSERNG 0x10008D39
#define KUIDWIDGETUI 0x10282822
#define KUIDBROWSERCACHECLEANER 0x20026777
#define KUIDOPERATORMENU 0x100039CE
#define KUIDOVISTORE 0x102829A0

// LOCAL CONSTANTS AND MACROS

// MODULE DATA STRUCTURES

// LOCAL FUNCTION PROTOTYPES

// FORWARD DECLARATIONS

TBool IsBrowser()
    {
    RProcess process;
    TSecureId secId = process.SecureId();
    TUint32 secIdInt = secId;
    
    return ( secIdInt == KUIDBROWSERNG );
    }


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

// -----------------------------------------------------------------------------
// CHttpCacheManager::CHttpCacheManager
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CHttpCacheManager::CHttpCacheManager():iCacheFolder( KDefaultCacheDir )
    {
    }

// -----------------------------------------------------------------------------
// CHttpCacheManager::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CHttpCacheManager::ConstructL()
    {
#ifdef __CACHELOG__
    HttpCacheUtil::WriteLog( 0, _L("-------------------------------------------"));
    HttpCacheUtil::WriteLog( 0, _L("| Creating new CHttpCacheManager Instance |"), (TInt)this);
    HttpCacheUtil::WriteLog( 0, _L("-------------------------------------------"));
#endif
    
    CreateCacheHandlersL();
#ifdef __CACHELOG__
    HttpCacheUtil::WriteLog( 0, _L("Created cache handlers"), (TInt)this);
#endif

    // read offline mode
    if( FeatureManager::FeatureSupported( KFeatureIdOfflineMode ) )
        {
        // check if connection is not allowed and
        // come up with a listerner on the offline setting
        TInt connAllowed( 1 );

        iOfflineRepository = CRepository::NewL( KCRUidCoreApplicationUIs );

        iOfflineRepository->Get( KCoreAppUIsNetworkConnectionAllowed, connAllowed );

        iOfflineMode = !connAllowed;

        iOfflineNotifyHandler = CCenRepNotifyHandler::NewL( *this, *iOfflineRepository,
            CCenRepNotifyHandler::EIntKey, (TUint32)KCoreAppUIsNetworkConnectionAllowed );
        iOfflineNotifyHandler->StartListeningL();
        }
    }

// -----------------------------------------------------------------------------
// CHttpCacheManager::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
EXPORT_C CHttpCacheManager* CHttpCacheManager::NewL()
    {
    CHttpCacheManager* self = new( ELeave ) CHttpCacheManager();

    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop();

    return self;
    }

// -----------------------------------------------------------------------------
// Destructor
// -----------------------------------------------------------------------------
CHttpCacheManager::~CHttpCacheManager()
    {
#ifdef __CACHELOG__
    HttpCacheUtil::WriteLog( 0, _L("-----------------------------------------"));
    HttpCacheUtil::WriteLog( 0, _L("| Destroying CHttpCacheManager Instance | "), (TInt)this);
    HttpCacheUtil::WriteLog( 0, _L("-----------------------------------------"));
#endif
    
    if( IsBrowser() )    // only enabled in browser
        {
        // delete crashcheck.dat file
        RFs rfs;
        rfs.Connect();
        rfs.SetSessionPath( iCacheFolder );
        rfs.Delete(KCrashCheck());
        rfs.Close();
        }
    
    delete iOfflineNotifyHandler;
    delete iOfflineRepository;
    delete iCache;
    delete iOperatorCache;
    delete iOpDomain;
    delete iphoneSpecificCache;
    delete iVSSWhiteList;
    delete iFileWriteHandler;
    }

// -----------------------------------------------------------------------------
// CHttpCacheManager::VSSRequestCheck
//
// -----------------------------------------------------------------------------
//
TBool CHttpCacheManager::VSSRequestCheckL( const RHTTPTransaction& aTrans,
                                           const RHTTPHeaders& aHttpHeader,
                                           const TDesC8& aUrl )
    {
    TBool VSSTransaction ( EFalse );
    if ( iVSSCacheEnabled && HttpCacheUtil::VSSCacheContent( aUrl, iVSSWhiteList ) )
        {
        RStringPool strP = aTrans.Session().StringPool();

#ifdef __CACHELOG__
        RHTTPHeaders headers ( aHttpHeader );
        HBufC8* responseHeaderStr8 = HttpCacheUtil::HeadersToBufferLC( headers, strP );
        HBufC* responseHeaderStr;
        if( responseHeaderStr8 )
            {
            responseHeaderStr = HBufC::NewL( responseHeaderStr8->Length() );
            responseHeaderStr->Des().Copy( responseHeaderStr8->Des() );
            CleanupStack::PopAndDestroy(); //  responseHeaderStr8;
            TPtrC headerStr( responseHeaderStr->Des() );
            HttpCacheUtil::WriteLog( 0, _L("========== VSS Header Start =========\n") );
            HttpCacheUtil::WriteLog( 0, responseHeaderStr->Des() );
            HttpCacheUtil::WriteLog( 0, _L("========== VSS Header End =========\n") );
            delete responseHeaderStr;
            }
#endif

        TPtrC8 nameStr ( KVSSHeaderFieldName() );

        RStringF VSSnameStr = strP.OpenFStringL( nameStr );
        CleanupClosePushL<RStringF>( VSSnameStr);

        THTTPHdrVal tempVal;
        if ( aHttpHeader.GetField( VSSnameStr, 0, tempVal ) == KErrNone )
            {
            TPtrC8 valueStr ( KVSSHeaderFieldValue() );
            RStringF VSSValueStr = strP.OpenFStringL( valueStr );
            CleanupClosePushL<RStringF>( VSSValueStr );

            if ( tempVal == VSSValueStr )
                {
                VSSTransaction      = ETrue;
                }
            CleanupStack::PopAndDestroy(); // VSSValueStr
            }
        CleanupStack::PopAndDestroy(); // VSSnameStr
        } //end if( iVSSCacheEnabled && HttpCacheUtil::VSSCacheContent( aUrl, iVSSWhiteList ) )
    return VSSTransaction;
    }

// -----------------------------------------------------------------------------
// CHttpCacheManager::RequestL
//
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CHttpCacheManager::RequestL(
    RHTTPTransaction& aTrans,
    TBrCtlDefs::TBrCtlCacheMode aCacheMode,
    THttpCacheEntry& aCacheEntry )
    {
    //
    TInt status( KErrNotFound );
    if( iCacheEnabled || iVSSCacheEnabled )
        {
        CHttpCacheHandler* cache = CacheHandler( aTrans.Request().URI().UriDes(), NULL ) ;
        // adjust cache mode in case of offline operation
        if( iOfflineMode )
            {
            aCacheMode = TBrCtlDefs::ECacheModeOnlyCache;
            }
        //
        if( cache )
            {
            status = cache->RequestL( aTrans, aCacheMode, aCacheEntry );
            // save cache handler even if the entry is not in the cache
            // so that when the response comes back, we do not need to
            // check the url
             if( status == KErrNotFound && iVSSCacheEnabled )
                {
                 status = iphoneSpecificCache->RequestL( aTrans, aCacheMode, aCacheEntry );
                 if( (status  == KErrNotReady) || (status == KErrNone ) )
                 {
                 aCacheEntry.iCacheHandler = iphoneSpecificCache;
                 }
                 else
                 {
                 // No entry found in any cache. Default to normal cache
                 aCacheEntry.iCacheHandler = cache;
                 }
                }
            else
            {
            aCacheEntry.iCacheHandler = cache;
            }
           } //end if( cache )

        }//end  if( iCacheEnabled || iVSSCacheEnabled )
    return status;
    }

// -----------------------------------------------------------------------------
// CHttpCacheManager::RequestHeadersL
//
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CHttpCacheManager::RequestHeadersL(
    RHTTPTransaction& aTrans,
    THttpCacheEntry& aCacheEntry )
    {
    TInt status( KErrNotFound );
    if( ( iCacheEnabled || iVSSCacheEnabled ) && aCacheEntry.iCacheHandler )
        {
        status = aCacheEntry.iCacheHandler->RequestHeadersL( aTrans, aCacheEntry );
        }
    return status;
    }

// -----------------------------------------------------------------------------
// CHttpCacheManager::RequestNextChunkL
//
// -----------------------------------------------------------------------------
//
EXPORT_C HBufC8* CHttpCacheManager::RequestNextChunkL(
    RHTTPTransaction& aTrans,
    TBool& aLastChunk,
    THttpCacheEntry& aCacheEntry )
    {
    HBufC8* bodyStr = NULL;
    if( ( iCacheEnabled || iVSSCacheEnabled ) && aCacheEntry.iCacheHandler )
        {
        bodyStr = aCacheEntry.iCacheHandler->RequestNextChunkL( aTrans, aLastChunk, aCacheEntry );
        }

    return bodyStr;
    }

// -----------------------------------------------------------------------------
// CHttpCacheManager::RequestClosed
//
// -----------------------------------------------------------------------------
//
EXPORT_C void CHttpCacheManager::RequestClosed(
    RHTTPTransaction* aTrans,
    THttpCacheEntry& aCacheEntry )
    {
    if( ( iCacheEnabled || iVSSCacheEnabled ) && aCacheEntry.iCacheHandler )
        {
        aCacheEntry.iCacheHandler->RequestClosed( aTrans, aCacheEntry );
        }
    }

// -----------------------------------------------------------------------------
// CHttpCacheManager::ReceivedResponseHeadersL
//
// -----------------------------------------------------------------------------
//
EXPORT_C void CHttpCacheManager::ReceivedResponseHeadersL(
  RHTTPTransaction& aTrans,
    THttpCacheEntry& aCacheEntry )
    {
    if ( iCacheEnabled || iVSSCacheEnabled )
        {
        if ( iVSSCacheEnabled &&
             VSSRequestCheckL( aTrans, aTrans.Response().GetHeaderCollection(),
                               aTrans.Request().URI().UriDes() ) )
            {
            //Modify the cache handler if VSS specific
            aCacheEntry.iCacheHandler = iphoneSpecificCache;
            }
        if ( aCacheEntry.iCacheHandler )
            {
            aCacheEntry.iCacheHandler->ReceivedResponseHeadersL( aTrans, aCacheEntry );
            }
        }
    }

// -----------------------------------------------------------------------------
// CHttpCacheManager::ReceivedResponseBodyDataL
//
// -----------------------------------------------------------------------------
//
EXPORT_C void CHttpCacheManager::ReceivedResponseBodyDataL(
  RHTTPTransaction& aTrans,
    MHTTPDataSupplier& aBodyDataSupplier,
    THttpCacheEntry& aCacheEntry )
    {
    if( ( iCacheEnabled || iVSSCacheEnabled ) && aCacheEntry.iCacheHandler )
        {
        aCacheEntry.iCacheHandler->ReceivedResponseBodyDataL( aTrans, aBodyDataSupplier, aCacheEntry );
        }
    }

// -----------------------------------------------------------------------------
// CHttpCacheManager::ResponseComplete
//
// -----------------------------------------------------------------------------
//
EXPORT_C void CHttpCacheManager::ResponseComplete(
    RHTTPTransaction& aTrans,
    THttpCacheEntry& aCacheEntry )
    {
    if( ( iCacheEnabled || iVSSCacheEnabled ) && aCacheEntry.iCacheHandler )
        {
        aCacheEntry.iCacheHandler->ResponseComplete( aTrans, aCacheEntry );
        }
    }

// -----------------------------------------------------------------------------
// CHttpCacheManager::RemoveAllL
// Removes all files from HTTP cache. Also tries to remove orphaned files,
// i.e files found on disk, but not registered in the cache's lookup table
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CHttpCacheManager::RemoveAllL()
    {
    TInt numOfBytes( 0 );
    // do not remove op cache
    if( iCacheEnabled )
        {
        numOfBytes = iCache->RemoveAllL();
        }

    //failure here is not mission critical
    TRAP_IGNORE( RemoveOrphanedFilesL() );

    return numOfBytes;
    }

// -----------------------------------------------------------------------------
// CHttpCacheManager::RemoveOrphanedFilesL
// Removes header/body files that exist on the file-system, but are not known to the in-memory Cache lookup table(s)
// We do this because cache performance degrades substantially if there are too many files in a Symbian FAT32 directory.
// Browser crash or out-of-battery situations may cause SaveLookuptable() to be not called, leading to such "orphaned files".
// Due to high file-server interaction, don't call this method from performance critical code.
// -----------------------------------------------------------------------------
void CHttpCacheManager::RemoveOrphanedFilesL()
    {
    // Only proceed if we are running the browser configuration, otherwise we will accidentally
    // remove content we shouldn't.
    // get our SID.
    if( !IsBrowser() )
        return;
    
    // Step 1. Get map of disk content
    RFs rfs;
    User::LeaveIfError( rfs.Connect() );
    CleanupClosePushL( rfs );
    
    // Map that contains pointers to fully-qualified file paths as Keys, and "to be deleted flag" as Value.
    // Initially we mark everything as a candidate for deletion.
    CHttpCacheFileHash *onDiskFilesMap = NULL;
    HttpCacheUtil::GenerateCacheContentHashMapL( onDiskFilesMap, rfs, iCacheFolder, KCacheFileNeedsDelete );
    CleanupStack::PushL( onDiskFilesMap );
    
    // mark everything in the cache as no delete needed.
    MarkAllCacheContentAsNoDelete( onDiskFilesMap );

    // tell any other clients that we are about to remove their cached content
    WipeAllOtherIndexFilesL( onDiskFilesMap, rfs );
    
    // delete any remaining marked files
    DeleteMarkedFilesL( onDiskFilesMap, rfs );

    CleanupStack::PopAndDestroy(1, onDiskFilesMap );
    CleanupStack::PopAndDestroy(1, &rfs );
    }

void CHttpCacheManager::MarkAllCacheContentAsNoDelete( CHttpCacheFileHash* aOnDiskFilesMap )
    {
    //Step 2. Get list of known (non-orphaned) files in each Cache's in-memory lookup table. Flag them as DO NOT DELETE
    //Ask CacheHandlers to add their KNOWN files to this array. No ownership transfer occurs.
    //Don't go ahead if any of the cache handlers choke to insure correct deletion of files.
    if( iCache )
        iCache->ValidateCacheEntriesL(aOnDiskFilesMap);
    if( iOperatorCache )
        iOperatorCache->ValidateCacheEntriesL(aOnDiskFilesMap);
    if( iphoneSpecificCache )
        iphoneSpecificCache->ValidateCacheEntriesL(aOnDiskFilesMap);
    }

void CHttpCacheManager::GenerateEmptyIndexFileL(const TDesC& aIndexFile, RFs& aRfs )
    {
    // Going to remove non-web client cache files in OrphanedFilesL call,
    // Signal to these clients by emptying (or creating) an 'empty' index file
    // do this before we start cleaning up files, to lessen the chances that any
    // of the files are in use when we're trying to delete them.
    
    // 'adopt' code from httpcachelookuptable for dealing with indexes.
    // save entries to index.dat
    RFileWriteStream writeStream;

    TInt ret = KErrNone;
    TInt tryCount = 0;
    for (tryCount = 0; tryCount < 5; tryCount++) 
        {
        ret = writeStream.Replace( aRfs, aIndexFile, EFileWrite );
        if (ret == KErrInUse)
            {
            // When the cache is full, it takes 65 - 85 miliseconds to write the index.
            // So wait 50 miliseconds and try again
            User::After(50000);
            }
        else
            {
            break;
            }
        }
    if( ret == KErrNone )
        {
        CleanupClosePushL( writeStream );
        writeStream.WriteInt32L( KCacheVersionNumber );
        writeStream.WriteInt32L( 0 ); // no entries in the index.
        writeStream.CommitL();
        CleanupStack::PopAndDestroy(); // writeStream
        }
    }

void CHttpCacheManager::WipeAllOtherIndexFilesL( CHttpCacheFileHash* aOnDiskFilesMap, RFs& aRfs )
    {
    // look through hashmap for any .dat files which don't belong to the instantiated caches and overwrite them with blank ones.
    THttpCacheFileHashIter iter( aOnDiskFilesMap->HashMap() );
    const TDesC* key;
    while ((key = iter.NextKey()) != 0)
        {
        const TFileInfo* value ( (iter.CurrentValue()) );
        if( value->iUserInt == KCacheFileNeedsDelete )
            {
            TPtrC ext( key->Right(KIndexFileExtension().Length()) );
            if (    ext.CompareF( KIndexFileExtension ) == 0 ) // find any .dat index files
                { 
                GenerateEmptyIndexFileL( *key , aRfs );
                }
            }
        }
    }

void CHttpCacheManager::DeleteMarkedFilesL( CHttpCacheFileHash* aOnDiskFilesMap, RFs& aRfs )
    {
    //Step 3. Delete all files on disk that don't belong to any of the Cache Handlers.
    CFileMan* fileMan = CFileMan::NewL( aRfs );
    THttpCacheFileHashIter iter( aOnDiskFilesMap->HashMap() );
    const TDesC* key;
    while ((key = iter.NextKey()) != 0)
        {
        const TFileInfo* value ( (iter.CurrentValue()) );
        if( value->iUserInt == KCacheFileNeedsDelete )
            {
            TPtrC ext( key->Right(KIndexFileExtension().Length()) );
            if (    ext.CompareF( KIndexFileExtension ) != 0 && // ignore any .dat index files
                    ext.CompareF( KValidationFileExtension ) != 0 ) // ignore any .val files
                { 
                fileMan->Delete( *key );
                }
            }
        }
    delete fileMan;
    }

// -----------------------------------------------------------------------------
// CHttpCacheManager::RemoveL
//
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CHttpCacheManager::RemoveL(
    const TDesC8& aUrl )
    {
    TInt status( KErrNotFound );
    if( iCacheEnabled )
        {
        // do not remove op cache
        status = iCache->RemoveL( aUrl );
        }
    return status;
    }

// -----------------------------------------------------------------------------
// CHttpCacheManager::Find
//
// -----------------------------------------------------------------------------
//
EXPORT_C TBool CHttpCacheManager::Find(
    const TDesC8& aUrl )
    {
    TBool found( EFalse );
    //
    if( iCacheEnabled || iVSSCacheEnabled )
        {
        CHttpCacheHandler* cache = CacheHandler( aUrl, NULL );
        //
        if( cache )
            {
            found = cache->Find( aUrl );
            }

        if( !found && iVSSCacheEnabled )
            {
            found = iphoneSpecificCache->Find( aUrl );
            }
        }
    return found;
    }

// -----------------------------------------------------------------------------
// CHttpCacheManager::VSSHeaderCheck
//
// -----------------------------------------------------------------------------
//
TBool CHttpCacheManager::VSSHeaderCheck( TDes8*  aHttpHeaderString ) const
    {
    TBool found ( EFalse );
    if( aHttpHeaderString->Size() > 0 )
        {
        TPtrC8 nameStr8( KVSSHeaderFieldName() );

        TInt VSSnameLocation = aHttpHeaderString->FindC( nameStr8 ) ;

        if ( VSSnameLocation != KErrNotFound )
            {
            TPtrC8 valueStr8( KVSSHeaderFieldValue() );
            TInt VSSvalueLocation = aHttpHeaderString->FindC( KVSSHeaderFieldValue() );

            if ( (VSSvalueLocation != KErrNotFound ) && ( VSSnameLocation < VSSvalueLocation ) )
                {
                found = ETrue;
                }
            } //end if ( VSSnameLocation != KErrNotFound )
        } //end  if( aHttpHeaderString.Size() > 0 )

    return found;
    }

// -----------------------------------------------------------------------------
// CHttpCacheManager::SaveL
//
// -----------------------------------------------------------------------------
//
EXPORT_C TBool CHttpCacheManager::SaveL(
    const TDesC8& aUrl,
    const TDesC8& aHeader,
    const TDesC8& aContent,
    const TDesC8&  aHttpHeaderString)
    {
    TBool saved( EFalse );
    //
    if( iCacheEnabled || iVSSCacheEnabled )
        {
        HBufC8* headerStr = HBufC8::NewL( aHttpHeaderString.Length() );
        headerStr->Des().Copy( aHttpHeaderString );
        TPtr8 headerStrPtr8 ( headerStr->Des() ); //Any Type of TPtrc8
        CHttpCacheHandler* cache = CacheHandler( aUrl, &headerStrPtr8 );
        delete headerStr;
        //
        if( cache )
            {
            saved = cache->SaveL( aUrl, aHeader, aContent );
            }
        }
    return saved;
    }

// -----------------------------------------------------------------------------
// CHttpCacheManager::AddHeaderL
// Not supported for Vodafone specific storage.
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CHttpCacheManager::AddHeaderL(
    const TDesC8& aUrl,
    const TDesC8& aName,
    const TDesC8& aValue )
    {
    TBool status( KErrNotFound );
    //
    if( iCacheEnabled || iVSSCacheEnabled )
        {
        CHttpCacheHandler* cache = CacheHandler( aUrl, NULL );
        //
        if( cache )
            {
            status = cache->AddHeaderL( aUrl, aName, aValue );
            }
        }
    return status;
    }

// -----------------------------------------------------------------------------
// CHttpCacheManager::CreateCacheHandlersL
//
// -----------------------------------------------------------------------------
//
void CHttpCacheManager::HandleNotifyString(
    const TUint32 aKeyId,
    const TDesC& aValue )
    {
    // check offline mode
    if( aKeyId == KCoreAppUIsNetworkConnectionAllowed )
        {
        iOfflineMode = ( aValue == _L("1") ? EFalse : ETrue );
        }
    }

// -----------------------------------------------------------------------------
// CHttpCacheManager::CreateCacheHandlersL
//
// -----------------------------------------------------------------------------
//
void CHttpCacheManager::CreateCacheHandlersL()
    {
    // get our SID.
    RProcess process;
    TSecureId secId = process.SecureId();
    TUint32 secIdInt = secId;

    CRepository* repository = CRepository::NewLC(KCRUidCacheManager);
    CRepository* repositoryDiskLevel = CRepository::NewLC(KCRUidDiskLevel);

    // Get Cache Postpone Parameters.
    THttpCachePostponeParameters postpone;
    TBool newCentrepPresent;
    GetPostponeParamsL( newCentrepPresent, postpone, repository );

    // apply default cache configuration - no cache for anyone, sizes set to default.
    iCacheEnabled = EFalse;
    TInt cacheSize( KDefaultCacheSize );
    iCacheFolder.Copy( KDefaultCacheDir );
   
    TBool opCacheEnabled( EFalse );
    iOperatorCache = 0;
    TInt opCacheSize = KDefaultOperatorCacheSize;
    TFileName opCacheFolder( KDefaultOperatorCacheDir );
    
    iVSSCacheEnabled = EFalse;
    TInt vssCacheSize = KDefaultOperatorCacheSize;
    TFileName vssCacheFolder( KDefaultVssCacheDir );

    if(newCentrepPresent)
        {
        // if the new Centrep file is present, we trust it's configured properly.
        GetHttpCacheConfigL( *repository, iCacheEnabled, cacheSize, iCacheFolder );
        GetOperatorCacheConfigL( *repository, opCacheEnabled, opCacheSize, opCacheFolder );
        GetVSSCacheConfigL( *repository, iVSSCacheEnabled, vssCacheSize, vssCacheFolder );
        }
    
    // look for per-process overrides in central repository.  Do this before we get drive critical levels so we're on the right
    // drive.
    // This has a default string which applies the new per-client default configuration
    ApplyCacheOverridesL(*repository, secIdInt, iCacheEnabled, cacheSize, opCacheEnabled, iVSSCacheEnabled, iCacheFolder, KDefaultCacheDrive());

    TInt criticalLevel = 0;
    GetCriticalDriveLevelsL( *repositoryDiskLevel, iCacheFolder, criticalLevel );
    
    // Create any caches we should be using.
    if (iCacheEnabled)
        {
        CreateHttpCacheL( secIdInt, cacheSize, criticalLevel, postpone );

        if (opCacheEnabled)
            {
            CreateOperatorCacheL( *repository, opCacheFolder, opCacheSize, criticalLevel, postpone );
            }
        } //end if( iCacheEnabled )

    if ( iVSSCacheEnabled )
        {
        CreateVssCacheL( *repository, vssCacheFolder, vssCacheSize, criticalLevel, postpone );
        }

    CleanupStack::PopAndDestroy(2); // repository, repositoryDiskLevel
    CrashCheckL( secIdInt );
    }

void CHttpCacheManager::ApplyCacheOverridesL(CRepository& aRepository, const TUint32& aSecIdInt, TBool& aCacheEnabled, TInt& aCacheSize, TBool& aOpCacheEnabled, TBool& aVssCacheEnabled, TDes& aPath, const TDesC& aDefaultDrive)
    {
    TDriveUnit drive(aDefaultDrive);
    
    // set defaults
    if(aSecIdInt == KUIDBROWSERNG)       // for the browser, force use of Operator and VSS caches
        {
        aOpCacheEnabled = ETrue;
        aVssCacheEnabled = ETrue;
        }
    
    // read override string from centrep
    HBufC16 *overrideBuf = HBufC16::NewLC(64);
    TPtr overrideStr(overrideBuf->Des());
    TInt strLen;
    TInt err = aRepository.Get(KCacheManagerHttpCacheProcessOverride, overrideStr, strLen);
    if(strLen > overrideBuf->Length())
        {
        overrideBuf = overrideBuf->ReAllocL(strLen);
        // make sure cleanup stack contains correct pointer since ReAllocL always allocates a new des for larger space.
        CleanupStack::Pop();
        CleanupStack::PushL(overrideBuf);
        // reassign the TPtr
        overrideStr.Set(overrideBuf->Des());
        // pull in the whole string
        aRepository.Get(KCacheManagerHttpCacheProcessOverride, overrideStr, strLen);
        }
    // if we failed to load an override string, use the default.
    if( overrideStr.Length() == 0 )
        {
        CleanupStack::PopAndDestroy( overrideBuf );
        overrideBuf = KDefaultOverrideString().AllocLC();
        overrideStr.Set( overrideBuf->Des() );
        }
    // Built in Lex likes white space to separate strings, but easier to enter with ; separators.  Replace all ; with spaces.
    TInt pos=0;
    do{
        if(overrideStr[pos] == ';')
            {
            overrideStr[pos] = ' ';
            }
        pos++;
    }while(pos < overrideStr.Length());
    
    TLex overrideLex(overrideStr);
    do{
        TUint32 tempId;
        User::LeaveIfError(overrideLex.BoundedVal(tempId,EHex,KMaxTUint32));
        if(overrideLex.TokenLength() != 8)  // if we're not pointing at an SID in the string, we are incorrect and the override is broken.
            User::Leave(KErrCorrupt);
        overrideLex.SkipSpace();
        TInt32 tempCacheEnabled;
        User::LeaveIfError(overrideLex.BoundedVal(tempCacheEnabled,KMaxTInt32));
        overrideLex.SkipSpace();
        TInt32 tempCacheSize;
        User::LeaveIfError(overrideLex.BoundedVal(tempCacheSize,KMaxTInt32));
        overrideLex.SkipSpace();
        TDriveUnit tempDrive(overrideLex.NextToken());
        overrideLex.SkipSpaceAndMark();
        // found a hex SID matching ours, use the parameters.
        if(tempId == aSecIdInt)
            {
            aCacheEnabled = tempCacheEnabled;
            aCacheSize = tempCacheSize * 1024; // conf is in KB
            drive = tempDrive;
            break;
            }
    }while(!overrideLex.Eos());

    // Modify drive letter on aPath to match
    TParsePtr parsePath(aPath);
    TPtrC pathStr(parsePath.Path());
    TPath tempPath;
    tempPath.Format(_L("%c:%S"), TInt(drive)+'A', &pathStr);
    aPath.Copy(tempPath);
    HttpCacheUtil::EnsureTrailingSlash( aPath );
    
    CleanupStack::PopAndDestroy(overrideBuf);
    }

// -----------------------------------------------------------------------------
// CHttpCacheManager::CacheHandler
//
// -----------------------------------------------------------------------------
//
CHttpCacheHandler* CHttpCacheManager::CacheHandler(
    const TDesC8& aUrl,
    TDes8* aHttpHeaderString ) const
    {
    CHttpCacheHandler* cache;
    if( iVSSCacheEnabled && aHttpHeaderString && VSSHeaderCheck( aHttpHeaderString ) )
        {
        cache = iphoneSpecificCache;
        }
    else
        {
        cache = ( ( iOpDomain && HttpCacheUtil::OperatorCacheContent( iOpDomain->Des(), aUrl ) ) ? iOperatorCache : iCache );
        }
    return cache;
    }

void CHttpCacheManager::GetPostponeParamsL( TBool& aNewCentrepPresent, THttpCachePostponeParameters& aParams, CRepository* aRepo )
    {
    aNewCentrepPresent = EFalse;
    if (KErrNone == aRepo->Get(KCacheWritePostponeEnabled,
            aParams.iEnabled))
        {
        User::LeaveIfError(aRepo->Get(
                KCacheWritePostponeFreeRAMThreshold,
                aParams.iFreeRamThreshold));
        User::LeaveIfError(aRepo->Get(
                KCacheWritePostponeImmediateWriteThreshold,
                aParams.iImmediateWriteThreshold));
        User::LeaveIfError(aRepo->Get(KCacheWritePostponeWriteTimeout,
                aParams.iWriteTimeout));
        
        aNewCentrepPresent = ETrue;
        }
    }

void CHttpCacheManager::GetHttpCacheConfigL( CRepository& aRepository, TBool& aCacheEnabled, TInt& aCacheSize, TDes& aCacheFolder )
    {
    // General HTTP Cache
    TBool cacheEnabled( EFalse );
    aCacheFolder.Copy( KDefaultCacheDir );
    
    TInt err = aRepository.Get(KCacheManagerHttpCacheEnabled, cacheEnabled);
    aCacheEnabled = cacheEnabled;
    if( err == KErrNone )
        {
        // cache size
        TInt tempCacheSize;
        if( aRepository.Get(KCacheManagerHttpCacheSize, tempCacheSize) == KErrNone )
            aCacheSize = tempCacheSize;

        // cache location
        TFileName tempCacheLocation;
        if( aRepository.Get(KCacheManagerHttpCacheFolder, tempCacheLocation) == KErrNone )
            aCacheFolder.Copy( tempCacheLocation );
        }
    HttpCacheUtil::EnsureTrailingSlash( aCacheFolder );
    }

void CHttpCacheManager::GetOperatorCacheConfigL( CRepository& aRepository, TBool& aOpCacheEnabled, TInt& aOpCacheSize, TDes& aOpCacheFolder )
    {
    // Operator Cache
    aOpCacheEnabled = FeatureManager::FeatureSupported(KFeatureIdOperatorCache);
    if( aOpCacheEnabled )
        {
        TInt tempOpCacheSize;
        if( aRepository.Get(KOperatorCacheSize, tempOpCacheSize) == KErrNone )
            aOpCacheSize = tempOpCacheSize;

        // op cache folder
        TFileName tempOpCacheFolder;
        if( aRepository.Get(KOperatorCacheFolder, tempOpCacheFolder) == KErrNone )
            aOpCacheFolder.Copy( tempOpCacheFolder );
        }
    HttpCacheUtil::EnsureTrailingSlash( aOpCacheFolder );
    }

void CHttpCacheManager::GetVSSCacheConfigL( CRepository& aRepository, TBool& aVSSCacheEnabled, TInt& aVssCacheSize, TDes& aVssCacheFolder )
    {
    // VSS Cache
    TInt VSScacheEnabled(0);
    TInt err = aRepository.Get(KPhoneSpecificCacheEnabled, VSScacheEnabled);
    aVSSCacheEnabled = VSScacheEnabled;

    if ( err == KErrNone && aVSSCacheEnabled )
        {
        TInt tempVSSCacheSize;
        if( aRepository.Get(KPhoneSpecificCacheSize, tempVSSCacheSize) == KErrNone )
            aVssCacheSize = tempVSSCacheSize;

        TFileName tempVSScacheFolder;
        if( aRepository.Get(KPhoneSpecificCacheFolder, tempVSScacheFolder) == KErrNone )
            aVssCacheFolder.Copy( tempVSScacheFolder );
        }
    HttpCacheUtil::EnsureTrailingSlash( aVssCacheFolder );
    }

void CHttpCacheManager::GetCriticalDriveLevelsL( CRepository& aRepository, const TDesC& aCacheFolder, TInt& aCriticalLevel )
    {
    // get drive letter for sysutil
    TParsePtrC pathParser( aCacheFolder );
    TDriveUnit drive = pathParser.Drive();

    // get critical level
    // RAM drive can have different critical level
    TVolumeInfo vinfo;
    User::LeaveIfError(CCoeEnv::Static()->FsSession().Volume(vinfo, drive));
    //
    User::LeaveIfError(aRepository.Get((vinfo.iDrive.iType == EMediaRam ? KRamDiskCriticalLevel : KDiskCriticalThreshold), aCriticalLevel));
    }

void CHttpCacheManager::CreateHttpCacheL( const TInt& aSecIdInt, const TInt& aCacheSize, const TInt& aCriticalLevel, const THttpCachePostponeParameters& aPostpone )
    {
    // browser gets the normal index.dat name, other clients get <SID>.dat
    TFileName indexFile( KDefaultIndexFile() );
    if( aSecIdInt != KUIDBROWSERNG )
        {
        indexFile.Format(_L("%08x.dat"), aSecIdInt);
        }
    // create cache handler
    iCache = CHttpCacheHandler::NewL( aCacheSize, iCacheFolder, indexFile, aCriticalLevel, aPostpone);
    }

void CHttpCacheManager::CreateOperatorCacheL( CRepository& aRepository, const TDesC& aOpCacheFolder, const TInt& aOpCacheSize, const TInt& aCriticalLevel, const THttpCachePostponeParameters& aPostpone )
    {
    TBuf<512> url;
    // if domain is missing, then no need to read further
    if (aRepository.Get(KOperatorDomainUrl, url) == KErrNone)
        {
        HBufC8* opDomain8 = HBufC8::NewL(url.Length());
        CleanupStack::PushL(opDomain8);
        opDomain8->Des().Append(url);
    
        TInt slashPos = opDomain8->LocateReverse('/');
        if (slashPos == -1)
            {
            slashPos = 0;
            }
    
        TPtrC8 temp = opDomain8->Left(slashPos);
        iOpDomain = temp.AllocL();
        CleanupStack::PopAndDestroy(opDomain8);
    
        // create op cache
        iOperatorCache = CHttpCacheHandler::NewL( aOpCacheSize, aOpCacheFolder, KDefaultOpIndexFile(), aCriticalLevel, aPostpone);
        }
    }

void CHttpCacheManager::CreateVssCacheL( CRepository& aRepository, const TDesC& aVssCacheFolder, const TInt& aVssCacheSize, const TInt& aCriticalLevel, const THttpCachePostponeParameters& aPostpone )
    {
    //Get the white list
    TBuf<2048> whiteList;
    
    if (aRepository.Get(KPhoneSpecificCacheDomainUrl, whiteList) == KErrNone)
        {
        iVSSWhiteList = HBufC8::NewL(whiteList.Length());
        iVSSWhiteList->Des().Append(whiteList);
        }
    else
        {
        iVSSWhiteList = NULL;
        }
    
    // create cache handler
    iphoneSpecificCache = CHttpCacheHandler::NewL(aVssCacheSize, aVssCacheFolder, KDefaultVSSIndexFile(), aCriticalLevel, aPostpone);
    }

void CHttpCacheManager::CrashCheckL( const TInt& aSecIdInt )
    {
    if( aSecIdInt == KUIDBROWSERNG )
        {
        //////////////////////////////////////////////////////////////////////////////////////////////////////
        // Remove orphan files if browser didn't close cache properly - should only happen very occasionally
        //
        // We leave a file on the drive when we are in use and remove it when we close properly
        // If the file is already there we expect files in the cache which have not been added to the index.
        //
        //////////////////////////////////////////////////////////////////////////////////////////////////////
        RFs rfs;
        User::LeaveIfError( rfs.Connect() );
        CleanupClosePushL( rfs );
        rfs.SetSessionPath( iCacheFolder );
        RFile crashTest;
        TInt err = crashTest.Create(rfs,KCrashCheck(),EFileRead);
        crashTest.Close();  
        if ( err == KErrAlreadyExists )
            {
    #ifdef __CACHELOG__
            HttpCacheUtil::WriteLog(0, _L("Crash detected - removing orphan files"));
    #endif
            RemoveOrphanedFilesL();
            }
        CleanupStack::PopAndDestroy( &rfs );
        }
    }

//  End of File
