javamanager/javasettings/appmngrplugin/src/appmngr2midletmanifestreader.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Wed, 13 Oct 2010 14:23:59 +0300
branchRCL_3
changeset 83 26b2b12093af
parent 66 2455ef1f5bbc
permissions -rw-r--r--
Revision: v2.2.17 Kit: 201041

/*
* Copyright (c) 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:  CManifestReader implementation file
*
*/


#include <caf/content.h>
#include <zipfile.h>
#include <utf.h>
#include <e32cmn.h>

#include <javaattribute.h>

#include "appmngr2midletmanifestreader.h"
#include "logger.h"                         // LOG

const TInt KAllRead = -1;
const TInt KAttributeMinSize = 3;
const TInt KFilePrefix = 4;

_LIT(KManifestEntryName, "META-INF/MANIFEST.MF");

const TUint32 LF  = 10;
const TUint32 CR  = 13;
const TUint32 SP  = 32;
const TUint32 COLON = 58;


AppMngr2MidletManifestReader::AppMngr2MidletManifestReader(RFs& aFs) :
        mFs(aFs), mSessionOpen(false)
{
}

AppMngr2MidletManifestReader::~AppMngr2MidletManifestReader()
{
    // Close the file session (if opened by ManifestReader)
    if (mSessionOpen)
    {
        mFs.Close();
    }
}

void AppMngr2MidletManifestReader::ReadManifestL(
    const TDesC& aManifestFile,
    RPointerArray<MJavaAttribute>& aAttributes)
{
    if (mManifestContent.get())
    {
        mManifestContent.reset(0);
    }

    ContentL(aManifestFile);

    ReadAttributesL(aAttributes);
}

void AppMngr2MidletManifestReader::ReadAttributesL(
    RPointerArray<MJavaAttribute>& aAttributes)
{
    TPtr16 contentPtr(mManifestContent->Des());

    //remove white space from the beginning of the file
    contentPtr.TrimLeft();

    TInt attributeIndex = -1;

    // Continue until new line is found and manifest content is more than 3.
    // It can't be shorter than a:b\n. Actually spec specifies that it must be 'a: b\n'
    // that makes four but for the robustness as also user inserted variables
    // are read. Parameter can be e.g. either TestCase1:myCase or TestCase1: myCase.
    while ((attributeIndex = ReadLineIndexL(EFalse)) != KAllRead)
    {
        HBufC16* attribute = contentPtr.Left(attributeIndex).AllocLC();
        TPtr16 attributePtr(attribute->Des());
        attributePtr.Trim();

        TInt colonIndex = attributePtr.Locate(COLON);

        // Invalid attribute. Name not found
        if (KErrNotFound == colonIndex)
        {
            ELOG(EJavaAppMngrPlugin, "MF parsing failed: No attribute name defined");
            //User::Leave(KJavaParseNameAttributeMissing);
            User::Leave(KErrArgument);
        }

        // Implementation expects that attribute name fits to first line.
        HBufC16* name = attributePtr.Left(colonIndex).AllocLC();
        TPtr16 namePtr(name->Des());
        namePtr.Trim();

        ValidateAttributeNameL(namePtr);

        // Rest of the manifest data can be continuational value.
        HBufC16* value = HBufC16::NewLC(contentPtr.Length());
        TPtr16 valuePtr(value->Des());

        // Skip colon
        valuePtr.Append(attributePtr.Mid(colonIndex + 1));
        valuePtr.Trim();

        ReadContinuationLineL(valuePtr, contentPtr, attributeIndex);

        // If manifest attribute value is not defined attribute is
        // skipped and it is not returned to caller.
        if (valuePtr.Length() > 0)
        {
            CJavaAttribute* javaAttribute
            = CJavaAttribute::NewL(namePtr, valuePtr, EFalse);
            aAttributes.Append(javaAttribute);
        }

        CleanupStack::PopAndDestroy(value);
        CleanupStack::PopAndDestroy(name);
        CleanupStack::PopAndDestroy(attribute);

        // Delete already handled attribute from the buffer.
        // Content is read until the enter and it must be deleted to reach next one.
        contentPtr.Delete(0, attributeIndex + 1);
        contentPtr.TrimLeft();
    }
}

void AppMngr2MidletManifestReader::ContentL(const TDesC& aManifestFileName)
{
    RFile file;
    TInt err = file.Open(mFs,
                         aManifestFileName,
                         EFileStream | EFileShareAny | EFileRead);

    User::LeaveIfError(err);
    CleanupClosePushL(file);

    std::auto_ptr<HBufC8>fileStart(HBufC8::NewL(KFilePrefix));
    TPtr8 fileStartPtr(fileStart->Des());

    std::auto_ptr<HBufC16>filePrefix(HBufC16::NewL(KFilePrefix));
    TPtr16 filePrefixPtr(filePrefix->Des());

    err = file.Read(fileStartPtr, KFilePrefix);
    User::LeaveIfError(err);

    err = CnvUtfConverter::ConvertToUnicodeFromUtf8(
              filePrefixPtr, fileStartPtr);

    if (KErrNone != err)
    {
        ELOG1(EJavaAppMngrPlugin, "MF UTF-8 to unicode conversion failed: %d", err);
        // User::Leave(KJavaErrNoneUTF8);
        User::Leave(KErrArgument);
    }

    if (fileStartPtr.Length() < KFilePrefix)
    {
        ELOG(EJavaAppMngrPlugin, "Not enough content at manifest file");
        // User::Leave(KJavaErrInvalidManifest);
        User::Leave(KErrArgument);
    }

    // Check from CAF if file is DRM protected.
    TBool drmProtected = EFalse;
    std::auto_ptr<ContentAccess::CContent>content(
        ContentAccess::CContent::NewL(file));

    err = content->GetAttribute(ContentAccess::EIsProtected, drmProtected);
    if (KErrNone != err)
    {
        ELOG1(EJavaAppMngrPlugin, "DRM protection check failed: %d", err);
        User::Leave(KErrArgument);
    }

    // Detect JAR package. JAR is either a DRM protected file or a ZIP file.
    // ZIP file has PK\003\004 prefix.
    if (drmProtected
            || (filePrefixPtr[0] == 0x50
                && filePrefixPtr[1] == 0x4B
                && filePrefixPtr[2] == 0x03
                && filePrefixPtr[3] == 0x04))
    {
        ReadManifestContentFromPackageL(file);
    }
    else
    {
        // Plain manifest file
        ReadManifestContentL(file);
    }
    CleanupStack::PopAndDestroy(&file);
}

void AppMngr2MidletManifestReader::ReadManifestContentL(RFile& aManifestFile)
{
    // Reads the entire file into a heap descriptor. Use the content
    // access framework to access the manifest.
    ContentAccess::CContent* content
    = ContentAccess::CContent::NewLC(aManifestFile);
    ContentAccess::CData* manifestFile
    = content->OpenContentL(ContentAccess::EPeek);
    CleanupStack::PushL(manifestFile);

    HBufC8* manifestFileData = ReadRawManifestFileL(*manifestFile);
    TPtr8 ptr(manifestFileData->Des());

    CleanupStack::PopAndDestroy(manifestFile);
    CleanupStack::PopAndDestroy(content);
    CleanupStack::PushL(manifestFileData);

    mManifestContent.reset(HBufC16::NewL(ptr.Size()));
    TPtr16 ucsPtr(mManifestContent->Des());
    TInt err = CnvUtfConverter::ConvertToUnicodeFromUtf8(ucsPtr, ptr);

    if (KErrNone != err)
    {
        ELOG1(EJavaAppMngrPlugin, "MF UTF-8 to unicode conversion failed: %d", err);
        //User::Leave(KJavaErrNoneUTF8);
        User::Leave(KErrArgument);
    }
    CleanupStack::PopAndDestroy(manifestFileData);
}

HBufC8* AppMngr2MidletManifestReader::ReadRawManifestFileL(
    ContentAccess::CData& aManifestFile)
{
    // Reads size of file
    TInt manifestSize;
    aManifestFile.DataSizeL(manifestSize);
    // allocates buffer for file contents, if this is too big and device runs
    // out of memory function will leave. Do not really care about this as file
    // was unlikely to have been a manifest file at all.
    HBufC8* manifestFileData = HBufC8::NewMaxLC(manifestSize);
    TPtr8 manifestFileDataPtr(manifestFileData->Des());

    // Now read the manifest file
    User::LeaveIfError(aManifestFile.Read(manifestFileDataPtr, manifestSize));
    CleanupStack::Pop(manifestFileData);

    return manifestFileData;
}

void AppMngr2MidletManifestReader::ReadManifestContentFromPackageL(
    RFile& aPackageFile)
{
    CZipFile* zipFile = CZipFile::NewL(mFs, aPackageFile);
    CleanupStack::PushL(zipFile);

    // Seek manifest file
    CZipFileMember* zippedFile =
        zipFile->CaseSensitiveOrCaseInsensitiveMemberL(KManifestEntryName());

    if (!zippedFile)
    {
        ELOG(EJavaAppMngrPlugin, "Package is missing manifest");
        //User::LeaveIfError(KJavaErrMissingManifest);
        User::Leave(KErrArgument);
    }
    CleanupStack::PushL(zippedFile);

    TUint uncompressedSize = zippedFile->UncompressedSize();
    if ((TUint)uncompressedSize>=(KMaxTInt/2))
    {
        ELOG(EJavaAppMngrPlugin, "Invalid manifest");
        //User::Leave(KJavaErrInvalidManifest);
        User::Leave(KErrArgument);
    }
    HBufC8* resultData = HBufC8::NewLC(uncompressedSize);

    RZipFileMemberReaderStream* zippedStream = 0;
    TInt err = zipFile->GetInputStreamL(zippedFile, zippedStream);
    User::LeaveIfError(err);
    CleanupStack::PushL(zippedStream);

    TPtr8 ptr(resultData->Des());
    User::LeaveIfError(zippedStream->Read(ptr, uncompressedSize));

    CleanupStack::PopAndDestroy(zippedStream);

    mManifestContent.reset(HBufC16::NewL(uncompressedSize));
    TPtr16 ucsPtr(mManifestContent->Des());
    err = CnvUtfConverter::ConvertToUnicodeFromUtf8(ucsPtr, ptr);

    if (KErrNone != err)
    {
        ELOG1(EJavaAppMngrPlugin, "UTF-8 to unicode conversion failed: %d", err);
        //User::Leave(KJavaErrNoneUTF8);
        User::Leave(KErrArgument);
    }

    CleanupStack::PopAndDestroy(resultData);
    CleanupStack::PopAndDestroy(zippedFile);
    CleanupStack::PopAndDestroy(zipFile);
}

TInt AppMngr2MidletManifestReader::ReadLineIndexL(TBool aContinuation)
{
    TPtr16 contentPtr(mManifestContent->Des());

    if (contentPtr.Length() < KAttributeMinSize)
    {
        // EOF. This covers also case where all rest of the data
        // was whitespace characters trimmed were trimmed off before
        // going to next line.
        return KAllRead;
    }

    TInt attributeIndex = contentPtr.Locate(LF);
    TInt tmp = contentPtr.Locate(CR);

    // New line can be CR LF | LF | CR. Let's take
    // the first appearance of new line character.
    if ((tmp < attributeIndex && tmp != KErrNotFound)
            || attributeIndex == KErrNotFound)
    {
        // This covers also new line indicated only with CR.
        attributeIndex = tmp;
    }

    if (KErrNotFound == attributeIndex && KErrNotFound == tmp)
    {
        // Check if no enter after last line
        if (contentPtr.Length() > KAttributeMinSize)
        {
            attributeIndex = contentPtr.Length();
        }
        else
        {
            ELOG(EJavaAppMngrPlugin, "No attribute found");
            //User::Leave(KJavaErrInvalidManifest);
            User::Leave(KErrArgument);
        }
    }
    else if ((attributeIndex < KAttributeMinSize) && !aContinuation)
    {
        // Minimum parameter length is 4 e.g. 'a: b\n' or 'a:b\n'
        // This is a bit more robust than manifest specification as
        // it expects nameCOLONSPACEvalue format.
        ELOG(EJavaAppMngrPlugin, "Invalid attribute");
        //User::Leave(KJavaErrInvalidManifest);
        User::Leave(KErrArgument);
    }
    else if (attributeIndex <= 0)
    {
        // Minimum continuation parameter length is 1 e.g. 'a\n'
        ELOG(EJavaAppMngrPlugin, "Invalid continuation attribute");
        //User::Leave(KJavaErrInvalidManifest);
        User::Leave(KErrArgument);
    }

    return attributeIndex;
}

void AppMngr2MidletManifestReader::ReadContinuationLineL(TDes16& aValue,
        TDes& aContent,
        TInt& aIndex)
{
    TBool continuation = ETrue;

    while (continuation)
    {
        if (aContent.Length() <= aIndex + 2)
        {
            // Last line
            continuation = EFalse;
        }
        // Continuation can be CR LF SPACE | LF SPACE | CR SPACE
        else if ((aContent[ aIndex ] == CR
                  && aContent[ aIndex + 1 ] == LF
                  && aContent[ aIndex + 2 ] == SP)
                 ||
                 (aContent[ aIndex ] == CR
                  && aContent[ aIndex + 1 ] == SP)
                 ||
                 (aContent[ aIndex ] == LF
                  && aContent[ aIndex + 1 ] == SP))
        {
            // Clean previous entry.
            aContent.Delete(0, aIndex + 1);

            // Delete continuation indicator. Trim cannot be used here. If continuation
            // starts with SP it will be trimmed out and that must be prevented.

            // CR SP or LF SP case
            if (aContent[0] == SP)
            {
                if ((aContent.Length() > 1) &&
                        (aContent[1] != CR) && (aContent[1] != LF))
                {
                    aContent.Delete(0, 1);//for Manifest i JAD
                }
            }
            // CR LF SP case
            else
            {
                if ((aContent.Length() > 2) &&
                        (aContent[2] != CR) && (aContent[2] != LF))
                {
                    aContent.Delete(0, 2);//for Manifest i JAD
                }
                else
                {
                    aContent.Delete(0, 1);//for Manifest i JAD
                }
            }

            aIndex = ReadLineIndexL(ETrue);
            if (aIndex != KAllRead)
            {
                // Do not append whitespace combination. Only content.
                aValue.Append(aContent.Left(aIndex));
                aValue.TrimRight();
            }
            else
            {
                // Can't be continuation if all read. Not leaving
                // as last line can easilly contain unnecessary SPACEs.
                continuation = EFalse;
            }
        }
        else
        {
            continuation = EFalse;
        }
    }
}

void AppMngr2MidletManifestReader::ValidateAttributeNameL(TDesC16& aName)
{
    if (aName.Length() == 0)
    {
        ELOG(EJavaAppMngrPlugin, "Invalid attribute name");
        // User::Leave(KJavaParseInvalidAttributeName);
        User::Leave(KErrArgument);
    }
    TLex lexer(aName);
    TChar ch;

    // Manifest name cannot start with - and _ chars.
    ch = lexer.Get();

    if (ch == '_' || ch == '-')
    {
        ELOG(EJavaAppMngrPlugin, "Invalid attribute name start");
        //User::Leave(KJavaParseInvalidAttributeName);
        User::Leave(KErrArgument);

    }

    lexer.UnGet();

    while ((ch = lexer.Get()) != '\0')
    {
        // Allowed chars: {A-Z} | {a-z} | {0-9} | - | _
        if (!('0' <= ch && ch <= '9')
                && !(ch >= 'a' && ch <= 'z')
                && !(ch >= 'A' && ch <= 'Z')
                && !('-' == ch)
                && !('_' == ch)
                && !('.' == ch))
        {
            ELOG(EJavaAppMngrPlugin, "Invalid attribute name");
            //User::Leave(KJavaParseInvalidAttributeName);
            User::Leave(KErrArgument);
        }
    }
}