diff -r 000000000000 -r dd21522fd290 widgets/widgetinstaller/src/WidgetUIConfigHandler.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/widgets/widgetinstaller/src/WidgetUIConfigHandler.cpp Mon Mar 30 12:54:55 2009 +0300 @@ -0,0 +1,822 @@ +/* +* Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of the License "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: Widget installer info file parsing. +* +*/ + + +#include +#include +#include +#include +#include +#include "WidgetUIConfigHandler.h" +#include "SWInstWidgetUid.h" +#include "WidgetInstallerInternalCRKeys.h" +#include +#include + + +// DTD + +_LIT8( KNokiaId, "-//Nokia//DTD PLIST" ); // only the first part without version +_LIT8( KAppleId, "-//Apple Computer//DTD PLIST" ); // only the first part without version + +// parsed elements in the DTD (XML element names are case sensitive) + +_LIT8( KKey,"key" ); +_LIT8( KString,"string" ); +_LIT8( KInt,"integer" ); +_LIT8( KTrue,"true" ); +_LIT8( KFalse,"false" ); + +// properties from widget metadata file + +// NOTE: Comments "required" and "optional" apply to our requirements +// for registering a widget. + +// NOTE: These property names are not XML element names, they are text +// content for the element, so we have made the match case +// insensitive and capitalization here is just for readability. + +// Apple unique key names +_LIT( KAppleBundleIdentifier, "CFBundleIdentifier" ); // required +_LIT( KAppleDisplayName, "CFBundleDisplayName" ); // required +_LIT( KAppleBundleVersion, "CFBundleVersion" ); // optional +_LIT( KAppleAllowFullAccess, "AllowFullAccess" ); // optional +// For Apple, AllowFullAccess is mapped to AllowNetworkAccess unless +// AllowNetworkAccess appears too. + +// Nokia unique key names +_LIT( KNokiaIdentifier, "Identifier" ); // required +_LIT( KNokiaDisplayName, "DisplayName" ); // required +_LIT( KNokiaVersion, "Version" ); // optional +_LIT( KNokiaMiniViewEnabled, "MiniViewEnabled" ); // optional +_LIT( KBlanketPermGranted, "BlanketPermissionGranted" ); + +// Apple/Nokia shared key names +_LIT( KMainHTML, "MainHTML" ); // required +_LIT( KAllowNetworkAccess, "AllowNetworkAccess" ); // optional +_LIT( KHeight, "Height" ); // optional +_LIT( KWidth, "Width" ); // optional + + +enum TWidgetPropertyFlag + { + EMandatory = 1, + EConfig = 2, + EApple = 4, + ENokia = 8, + ESpecial = 16 + }; + +#define CONFIG_MUST ( EMandatory | EConfig ) +#define CONFIG_APPLE_MUST ( EMandatory | EConfig | EApple ) +#define CONFIG_NOKIA_MUST ( EMandatory | EConfig | ENokia ) + +#define CONFIG_MAY ( EConfig ) +#define CONFIG_APPLE_MAY ( EConfig | EApple ) +#define CONFIG_NOKIA_MAY ( EConfig | ENokia ) + +#define CONFIG_APPLE_SPECIAL ( EConfig | EApple | ESpecial ) + +using namespace SwiUI; + +// ============================================================================ +// Traverse to the next Node +// +// @param aNode: current node +// @since 3.1 +// @return next node +// ============================================================================ +// +xmlNode* CWidgetUIConfigHandler::TraverseNextNode( xmlNode* n ) + { + // depth first + if ( n->children ) + { + n = n->children; + } + else + { + // go up while no sibling + while ( n->parent && !n->next ) + { + n = n->parent; + } + // sibling? + if ( n->next ) + { + n = n->next; + } + else // done + { + n = NULL; + } + } + return n; + } + +// ============================================================================ +// CWidgetUIConfigHandler::NewL() +// two-phase constructor +// +// @since 3.1 +// ============================================================================ +// +CWidgetUIConfigHandler* CWidgetUIConfigHandler::NewL() + { + CWidgetUIConfigHandler *self = new (ELeave) CWidgetUIConfigHandler(); + CleanupStack::PushL( self ); + self->ConstructL(); + CleanupStack::Pop(); + return self; + } + +// ============================================================================ +// CWidgetUIConfigHandler::CWidgetUIConfigHandler() +// C++ constructor +// +// @since 3.1 +// ============================================================================ +// +CWidgetUIConfigHandler::CWidgetUIConfigHandler() + { + iProperties[EPropertyDescriptionAppleBundleIdentifier].id = EBundleIdentifier; + iProperties[EPropertyDescriptionAppleBundleIdentifier].name.Set( KAppleBundleIdentifier ); + iProperties[EPropertyDescriptionAppleBundleIdentifier].type = EWidgetPropTypeString; + iProperties[EPropertyDescriptionAppleBundleIdentifier].flags = CONFIG_APPLE_MUST; + // + iProperties[EPropertyDescriptionAppleBundleDisplayName].id = EBundleDisplayName; + iProperties[EPropertyDescriptionAppleBundleDisplayName].name.Set( KAppleDisplayName ); + iProperties[EPropertyDescriptionAppleBundleDisplayName].type = EWidgetPropTypeString; + iProperties[EPropertyDescriptionAppleBundleDisplayName].flags = CONFIG_APPLE_MUST; + // + iProperties[EPropertyDescriptionAppleBundleVersion].id = EBundleVersion; + iProperties[EPropertyDescriptionAppleBundleVersion].name.Set( KAppleBundleVersion ); + iProperties[EPropertyDescriptionAppleBundleVersion].type = EWidgetPropTypeString; + iProperties[EPropertyDescriptionAppleBundleVersion].flags = CONFIG_APPLE_MAY; + // + iProperties[EPropertyDescriptionAppleAllowFullAccess].id = EWidgetPropertyIdInvalid; + iProperties[EPropertyDescriptionAppleAllowFullAccess].name.Set( KAppleAllowFullAccess ); + iProperties[EPropertyDescriptionAppleAllowFullAccess].type = EWidgetPropTypeBool; + iProperties[EPropertyDescriptionAppleAllowFullAccess].flags = CONFIG_APPLE_SPECIAL; + // + iProperties[EPropertyDescriptionNokiaIdentifier].id = EBundleIdentifier; + iProperties[EPropertyDescriptionNokiaIdentifier].name.Set( KNokiaIdentifier ); + iProperties[EPropertyDescriptionNokiaIdentifier].type = EWidgetPropTypeString; + iProperties[EPropertyDescriptionNokiaIdentifier].flags = CONFIG_NOKIA_MUST; + // + iProperties[EPropertyDescriptionNokiaDisplayName].id = EBundleDisplayName; + iProperties[EPropertyDescriptionNokiaDisplayName].name.Set( KNokiaDisplayName ); + iProperties[EPropertyDescriptionNokiaDisplayName].type = EWidgetPropTypeString; + iProperties[EPropertyDescriptionNokiaDisplayName].flags = CONFIG_NOKIA_MUST; + // + iProperties[EPropertyDescriptionNokiaVersion].id = EBundleVersion; + iProperties[EPropertyDescriptionNokiaVersion].name.Set( KNokiaVersion ); + iProperties[EPropertyDescriptionNokiaVersion].type = EWidgetPropTypeString; + iProperties[EPropertyDescriptionNokiaVersion].flags = CONFIG_NOKIA_MAY; + // + iProperties[EPropertyDescriptionMainHTML].id = EMainHTML; + iProperties[EPropertyDescriptionMainHTML].name.Set( KMainHTML ); + iProperties[EPropertyDescriptionMainHTML].type = EWidgetPropTypeString; + iProperties[EPropertyDescriptionMainHTML].flags = CONFIG_MUST; + // + iProperties[EPropertyDescriptionAllowNetworkAccess].id = EAllowNetworkAccess; + iProperties[EPropertyDescriptionAllowNetworkAccess].name.Set( KAllowNetworkAccess ); + iProperties[EPropertyDescriptionAllowNetworkAccess].type = EWidgetPropTypeBool; + iProperties[EPropertyDescriptionAllowNetworkAccess].flags = CONFIG_MAY; + // + iProperties[EPropertyDescriptionHeight].id = EHeight; + iProperties[EPropertyDescriptionHeight].name.Set( KHeight ); + iProperties[EPropertyDescriptionHeight].type = EWidgetPropTypeInt; + iProperties[EPropertyDescriptionHeight].flags = CONFIG_MAY; + // + iProperties[EPropertyDescriptionWidth].id = EWidth; + iProperties[EPropertyDescriptionWidth].name.Set( KWidth ); + iProperties[EPropertyDescriptionWidth].type = EWidgetPropTypeInt; + iProperties[EPropertyDescriptionWidth].flags = CONFIG_MAY; + // + iProperties[EPropertyDescriptionNokiaMiniViewEnable].id = EMiniViewEnable; + iProperties[EPropertyDescriptionNokiaMiniViewEnable].name.Set( KNokiaMiniViewEnabled ); + iProperties[EPropertyDescriptionNokiaMiniViewEnable].type = EWidgetPropTypeBool; + iProperties[EPropertyDescriptionNokiaMiniViewEnable].flags = CONFIG_NOKIA_MAY; + // + iProperties[EPropertyDescriptionNokiaPromptNeeded].id = EBlanketPermGranted; + iProperties[EPropertyDescriptionNokiaPromptNeeded].name.Set( KBlanketPermGranted ); + iProperties[EPropertyDescriptionNokiaPromptNeeded].type = EWidgetPropTypeBool; + iProperties[EPropertyDescriptionNokiaPromptNeeded].flags = CONFIG_NOKIA_MAY; + } + +// ============================================================================ +// CWidgetUIConfigHandler::ConstructL() +// C++ constructor +// +// @since 3.1 +// ============================================================================ +// +void CWidgetUIConfigHandler::ConstructL() + { + } + +// ============================================================================ +// CWidgetUIConfigHandler::~CWidgetUIConfigHandler() +// destructor +// +// @since 3.1 +// ============================================================================ +// +CWidgetUIConfigHandler::~CWidgetUIConfigHandler() + { + } + +// ============================================================================ +// Get the key type out of the keyname +// +// @param aKeyName The name of the key: KeyName +// @since 3.1 +// @return Key type. +// ============================================================================ +// +TWidgetPropertyId CWidgetUIConfigHandler::GetPropertyId( + const TDesC& aKeyName, + DtdType aDtdType, + TWidgetPropertyDescriptionId& aPropertyDescriptionId ) + { + aPropertyDescriptionId = EPropertyDescriptionIdInvalid; + TInt i = 0; + for (; i < EPropertyDescriptionIdCount; ++i ) + { + if ( (EDtdTypeApple == aDtdType) + && ( iProperties[i].flags & ENokia ) ) + { + continue; + } + if ( (EDtdTypeNokia == aDtdType) + && ( iProperties[i].flags & EApple ) ) + { + continue; + } + // we use case insensitive match for property names + if ( 0 == aKeyName.CompareF( iProperties[i].name ) ) + { + aPropertyDescriptionId = static_cast(i); + if ( iProperties[i].flags & ESpecial ) + { + return EWidgetPropertyIdInvalid; + } + return iProperties[i].id; + } + } + return EWidgetPropertyIdInvalid; + } + +// ============================================================================ +// CWidgetUIConfigHandler::ToUnicodeL +// Utility to bundle transcoding to unicode steps. +// +// @since 3.1 +// @param aEncoding input buffer encoding +// @param aUnicodeSizeMultiplier how many bytes of input make one unicode char +// @param aInBuf input data in encoding +// @param aOutBuf malloc'ed output buf, caller takes ownership +// @param aFileSession CCnvCharacterSetConverter requires it +// ============================================================================ +// +void CWidgetUIConfigHandler::ToUnicodeL( TInt aEncoding, + TInt aUnicodeSizeMultiplier, + TPtrC8 aInBuf, HBufC16** aOutBuf, + RFs& aFileSession ) + { + *aOutBuf = NULL; + + // outbuf sizing and alloction + HBufC16* outBuf = HBufC16::NewLC(aUnicodeSizeMultiplier * aInBuf.Length()); + TPtr16 outPtr = outBuf->Des(); + + // convert to unicode + CCnvCharacterSetConverter* charConv = CCnvCharacterSetConverter::NewLC(); + charConv->PrepareToConvertToOrFromL( aEncoding, aFileSession ); + TInt state = CCnvCharacterSetConverter::KStateDefault; + TInt rep = 0; // number of unconvertible characters + TInt rIndx = 0; // index of first unconvertible character + User::LeaveIfError( + charConv->ConvertToUnicode( outPtr, aInBuf, state, rep, rIndx ) ); + CleanupStack::PopAndDestroy( charConv ); + + CleanupStack::Pop(); // outBuf + *aOutBuf = outBuf; + } + +// ============================================================================ +// CWidgetUIConfigHandler::GetContentL +// Utility to bundle extraction of XML text content +// +// @since 3.1 +// @param aEncoding input buffer encoding +// @param aUnicodeSizeMultiplier how many bytes of input make one unicode char +// @param aInBuf input data in encoding +// @param aOutBuf malloc'ed output buf, caller takes ownership +// @param aFileSession CCnvCharacterSetConverter requires it +// ============================================================================ +// +void CWidgetUIConfigHandler::GetContentL( RFs& aFileSession, + xmlDocPtr aDoc, + xmlNode* aNode, + HBufC** aContent ) + { + // xml uses UTF-8 for the internal representation + xmlChar* xmlContent = + xmlNodeListGetString( aDoc, aNode, 1 /* expand entities inline */); + if ( NULL == xmlContent ) + { + User::Leave( OOM_FLAG ? KErrNoMemory : KErrCorrupt ); + } + // we must transcode UTF-8 to UCS-2 (historical + // and now inaccurate name "unicode") + CleanupStack::PushL( xmlContent ); + TPtrC8 content( xmlContent ); + ToUnicodeL( KCharacterSetIdentifierUtf8, 2, + content, aContent, aFileSession ); + CleanupStack::PopAndDestroy(); // xmlContent equivalent to xmlFree() + if ( NULL == *aContent ) + { + User::Leave( KErrCorrupt ); + } + } + +// ============================================================================ +// CWidgetUIConfigHandler::ParseValidateBundleMetadataL +// Parse the widget info file and create widget entry +// check for required keys and values +// +// @since 3.1 +// @param aBuffer The buffer contains widget info file content. +// @param aPropertyValues output filled with parsed values from buf +// ============================================================================ +// +void CWidgetUIConfigHandler::ParseValidateBundleMetadataL( + TPtr8 aBuffer, + RPointerArray& aPropertyValues, + RFs& aFileSession ) + { + /* + steps: 1. parse bundle metadata (ex., info.plist) and put + results in aPropertyValues; 2. validate the metadata 2a. are + required keys present? 2b. are values sane? + + leaves: 1. doesn't parse -> KErrCorrupt; 2. DTD not Nokia and + cenrep key KWidgetInstallerStrictMode is 1 -> KErrNotSupported; + 3. key type bool but child element not true or false -> + KErrCorrupt; 4. key type integer but child element not integer + -> KErrCorrupt; 5. key type integer and child element integer + but unparsable integer value -> KErrCorrupt; 6. key type string + and child element not string -> KErrCorrupt; 7. key type string + and child element string but does not contain text -> + KErrCorrupt; 8. required keys not present -> KErrNotSupported + 9. key values not sane -> KErrNotSupported; 10. Heap allocation + failure -> KErrNoMem + */ + + TInt nokiaOnly = 0; + TRAP_IGNORE( + CRepository* rep = CRepository::NewL( TUid::Uid( KSWInstWidgetUIUid ) ); + rep->Get( KWidgetInstallerStrictMode, nokiaOnly ); + delete rep; ); + + // initialize the parser and check compiled code matches lib version + +#if 0 +// xmllib has a memory leak, so in order to detect mem leaks outside +// of the xmllib, this code fills in the values that the parse step +// would and returns. you have to fill in the values for the specific +// Info.plist you are installing + + _LIT( KIdentifier, "com.aws.widget.beta1" ); + _LIT( KName, "WeatherBug" ); + _LIT( KHtml, "index.html" ); + *(aPropertyValues[EBundleIdentifier]) = KIdentifier; + *(aPropertyValues[EBundleDisplayName]) = KName(); + *(aPropertyValues[EMainHTML]) = KHtml; + *(aPropertyValues[EAllowNetworkAccess]) = 1; + *(aPropertyValues[ENokiaWidget]) = 0; + + // TODO: We decided to drop BundleName and just use + // DisplayName but the registry code has errors in it and uses + // BundleName when it should use DisplayName so as a workaround, + // set BundleName to DisplayName. Should eventually remove + // BundleName from set of registry values. + const TDesC& name = *(aPropertyValues[EBundleDisplayName]); + *(aPropertyValues[EBundleName]) = name; + +#else + LIBXML_TEST_VERSION + + xmlDocPtr doc; // resulting document tree + + doc = xmlReadMemory( (const char *)aBuffer.Ptr(), aBuffer.Length(), + NULL, // no base URL + NULL, // get encoding from doc + XML_PARSE_NOWARNING | XML_PARSE_NONET ); // options + // parse failed check + if ( !doc ) + { + xmlCleanupParser(); + User::Leave( KErrCorrupt ); + } + + // determine doctype + xmlNode* n; + xmlDtd* dtd = NULL; + for ( n = doc->children; n; n = n->next ) + { + if ( XML_DTD_NODE == n->type ) + { + dtd = (xmlDtd*)n; + break; + } + } + iDtdType = EDtdTypeUnknown; + if ( dtd ) + { + TPtrC8 id( dtd->ExternalID ); + if ( 0 == id.Left(KNokiaId().Length()).Compare(KNokiaId()) ) + { + iDtdType = EDtdTypeNokia; + } + else if ( 0 == id.Left(KAppleId().Length()).Compare(KAppleId()) ) + { + iDtdType = EDtdTypeApple; + } + } + + // save for registry so non-nokia installed on memory cards can be blocked + // when inserted in nokia-only configured device + *(aPropertyValues[ENokiaWidget]) = ( (EDtdTypeNokia == iDtdType) ? 1 : 0 ); + + // handle cenrep nokia only setting + if ( (EDtdTypeUnknown == iDtdType) + || (nokiaOnly && ( EDtdTypeNokia != iDtdType )) ) + { + User::Leave( KErrNotSupported ); + } + + xmlNode* rootElement = xmlDocGetRootElement( doc ); + TWidgetPropertyId valId( EWidgetPropertyIdInvalid ); + TWidgetPropertyDescriptionId propertyDescriptionId( EPropertyDescriptionIdInvalid ); + + for ( n = rootElement; n; n = TraverseNextNode( n ) ) + { + if ( XML_ELEMENT_NODE == n->type ) + { + TPtrC8 element( n->name ); + + if ( 0 == element.Compare( KKey() ) ) + { + valId = EWidgetPropertyIdInvalid; + HBufC* keyName; + GetContentL( aFileSession, doc, n->children, &keyName ); + CleanupStack::PushL( keyName ); + TPtr name( keyName->Des() ); + name.Trim(); // remove surrounding whitespace + valId = GetPropertyId( name, iDtdType, propertyDescriptionId ); + CleanupStack::PopAndDestroy( keyName ); + + // reject duplicate keys based on value already being + // set (unset values have type unknown) + if ( ( EWidgetPropertyIdInvalid != valId ) + && ( EWidgetPropTypeUnknown + != aPropertyValues[valId]->iType ) ) + { + User::Leave( KErrCorrupt ); + } + } + else if ( EWidgetPropertyIdInvalid != valId ) + { + switch ( iProperties[propertyDescriptionId].type ) + { + case EWidgetPropTypeBool: + // map true to 1 and false to 0 + if ( 0 == element.Compare( KTrue ) ) + { + *(aPropertyValues[valId]) = 1; + } + else if ( 0 == element.Compare( KFalse ) ) + { + *(aPropertyValues[valId]) = 0; + } + else + { + User::Leave( KErrCorrupt ); + } + break; + case EWidgetPropTypeInt: + { + if ( 0 == element.Compare( KInt() ) ) + { + HBufC* keyVal; + GetContentL( aFileSession, doc, n->children, &keyVal ); + CleanupStack::PushL( keyVal ); + TPtr value( keyVal->Des() ); + value.Trim(); // remove surrounding whitespace + TLex tlex; + tlex.Assign( value ); + TInt x; + TInt e = tlex.Val( x ); + CleanupStack::PopAndDestroy( keyVal ); + if ( !e && tlex.Eos() ) + { + *(aPropertyValues[valId]) = x; + } + else + { + User::Leave( KErrCorrupt ); + } + } + else + { + User::Leave( KErrCorrupt ); + } + } + break; + case EWidgetPropTypeString: + if ( 0 == element.Compare( KString() ) ) + { + HBufC* keyVal; + GetContentL( aFileSession, doc, n->children, &keyVal ); + CleanupStack::PushL( keyVal ); + TPtr value( keyVal->Des() ); + value.Trim(); // remove surrounding whitespace + *(aPropertyValues[valId]) = value; + CleanupStack::PopAndDestroy( keyVal ); + } + else + { + User::Leave( KErrCorrupt ); + } + break; + default: + // ignore other things + break; + } + } + else if ( EPropertyDescriptionIdInvalid != propertyDescriptionId ) + { + if ( EPropertyDescriptionAppleAllowFullAccess == propertyDescriptionId ) + { + // only set if AllowNetworkAccess is not yet set + if ( aPropertyValues[EAllowNetworkAccess]->iType != EWidgetPropTypeInt ) + { + // map true to 1 and false to 0 + if ( 0 == element.Compare( KTrue ) ) + { + *(aPropertyValues[EAllowNetworkAccess]) = 1; + } + else if ( 0 == element.Compare( KFalse ) ) + { + *(aPropertyValues[EAllowNetworkAccess]) = 0; + } + else + { + User::Leave( KErrCorrupt ); + } + } + } + } + } // if n is element + } // for + + // validate: all required keys are present + TInt i = 0; + for ( ; i < EPropertyDescriptionIdCount; ++i ) + { + if ( (iProperties[i].flags & EMandatory) + && (iProperties[i].flags & EConfig) + // a prop name maps to some value id + && ((EWidgetPropertyIdInvalid != iProperties[i].id) + && (aPropertyValues[iProperties[i].id]->iType + != iProperties[i].type)) ) + { + // if cause of mismatch is bool mapped to int, then continue check + if ( (EWidgetPropTypeInt != aPropertyValues[iProperties[i].id]->iType) + || (EWidgetPropTypeBool != iProperties[i].type) ) + { + User::Leave( KErrNotSupported ); + } + } + } + + // TODO: We decided to drop BundleName and just use + // DisplayName but the registry code has errors in it and uses + // BundleName when it should use DisplayName so as a workaround, + // set BundleName to DisplayName. Should eventually remove + // BundleName from set of registry values. + const TDesC& name = *(aPropertyValues[EBundleDisplayName]); + *(aPropertyValues[EBundleName]) = name; + + // validate values are sane + ValidateL( aPropertyValues ); + + xmlFreeDoc(doc); + xmlCleanupParser(); +#endif + } + +// ============================================================================ +// CWidgetUIConfigHandler::ValidateL +// Check values. +// +// @since 3.1 +// @param aPropertyValues output filled with parsed values from buf +// ============================================================================ +// +void CWidgetUIConfigHandler::ValidateL( + RPointerArray& aPropertyValues ) + { + // leaves with KErrCorrupt if any values are rejected + + // The limits and their justification: + // + // Strings are limited to KMaxFileName and must not be empty. + // + // For values like "Identifier" that will be used in filenames the + // fundamental limit in Symbian is KMaxFileName (from e32const.h + // and included by essentially all higher level e32*.h). Since + // these values will be concatenated with other directory + // components, we might think to use a heuristic limit less than + // KMaxFileName, but this introduces a needless arbitrary limit + // and it will be cleaner to check length against KMaxFileName + // after concatenation. Checking here just means detecting a + // limit violation earlier and maybe giving a better error + // message. Also, the KErrBadName error value from filesystem + // operations will signal a pathname problem and MUST be checked. + // + // For strings like "DisplayName" that will be used in menus there + // is no clear predefined value given that displays come in + // various sizes and various fonts are used. I'm going to impose + // the same limit as for values used in filenames on the belief + // that this is useful and not harmfull. + + // Also, check that values are not empty. + + TInt i = 0; + for (; i < EPropertyDescriptionIdCount; ++i ) + { + if ( EWidgetPropTypeString == aPropertyValues[i]->iType ) + { + const TDesC& s = *(aPropertyValues[i]); + if ( !s.Length() || ( s.Length() > KMaxFileName ) ) + { + User::Leave( KErrCorrupt ); + } + } + } + } + +// ============================================================================ +// CWidgetUIConfigHandler::ParseInfoLocL() +// +// @since 3.1 +// ============================================================================ +// +void CWidgetUIConfigHandler::ParseInfoLocL( + TPtrC8 aBuffer, + RFs& aFileSession, + CWidgetPropertyValue& aBundleDisplayName ) + { + // This logic is for localization of the display name. The + // display name appears in a localization file in the form: + // DisplayName = "something"; (for Nokia DTD) or + // CFBundleDisplayName = "something"; (for Apple DTD). + + // The encoding of the localization file is not given so it must + // be automatically recognized. The default is UTF-8 but if a + // byte-order-mark is the first character then the BOM determines + // the encoding. + + // get key name ID for display name depending on DTD + TWidgetPropertyDescriptionId displayNameId = EPropertyDescriptionIdInvalid; + if ( iDtdType == EDtdTypeNokia ) + { + displayNameId = EPropertyDescriptionNokiaDisplayName; + } + else if ( iDtdType == EDtdTypeApple ) + { + displayNameId = EPropertyDescriptionAppleBundleDisplayName; + } + if ( EPropertyDescriptionIdInvalid == displayNameId ) + { + User::Leave( KErrCorrupt ); + } + + TPtrC8 inBuf; + + // default input encoding, no BOM + TInt encoding = KCharacterSetIdentifierUtf8; + TInt unicodeSizeMultiplier = 2; // a safe value which may waste some space + inBuf.Set( aBuffer ); + + // check for BOM, we only recognize UTF-16(BE/LE) and UTF-8, + // remove BOM if found + if ( aBuffer[0] == 0xFE && aBuffer[1] == 0xFF ) + { + encoding = KCharacterSetIdentifierUnicodeBig; + unicodeSizeMultiplier = 1; + inBuf.Set( aBuffer.Right( aBuffer.Length() - 2 ) ); + } + else if ( aBuffer[0] == 0xFF && aBuffer[1] == 0xFE ) + { + encoding = KCharacterSetIdentifierUnicodeLittle; + unicodeSizeMultiplier = 1; + inBuf.Set( aBuffer.Right( aBuffer.Length() - 2 ) ); + } + else if ( aBuffer[0] == 0xEF && aBuffer[1] == 0xBB && aBuffer[2] == 0xBF ) + { + encoding = KCharacterSetIdentifierUtf8; + unicodeSizeMultiplier = 2; // a safe value which may waste some space + inBuf.Set( aBuffer.Right( aBuffer.Length() - 3 ) ); + } + + // convert to unicode + HBufC16* outBuf; + ToUnicodeL( encoding, unicodeSizeMultiplier, + inBuf, &outBuf, aFileSession ); + CleanupStack::PushL( outBuf ); + TPtr16 outPtr = outBuf->Des(); + // convert the display name key name to unicode since it is just + // _LIT() and so might be 8 bit and we want 16 bit "unicode" + TPtrC keyName = iProperties[displayNameId].name; + HBufC16* keyNameUnicode = HBufC16::NewLC( keyName.Length() ); + TPtr16 keyNameUnicodePtr( keyNameUnicode->Des() ); + keyNameUnicodePtr.Copy( keyName ); + + // parse the display name + outPtr.TrimLeft(); // remove leading whitespace + TInt pos = outPtr.Find( keyNameUnicodePtr ); + if ( (KErrNotFound == pos) || (0 != pos) ) + { + User::Leave( KErrCorrupt ); + } + + // rest contains buffer after the DisplayName, + // i.e. = "some localized text" + TPtr16 rest = + outPtr.RightTPtr( + outPtr.Length() + - pos + - keyNameUnicode->Length() ); + const TUint16* data = rest.Ptr(); + TBool hasEqual = EFalse; // already pass the = sign? + + // start pos and end pos of localized displayname + TUint8 start = 0; + TUint8 end = 0; + for ( TInt i = 0; i < rest.Length(); i++ ) + { + if ( data[i] == ' ' || data[i] == '\t' ) + { + continue; + } + if ( data[i] == '=' ) + { + hasEqual = ETrue; + continue; + } + if ( data[i] == '\"' ) + { + if ( !start ) + { + start = i + 1; + continue; + } + // else already inquote, so this is close quote + end = i; + break; + } + if ( !hasEqual || !start ) + { + User::Leave( KErrCorrupt ); + } + } + + if ( end <= start ) + { + User::Leave( KErrCorrupt ); + } + TInt left = pos + keyNameUnicode->Length() + start; + aBundleDisplayName = outPtr.MidTPtr( left, end - start); + CleanupStack::PopAndDestroy( 2 ); // outBuf, keyNameUnicode + } + +// End of File