/*
* Copyright (c) 2006-2010 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:  Implementation of CSelectiveFormatter class
*
*/


#include <f32file.h>
#include <swi/sisregistrysession.h>
#include <swi/sisregistryentry.h>
#include <swi/sisregistrypackage.h>

#include "dirstackentry.h"
#include "selectiveformatter.h"
#include "formatterrfsplugincommon.h"
#include "rfsfileman.h"
#include "trace.h"

_LIT(KZDrive,"z");
// ================= MEMBER FUNCTIONS =======================

// ---------------------------------------------------------------------------
// CSelectiveFormatter::NewL
// ---------------------------------------------------------------------------
//
CSelectiveFormatter* CSelectiveFormatter::NewLC( const TDesC& aExcludeListFile )
    {
    CSelectiveFormatter* self = new ( ELeave ) CSelectiveFormatter;
    CleanupStack::PushL( self );
    self->ConstructL( aExcludeListFile );
    return self;
    }


// ---------------------------------------------------------------------------
// CSelectiveFormatter::~CSelectiveFormatter
// ---------------------------------------------------------------------------
//
CSelectiveFormatter::~CSelectiveFormatter()
    {
    FUNC_LOG;

    delete iFileMan;

    iExcludeList.ResetAndDestroy();
    iExcludeList.Close();

    iDirStack.ResetAndDestroy();
    iDirStack.Close();

    iFs.Close();
    }


// ---------------------------------------------------------------------------
// CSelectiveFormatter::FindAndDeleteL
// ---------------------------------------------------------------------------
//
void CSelectiveFormatter::FindAndDeleteL( const TDesC& aFullPath )
    {
    FUNC_LOG;

    iDirStack.ResetAndDestroy();
    if ( PreserveType( aFullPath ) != CExcludeListEntry::EPreserveAll )
        {
        HBufC* buffer = HBufC::NewLC( KMaxPath );
        TPtr ptr = buffer->Des();
        CDirStackEntry::PushDirEntryL( iDirStack, aFullPath, iFs ); // Setup root dir
        while ( iDirStack.Count() > 0 )
            {
            CDirStackEntry& stackEntry = *( iDirStack[ iDirStack.Count() - 1 ] );
            const TEntry* dirEntry = stackEntry.GetNextEntry( ptr );
            if ( dirEntry )
                {
                HandleDirEntryL( ptr, *dirEntry );
                }
            else
                {
                // Dir has been processed. Pop it and continue.
                CDirStackEntry::PopAndDestroyDirEntry( iDirStack );
                }
            }
        CleanupStack::PopAndDestroy( buffer );
        }
    else
        {
        INFO_1( "Keep dir: '%S'", &aFullPath );
        }
    }


// ---------------------------------------------------------------------------
// CSelectiveFormatter::CSelectiveFormatter
// ---------------------------------------------------------------------------
//
CSelectiveFormatter::CSelectiveFormatter()
    {
    FUNC_LOG;
    }


// ---------------------------------------------------------------------------
// CSelectiveFormatter::ConstructL
// ---------------------------------------------------------------------------
//
void CSelectiveFormatter::ConstructL( const TDesC& aExcludeListFile )
    {
    FUNC_LOG;

    User::LeaveIfError( iFs.Connect() );
    iFileMan = CRfsFileMan::NewL( iFs );

    // Handle excludelist.txt
    if ( aExcludeListFile.Match( KEmptyParameter ) == KErrNotFound )
        {
        ReadExcludeListL( aExcludeListFile );
        }
    // Handle app specific files
    HandleAppExcludeListsL();
    
    // Handle NR-Applications
    HandleNrExcludeListsL();
    
    if( !iValidExcludeListFound )
        {
        User::Leave( KErrInvalidExcList );
        }
    }


// ---------------------------------------------------------------------------
// CSelectiveFormatter::ReadExcludeListL
// ---------------------------------------------------------------------------
//
void CSelectiveFormatter::ReadExcludeListL( const TDesC& aExcludeListFile )
    {
    FUNC_LOG;

    RFile file;
    TInt err = file.Open( iFs, aExcludeListFile, EFileRead );
    ERROR_1( err, "Failed to open '%S'", &aExcludeListFile );
    if ( err == KErrNone )
        {
        // exclude list successfully opened -> Selective format.
        INFO_1( "Using exclude list '%S'", &aExcludeListFile );
        CleanupClosePushL( file );

        // Add exclude list file itself to exclude list
        CExcludeListEntry* entry = CExcludeListEntry::NewLC( aExcludeListFile ); // take ownership
        iExcludeList.AppendL( entry );
        CleanupStack::Pop( entry );

        TFileText text;
        text.Set( file );
        TFileName buffer;
        text.Read( buffer ); //carry on if read fails
        while ( buffer.Length() > 0 )
            {
            entry = CExcludeListEntry::NewL( buffer );
            err = iExcludeList.InsertInOrder( entry, CExcludeListEntry::Compare ); // take ownership
            if( err != KErrNone )
                {
                delete entry; // delete entry if ownership not transferred

                if( err != KErrAlreadyExists )
                    {
                    User::Leave( err );
                    }
                }            

            text.Read( buffer ); //carry on if read fails
            iValidExcludeListFound = ETrue;
            }

        CleanupStack::PopAndDestroy( &file );
        }
    }


// ---------------------------------------------------------------------------
// CSelectiveFormatter::PreserveType
// ---------------------------------------------------------------------------
//
CExcludeListEntry::TPreserveType
CSelectiveFormatter::PreserveType( const TDesC& aFullPath ) const
    {
    FUNC_LOG;

    CExcludeListEntry::TPreserveType ret = CExcludeListEntry::EPreserveNone;
    CExcludeListEntry::TMatchEntry entry( aFullPath );
    TInt pos = iExcludeList.Find( entry, CExcludeListEntry::CompareMatch );
    if ( pos >= 0 && pos < iExcludeList.Count() )
        {
        TPtrC matchPath = iExcludeList[pos]->Path();
        ret = iExcludeList[pos]->Type( entry );
        INFO_3( "'%S' matches '%S' type %d", &aFullPath, &matchPath, ret );
        }

    return ret;
    }


// ---------------------------------------------------------------------------
// CSelectiveFormatter::HandleDirEntryL
// ---------------------------------------------------------------------------
//
void CSelectiveFormatter::HandleDirEntryL(
    const TDesC& aFullPath,
    const TEntry& aEntry )
    {
    FUNC_LOG;

    CExcludeListEntry::TPreserveType preserve = PreserveType( aFullPath );

    if ( preserve == CExcludeListEntry::EPreserveNone )
        {
        TRAPD_ERR( err, iFileMan->DeleteL( aFullPath, aEntry ) );
        ERROR_1( err, "Failed to delete '%S'", &aFullPath );
        }
    else if ( aEntry.IsDir() && preserve != CExcludeListEntry::EPreserveAll )
        {
        INFO_1( "Keep matches from: '%S'", &aFullPath );
        CDirStackEntry::PushDirEntryL( iDirStack, aFullPath, iFs );
        }
    else
        {
        INFO_1( "Keep '%S'", &aFullPath );
        }
    }

// ---------------------------------------------------------------------------
// CSelectiveFormatter::HandleAppExcludeListsL
// ---------------------------------------------------------------------------
//
void CSelectiveFormatter::HandleAppExcludeListsL()
    {
    FUNC_LOG;
    HBufC* buf = HBufC::NewLC( KMaxPath );
    TPtr bufPtr = buf->Des();
    iSystemDrive = iFs.GetSystemDriveChar();
    
    // Search from rom and system drives.
    bufPtr.Append( KApplicationExcludeListPath );
    bufPtr.Append( KApplicationExcludeListFileSearchPattern );
    HandleAppExcludeListsOnDriveL( bufPtr, KDefaultRom()[0] );
    bufPtr.Zero();
    bufPtr.Append( KApplicationExcludeListPath );
    bufPtr.Append( KApplicationExcludeListFileSearchPattern );
    HandleAppExcludeListsOnDriveL( bufPtr, iSystemDrive );

    CleanupStack::PopAndDestroy( buf );
    }

// ---------------------------------------------------------------------------
// CSelectiveFormatter::HandleAppExcludeListsOnDriveL
// ---------------------------------------------------------------------------
//
void CSelectiveFormatter::HandleAppExcludeListsOnDriveL( TPtr aBuf, TChar aDrive )
    {
    FUNC_LOG;
    CDir* dir = NULL;
    
    aBuf[0] = aDrive;
    TInt err = iFs.GetDir( aBuf, KEntryAttNormal, ESortNone, dir );

    ERROR_1( err, "RFs::GetDir error while reading drive %c", (TUint)aDrive );
    
    if( err == KErrNone )
        {
        CleanupStack::PushL( dir );
        // Remove search pattern.
        aBuf.Delete( KApplicationExcludeListPath().Length(), 
            KApplicationExcludeListFileSearchPattern().Length() );
    
        for( TInt i = 0; i < dir->Count(); i++ )
            {
            TBool upgradeFound( EFalse );
            const TEntry& entry = ( *dir )[i];
            aBuf.Append( entry.iName );
    
            if( aDrive == KDefaultRom()[0] )
                {
                // Change to system drive for upgrade check
                aBuf[0] = iSystemDrive;
                RFile file;
                err = file.Open( iFs, aBuf, EFileRead );
                if( err == KErrNone )
                    {
                    // Upgrade found - rom file not used
                    upgradeFound = ETrue;
                    file.Close();
                    }
                else
                    {
                    // Upgrade not found - use rom file                
                    aBuf[0] = KDefaultRom()[0];
                    }
                }

            if( !upgradeFound )
                {
                RFile file;
                err = file.Open( iFs, aBuf, EFileRead );
                if ( err == KErrNone )
                    {
                    TInt fileSize( 0 );
                    err = file.Size( fileSize );
                    file.Close();
                    INFO_3( "Application exclude list '%S', err %d, size %d ", &aBuf, err, fileSize );
                    if ( err != KErrNone || fileSize == 0 )
                        {
                        // Empty file
                        INFO_1( "Application exclude list '%S' is empty", &aBuf );
                        }
                    else
                        {
                        ReadExcludeListL( aBuf );                    
                        }
                    }
                }
            // Remove file name.
            aBuf.Delete( KApplicationExcludeListPath().Length(), entry.iName.Length() );
            }
        CleanupStack::PopAndDestroy( dir );
        dir = NULL;
        }
    }

// ---------------------------------------------------------------------------
// CSelectiveFormatter::HandleNrExcludeListsL
// ---------------------------------------------------------------------------
//
void CSelectiveFormatter::HandleNrExcludeListsL()
    {
    INFO( "CSelectiveFormatter::HandleNrExcludeListsL() START ");
    
    Swi::RSisRegistrySession session;
    CleanupClosePushL(session);
    User::LeaveIfError(session.Connect());
    
    INFO( "In CSelectiveFormatter::HandleNrExcludeListsL() RSisRegistrySession::Connect() established");
    // Get the installed application UIDs
    RArray<TUid> uids;
    CleanupClosePushL(uids);
    session.InstalledUidsL(uids);
    TInt uidcount = uids.Count(); 
    
    Swi::RSisRegistryEntry regEntry;
    Swi::RSisRegistryEntry augmentForRegEntry;
    CleanupClosePushL(regEntry);
    CleanupClosePushL(augmentForRegEntry);
    
    // Array of registry files i.e., .reg and .ctl for the installed apps
    RPointerArray<HBufC> registryFiles;
    
    // Array of registry files i.e., .reg and .ctl for the augmented apps
    RPointerArray<HBufC> augmentedRegistryFiles;
    
    // Array of files installed through package.
    RPointerArray<HBufC> nonRemovableFiles;
    
    // Array of augmented files installed through package.
    RPointerArray<HBufC> nonRemovableAugmentedFiles;
    
    CleanupResetAndDestroyPushL(registryFiles);
    CleanupResetAndDestroyPushL(augmentedRegistryFiles);
    CleanupResetAndDestroyPushL(nonRemovableFiles);
    CleanupResetAndDestroyPushL(nonRemovableAugmentedFiles);
    
    TInt count;
    
    //Array of augmented packages
    RPointerArray<Swi::CSisRegistryPackage> augmentationPackages;
    CleanupResetAndDestroyPushL(augmentationPackages);
        
    for ( TInt iter=0; iter<uidcount; iter++)
     {
     User::LeaveIfError(regEntry.Open(session,uids[iter]));
     if(EFalse == regEntry.RemovableL())
         {
         INFO( "In CSelectiveFormatter::HandleNrExcludeListsL() get the nonRemovable and registry files");
         
         regEntry.FilesL(nonRemovableFiles);
         regEntry.RegistryFilesL(registryFiles);
         TInt fileCount = nonRemovableFiles.Count(); 
         for (TInt nonRemovableFilesCount=fileCount-1; nonRemovableFilesCount>=0;nonRemovableFilesCount--)
             {
             TPtr nrFileName(nonRemovableFiles[nonRemovableFilesCount]->Des());
             if(nrFileName.Left(1) == KZDrive )
                 {
                 delete nonRemovableFiles[nonRemovableFilesCount];
                 nonRemovableFiles.Remove(nonRemovableFilesCount);
                 }
             }
         // Look for augmentations.
         if(regEntry.IsAugmentationL())
             {
             regEntry.AugmentationsL(augmentationPackages);
             count = regEntry.AugmentationsNumberL();
             for (TInt augPkgCount=0; augPkgCount < count; ++augPkgCount)
                 {
                 User::LeaveIfError(augmentForRegEntry.OpenL(session,*augmentationPackages[augPkgCount]));
                 if(EFalse == augmentForRegEntry.RemovableL())
                     {
                     INFO( "In CSelectiveFormatter::HandleNrExcludeListsL() get the augmented nonRemovable and registry files");
                     augmentForRegEntry.FilesL(nonRemovableAugmentedFiles);
                     augmentForRegEntry.RegistryFilesL(augmentedRegistryFiles);
                     }
                 augmentForRegEntry.Close();
                 }
             }
         }
     AppendNrlisttoExcludeListL(nonRemovableFiles);
     nonRemovableFiles.ResetAndDestroy();
     regEntry.Close();
     }
    INFO( "In CSelectiveFormatter::HandleNrExcludeListsL() append the list of files to the excludelist ");
    
    AppendNrlisttoExcludeListL(nonRemovableAugmentedFiles);
    AppendNrlisttoExcludeListL(augmentedRegistryFiles);
    AppendNrlisttoExcludeListL(registryFiles);
    
    CleanupStack::PopAndDestroy(9,&session);
    INFO( "CSelectiveFormatter::HandleNrExcludeListsL() End");
    }

// ---------------------------------------------------------------------------
// CSelectiveFormatter::HandleNrExcludeListsL
// ---------------------------------------------------------------------------
//

void CSelectiveFormatter::AppendNrlisttoExcludeListL(RPointerArray<HBufC> &aFileNameArr)
    {
    INFO( "CSelectiveFormatter::AppendNrlisttoExcludeListL() START ");
    TInt size = aFileNameArr.Count();
    CExcludeListEntry* entry;
    TInt err;
    for ( TInt i=0; i < size; i++)
        {
        entry = CExcludeListEntry::NewL( aFileNameArr[i]->Des() );
        err = iExcludeList.InsertInOrder( entry, CExcludeListEntry::Compare ); // take ownership
        if( err != KErrNone )
            {
            delete entry; // delete entry if ownership not transferred
            
            if( err != KErrAlreadyExists )
                {
                INFO_1( "CSelectiveFormatter::AppendNrlisttoExcludeListL() leaves with error code %d",err);
                User::Leave( err );
                }
            }            
        }
    INFO( "CSelectiveFormatter::AppendNrlisttoExcludeListL() END ");
    }
