omadrm/drmlicensemanager/src/DRMLicenseManager.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Wed, 09 Jun 2010 09:59:43 +0300
branchRCL_3
changeset 20 29f3cf766061
parent 12 8a03a285ab14
permissions -rw-r--r--
Revision: 201021 Kit: 2010123

/*
* Copyright (c) 2004-2009 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 the license manager functionality used in the
*                install process
*
*/


// INCLUDE FILES
#include <e32std.h>
#include <e32base.h>
#include <apmstd.h>
#include <e32math.h>
#include <f32file.h>
#include <s32buf.h>
#include <s32crypt.h>
#include <utf.h>
#include <DRMRights.h>
#include <DRMCommon.h>
#include <Oma1DcfCreator.h>
#include <sysutil.h>     // Disk space checking

#include "zipfile.h"
#include "zipfilememberiterator.h"
#include "DRMLicenseManager.h"

// LOCAL CONSTANTS AND MACROS
_LIT(KDefinitionFileName, "datafiles.def");
_LIT(KSISSuffix, ".sis");
_LIT(KSISXSuffix, ".sisx");
_LIT8(KDefaultMIMEType, "application/binary");
_LIT8( KIssuer , "Rights-Issuer" );

const TInt KSISBufferSize = 512;
const TInt KMaxTypeLength = 11 + 2; // Length of "application" + CR + LF
const TInt KMaxFileDescriptionLength = KMaxPath + KMaxPath + KMaxDataTypeLength;


// ============================ LOCAL FUNCTIONS ================================
// -----------------------------------------------------------------------------
// DriveOfPathL
// -----------------------------------------------------------------------------
LOCAL_C inline TInt DriveOfPathL(
    RFs& aFs,
    const TDesC aPathName,
    TInt aFallbackDrive = KDefaultDrive )
    {
    // find drive for destination for free space check
    TInt driveNumber( KDefaultDrive );
    TParsePtrC pptr( aPathName );
    TInt err( KErrNone );
    if ( pptr.DrivePresent() )
        {
        err = RFs::CharToDrive( pptr.Drive()[ 0 ], driveNumber );
        User::LeaveIfError( err );
        }
    else if ( aFallbackDrive != KDefaultDrive )
        {
        driveNumber = aFallbackDrive;
        }
    else
        {
        HBufC* sessionPath( HBufC::NewLC( KMaxPath ) );
        TPtr pathPtr( sessionPath->Des() );
        User::LeaveIfError( aFs.SessionPath( pathPtr ) );

        TParsePtrC sPPtr( pathPtr );
        if ( sPPtr.DrivePresent() )
            {
            err = RFs::CharToDrive( sPPtr.Drive()[ 0 ], driveNumber );
            User::LeaveIfError( err );
            }
        else
            {
            driveNumber = RFs::GetSystemDrive();
            }
        CleanupStack::PopAndDestroy( sessionPath );
        }
    return driveNumber;
    }

// -----------------------------------------------------------------------------
// CheckNeededFreeSpaceL
// -----------------------------------------------------------------------------
LOCAL_C void CheckNeededFreeSpaceL(
    RFs& aFs,
    CZipFile*& aZipFile,
    RPointerArray< TDRMDataFile >& aDataFiles,
    const TDesC& aDestination,
    TInt& aError )
    {
    static const TInt KExtraSpaceForDcf( 1024 );
    CArrayFixFlat< TInt >* arrayOfNeededSpaces( NULL );
    // Get target drive for relative files
    TInt driveForRelativePath( DriveOfPathL( aFs, aDestination ) );

    arrayOfNeededSpaces = new ( ELeave ) CArrayFixFlat< TInt >( KMaxDrives );
    CleanupStack::PushL(  arrayOfNeededSpaces );

    arrayOfNeededSpaces->SetReserveL( KMaxDrives );
    for ( TInt j( 0 ); j < KMaxDrives; ++j )
        {
        arrayOfNeededSpaces->AppendL(0);
        }

    for ( TInt i = 0; i < aDataFiles.Count() && aError == KErrNone; i++ )
        {
        TDRMDataFile* dataFile(
            static_cast< TDRMDataFile* >( aDataFiles[ i ] ) );
        CZipFileMember* member(
            aZipFile->CaseInsensitiveMemberL( dataFile->iSourceName ) );
        if ( member )
            {
            CleanupStack::PushL( member );
            TInt driveNumber( DriveOfPathL(
                    aFs, dataFile->iTargetName, driveForRelativePath ) );
            ( *arrayOfNeededSpaces )[ driveNumber ] +=
                member->UncompressedSize() + KExtraSpaceForDcf;
            CleanupStack::PopAndDestroy( member );
            }
        else
            {
            aError = CDRMLicenseManager::EPIPInvalid;
            }
        }

    for ( TInt j( 0 ); j < KMaxDrives; ++j )
        {
        TUint element( ( *arrayOfNeededSpaces )[ j ] );
        if ( element && // no need to check if no space required
            SysUtil::DiskSpaceBelowCriticalLevelL( &aFs, element, j) )
            {
            User::Leave( KErrDiskFull );
            }
        }
    CleanupStack::PopAndDestroy( arrayOfNeededSpaces );
    }

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

// -----------------------------------------------------------------------------
// CDRMLicenseManager::CDRMLicenseManager
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//
CDRMLicenseManager::CDRMLicenseManager():
    iFs(NULL), iRights(NULL), iRightsIssuer(NULL)
    {
    }

// -----------------------------------------------------------------------------
// CDRMLicenseManager::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CDRMLicenseManager::ConstructL()
    {
    iFs = new RFs();
    User::LeaveIfNull(iFs);
    User::LeaveIfError(iFs->Connect());
    }

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

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

    return self;
    }


// Destructor
EXPORT_C CDRMLicenseManager::~CDRMLicenseManager()
    {
    iFs->Close();
    delete iFs;

    iDataFiles.ResetAndDestroy();

    if (iZipFile)
        {
        iZipFile->Close();
        delete iZipFile;
        }

    if (iRights)
        {
        delete iRights;
        }

    if (iRightsIssuer)
        {
        delete iRightsIssuer;
        }
    }

LOCAL_C TBool GetRightsIssuerL(const TFileName aFileName, HBufC8*& aRightsIssuerURL)
    {
    TInt err(KErrNotFound);

    HBufC8* name = HBufC8::NewLC(16);
    TPtr8 headerName( name->Des() );
    headerName.Copy( KIssuer );

    DRMCommon* c = DRMCommon::NewL();
    CleanupStack::PushL(c);
    err = c->GetFileHeader(aFileName, headerName, aRightsIssuerURL);
    CleanupStack::PopAndDestroy(c);
    CleanupStack::PopAndDestroy(name);

    if(err == DRMCommon::EOk && aRightsIssuerURL)
        {
        return ETrue;
        }

    return EFalse;
    }

LOCAL_C TBool GetRightsIssuerL(RFile aFileName, HBufC8*& aRightsIssuerURL)
    {
    TInt err(KErrNotFound);

    HBufC8* name = HBufC8::NewLC(16);
    TPtr8 headerName( name->Des() );
    headerName.Copy( KIssuer );

    DRMCommon* c = DRMCommon::NewL();
    CleanupStack::PushL(c);
    err = c->GetFileHeader(aFileName, headerName, aRightsIssuerURL);
    CleanupStack::PopAndDestroy(c);
    CleanupStack::PopAndDestroy(name);

    if(err == DRMCommon::EOk && aRightsIssuerURL)
        {
        return ETrue;
        }

    return EFalse;
    }

// -----------------------------------------------------------------------------
// CDRMLicenseManager::ProcessL
// Read the PIP definition file and encrypt each referenced data file
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CDRMLicenseManager::ProcessL(
    const TDesC& aZIPFile,
    const TDesC& aDestination)
    {
    TInt r = KErrNone;
    DRMCommon* drm = NULL;
    RPointerArray<CDRMRights>* rights = NULL;

    // Clean up
    if (iZipFile)
        {
        iZipFile->Close();
        delete iZipFile;
        iZipFile = NULL;
        }

    if (iRights)
        {
        delete iRights;
        iRights = NULL;
        }

    if (iRightsIssuer)
        {
        delete iRightsIssuer;
        iRightsIssuer = NULL;
        }

    GetRightsIssuerL(aZIPFile, iRightsIssuer);

    iDataFiles.ResetAndDestroy();

    // Get rights object for the PIP file
    iZipFile = CZipFile::NewL(*iFs, aZIPFile);
    drm = DRMCommon::NewL();
    CleanupStack::PushL(drm);
    r = drm->GetDetailedFileRights(aZIPFile, rights);

    // Read the definition file
    if (r == KErrNone)
        {
        iRights = (*rights)[0];
        rights->Remove(0);
        rights->ResetAndDestroy();
        delete rights;
        r = ReadDefinitionFileL();
        }

    // Process data files
    if (r == KErrNone)
        {
        r = ProcessDataFilesL(aDestination);
        }

    CleanupStack::PopAndDestroy(drm);

    return r;
    }

// -----------------------------------------------------------------------------
// CDRMLicenseManager::ProcessL
// Read the PIP definition file and encrypt each referenced data file
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CDRMLicenseManager::ProcessL(
    RFile& aZIPFile,
    const TDesC& aDestination)
    {
    TInt r = KErrNone;
    DRMCommon* drm = NULL;
    RPointerArray<CDRMRights>* rights = NULL;

    // Clean up
    if (iZipFile)
        {
        iZipFile->Close();
        delete iZipFile;
        iZipFile = NULL;
        }

    if (iRights)
        {
        delete iRights;
        iRights = NULL;
        }

    if (iRightsIssuer)
        {
        delete iRightsIssuer;
        iRightsIssuer = NULL;
        }

    GetRightsIssuerL(aZIPFile, iRightsIssuer);

    iDataFiles.ResetAndDestroy();

    // Get rights object for the PIP file
    iZipFile = CZipFile::NewL(*iFs, aZIPFile);
    drm = DRMCommon::NewL();
    CleanupStack::PushL(drm);
    r = drm->GetDetailedFileRights(aZIPFile, rights);

    // Read the definition file
    if (r == KErrNone)
        {
        iRights = (*rights)[0];
        rights->Remove(0);
        rights->ResetAndDestroy();
        delete rights;
        r = ReadDefinitionFileL();
        }

    // Process data files
    if (r == KErrNone)
        {
        r = ProcessDataFilesL(aDestination);
        }

    CleanupStack::PopAndDestroy(drm);

    return r;
    }

// -----------------------------------------------------------------------------
// CDRMLicenseManager::ExtractSISFileL
// Find the SIS member of the ZIP file and write it to the target destination.
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CDRMLicenseManager::ExtractSISFileL(
    const TDesC& aZIPFile,
    const TDesC& aDestination)
    {
    CZipFileMember* sisFile = NULL;
    TInt r = KErrNone;

    if (iZipFile)
        {
        iZipFile->Close();
        delete iZipFile;
        iZipFile = NULL;
        }

    if (iRightsIssuer)
        {
        delete iRightsIssuer;
        iRightsIssuer = NULL;
        }

    GetRightsIssuerL(aZIPFile, iRightsIssuer);

    iZipFile = CZipFile::NewL(*iFs, aZIPFile);

    __UHEAP_MARK;
    sisFile = GetSISMemberL();

    if (sisFile)
        {
        WriteSISMemberL(sisFile, aDestination);
        delete sisFile;
        }
    else
        {
        r = ESISNotFound;
        }
    __UHEAP_MARKEND;

    return r;
    }

// -----------------------------------------------------------------------------
// CDRMLicenseManager::ExtractSISFileL
// Find the SIS member of the ZIP file and write it to the target destination.
// -----------------------------------------------------------------------------
//
EXPORT_C TInt CDRMLicenseManager::ExtractSISFileL(
    RFile& aZIPFile,
    const TDesC& aDestination)
    {
    CZipFileMember* sisFile = NULL;
    TInt r = KErrNone;

    if (iZipFile)
        {
        iZipFile->Close();
        delete iZipFile;
        iZipFile = NULL;
        }

    if (iRightsIssuer)
        {
        delete iRightsIssuer;
        iRightsIssuer = NULL;
        }

    GetRightsIssuerL(aZIPFile, iRightsIssuer);

    iZipFile = CZipFile::NewL(*iFs, aZIPFile);

    __UHEAP_MARK;
    sisFile = GetSISMemberL();

    if (sisFile)
        {
        WriteSISMemberL(sisFile, aDestination);
        delete sisFile;
        }
    else
        {
        r = ESISNotFound;
        }
    __UHEAP_MARKEND;

    return r;
    }

// -----------------------------------------------------------------------------
// CDRMLicenseManager::GetSISMemberL
// Finds the SIS member in the ZIP file by looking for the .sis file ending.
// -----------------------------------------------------------------------------
//
EXPORT_C CZipFileMember* CDRMLicenseManager::GetSISMemberL(void)
    {
    CZipFileMemberIterator* members = NULL;
    CZipFileMember* sisFile = NULL;
    CZipFileMember* member = NULL;

    members = iZipFile->GetMembersL();
    CleanupStack::PushL(members);

    member = members->NextL();

    while (member && !sisFile)
        {
        if (member->Name()->Right(4).CompareF(KSISSuffix) == 0)
            {
            sisFile = member;
            }
        else if (member->Name()->Right(5).CompareF(KSISXSuffix) == 0)
            {
            sisFile = member;
            }
        else
            {
            delete member;
            member = NULL;
            member = members->NextL();
            }
        }

    CleanupStack::PopAndDestroy(members);

    if(!sisFile)
        {
        User::Leave(KErrNotFound);
        }

    return sisFile;
    }

// -----------------------------------------------------------------------------
// CDRMLicenseManager::WriteSISMemberL
// Write a SIS member to disk.
// -----------------------------------------------------------------------------
//
void CDRMLicenseManager::WriteSISMemberL(
    CZipFileMember* aSisMember,
    const TDesC& aDestination)
    {
    RZipFileMemberReaderStream* input = NULL;
    RFileWriteStream output;
    TBuf8<KSISBufferSize> buffer;

    // Check free space and leave if not enough space available
    if ( SysUtil::DiskSpaceBelowCriticalLevelL(
            iFs,
            aSisMember->UncompressedSize(),
            DriveOfPathL( *iFs, aDestination ) ) )
        {
        User::Leave( KErrDiskFull );
        }

    iZipFile->GetInputStreamL(aSisMember, input);
    CleanupStack::PushL(input);

    iFs->SetSessionPath(aDestination);
    User::LeaveIfError(output.Replace(*iFs, *aSisMember->Name(), EFileWrite));
    output.PushL();
    do
        {
        input->Read(buffer, KSISBufferSize);
        if (buffer.Size() > 0)
            {
            output.WriteL(buffer);
            }
        }
    while (buffer.Size() > 0);
    output.Close();

    CleanupStack::PopAndDestroy(&output);
    CleanupStack::PopAndDestroy(input);
    }

// -----------------------------------------------------------------------------
// CDRMLicenseManager::ReadDefinitionFileL
// Read the PIP type and definition lines from the definition file.
// -----------------------------------------------------------------------------
//
TInt CDRMLicenseManager::ReadDefinitionFileL(void)
    {
    CZipFileMember* definitionFile = NULL;
    RZipFileMemberReaderStream* input = NULL;
    TBuf8<KMaxTypeLength> type;
    TInt r = KErrNone;

    TRAP(r, definitionFile =
        iZipFile->CaseInsensitiveMemberL(KDefinitionFileName));

    if (r == KErrNone && definitionFile)
        {
        CleanupStack::PushL(definitionFile);
        iZipFile->GetInputStreamL(definitionFile, input);
        ReadLine(input, type);
        while (r == KErrNone)
            {
            r = ReadFileDescription(input);
            }
        delete input;
        CleanupStack::PopAndDestroy(definitionFile);
        }
    else
        {
        r = EPIPInvalid;
        }

    if (r == KErrEof)
        {
        r = KErrNone;
        }

    return r;
    }

// -----------------------------------------------------------------------------
// CDRMLicenseManager::ReadFileDescription
// Read the name, type and target name of a data file.
// -----------------------------------------------------------------------------
//
TInt CDRMLicenseManager::ReadFileDescription(
    RZipFileMemberReaderStream* aStream)
    {
    TBuf8<KMaxFileDescriptionLength> description;
    TInt r = KErrNone;
    TDRMDataFile* dataFile = NULL;

    ReadLine(aStream, description);
    description.TrimAll();
    if (description.Length() > 0)
        {
        TLex8 lex(description);

        dataFile = new TDRMDataFile();
        if( !dataFile )
            {
            return KErrNoMemory;
            }

        // Get the original file name
        lex.SkipSpaceAndMark();
        lex.SkipCharacters();
        CnvUtfConverter::ConvertToUnicodeFromUtf8(
            dataFile->iSourceName, lex.MarkedToken());

        // Get the MIME type. If not present, "application/binary" is used
        // and the target file name is the source file name
        lex.SkipSpaceAndMark();
        lex.SkipCharacters();
        if (lex.TokenLength() > 0)
            {
            dataFile->iMimeType = lex.MarkedToken();

            // Get the target file name. If not present, the target file name
            // will be the original file name, stored in the install directory.
            lex.SkipSpaceAndMark();
            lex.SkipCharacters();
            if (lex.TokenLength() > 0)
                {
                CnvUtfConverter::ConvertToUnicodeFromUtf8(
                    dataFile->iTargetName, lex.MarkedToken());
                }
            else
                {
                dataFile->iTargetName = dataFile->iSourceName;
                }
            }
        else
            {
            dataFile->iMimeType = KDefaultMIMEType;
            dataFile->iTargetName = dataFile->iSourceName;
            }

        iDataFiles.Append(dataFile);
        }
    else
        {
        r = KErrEof;
        }
    return r;
    }

// -----------------------------------------------------------------------------
// CDRMLicenseManager::ReadLine
// Read one line ending with 0x0d or until the input buffer is full.
// -----------------------------------------------------------------------------
//
void CDRMLicenseManager::ReadLine(
    RZipFileMemberReaderStream* aStream,
    TDes8& aLine)
    {
    TBuf8<1> c;
    TInt n = 0;
    TBool done = EFalse;

    while (!done)
        {
        if (aLine.Length() < aLine.MaxLength())
            {
            aStream->Read(c, 1);
            if (c.Length() > 0 && c[0] != 0x0d)
                {
                aLine.Append(c);
                n++;
                if (n == aLine.MaxLength() + 1)
                    {
                    done = ETrue;
                    }
                }
            else
                {
                done = ETrue;
                }
            }
        else
            {
            done = ETrue;
            }
        }

    if (c.Length() > 0 && c[0] == 0x0d)
        {
        aStream->Read(c, 1);
        }
    }

// -----------------------------------------------------------------------------
// CDRMLicenseManager::ProcessDataFilesL
// Get all data files from the ZIP file and encrypt them.
// -----------------------------------------------------------------------------
//
TInt CDRMLicenseManager::ProcessDataFilesL(
    const TDesC& aDestination)
    {
    TDRMDataFile* dataFile( NULL );
    CZipFileMember* member( NULL );
    TInt r( KErrNone );

    // check if there is enough free space for the process
    CheckNeededFreeSpaceL( *iFs, iZipFile, iDataFiles, aDestination, r );

    for ( TInt i = 0; i < iDataFiles.Count() && r == KErrNone; i++ )
        {
        dataFile = static_cast< TDRMDataFile* >( iDataFiles[ i ] );
        member = iZipFile->CaseInsensitiveMemberL( dataFile->iSourceName );
        if ( member )
            {
            CleanupStack::PushL(member);
            EncryptDataFileL(dataFile, aDestination, member);
            CleanupStack::PopAndDestroy(member);
            }
        else
            {
            r = EPIPInvalid;
            }
        }

    return r;
    }

// -----------------------------------------------------------------------------
// CDRMLicenseManager::EncryptDataFileL
// Encrypt a single data file, reusing the outer PIP's RO. For application
// protection, the file is encrypted again.
// -----------------------------------------------------------------------------
//
void CDRMLicenseManager::EncryptDataFileL(
    TDRMDataFile* aDataFile,
    const TDesC& aDestination,
    CZipFileMember* aZipMember)
    {
    RZipFileMemberReaderStream* input = NULL;
    RFileWriteStream output;
    TBuf8<KSISBufferSize> buffer;
    COma1DcfCreator* drm = NULL;
    TFileName outputFile;
    DRMCommon* common = NULL;

    drm = COma1DcfCreator::NewL();
    CleanupStack::PushL(drm);

    iZipFile->GetInputStreamL(aZipMember, input);
    CleanupStack::PushL(input);

    // Set the path so we can use relative paths
    iFs->SetSessionPath(aDestination);

    output.PushL();
    // Create the output file stream
    User::LeaveIfError(output.Replace(*iFs, aDataFile->iTargetName,
        EFileWrite));

    // Encrypt
    drm->EncryptInitializeL(output, aDataFile->iMimeType, iRights);
    do
        {
        input->Read(buffer, KSISBufferSize);
        if (buffer.Size() > 0)
            {
            drm->EncryptUpdateL(buffer);
            }
        }
    while (buffer.Size() > 0);
    drm->EncryptFinalizeL();
    output.Close();
    CleanupStack::PopAndDestroy( &output );

    // Add the rights issuer uri to the file if it exists
    if( iRightsIssuer )
        {
        common = DRMCommon::NewL();
        CleanupStack::PushL(common);
        outputFile.Append( aDataFile->iTargetName );
        common->SetFileHeader( outputFile, KIssuer,
                               iRightsIssuer->Des());
        CleanupStack::PopAndDestroy(common);
        }
    CleanupStack::PopAndDestroy(input);
    CleanupStack::PopAndDestroy(drm);
    }