javamanager/javasettings/appmngrplugin/src/appmngr2midletmanifestreader.cpp
branchRCL_3
changeset 19 04becd199f91
child 60 6c158198356e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/javamanager/javasettings/appmngrplugin/src/appmngr2midletmanifestreader.cpp	Tue Apr 27 16:30:29 2010 +0300
@@ -0,0 +1,475 @@
+/*
+* 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);
+        }
+    }
+}