/*
* Copyright (c) 2002-2007 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: 
*           Phonebook 2 DRM manager.
*
*/


// INCLUDE FILES
#include "CPbk2DrmManager.h"

// Phonebook 2
#include <Pbk2CommonUi.rsg>

// System includes
#include <featmgr.h>
#include <DRMCommon.h>
#include <StringLoader.h>
#include <aknnotewrappers.h>
#include <apgcli.h>                     // RApaLsSession
#include <musicplayerdomaincrkeys.h>    // KCRUidMusicPlayerFeatures
#include <centralrepository.h>
#include <DRMHelper.h>
#include <coemain.h>

#ifdef RD_DRM_COMMON_INTERFACE_FOR_OMA_AND_WMDRM
#include <drmutility.h>
#include <drmagents.h>
#endif

/// Unnamed namespace for local definitions
namespace {

_LIT(KSpaceTxt, " ");

} /// namespace

// --------------------------------------------------------------------------
// CPbk2DrmManager::CPbk2DrmManager
// --------------------------------------------------------------------------
//
CPbk2DrmManager::CPbk2DrmManager() :
        iDrmEnabled( EFalse )
    {
    }

// --------------------------------------------------------------------------
// CPbk2DrmManager::~CPbk2DrmManager
// --------------------------------------------------------------------------
//
EXPORT_C CPbk2DrmManager::~CPbk2DrmManager()
    {
    if ( iDrmClient )
        {
        iDrmClient->Disconnect();
        }
    delete iDrmClient;
    delete iDrmHelper;
    delete iMusicPlayerFeatures;
    
    FeatureManager::UnInitializeLib();
    }

// --------------------------------------------------------------------------
// CPbk2DrmManager::NewL
// --------------------------------------------------------------------------
//
EXPORT_C CPbk2DrmManager* CPbk2DrmManager::NewL()
    {
    CPbk2DrmManager* self = new ( ELeave ) CPbk2DrmManager;
    CleanupStack::PushL( self );
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;
    }

// --------------------------------------------------------------------------
// CPbk2DrmManager::ConstructL
// --------------------------------------------------------------------------
//
void CPbk2DrmManager::ConstructL()
    {
    // Sets up TLS, must be done before FeatureManager is used
    FeatureManager::InitializeLibL();

    iDrmHelper = CDRMHelper::NewL();
    iDrmClient = DRMCommon::NewL();
    if ( iDrmClient->Connect() == DRMCommon::EOk )
        {
        // If unable to connect, DRM protection is always on
        iDrmEnabled = ETrue;
        }
    
    iMusicPlayerFeatures = CRepository::NewLC( KCRUidMusicPlayerFeatures );
    CleanupStack::Pop(); // iMusicPlayerFeatures
    }

#ifndef RD_DRM_COMMON_INTERFACE_FOR_OMA_AND_WMDRM

// Some magic constants
static const TInt KMinContentLength( 16 );
_LIT8( KContentProtectionType, "DRM" );
_LIT8( KASFHeaderObject, "75B22630668E11CFA6D900AA0062CE6C" );


// -----------------------------------------------------------------------------
// FormatGUID
// -----------------------------------------------------------------------------
//
LOCAL_C void FormatGUID( TDes8& aGUID )
    {
    TBuf8<16> copyGUID( aGUID );
    TInt i;
    for( i = 0; i < 4; i++ )
        {
        copyGUID[i] = aGUID[3-i];
        }
    for( i = 4; i < 6; i++ )
        {
        copyGUID[i] = aGUID[9 - i];
        }
    for( i = 6; i < 8; i++ )
        {
        copyGUID[i] = aGUID[13 - i];
        }
    for( i = 8; i < 16 ; i++ )
        {
        copyGUID[i] = aGUID[i];
        }
    aGUID.Delete( 0, 32 );
    for( i = 0; i <16; i++ )
        {
        aGUID.AppendNumFixedWidthUC( copyGUID[i], EHex, 2 );
        }
    }

// -----------------------------------------------------------------------------
// ConvertToInt64
// -----------------------------------------------------------------------------
//
LOCAL_C TInt64 ConvertToInt64( TDesC8& aDes )
    {
    TInt64 num = 0;
    TInt i;
    for( i = 7 ; i >= 0; i-- )
        {
        num <<= 8;
        num |= aDes[i];
        }
    return num;
    }


// -----------------------------------------------------------------------------
// IsProtectedWmDrmL
// returns ETrue, if file is protected WMDRM file
//         EFalse if file is not protected WMDRM file
// Leaves with KErrUnderflow if file has too little data to decide
//         whether WmDrm or not
//         may also leave with other system wide error code
// -----------------------------------------------------------------------------
//
LOCAL_C TBool IsProtectedWmDrmL( RFile& aFileHandle )
    {
    TInt r( KErrNone );
    HBufC8* buffer( NULL );
    TInt pos( 0 );
    RFile file;
    TBuf8< 32 > header;

    TInt64 headerSize( 0 );
    TBool isProtectedWmDrm( EFalse );
    TPtr8 headerPtr( NULL, 0 );

    // Leave if given handle is invalid
    if( !aFileHandle.SubSessionHandle() )
        {
        User::Leave( KErrBadHandle );
        }

    file.Duplicate( aFileHandle );
    CleanupClosePushL( file );

    User::LeaveIfError( file.Seek( ESeekStart, pos ) );

    // Check if the file is an ASF file
    // TODO: Check on runtime wether WM DRM is supporeted or not
    //       Take Implementation from DRM::CDrmUtility::GetDrmInfoL,
    //       which is not yet available

    User::LeaveIfError( file.Read( 0, header, KMinContentLength ) );
    if( header.Length() < KMinContentLength )
        {
        User::Leave( KErrUnderflow );
        }

    FormatGUID( header );

    if( header == KASFHeaderObject )
        {
        // It's ASF, check still whether it's WM DRM protected or not
        file.Read( header,8 );
        headerSize = ConvertToInt64( header );
        if( headerSize <= 30 )
            {
            User::Leave( KErrUnderflow );
            }
        if ( headerSize > KMaxTInt16 )
            {
            User::Leave( KErrOverflow );
            }
        buffer = HBufC8::NewLC( headerSize );

        headerPtr.Set( buffer->Des() );
        User::LeaveIfError( file.Read( headerPtr, headerSize - 24 ) );

        r = headerPtr.Find( KContentProtectionType );
        if ( KErrNotFound != r )
            {
            isProtectedWmDrm = ETrue;
            }
        CleanupStack::PopAndDestroy( buffer ); // buffer
        }
        CleanupStack::PopAndDestroy(); // file

    return isProtectedWmDrm;
    }

#endif // RD_DRM_COMMON_INTERFACE_FOR_OMA_AND_WMDRM

// -----------------------------------------------------------------------------
// CPbk2DrmManager::IsFileWMDRMProtectedL
// -----------------------------------------------------------------------------
//
TBool CPbk2DrmManager::IsFileWMDRMProtectedL( const TDesC& aFileName,
											TBool& aIsProtected  ) const
	{
	TBool res = EFalse;
	RFs& fsSession( CCoeEnv::Static()->FsSession() );
	RFile hFile;

	TInt err = hFile.Open( fsSession, aFileName, 
						EFileRead | EFileStream | EFileShareReadersOnly );
	if( err == KErrInUse )
		{
		err = hFile.Open( fsSession, aFileName, 
						EFileRead | EFileStream | EFileShareAny );
		}
	if( err != KErrNone )
		{
		User::Leave( err );
		}
	CleanupClosePushL( hFile );

#ifdef RD_DRM_COMMON_INTERFACE_FOR_OMA_AND_WMDRM
	TPtrC agent( KNullDesC );
	DRM::CDrmUtility* drmUtil( DRM::CDrmUtility::NewLC() );
	drmUtil->GetAgentL( hFile, agent );
	if( agent.Compare( DRM::KDrmWMAgentName ) == 0 )
		{
		res = ETrue;
		}
	else
		{
		res = iDrmClient->IsProtectedFile( aFileName, aIsProtected );	
		}	
	CleanupStack::PopAndDestroy( drmUtil );
#else
	res = IsProtectedWmDrmL( hFile );
	aIsProtected = EFalse;
#endif

	CleanupStack::PopAndDestroy( &hFile );
	return res;
	}

// --------------------------------------------------------------------------
// CPbk2DrmManager::IsProtectedFile
// --------------------------------------------------------------------------
//

EXPORT_C TInt CPbk2DrmManager::IsProtectedFile(
        const TDesC& aFileName,
        TBool& aIsProtected )
    {
    aIsProtected = ETrue;
        
    const TBool drmSupported =
        ( FeatureManager::FeatureSupported( KFeatureIdDrm )||
          FeatureManager::FeatureSupported( KFeatureIdDrmFull ) );

    TInt drmErrorCode( KErrNone );
    if ( !drmSupported )
        {
        // No DRM support, file retrieval always ok
        aIsProtected = EFalse;
        }
    else if ( iDrmEnabled )
        {
        // Check is the file protected
		drmErrorCode = IsFileWMDRMProtectedL( aFileName,aIsProtected );
        }
    return drmErrorCode;
    }

// --------------------------------------------------------------------------
// CPbk2DrmManager::IsRingingToneForbidden
// --------------------------------------------------------------------------
//
EXPORT_C TInt CPbk2DrmManager::IsRingingToneForbidden( 
        const TDesC& aFileName,
        TBool& aIsProtected )
    {
    aIsProtected = ETrue;
    TInt error( KErrNone );
    const TBool drmSupported =
        ( FeatureManager::FeatureSupported( KFeatureIdDrm ) ||
          FeatureManager::FeatureSupported( KFeatureIdDrmFull ) );

    if ( !drmSupported )
        {
        // No DRM support, file retrieval always ok
        aIsProtected = EFalse;
        }
    else
        {
        // DRM is enabled. File must be checked
        error = IsProtectedFile( aFileName, aIsProtected );
        if ( error == KErrNone )
            {
            if ( aIsProtected )
                {
                TRAP( error, CheckProtectedFileL( aFileName, aIsProtected ) );
                }
            else
                {
                TRAP( error, CheckUnprotectedFileL( aFileName, aIsProtected ) );
                }
            }
         else
         	{
         	error = 0;
         	TRAP_IGNORE( ShowErrorNoteL( R_QTN_PHOB_PROTECTED_TONE ) );
         	}           
        }
    return error;       
    }

// --------------------------------------------------------------------------
// CPbk2DrmManager::IsThumbnailForbidden
// --------------------------------------------------------------------------
//
EXPORT_C TInt CPbk2DrmManager::IsThumbnailForbidden( 
        const TDesC& aFileName,
        TBool& aIsProtected )
    {
    aIsProtected = ETrue;
    TInt error = IsProtectedFile( aFileName, aIsProtected );
    
    // Thumbnails with any DRM protection are not allowed
    if ( error == KErrNone && aIsProtected )
        {
        TRAP_IGNORE( ShowErrorNoteL( R_PBK2_QTN_DRM_NOT_ALLOWED ) );
        }
    
    return error;
    }

// --------------------------------------------------------------------------
// CPbk2DrmManager::ShowErrorNoteL
// --------------------------------------------------------------------------
//
void CPbk2DrmManager::ShowErrorNoteL( TInt aResource )
    {
    CEikonEnv* env = CEikonEnv::Static();
    HBufC* noteText = StringLoader::LoadLC( aResource, env );

    // DRM notifications are shown as waiting information notes
    CAknInformationNote* note = new ( ELeave ) CAknInformationNote( ETrue );
    note->ExecuteLD( *noteText );

    CleanupStack::PopAndDestroy( noteText ); //noteText
    }

// --------------------------------------------------------------------------
// CPbk2DrmManager::IsDrmRequiredForPlaybackL
// --------------------------------------------------------------------------
//
TBool CPbk2DrmManager::IsDrmRequiredForPlaybackL()
    {
    // It is possible to disable support for non-DRM protected rich audio
    // formats    
    TInt value( 0 );
    User::LeaveIfError(
        iMusicPlayerFeatures->Get( KRequireDRMInPlayback, value ) );    
    return static_cast<TBool>( value );
    }

// --------------------------------------------------------------------------
// CPbk2DrmManager::IsBlockedMimeTypeL
// --------------------------------------------------------------------------
//
TBool CPbk2DrmManager::IsBlockedMimeTypeL( const TDesC& aMimeType )
    {
    TBool ret = EFalse;    
    TBuf<1> tmp; // Magic: 1 char to get length of actual value
    TInt realLen = 0;
    TInt err = iMusicPlayerFeatures->Get(
        KPlaybackRestrictedMimeTypes, tmp, realLen );
    if ( err == KErrOverflow )
        {
        // Prepare list of blocked MIME types
        HBufC* listBuf = HBufC::NewLC( realLen + KSpaceTxt().Length() );
        TPtr list = listBuf->Des();
        User::LeaveIfError(
            iMusicPlayerFeatures->Get
                ( KPlaybackRestrictedMimeTypes, list ) );
        list.Append( KSpaceTxt );

        // Prepare buffer for aMimeType
        HBufC* mimeBuf = HBufC::NewLC( aMimeType.Length() +
                                       KSpaceTxt().Length() );
        TPtr mime = mimeBuf->Des();
        mime = aMimeType;
        // FindF() would find "audio/3gpp" in "audio/3gpp2" without
        // the added space
        mime.Append( KSpaceTxt );

        // If result is not KErrNotFound, this MIME-type is indeed blocked
        ret = ( list.FindF( mime ) >= 0 );

        CleanupStack::PopAndDestroy(2); // listBuf, mimeBuf
        }
    else
        {
        User::LeaveIfError( err );
        }    
    return ret;
    }

// --------------------------------------------------------------------------
// CPbk2DrmManager::CheckProtectedFileL
// --------------------------------------------------------------------------
//
void CPbk2DrmManager::CheckProtectedFileL( 
        const TDesC& aFileName,
        TBool& aProtected )
    {
    aProtected = ETrue;
    TBool automatedOk(EFalse);

    // Check that file can be set as automated content.
    TInt err = iDrmHelper->CanSetAutomated( aFileName, automatedOk );
    if ( err==KErrNone )
        {
        if ( !automatedOk )
            {
            ShowErrorNoteL(R_QTN_PHOB_DRM_PREV_RIGHTS_SET);
            }
        else
            {
            DRMCommon::TContentProtection protection;
            HBufC8* mimeType = NULL; // ignored
            HBufC8* contentUri = NULL;
            TUint dataLength; // ignored
            err = iDrmClient->GetFileInfo( 
                    aFileName, protection, mimeType, contentUri, dataLength );
            delete mimeType;
            mimeType = NULL;
            if( err )
                {
                delete contentUri;
                User::Leave( err );
                }

            CDRMRights* rights = NULL;
            err = iDrmClient->GetActiveRights( 
                    *contentUri, DRMCommon::EPlay, rights );

            switch ( err )
                {
                case CDRMRights::EFullRights: // Fall through
                case CDRMRights::ERestrictedRights:
                    {
                    CDRMRights::TRestriction restriction; // ignore
                    CDRMRights::TExpiration expiration;
                    TUint32 constType;

                    // Ignore return value
                    rights->GetRightsInfo( 
                        DRMCommon::EPlay, restriction, expiration, constType );

                    // This is CFM protected file, ringingtone is set to "no"
                    if ( ( rights->GetPermission().iInfoBits & ENoRingingTone )
                        // This is normal DRM protected tone
                        || ( constType & ( CDRMRights::ECountBased |
                                           CDRMRights::ETimeIsAccumulatedTime ) ) )
                        {
                        ShowErrorNoteL( R_QTN_PHOB_DRM_PREV_RIGHTS_SET );
                        }
                    else
                        {
                        switch ( expiration )
                            {
                            case CDRMRights::EValidRights:
                                {
                                aProtected = EFalse;
                                break;
                                }
                            case CDRMRights::EFutureRights:
                                {
                                iDrmHelper->HandleErrorL( 
                                    DRMCommon::ENoRights, aFileName );
                                break;
                                }
                            case CDRMRights::EExpiredRights:
                                {
                                iDrmHelper->HandleErrorL( 
                                    DRMCommon::ERightsExpired, aFileName );
                                break;
                                }
                            default:
                                {
                                // Do nothing
                                break;
                                }
                            }
                        }
                    break;
                    }
                case CDRMRights::EPreviewRights:
                    {
                    ShowErrorNoteL( R_QTN_PHOB_DRM_PREV_RIGHTS_SET );
                    break;
                    }
                case DRMCommon::ENoRights:
                    {
                    iDrmHelper->HandleErrorL( DRMCommon::ENoRights, aFileName );
                    break;
                    }
                default:
                    {
                    // Do nothing
                    break;
                    }
                }

            delete rights;
            delete contentUri;
            }
        }
    else
        {
        // Locked file case is handled separately.
        if ( err==KErrInUse )
            {
            CCoeEnv::Static()->HandleError( KErrInUse );
            }
        else
            {
            // Leave if DrmHelper not handle the error
            User::LeaveIfError( iDrmHelper->HandleErrorL(err,aFileName) );
            }
        }
    }

// --------------------------------------------------------------------------
// CPbk2DrmManager::CheckUnprotectedFileL
// --------------------------------------------------------------------------
//
void CPbk2DrmManager::CheckUnprotectedFileL( 
        const TDesC& aFileName, 
        TBool& aProtected )
    {
    aProtected = ETrue;    
    RApaLsSession apaLs;
    User::LeaveIfError( apaLs.Connect() );
    TUid appUidNotUsed = KNullUid;
    TDataType dataType;
    TInt err = apaLs.AppForDocument( aFileName, appUidNotUsed, dataType );
    apaLs.Close();
    User::LeaveIfError( err );

    // Check if this unprotected MIME type should be blocked
    if ( IsDrmRequiredForPlaybackL() &&
         IsBlockedMimeTypeL( dataType.Des() ) )
        {
        ShowErrorNoteL( R_QTN_PHOB_UNPROTECTED_TONE );
        }
    else
        {
        aProtected = EFalse;
        }
    }

//  End of File
