/*
* Copyright (c) 2005 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 class CFileSaver.   
*      
*
*/


// INCLUDE FILES
#include <platform/mw/Browser_platform_variant.hrh>
#include "FileSaver.h"
#include "CodBuffStorage.h"
#include "CodLoadObserver.h"
#include "RFeatMgr.h"
#include "CodUtil.h"
#include "CodLogger.h"
#include "CodError.h"
#include "CodPanic.h"
#include "DrmHandler.h"
#include "CodData.h"

#include <f32file.h>
#include <HttpDownloadMgrCommon.h>
#include <DocumentHandler.h>
#include <pathinfo.h>

#ifdef BRDO_APP_GALLERY_SUPPORTED_FF
#include <MGXFileManagerFactory.h>
#include <CMGXFileManager.h>
#endif

#include <DcfEntry.h>
#include <DcfRep.h>

const TInt KDefaultStorageBufferSize = 128 * 1024;
const TInt KDefaultStorageBufferSizePD = 16 * 1024;


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

// ---------------------------------------------------------
// CFileSaver::NewL()
// ---------------------------------------------------------
//
CFileSaver* CFileSaver::NewL
        (
        const TDesC8& aType,
        RFs& aFs,
        CDocumentHandler& aDocHandler,
        const TFileName& aTempPath,
        const TFileName& aRootPath,
        const TFileName& aFname
        )
    {
    CFileSaver* saver = new (ELeave) CFileSaver
        ( aType, aFs, aDocHandler, aTempPath, aRootPath, aFname );
    CleanupStack::PushL( saver );
    saver->ConstructL();
    CleanupStack::Pop( saver );
    return saver;
    }
// ---------------------------------------------------------
// CFileSaver::ConstructL()
// ---------------------------------------------------------
//
void CFileSaver::ConstructL()
    {
    CCodSaver::ConstructL();
    iBufferSize =  KDefaultStorageBufferSize ;
    iProgressiveDownload = EFalse ;
    
    iStorage = CCodBuffStorage::NewL(this);
    }

// ---------------------------------------------------------
// CFileSaver::CFileSaver()
// ---------------------------------------------------------
//
CFileSaver::CFileSaver
    (
    const TDesC8& aType,
    RFs& aFs,
    CDocumentHandler& aDocHandler,
    const TFileName& aTempPath,
    const TFileName& aRootPath,
    const TFileName& aFname
    )
: CCodSaver( aType ),
  iDocHandler( aDocHandler ),
  iFs( aFs ),
  iTempPath( aTempPath ),
  iRootPath( aRootPath ),
  iFname( aFname)
    {
    CLOG(( ECodEng, 2, _L("*** CFileSaver::CFileSaver") ));
    }
  
// ---------------------------------------------------------
// CFileSaver::~CFileSaver()
// ---------------------------------------------------------
//
CFileSaver::~CFileSaver()
    {
    CLOG(( ECodEng, 2, _L("-> CFileSaver::~CFileSaver") ));
    delete iDrmHandler;
    delete iStorage;
    CLOG(( ECodEng, 2, _L("<- CFileSaver::~CFileSaver") ));
    }

// ---------------------------------------------------------
// CFileSaver::OpenStoreL()
// ---------------------------------------------------------
//
void CFileSaver::OpenStoreL()
    {
    CLOG(( ECodEng, 2, _L("CFileSaver::OpenStoreL") ));
    __ASSERT_DEBUG( iState == EInit, CodPanic( ECodInternal ) );
    
    // Store (temp file) is not opened now.
    // Opening is deferred until first data chunk arrives.

    TInt err = iFile.Open(  iFs, 
                            iFname, 
                            EFileShareAny | 
                            EFileStream | 
#ifdef BRDO_RFILE_WRITE_DIRECT_IO_FF
                            EFileWrite |
                            EFileWriteDirectIO );
#else                            
                            EFileWrite );
#endif                            
                       
	if(err == KErrNotFound)                               
	    {
        err = iFile.Replace( iFs, 
                           iFname, 
                           EFileShareAny | 
                           EFileStream | 
#ifdef BRDO_RFILE_WRITE_DIRECT_IO_FF
                                       EFileWrite |
                                       EFileWriteDirectIO );
#else                           
                           EFileWrite );
#endif                           

	    }

    if( !err )
        {
        TInt pos( 0 );
        iFile.Seek( ESeekEnd, pos );
        }
    iSize = 0;
    iState = EStoreOpen;
    }
    
// ---------------------------------------------------------
// CFileSaver::AppendData()
// ---------------------------------------------------------
//
TInt CFileSaver::AppendData( const TDesC8& aData )
    {
    CLOG(( ECodEng, 4, \
        _L("-> CFileSaver::AppendData (%d) bytes"), aData.Length() ));
//TODO:    __ASSERT_DEBUG( iState == EStoreOpen, CodPanic( ECodInternal ) );
    TInt err = CheckMaxSize( aData.Size() );
    if ( !err && !iFile.SubSessionHandle() )
        {
//TODO:        __ASSERT_DEBUG( !iFname.Length(), CodPanic( ECodInternal ) );
        (void)iFs.MkDirAll( iTempPath );

        /*if( !iPausable )
            {
            // nothing persisted yet. If there's a file with the same name,
            // delete it.
            err = iFile.Replace( iFs, 
                                   iFname, 
                                   EFileShareAny | 
                                   EFileStream | 
                                   EFileWrite );
            }
        else*/

        }
    if ( !err )
        {
        err = iStorage->WriteOutNextBodyDataL(aData );
        }
    
    CLOG(( EHttpLoad, 2, _L("<- CFileSaver::AppendData returns (%d)"), err ));
    return err;
    }

// ---------------------------------------------------------
// CFileSaver::CloseStore()
// ---------------------------------------------------------
//
void CFileSaver::CloseStore()
    {
    CLOG(( ECodEng, 2, _L("-> CFileSaver::CloseStore") ));
    FlushL();
    iFile.Close();
    iState = EStoreClosed;
    CLOG(( ECodEng, 2, _L("<- CFileSaver::CloseStore") ));
    }

// ---------------------------------------------------------
// CFileSaver::CheckResponseAttributesL()
// ---------------------------------------------------------
//
void CFileSaver::CheckResponseAttributesL( const CCodData& aData )
    {
    CLOG(( ECodEng, 2, _L("-> CFileSaver::CheckResponseAttributesL") ));
    __ASSERT_DEBUG( iState == EStoreClosed, CodPanic( ECodInternal ) );
#ifdef __TEST_COD_LOG
    TPtrC8 mime( iType.Des8() );
    CLOG(( ECodEng, 4, _L8("  MIME==<%S>, size(%d)"), &mime, iSize ));
#endif /* def __TEST_COD_LOG */

    if ( !iSize )
        {
        CLOG(( ECodEng, 4, _L("  0 bytes data") ));
        User::Leave( KErrCodAttributeMismatch );
        }
//TODO:    __ASSERT_DEBUG( iFname.Length(), CodPanic( ECodInternal ) );

    // Compare content MIME type against descriptor.

    // Size is not checked, no exact match is required. Quote form spec:
    // "The storage size and the execution size are dependent on the
    // environment and may be different from the value of the size attribute.
    // The transport size may also be different, if compression or some
    // packaging format is used."
    //
    // There is a safety upper bound on the transaction size, that is already
    // applied. See SetMaxSize().
    
    TDataType drm( KOma1DrmMessageContentType );
    TDataType dcf( KOma1DcfContentType );

    if ( iType == drm )
        {
        // DRM message arrived.
        // DRM filter should have already turned this into a DCF. Fail now.
        CLOG(( ECodEng, 4, _L("  DRM unexpected") ));
        User::Leave( KErrCodAttributeMismatch );
        }
    else if ( iType == dcf )
        {
        // DCF arrived. MIME type check may be limited:
        // - Received file (inside DRM) must be DRM-supported (CDrmHandler
        //   does this).
        // - COD file must specify COD, DRM, or the received MIME type.
        __ASSERT_DEBUG( !iDrmHandler, CodPanic( ECodInternal ) );
        iDrmHandler = CDrmHandler::NewL( iFname );
        TDataType typeInsideDcf( iDrmHandler->Type() );
#ifdef __TEST_COD_LOG
        mime.Set( typeInsideDcf.Des8() );
        CLOG(( ECodEng, 4, _L8("  inside DCF: MIME==<%S>"), &mime ));
#endif /* def __TEST_COD_LOG */
        if ( ! ((*aData[aData.ActiveDownload()]).HasType( typeInsideDcf.Des8() ) ||
                (*aData[aData.ActiveDownload()]).HasType( KOma1DrmMessageContentType ) ||
                (*aData[aData.ActiveDownload()]).HasType( KOma1DcfContentType ) )
           )
            {
            // MIME type mismatch, attribute mismatch.
            CLOG(( ECodEng, 4,  _L("  type mismatch") ));
            User::Leave( KErrCodAttributeMismatch );
            }
        }
    else
        {
        // Other than DRM stuff arrived. Proper check for MIME type.
        if( !(*aData[aData.ActiveDownload()]).HasType( iType.Des8() ) )
            {
            CLOG(( ECodEng, 4, _L("  mismatch") ));
            User::Leave( KErrCodAttributeMismatch );
            }
        }

    iState = ERespChecked;
    CLOG(( ECodEng, 2, _L("<- CFileSaver::CheckResponseAttributesL (match)") ));
    }


// ---------------------------------------------------------
// CFileSaver::BulkInstallL()
// ---------------------------------------------------------
//
void CFileSaver::BulkInstallL( TRequestStatus* aStatus, const CCodData &aData, const TBool aAttached )
    {
    CLOG(( ECodEng, 2, _L("-> CFileSaver::BulkInstallL") ));
    __ASSERT_DEBUG( iState == ERespChecked, CodPanic( ECodInternal ) );
    __ASSERT_DEBUG( aStatus, CodPanic( ECodInternal ) );

/* check if the PD app has been started (PD appl is responsible for file moving after play is done)
   In this case leave and go to the next state.
*/
    if (!aAttached)
	    {
#if 0 
        RFs fs;
        TInt err( KErrNone );
        HBufC* filename = HBufC::NewLC(KMaxFileName);
        TPtr filenamePtr = filename->Des();
        filenamePtr = iRootPath;
        filenamePtr.Append(_L("download\\"));
        User::LeaveIfError( fs.Connect() );
        CleanupClosePushL(fs);
        CFileMan* file=CFileMan::NewL(fs);
        CleanupStack::PushL(file);
        TInt error = fs.MkDirAll(filenamePtr);
        if (error!=KErrNone && error!=KErrAlreadyExists)
           {
            User::Leave(error);   
           }
   
        // Find a unique name to avoid any conflict.
        // Here iFname has full path of current location of file
        // and filename has destination path.
        FindUniqueDestinationFileNameL( iFname, filename );
        
        filenamePtr = filename->Des();     
               
        err = file->Move(iFname, filenamePtr, CFileMan::EOverWrite);
    
        if(err != KErrNone)
           {
           User::Leave(err);
           }
       
        iFname = filenamePtr;              
        NotifyMediaGalleryL( filenamePtr );        
        CleanupStack::PopAndDestroy(file);
        CleanupStack::PopAndDestroy(&fs);
        CleanupStack::PopAndDestroy(filename);
#else
        RFs fs;
        User::LeaveIfError( fs.Connect() );
        CleanupClosePushL(fs);
        CFileMan* file=CFileMan::NewL(fs);
        CleanupStack::PushL(file);
        
        
        for( TInt i = 1; i <= aData.Count() ; ++i )
            {
            HBufC* filename = HBufC::NewLC(KMaxFileName);
            TPtr filenamePtr = filename->Des();
            filenamePtr = (*aData[i]).iRootPath;

            filenamePtr.Append(_L("download\\"));
            TInt error = fs.MkDirAll(filenamePtr);
            if (error!=KErrNone && error!=KErrAlreadyExists)
               {
                User::Leave(error);   
               }
            iFname = (*aData[i]).iFullName->Des();
            
            // Find a unique name to avoid any conflict.
            // Here iFname has full path of current location of file
            // and filename has destination path.
            FindUniqueDestinationFileNameL( iFname, filename );
            
            filenamePtr = filename->Des();
            TInt err = file->Move(iFname, filenamePtr, CFileMan::EOverWrite);
            if(err != KErrNone)
            	{
            	User::LeaveIfError(err);
            	}
            iFname = filenamePtr;              
            NotifyMediaGalleryL( filenamePtr );

            (*aData[i]).iFileName = iFname;
            (*aData[i]).iFullName = NameL();
            CleanupStack::PopAndDestroy(filename);
            }
        CleanupStack::PopAndDestroy(file);
        CleanupStack::PopAndDestroy(&fs);

#endif // RD_MULTIPLE_DRIVE
    // TODO set iType to that of the packaged object (??)
    CLOG(( ECodEng, 4, _L("CFileSaver::InstallL: saved==<%S> handler(0x%x)"), \
        &iFname, iHandler ));

	}

    // Normal file saving is not async.
    *aStatus = KRequestPending;
    User::RequestComplete( aStatus, KErrNone );

    iState = EInstalled;
    CLOG(( ECodEng, 2, _L("<- CFileSaver::BulkInstallL") ));
    }


// ---------------------------------------------------------
// CFileSaver::InstallL()
// ---------------------------------------------------------
//
void CFileSaver::InstallL( TRequestStatus* aStatus, const TDesC& /* aName */, const TBool aAttached )
    {
    CLOG(( ECodEng, 2, _L("-> CFileSaver::InstallL") ));
    __ASSERT_DEBUG( iState == ERespChecked, CodPanic( ECodInternal ) );
//    __ASSERT_DEBUG( iFname.Length(), CodPanic( ECodInternal ) );
    __ASSERT_DEBUG( aStatus, CodPanic( ECodInternal ) );

/* check if the PD app has been started (PD appl is responsible for file moving after play is done)
   In this case leave and go to the next state.
*/
    if (!aAttached)
	    {
#ifdef RD_MULTIPLE_DRIVE 
        RFs fs;
        TInt err( KErrNone );
        HBufC* filename = HBufC::NewLC(KMaxFileName);
        TPtr filenamePtr = filename->Des();
        filenamePtr = iRootPath;
        filenamePtr.Append(_L("download\\"));
        User::LeaveIfError( fs.Connect() );
        CleanupClosePushL(fs);
        CFileMan* file=CFileMan::NewL(fs);
        CleanupStack::PushL(file);
        TInt error = fs.MkDirAll(filenamePtr);
        if (error!=KErrNone && error!=KErrAlreadyExists)
           {
            User::Leave(error);   
           }
   
        // Find a unique name to avoid any conflict.
        // Here iFname has full path of current location of file
        // and filename has destination path.
        FindUniqueDestinationFileNameL( iFname, filename );
        
        filenamePtr = filename->Des();     
               
        err = file->Move(iFname, filenamePtr, CFileMan::EOverWrite);
    
        if(err != KErrNone)
           {
           User::Leave(err);
           }
       
        iFname = filenamePtr;              
        NotifyMediaGalleryL( filenamePtr );        
        CleanupStack::PopAndDestroy(file);
        CleanupStack::PopAndDestroy(&fs);
        CleanupStack::PopAndDestroy(filename);
#else
            RFs fs;
            HBufC* filename = HBufC::NewLC(KMaxFileName);
            TPtr filenamePtr = filename->Des();
            filenamePtr = iRootPath;

            filenamePtr.Append(_L("download\\"));
            User::LeaveIfError( fs.Connect() );
            CleanupClosePushL(fs);
            CFileMan* file=CFileMan::NewL(fs);
            CleanupStack::PushL(file);
            TInt error = fs.MkDirAll(filenamePtr);
            if (error!=KErrNone && error!=KErrAlreadyExists)
               {
                User::Leave(error);   
               }

            // Find a unique name to avoid any conflict.
            // Here iFname has full path of current location of file
            // and filename has destination path.
            FindUniqueDestinationFileNameL( iFname, filename );
            
            filenamePtr = filename->Des();
            TInt err = file->Move(iFname, filenamePtr, CFileMan::EOverWrite);
        
            if(err != KErrNone)
               {
               User::Leave(err);
                }
            iFname = filenamePtr;              
            NotifyMediaGalleryL( filenamePtr );
            CleanupStack::PopAndDestroy(file);
            CleanupStack::PopAndDestroy(&fs);
            CleanupStack::PopAndDestroy(filename);
#endif // RD_MULTIPLE_DRIVE
    // TODO set iType to that of the packaged object (??)
    CLOG(( ECodEng, 4, _L("CFileSaver::InstallL: saved==<%S> handler(0x%x)"), \
        &iFname, iHandler ));

	}

    // Normal file saving is not async.
    *aStatus = KRequestPending;
    User::RequestComplete( aStatus, KErrNone );

    iState = EInstalled;
    CLOG(( ECodEng, 2, _L("<- CFileSaver::InstallL") ));
    }

// ---------------------------------------------------------
// CFileSaver::CancelInstall()
// ---------------------------------------------------------
//
void CFileSaver::CancelInstall()
    {
    CLOG(( ECodEng, 2, _L("CFileSaver::CancelInstall") ));
    // Do nothing, saving is completed already (not async).
    }

// ---------------------------------------------------------
// CFileSaver::ReleaseContent()
// ---------------------------------------------------------
//
void CFileSaver::ReleaseContent( TFileName& aFname, TUid& aHandler )
    {
    CLOG(( ECodEng, 2, _L("-> CFileSaver::ReleaseContent") ));
    //__ASSERT_DEBUG( iState == EInstalled, CodPanic( ECodInternal ) );
    aFname = iFname;
    aHandler = iHandler;
    iFname = KNullDesC;
    iHandler = KNullUid;
    iSize = 0;
    delete iDrmHandler;
    iDrmHandler = NULL;
    iState = EInit;  //for PD iState = StartFeatch
    CLOG(( ECodEng, 2, _L("<- CFileSaver::ReleaseContent") ));
    }

// ---------------------------------------------------------
// CFileSaver::ReleaseFileName()
// ---------------------------------------------------------
//
void CFileSaver::ReleaseFileName( TFileName& aFname)
{
    CLOG(( ECodEng, 2, _L("-> CFileSaver::ReleaseFileName") ));
    aFname = iFname;
}

// ---------------------------------------------------------
// CFileSaver::Cleanup()
// ---------------------------------------------------------
//
void CFileSaver::Cleanup( TBool aDeleteFile )
    {
    CLOG(( ECodEng, 2, _L("-> CFileSaver::Cleanup") ));
    // Clean up the temporary file, ignore errors.
    // Expected error: KErrNotFound (if there is no temp file).
    // Unexpected error: all the rest -> nothing we can do with them.
    CloseStore();
    
	if( aDeleteFile )
	    {
#ifdef __TEST_COD_LOG
        TInt err =  // ('Log-only' variable.)
#endif /* def __TEST_COD_LOG */
        iFs.Delete( iFname );
        CLOG(( ECodEng, 4, \
        _L("CFileSaver::Cleanup <%S> err(%d)"), &iFname, err ));
	    }

    iFname = KNullDesC; // Now we are done with the file.
    iHandler = KNullUid;
    iSize = 0;
    if ( iDrmHandler )
        {
        iDrmHandler->Cleanup();
        delete iDrmHandler;
        iDrmHandler = NULL;
        }
    iState = EInit;
    CLOG(( ECodEng, 2, _L("<- CFileSaver::Cleanup") ));
    }

  
// ---------------------------------------------------------
// CFileSaver::UpdateDCFRepositoryL()
// Update saved file to DCFRepository  
// ---------------------------------------------------------
// 
void CFileSaver::UpdateDCFRepositoryL( const TDesC& aFileName )
    {
    CDcfEntry* dcfEntry = NULL;
    dcfEntry = CDcfEntry::NewL();    
    CleanupStack::PushL( dcfEntry );
    
    CDcfRep* dcfRep = NULL;
    dcfRep = CDcfRep::NewL();
    CleanupStack::PushL( dcfRep );

    dcfEntry->SetLocationL( aFileName, 0 );    
    dcfRep->UpdateL( dcfEntry );
    CleanupStack::PopAndDestroy(2); // dcfEntry, dcfRep
    }   
    
// ---------------------------------------------------------
// CFileSaver::UpdateMediaGalleryIfNeededL()
// Calls MediaGallery Update method so that media gallery
// knows update its view.
// ---------------------------------------------------------
// 


void CFileSaver::UpdateMediaGalleryIfNeededL( const TDesC& aFileName )
    {
#ifdef BRDO_APP_GALLERY_SUPPORTED_FF           
    CMGXFileManager* fm = MGXFileManagerFactory::NewFileManagerL( iFs );
    CleanupStack::PushL(fm);
    fm->UpdateL(aFileName);
    CleanupStack::PopAndDestroy(); // fm
#endif    
    }
// ---------------------------------------------------------
// CFileSaver::NotifyMediaGalleryL()
// Notify media gallery about the new file.
// ---------------------------------------------------------
// 
void CFileSaver::NotifyMediaGalleryL( const TDesC& aFileName )
    {
#ifdef BRDO_APP_GALLERY_SUPPORTED_FF    
    
    CMGXFileManager* mgFileManager = MGXFileManagerFactory::NewFileManagerL( iFs );
    CleanupStack::PushL( mgFileManager );

    TRAP_IGNORE( mgFileManager->UpdateL() );
    CleanupStack::PopAndDestroy( mgFileManager );

    // Notify Media Gallery
    TRAP_IGNORE( UpdateMediaGalleryIfNeededL( aFileName ) );

#endif
    
    
    // Notify DCF repository
    TRAP_IGNORE( UpdateDCFRepositoryL( aFileName ) );
    }
// ---------------------------------------------------------
// CFileSaver::DownloadedFileSize()
// ---------------------------------------------------------
//
TInt CFileSaver::DownloadedFileSize()
    {
    // check how many bytes are already persisted
    TInt DownloadedSize(0);
    TInt err = iFile.Size( (TInt&)DownloadedSize );
    
    CLOG(( ECodStorage, 2, _L("CFileSaver::DownloadedFileSize  Downloaded size = %d  error = %d"), \
               DownloadedSize,err ));

    if(err != KErrNone)
        return err;
    return DownloadedSize;
    }
    
    
/**
* Discard the old contents
*/        
void CFileSaver::ResetL()
    {
    
    
    iFile.Close();
    
    User::LeaveIfError( iFile.Replace(  iFs, 
                                        iFname, 
                                        EFileShareAny | 
                                        EFileStream | 
                                        EFileWrite ) );
    }


// -----------------------------------------------------------------------------
// CHttpStorage::SetProgressiveMode
// -----------------------------------------------------------------------------
//
void CFileSaver::SetProgressiveMode( TBool aValue )
    {
    iProgressiveDownload = aValue;
    if( iProgressiveDownload )
        {
        iBufferSize = KDefaultStorageBufferSizePD;
        }
    
    
    if( iBufferSize != iStorage->CurrentBufferSize())
        {
        FlushL();
        }

    if ( iFile.SubSessionHandle() )
        {
     	
        if( iProgressiveDownload )
            {
            if( iLength != KDefaultContentLength )    
                {
                iFile.SetSize( iLength );
                }
            }
        else
            {
            iFile.SetSize( iSize );
            }
        }
    }


// -----------------------------------------------------------------------------
// CHttpStorage::Flush
// Flush internal buffer to disk
// (other items were commented in a header).
// -----------------------------------------------------------------------------
//
void CFileSaver::FlushL()
    {
    CLOG(( ECodEng, 2, _L("-> CFileSaver::FlushL") ));
    iStorage->FlushBuffersL();
    iStorage->ResetBuffers();
    CLOG(( ECodEng, 2, _L("<- CFileSaver::FlushL") ));
    }



// -----------------------------------------------------------------------------
// CFileSaver::OnCompleted
// Called if download completed.
// -----------------------------------------------------------------------------
//
void CFileSaver::OnComplete()
    {
    CLOG(( ECodEng, 2, _L("-> CFileSaver::OnComplete") ));    
    FlushL();
    iStorage->ClearErrors();
    iFile.Close();
    CLOG(( ECodEng, 2, _L("<- CFileSaver::OnComplete") ));     
    }




// ---------------------------------------------------------
// CFileSaver::Name()
// ---------------------------------------------------------
HBufC* CFileSaver::NameL() const
    {
    return iFname.AllocL();
    }
