diff -r ae942d28ec0e -r 2455ef1f5bbc javamanager/javasettings/appmngrplugin/src/appmngr2midletmanifestreader.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/javamanager/javasettings/appmngrplugin/src/appmngr2midletmanifestreader.cpp Wed Sep 01 12:33:18 2010 +0100 @@ -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 +#include +#include +#include + +#include + +#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& aAttributes) +{ + if (mManifestContent.get()) + { + mManifestContent.reset(0); + } + + ContentL(aManifestFile); + + ReadAttributesL(aAttributes); +} + +void AppMngr2MidletManifestReader::ReadAttributesL( + RPointerArray& 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.AppendL(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_ptrfileStart(HBufC8::NewL(KFilePrefix)); + TPtr8 fileStartPtr(fileStart->Des()); + + std::auto_ptrfilePrefix(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_ptrcontent( + 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); + } + } +}