/*
* Copyright (c) 2006, 2008 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: This file contains the header file of the CMidletUI class.
*
* This class implements the ECom SWInstUIPluginAPI interface
* for midlet installation.
*
*
*/
#include <f32file.h>
#include <bautils.h>
#include <e32cmn.h>
#include <libxml2_globals.h>
#include <libc/stdlib.h>
#include <libxml2_parser.h>
#include <libxml2_tree.h>
#include "Browser_platform_variant.hrh"
#ifdef BRDO_SYMBIAN_LIBXML_FF
#include <xmlengxestd.h>
#endif
#include "WidgetInstaller.h"
#include "WidgetConfigHandler.h" // info.plist parser
#include "WidgetRegistrationManager.h" // interface to "shell"
#include "IconConverter.h"
#include "WidgetBackupRegistryXml.h"
// CONSTANTS
_LIT( KInfoPList,"Info.plist" );
_LIT( KIconFile, "Icon.png" );
_LIT( KMBMExt, ".mbm");
_LIT( KLprojExt, ".lproj" );
_LIT( KInfoPlistStrings, "InfoPlist.strings" );
_LIT( KWidgetPropFile, "\\private\\10282822\\WidgetEntryStore.xml" );
// For parsing backedup registration file
_LIT8( KWidgetRegistry, "widgetregistry" );
_LIT8( KEntry, "entry" );
_LIT8( KXmlPropTag, "prop" );
_LIT8( KXmlValTag, "val" );
_LIT8( KXmlTypeTag, "type" );
_LIT( KXmlDataTypeBool, "bool" );
_LIT( KXmlDataTypeInt, "int" );
_LIT( KXmlDataTypeString, "string" );
_LIT( KXmlDataTypeUid, "uid" );
// TODO MW has a hard dependency to APP domain. Not very good...
// TODO Hard-coded UID.
_LIT( KWidgetAppDir, "\\private\\10282822\\" );
_LIT( KBackSlash, "\\" );
// todo: other keystring.dat for preference
_LIT(KWidgetPref, "prefs.dat");
// =========================== MEMBER FUNCTIONS ===============================
// ============================================================================
// CWidgetInstaller::NewL()
// two-phase constructor
//
// @since 3.1
// @return pointer to CWidgetInstaller
// ============================================================================
//
EXPORT_C CWidgetInstaller* CWidgetInstaller::NewL()
{
CWidgetInstaller* self
= new (ELeave) CWidgetInstaller();
CleanupStack::PushL( self );
self->ConstructL();
CleanupStack::Pop( self );
return self;
}
// ============================================================================
// CWidgetInstaller::CWidgetInstaller()
// C++ constructor
//
// @since 3.1
// ============================================================================
//
CWidgetInstaller::CWidgetInstaller()
: iPropertyValues( EWidgetPropertyIdCount )
{
}
// ============================================================================
// CWidgetInstaller::ConstructL()
// Symbian second phase constructor
//
// @since 3.1
// ============================================================================
//
void CWidgetInstaller::ConstructL()
{
User::LeaveIfError( iRfs.Connect() );
User::LeaveIfError( iRfs.ShareProtected() );
iFileMgr = CFileMan::NewL( iRfs );
iAppManager = CWidgetRegistrationManager::NewL( iRfs );
iWidgetConfigHandler = CWidgetConfigHandler::NewL();
// delay registry connect because registry is a client and that
// produces a circular dependency
iServerConnected = EFalse;
// empty values
for ( TInt i( 0 ); i < EWidgetPropertyIdCount; ++i )
{
CWidgetPropertyValue* value = CWidgetPropertyValue::NewL();
User::LeaveIfError( iPropertyValues.Insert( value, i ) );
}
*(iPropertyValues[EWidgetPropertyListVersion]) = WIDGETPROPERTYLISTVERSION;
#ifdef _DEBUG
_LIT(KDir, "WidgetBUR");
_LIT(KFile, "WidgetBURInstaller.log");
TInt err( 0 );
err = iFileLogger.Connect();
if ( err == KErrNone )
{
iFileLogger.CreateLog( KDir(), KFile(), EFileLoggingModeOverwrite );
iCanLog = ETrue;
}
#endif
}
// ============================================================================
// CWidgetInstaller::~CWidgetInstaller()
// destructor
//
// @since 3.1
// ============================================================================
//
CWidgetInstaller::~CWidgetInstaller()
{
TInt i = 0;
for ( ; i < EWidgetPropertyIdCount; ++i )
{
delete iPropertyValues[i];
}
iPropertyValues.Close();
delete iFileMgr;
iRfs.Close();
delete iWidgetConfigHandler;
delete iAppManager;
if ( iServerConnected )
{
iRegistry.Disconnect();
}
#ifdef _DEBUG
iFileLogger.Close();
#endif
}
// ============================================================================
// CWidgetInstaller::InstallL()
// NORMAL INSTALL STEPS:
// 2. get metadata file (e.g. Info.plist) and parse it
// 3. if needed, replace identically named existing widget)
// 4. do free space check
// 5. if replacing existing widget then move currently installed files
// to backup for possible restore on install error
//
// @since 3.1
// ============================================================================
//
EXPORT_C void CWidgetInstaller::InstallL(
TDesC& aRestoreDir )
{
if ( EFalse == iServerConnected )
{
User::LeaveIfError( iRegistry.Connect() );
iServerConnected = ETrue;
}
// The bool will be flase until doing something that
// needs to be cleaned up on error/cancel.
iOverwriting = EFalse;
TRAPD( error,
{
// do initial bundle processing (parse and validate, total size,
// check if replacing existing widget with same bundle ID, etc.)
//
// this step doesn't do anything that would need to be undone
TBool replaceExisting = PreprocessWidgetBundleL( aRestoreDir );
SetDriveAndCheckSpaceL();
if ( replaceExisting )
{
BackupL(); // nothing need be undone if backup leaves
}
}
); // TRAPD
if ( error == KErrNone )
{
ProcessRestoreDirL( aRestoreDir );
}
else if ( error == KErrAlreadyExists )
{
// max number of uid reached, to avoid SBE undo previous widget restore, don't leave with error
return;
}
else
{
User::Leave( error );
}
}
// ============================================================================
// CWidgetInstaller::SearchWidgetRootL
//
// @since 3.2
// ============================================================================
//
TInt CWidgetInstaller::SearchWidgetRootL( const TDesC& aDir, TFileName& aFoundName )
{
CDir* entryList = NULL;
TBool found = EFalse;
RFile rFile;
// aDir = "\private\[WidgetBackupRestoreUid]\WidgetBURTemp\bundleID\"
TInt err = iRfs.GetDir( aDir, KEntryAttMatchMask, EDirsFirst, entryList );
if ( err == KErrNone )
{
CleanupStack::PushL(entryList);
// check for directory entries
TInt cnt = entryList->Count();
for (TInt i=0; i<cnt; i++)
{
const TEntry& fileEntry = (*entryList)[i];
TFileName tempFile( aDir );
tempFile.Append( fileEntry.iName );
tempFile.Append( KBackSlash );
TInt64 dirSize = GetDirSizeL( tempFile );
if ( 0 == dirSize )
continue;
tempFile.Append( KInfoPList );
TInt error = rFile.Open( iRfs, tempFile, EFileRead );
CleanupClosePushL( rFile );
CleanupStack::PopAndDestroy(); // rFile
if ( KErrNone == error )
{
aFoundName.Copy( fileEntry.iName );
found = ETrue;
break;
}
} // end of for
CleanupStack::PopAndDestroy(); // entryList
} // end of if iRfs
return found;
}
// ============================================================================
// CWidgetInstaller::FixWidgetPropsL()
// Handles preprocessing of widget properties
//
// @since 5.0
// ============================================================================
//
void CWidgetInstaller::FixWidgetPropsL()
{
TInt backupBlanketPerm = -1;
CWidgetBackupRegistryXml* aXmlProcessor = CWidgetBackupRegistryXml::NewL();
RPointerArray<CWidgetPropertyValue> backupPropertyValues;
TInt i = 0;
// empty values
for ( ; i < EWidgetPropertyIdCount; ++i )
{
CWidgetPropertyValue* value = CWidgetPropertyValue::NewL();
CleanupStack::PushL( value );
User::LeaveIfError( backupPropertyValues.Insert( value, i ) );
CleanupStack::Pop( value );
}
// Algorithm
// 1. Look for the entry
// 2. Check if the EBundleIdentifier is what we are looking for
// 3. If so, looking for the value of EBlanketPermGranted
// 4. If exist, then we fix it
// hardcode the filename first
RFile file;
TFileName propFile( KWidgetPropFile );
User::LeaveIfError( file.Open( iRfs, propFile, EFileRead ));
CleanupClosePushL( file );
TInt size;
User::LeaveIfError ( file.Size ( size ));
HBufC8* buf = HBufC8::NewLC ( size );
TPtr8 bufPtr ( buf->Des() );
User::LeaveIfError( file.Read( bufPtr ) );
// initialize the parser and check compiled code matches lib version
LIBXML_TEST_VERSION
xmlDocPtr doc;
doc = xmlReadMemory( (const char *)bufPtr.Ptr(), bufPtr.Length(),
NULL,
NULL,
0);
if ( !doc )
{
User::Leave( KErrCorrupt );
}
xmlNode* rootElement = xmlDocGetRootElement( doc );
TPtrC8 rootTag( rootElement->name );
if ( 0 != rootTag.Compare( KWidgetRegistry() ) )
{
User::Leave( KErrCorrupt );
}
for ( xmlNode* m = rootElement->children;
m;
m = m->next )
{
if ( m->type == XML_ELEMENT_NODE )
{
TPtrC8 element( m->name );
if ( 0 == element.Compare( KEntry() ) )
{
backupBlanketPerm = -1; // reset this value for every entry
if ( NULL == m->children )
{
// malformed? should we require entry to have
// some minimal info?
continue;
}
// extract one entry
xmlNode* n;
n = m->children;
for ( ;
n;
)
{
while ( n && ( n->type != XML_ELEMENT_NODE ))
{
n = n->next;
}
if ( NULL == n )
{
break;
}
TPtrC8 propTag( n->name );
if ( 0 != propTag.Compare( KXmlPropTag() ) )
{
// unrecognized subtree?
break;
}
// validate n->children != NULL and type XML_TEXT_NODE
HBufC* name;
aXmlProcessor->GetContentL( iRfs, doc, n->children, &name );
// get value array index (TWidgetPropertyId) for name
TPtr namePtr( name->Des() );
TInt propId =
aXmlProcessor->GetPropertyId( namePtr );
delete name;
name = NULL;
if ( EWidgetPropertyIdInvalid == propId )
{
User::Leave( KErrNoMemory );
}
n = n->children->next; // down to val
if ( NULL == n )
{
User::Leave( KErrCorrupt );
}
TPtrC8 valTag( n->name );
if ( 0 != valTag.Compare( KXmlValTag() ) )
{
User::Leave( KErrCorrupt );
}
if (propId >= EWidgetPropertyIdCount) // unsupported property
{
HBufC* value = NULL;
if (n->children)
{
aXmlProcessor->GetTextContentAsStringL( iRfs, doc, n->children, &value );
}
else
{
value = KNullDesC().AllocL();
}
n = (n->parent)->next; // up two and next sibling
continue;
}
HBufC* value;
aXmlProcessor->GetContentL( iRfs, doc, n->children, &value );
CleanupStack::PushL( value );
n = n->children->next; // down to type
if ( NULL == n )
{
User::Leave( KErrCorrupt );
}
TPtrC8 typeTag( n->name );
if ( 0 != typeTag.Compare( KXmlTypeTag() ) )
{
User::Leave( KErrCorrupt );
}
// validate n->children != NULL and type XML_TEXT_NODE
HBufC* type;
aXmlProcessor->GetContentL( iRfs, doc, n->children, &type );
CleanupStack::PushL( type );
//
// assume void/unknown is not put in XML format so anything
// not recognized should be handled like other unrecognized
// subtree
TWidgetPropertyType typeEnum = EWidgetPropTypeUnknown;
if ( 0 == type->Des().Compare( KXmlDataTypeBool() ) )
{
typeEnum = EWidgetPropTypeBool;
}
else if ( 0 == type->Des().Compare( KXmlDataTypeInt() ) )
{
typeEnum = EWidgetPropTypeInt;
}
else if ( 0 == type->Des().Compare( KXmlDataTypeString() ) )
{
typeEnum = EWidgetPropTypeString;
}
else if ( 0 == type->Des().Compare( KXmlDataTypeUid() ) )
{
typeEnum = EWidgetPropTypeUid;
}
CleanupStack::PopAndDestroy( type );
// set prop according to type
switch ( typeEnum )
{
case EWidgetPropTypeBool:
if ( 0 == value->Des().Compare( _L("0") ) )
{
*backupPropertyValues[propId] = 0;
}
else
{
*(backupPropertyValues[propId]) = 1;
}
break;
case EWidgetPropTypeInt:
TLex toInt( value->Des() );
TInt k;
if ( KErrNone != toInt.Val( k ) )
{
User::Leave( KErrCorrupt );
}
if ( propId == EBlanketPermGranted )
backupBlanketPerm = k;
break;
case EWidgetPropTypeString:
*(backupPropertyValues[propId]) = *value;
break;
case EWidgetPropTypeUid:
{
TLex toUid( value->Des() );
TInt u;
if ( KErrNone != toUid.Val( u ) )
{
User::Leave( KErrCorrupt );
}
*(backupPropertyValues[propId]) = TUid::Uid( u );
}
break;
};
CleanupStack::Pop( value );
if ( EWidgetPropTypeString != typeEnum )
{
delete value;
}
n = ((n->parent)->parent)->next; // up two and next sibling
}
// Compare to see if it's this widget
if ( 0 == (iPropertyValues[EBundleIdentifier]->iValue.s)->Compare(*backupPropertyValues[EBundleIdentifier]->iValue.s) )
{
if ( backupBlanketPerm != -1 )
{
(*iPropertyValues[EBlanketPermGranted]) = backupBlanketPerm;
}
break;
}
} // if <entry>
} // if n is element
} // for
TInt j = 0;
for ( ; j < EWidgetPropertyIdCount; ++j )
{
delete backupPropertyValues[j];
}
backupPropertyValues.Close();
// cleanup
xmlFreeDoc(doc);
xmlCleanupParser();
#ifdef BRDO_SYMBIAN_LIBXML_FF
XmlEngineCleanup();
#else
xmlCleanupGlobalData();
#endif
CleanupStack::PopAndDestroy( 2, &file ); // buf, file
delete aXmlProcessor;
}
// ============================================================================
// CWidgetInstaller::PreprocessWidgetBundleL()
// Handles parsing and creating widget
// Widget data are still in "\private\[WidgetBackupRestoreUid]\WidgetBURTemp\bundleID\
//
// @since 3.2
// ============================================================================
//
TBool CWidgetInstaller::PreprocessWidgetBundleL( TDesC& aRestoreDir )
{
TBool found = EFalse;
RFile rFile;
// Reset the PropertyValues array, so as to erase the previous widget values,
// otherwise widget installation can fail due to the check for duplicate keys
// in ParseValidateBundleMetadataL(). Starting from the second element in the
// array, since the first element (EWidgetPropertyListVersion) is already
// initialized to WIDGETPROPERTYLISTVERSION.
for (TInt i=1; i<EWidgetPropertyIdCount; i++)
{
iPropertyValues[i]->Reset();
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// find the top-level directory under which to get metadata
// aRestoreDir = "\private\[WidgetBackupRestoreUid]\WidgetBURTemp\bundleID"
// tempFile = "\private\[WidgetBackupRestoreUid]\WidgetBURTemp\bundleID\"
// iBundleRootName = "WeatherBug.wdgt"
TFileName tempFile( aRestoreDir );
tempFile.Append( KBackSlash );
// searching for a directory which contains info.plist (under tempFile),
// rather than return the first directory found.
TInt foundRoot = SearchWidgetRootL( tempFile, iBundleRootName );
if( !foundRoot )
{
User::Leave( KErrNotSupported );
}
// root dir in widget bundle (not install root) of WidgetBackupRestore
// e.g. bundleRoot = "\private\[WidgetBackupRestoreUid]\WidgetBURTemp\bundleID\WeatherBug.wdgt\"
TFileName bundleRoot( tempFile );
bundleRoot.Append( iBundleRootName );
bundleRoot.Append( KBackSlash );
TInt64 bundleRootSize = GetDirSizeL( bundleRoot );
if ( 0 == bundleRootSize )
{
User::Leave( KErrNotSupported );
}
*(iPropertyValues[EFileSize]) = bundleRootSize;
//////////////////////////////////////////////////////////////////////////////////////////////////
// metadata file: "\private\[WidgetBackupRestoreUid]\WidgetBURTemp\bundleID\WeatherBug.wdgt\Info.plist"
tempFile.Copy( bundleRoot );
tempFile.Append( KInfoPList );
TInt err = rFile.Open( iRfs, tempFile, EFileRead );
if ( KErrNone != err )
{
// missing metadata file
User::Leave( KErrNotSupported );
}
CleanupClosePushL( rFile );
TInt size = 0;
rFile.Size( size );
HBufC8* buffer = HBufC8::NewLC( size );
TPtr8 bufferPtr( buffer->Des() );
User::LeaveIfError( rFile.Read( bufferPtr ) );
// METADATA PARSE
iWidgetConfigHandler->ParseValidateBundleMetadataL(
bufferPtr, iPropertyValues, iRfs );
CleanupStack::PopAndDestroy( 2, &rFile ); // rFile, buffer
// Fix the widget properties from restored file
TRAP_IGNORE (FixWidgetPropsL()); // Even the fixing leaves, it should not stop the process going
//////////////////////////////////////////////////////////////////////////////////////////////////
// EXISTING WIDGET?
if( iRegistry.WidgetExistsL( *(iPropertyValues[EBundleIdentifier]) ) )
{
// replacement for currently installed widget
*(iPropertyValues[EUid]) = iRegistry.GetWidgetUidL(
*(iPropertyValues[EBundleIdentifier]));
found = ETrue;
// get original install dir from registry in case user
// decides to "overrite" to another memory location
// e.g. iOriginalDir = "\private\[WidgetUIUid]\bundleID\"
iRegistry.GetWidgetPath( TUid::Uid( *(iPropertyValues[EUid]) ),
iOriginalDir );
}
else
{
// new widget, get an unused UID for this widget
TDriveUnit phoneMemDriveUnit( EDriveC );
const TDesC& drive = phoneMemDriveUnit.Name();
*(iPropertyValues[EUid]) = iRegistry.GetAvailableUidL(drive[0]).iUid;
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// see if main.html exists
// main.html file: "\private\[WidgetBackupRestoreUid]\WidgetBURTemp\bundleID\WeatherBug.wdgt\YahooTraffic.html"
tempFile.Copy( bundleRoot );
tempFile.Append( *(iPropertyValues[EMainHTML]) );
if( !BaflUtils::FileExists( iRfs, tempFile ) )
{
User::Leave( KErrCorrupt );
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// infoLoc file: "\private\[WidgetBackupRestoreUid]\WidgetBURTemp\bundleID\WeatherBug.wdgt\en.lproj\InfoPlist.strings"
TBuf<32> lproj;
iRegistry.GetLprojName( lproj ); // e.g. "en", "fr", "zh_Hans" ...
TFileName infoLocFile( bundleRoot );
infoLocFile.Append( lproj );
infoLocFile.Append( KLprojExt );
infoLocFile.Append( KBackSlash );
infoLocFile.Append( KInfoPlistStrings );
err = rFile.Open( iRfs, infoLocFile, EFileRead );
CleanupClosePushL( rFile );
if ( err == KErrNone )
{
TInt size = 0;
rFile.Size( size );
HBufC8* buffer = HBufC8::NewLC( size );
TPtr8 bufferPtr( buffer->Des() );
err = rFile.Read( bufferPtr );
User::LeaveIfError( err );
// parse the l10n file and localize the bundle display name
iWidgetConfigHandler->ParseInfoLocL(
bufferPtr, iRfs, *(iPropertyValues[EBundleDisplayName]) );
CleanupStack::PopAndDestroy( buffer ); // buffer
}
CleanupStack::PopAndDestroy( &rFile ); // rFile
return found;
}
void CWidgetInstaller::ProcessRestoreDirL( TDesC& aRestoreDir )
{
TInt err = KErrNone;
HBufC* newDir( HBufC::NewLC( KMaxFileName ) );
HBufC* tmpDir( HBufC::NewLC( KMaxFileName ) );
//////////////////////////////////////////////////////////////////////////////////////////////////
// widgetAppDir = "\private\[WidgetUIUid]\"
// If the path is missing create it.
if ( !BaflUtils::FolderExists( iRfs, KWidgetAppDir ) )
{
User::LeaveIfError( iRfs.MkDir( KWidgetAppDir ) );
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// newDir = "\private\[WidgetUIUid]\bundleID\"
*newDir = *(iPropertyValues[EBasePath]);
// oldDir = "\private\[WidgetBackupRestoreUid]\WidgetBURTemp\bundleID"
*tmpDir = aRestoreDir;
// oldDir = "\private\[WidgetBackupRestoreUid]\WidgetBURTemp\bundleID\"
tmpDir->Des().Append( KBackSlash );
TInt renameError = iRfs.Rename( *tmpDir, *newDir );
if (renameError)
{
// copy if different drive or KErrAlreadyExists
CFileMan* fileManager = CFileMan::NewL( iRfs );
CleanupStack::PushL( fileManager );
User::LeaveIfError( fileManager->Copy( *tmpDir, *newDir, CFileMan::ERecurse ) );
User::LeaveIfError( fileManager->Attribs(*newDir, KEntryAttNormal, KEntryAttReadOnly,
TTime(0), CFileMan::ERecurse) );
CleanupStack::PopAndDestroy( fileManager ); // fileMananger
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// e.g. bundleRootWithDrive = "C:\private\[WidgetUIUid]\bundleID\WeatherBug.wdgt\"
// e.g. iBundleRootName = "WeatherBug.wdgt"
TFileName bundleRootWithDrive( *newDir );
bundleRootWithDrive.Append( iBundleRootName );
bundleRootWithDrive.Append( KBackSlash );
//////////////////////////////////////////////////////////////////////////////////////////////////
// update MainHTML info
TFileName mainHtml( bundleRootWithDrive );
mainHtml.Append( *(iPropertyValues[EMainHTML]) );
*(iPropertyValues[EMainHTML]) = mainHtml;
//////////////////////////////////////////////////////////////////////////////////////////////////
// parse the Icon.png file
// e.g. iconPath = "C:\private\[WidgetUIUid]\bundleID\WeatherBug.wdgt\"
TFileName iconPath( bundleRootWithDrive );
TFileName iconFile( iconPath );
iconFile.Append( KIconFile );
if ( BaflUtils::FileExists( iRfs, iconFile ) )
{
*(iPropertyValues[EIconPath]) = iconPath;
// scan the bundleRootWithDrive directory
TFileName oldMbmIconTemp( KNullDesC );
TBool mbmIconFound = SearchByExtL( bundleRootWithDrive, EDirsLast | EDescending, KMBMExt, oldMbmIconTemp );
// skip converting to .mbm file, simply rename oldUid.mbm to newUid.mbm
// where oldUid comes from backup widget,
// newUid comes from widget registration or reuse of existing widget uid
if( mbmIconFound )
{
TUid uid = TUid::Uid( *(iPropertyValues[EUid]) );
// e.g. newMbmIcon = "C:\private\[WidgetUIUid]\bundleID\WeatherBug.wdgt\[newUid].mbm"
TFileName newMbmIcon( iconPath );
newMbmIcon.Append( uid.Name() );
newMbmIcon.Append( KMBMExt() );
// e.g. oldMbmIcon = "C:\private\[WidgetUIUid]\bundleID\WeatherBug.wdgt\[oldUid].mbm"
TFileName oldMbmIcon( iconPath );
oldMbmIcon.Append( oldMbmIconTemp );
err = iRfs.Rename( oldMbmIcon, newMbmIcon );
// it's ok that oldMbm and newMbm share the same uid
if( err != KErrNone && err != KErrAlreadyExists )
{
User::Leave( err );
}
}
else
{
// This case is specific to the WidgetPreInstaller.
// Once the mbm file can be generated by the script this won't be needed.
// Create the mbm file from the icon file
TUid uid = TUid::Uid( *(iPropertyValues[EUid]) );
ConvertIconL(uid, iconPath);
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// delete "\private\[WidgetUIUid]\bundleID\prefs.dat"
TFileName widgetPref( *newDir );
widgetPref.Append(KWidgetPref);
err = iRfs.Delete( widgetPref );
// it's ok not to have pref.dat
if( err != KErrNone && err != KErrNotFound )
{
User::Leave( err );
}
CleanupStack::PopAndDestroy( 2, newDir );
//////////////////////////////////////////////////////////////////////////////////////////////////
FinishInstallL( KErrNone );
}
// ============================================================================
// CWidgetInstaller::FinishInstallL()
//
// @since 3.1
// ============================================================================
//
EXPORT_C void CWidgetInstaller::FinishInstallL( TInt aErr )
{
#ifdef _DEBUG
_LIT(KText, "FinishInstallL, aErr = %d, iOverwriting=%d");
if ( iCanLog )
{
iFileLogger.WriteFormat( KText, aErr, iOverwriting );
}
#endif
if ( EFalse == iServerConnected )
{
User::LeaveIfError( iRegistry.Connect() );
iServerConnected = ETrue;
}
if ( !aErr )
{
if ( iOverwriting )
{
TUid uid = TUid::Uid( *(iPropertyValues[EUid]) );
iRegistry.DeRegisterWidgetL( uid );
iAppManager->DeregisterWidgetL( uid );
}
iRegistry.RegisterWidgetL( iPropertyValues );
iAppManager->RegisterWidgetL( *(iPropertyValues[EMainHTML]),
*(iPropertyValues[EBundleDisplayName]),
*(iPropertyValues[EIconPath]),
*(iPropertyValues[EDriveName]),
TUid::Uid( *(iPropertyValues[EUid]) ) );
if ( iOverwriting )
{
// delete backup
TInt err = iFileMgr->RmDir( iBackupDir );
}
}
else
{
if ( iOverwriting )
{
// delete what was being installed, and restore previous
iFileMgr->RmDir( *(iPropertyValues[EBasePath]) );
RestoreL();
}
}
iOverwriting = EFalse;
}
/**
* @since 5.0
*/
EXPORT_C void CWidgetInstaller::RegisterWidgetL( const TDesC& aMainHTML,
const TDesC& aBundleDisplayName,
const TDesC& aIconPath,
const TDesC& aDriveName,
const TUid& aUid )
{
iAppManager->RegisterWidgetL( aMainHTML, aBundleDisplayName,
aIconPath, aDriveName, aUid );
}
/**
* @since 5.0
*/
EXPORT_C void CWidgetInstaller::DeregisterWidgetL( const TUid& aUid )
{
iAppManager->DeregisterWidgetL( aUid );
}
// ============================================================================
// CWidgetInstaller::RunError()
// It is called by WidgetActiveCallback when InstallL leaves.
//
// @since 3.2
// ============================================================================
//
EXPORT_C TInt CWidgetInstaller::RunError( TInt aError )
{
TRAP_IGNORE( FinishInstallL( aError ) );
return KErrNone; // indicates error was handled
}
// ============================================================================
// CWidgetInstaller::SetDriveAndCheckSpaceL()
//
// @since 3.2
// ============================================================================
//
TBool CWidgetInstaller::SetDriveAndCheckSpaceL()
{
TDriveUnit phoneMemDriveUnit( EDriveC );
TInt64 driveSpace( 0 );
TInt error = DriveInfo( (TInt)phoneMemDriveUnit, driveSpace );
if ( *(iPropertyValues[EFileSize]) > driveSpace )
{
User::Leave( KErrNoMemory );
}
UpdateWidgetBasePathL( phoneMemDriveUnit );
*(iPropertyValues[EDriveName]) = phoneMemDriveUnit.Name();
return ETrue;
}
// ============================================================================
// CWidgetInstaller::UpdateWidgetBasePathL()
//
// @since 3.1
// ============================================================================
//
void CWidgetInstaller::UpdateWidgetBasePathL( TDriveUnit& aDrive )
{
const TDesC& bundleId = *(iPropertyValues[EBundleIdentifier]);
HBufC* basePath = HBufC::NewL(
aDrive.Name().Length() + KWidgetAppDir().Length()
+ bundleId.Length() + 1 );
basePath->Des().Append( aDrive.Name() );
basePath->Des().Append( KWidgetAppDir );
basePath->Des().Append( *(iPropertyValues[EBundleIdentifier]) );
basePath->Des().Append( KBackSlash );
*(iPropertyValues[EBasePath]) = *basePath;
delete basePath;
}
// ============================================================================
// CWidgetInstaller::BackupL()
// When overwriting an installed widget, backup so can restore on error.
//
// @since 3.1
// ============================================================================
//
void CWidgetInstaller::BackupL()
{
#ifdef _DEBUG
_LIT(KText1, "inside BackupL, iOriginalDir=%S, iBackupDir=%S");
if ( iCanLog )
{
iFileLogger.WriteFormat( KText1, &iOriginalDir, &iBackupDir );
}
#endif
// if backup leaves, there is nothing caller needs to do to clean up
// move to backup (by renaming) the existing install dir for the widget
// first create a "unique" backup dir on the original dir drive
// (same drive required since can't "rename" across drives)
TParse p;
p.Set( iOriginalDir, NULL, NULL );
TFileName path;
path.Copy( p.Drive() );
path.Append( KWidgetAppDir );
// NOT A GOOD WAY TO CREATE A UNIQUE DIRECTORY First we create a
// unique file and then delete it and then create a directory with
// same name.
RFile file;
CleanupClosePushL( file );
User::LeaveIfError( file.Temp( iRfs, path, iBackupDir, EFileWrite ) );
CleanupStack::PopAndDestroy(); // file
// delete the temp file and...
iFileMgr->Delete( iBackupDir );
// ...convert to dir name
iBackupDir.Append( KBackSlash );
// make the backup
User::LeaveIfError( iFileMgr->Rename( iOriginalDir, iBackupDir ) );
// overwriting boolean is only true when backup is successful
iOverwriting = ETrue;
}
// ============================================================================
// CWidgetInstaller::RestoreL()
//
// @since 3.1
// ============================================================================
//
void CWidgetInstaller::RestoreL()
{
// move (by renaming) the backup copy to the install dir for the widget
// to assure a clean destination, try deleting install dir first
(void)iFileMgr->RmDir( iOriginalDir );
User::LeaveIfError( iFileMgr->Rename( iBackupDir, iOriginalDir ) );
// restore complete
iOverwriting = EFalse;
}
// ============================================================================
// CWidgetInstaller::DriveInfo
// Indicates if the given drive is available and ready to use.
// (other items were commented in a header).
//
// @since 3.2
// ============================================================================
//
TInt CWidgetInstaller::DriveInfo( TInt aDrive, TInt64& aDiskSpace )
{
TInt error = KErrNone;
TVolumeInfo volumeInfo;
error = iRfs.Volume( volumeInfo, aDrive );
if( !error )
{
aDiskSpace = volumeInfo.iFree;
}
return error;
}
// ============================================================================
// CWidgetInstaller::SearchByExtL
//
// @since 3.2
// ============================================================================
//
TInt CWidgetInstaller::SearchByExtL( const TDesC& aDir, TUint aSortKey, const TDesC& aExt, TFileName& aFoundName )
{
CDir* entryList = NULL;
TBool found = EFalse;
// aDir = "\private\[WidgetBackupRestoreUid]\WidgetBURTemp\bundleID\"
TInt err = iRfs.GetDir( aDir, KEntryAttMatchMask, aSortKey, entryList );
if ( err == KErrNone )
{
CleanupStack::PushL(entryList);
// check for directory entries
TInt extLen = aExt.Length();
TInt cnt = entryList->Count();
for (TInt i=0; i<cnt; i++)
{
const TEntry& fileEntry = (*entryList)[i];
// when looking for root folder, we don't pass aExt in this function; the first directory will be chosen
if ( ( aExt.Length() == 0 ) ||
( fileEntry.iName.Right( extLen ).Compare( aExt ) == 0 ) )
{
aFoundName.Copy( fileEntry.iName );
found = ETrue;
break;
}
} // end of for
CleanupStack::PopAndDestroy(); // entryList
} // end of if iRfs
return found;
}
// ============================================================================
// CWidgetInstaller::GetDirSizeL
//
// @since 3.2
// ============================================================================
//
TInt CWidgetInstaller::GetDirSizeL( const TDesC& aDir )
{
CDirScan* scanner = CDirScan::NewLC( iRfs );
scanner->SetScanDataL( aDir, KEntryAttMatchMask, EDirsLast );
TInt64 dirSize = 0;
CDir* results = NULL;
scanner->NextL( results );
while( results )
{
CleanupStack::PushL( results );
// Prescan to get the dir file size.
TInt64 fileSize = 0;
const TInt count = results->Count();
TInt i = 0;
for( i=0; i<count; i++ )
{
const TEntry& entry = (*results)[ i ];
if ( !entry.IsDir() )
{
fileSize += entry.iSize;
}
else
{
// sort by EDirLast
break;
}
}
dirSize += fileSize;
const TPtrC path( scanner->FullPath() );
CleanupStack::PopAndDestroy( results );
scanner->NextL( results );
}
CleanupStack::PopAndDestroy(); // scanner
return dirSize;
}
// ============================================================================
// CWidgetUIOperationsWatcher::ConvertIconL()
// Convert icon.png into mbm format for widget
//
// @since 5.0
// ============================================================================
//
void CWidgetInstaller::ConvertIconL(
TUid& aUid,
const TDesC& aIconPath )
{
TFileName pngIcon;
pngIcon.Copy( aIconPath );
if ( pngIcon.Length() )
{
pngIcon.Append( KIconFile );
TFileName mbmIcon;
mbmIcon.Copy( aIconPath );
mbmIcon.Append( aUid.Name() );
mbmIcon.Append( KMBMExt() );
CIconConverter* iconConverter = CIconConverter::NewL( this, iRfs );
CleanupStack::PushL(iconConverter);
iconConverter->StartToDecodeL( pngIcon, mbmIcon );
CActiveScheduler::Start(); // wait until the converter process finishes
CleanupStack::PopAndDestroy(); // iconConverter
}
}
// ============================================================================
// CWidgetInstaller::NotifyCompletionL()
// Icon conversion calls this when it is complete.
//
// @since 5.0
// ============================================================================
//
void CWidgetInstaller::NotifyCompletionL( TInt /*aErr*/ )
{
CActiveScheduler::Stop();
}
// ============================================================================
// CWidgetInstaller::WidgetPropertiesFromInstalledWidgetL()
// Generates the widget property values from an already installed widget.
//
// @since 5.0
// ============================================================================
//
EXPORT_C RPointerArray<CWidgetPropertyValue>*
CWidgetInstaller::WidgetPropertiesFromInstalledWidgetL(
RFs &aFs,
const TDesC& aWidgetPath, /* should end with backslash */
const TDesC& aLproj,
TUid aUid )
{
// output, ownership will be handed back to caller
RPointerArray<CWidgetPropertyValue>* propertyValues = new (ELeave)
RPointerArray<CWidgetPropertyValue>(
EWidgetPropertyIdCount );
CleanupClosePushL( *propertyValues );
TInt i = 0;
// empty values
for ( ; i < EWidgetPropertyIdCount; ++i )
{
CWidgetPropertyValue* value = CWidgetPropertyValue::NewL();
User::LeaveIfError( propertyValues->Insert( value, i ) );
}
*(*propertyValues)[EWidgetPropertyListVersion] = WIDGETPROPERTYLISTVERSION;
// UID
*(*propertyValues)[EUid] = aUid.iUid;
// size
TInt64 bundleRootSize = GetDirSizeL( aWidgetPath );
if ( 0 == bundleRootSize )
{
User::Leave( KErrNotSupported );
}
*(*propertyValues)[EFileSize] = bundleRootSize;
// Info.plist
TFileName manifest ( aWidgetPath );
manifest.Append( KInfoPList );
RFile rFile;
User::LeaveIfError( rFile.Open( aFs, manifest, EFileRead ) );
CleanupClosePushL( rFile );
TInt size = 0;
rFile.Size( size );
HBufC8* buffer = HBufC8::NewLC( size );
TPtr8 bufferPtr( buffer->Des() );
User::LeaveIfError( rFile.Read( bufferPtr ) );
iWidgetConfigHandler->ParseValidateBundleMetadataL(
bufferPtr, *propertyValues, aFs );
CleanupStack::PopAndDestroy( ) ; // buffer
CleanupStack::Pop();
rFile.Close();
// update base path from metadata
*(*propertyValues)[EBasePath] = aWidgetPath;
// update drive name
*(*propertyValues)[EDriveName] = aWidgetPath.Left( 2 );
// update main HTML
TFileName mainHtml( aWidgetPath );
mainHtml.Append( *(*propertyValues)[EMainHTML] );
*(*propertyValues)[EMainHTML] = mainHtml;
// update icon path, depends on Icon.png and mbm versions existing
TFileName iconFile( aWidgetPath );
iconFile.Append( KIconFile );
if ( BaflUtils::FileExists( aFs, iconFile ) )
{
// scan the directory for mbm extension file with processed icon images
TFileName mbmFilename;
TBool mbmFound = SearchByExtL( aWidgetPath,
EDirsLast | EDescending,
KMBMExt,
mbmFilename );
if ( mbmFound )
{
// rename mbm file using supplied UID
// [A-Z]:\private\[WidgetUIUid]\[bundleID]\[zip-root]\[UID].mbm"
TFileName oldMbmFilename( aWidgetPath );
oldMbmFilename.Append( mbmFilename );
TFileName newMbmFilename( aWidgetPath );
newMbmFilename.Append( aUid.Name() );
newMbmFilename.Append( KMBMExt() );
TInt error = iRfs.Rename( oldMbmFilename, newMbmFilename );
// it's ok that oldMbm and newMbm share the same uid
if( error != KErrNone && error != KErrAlreadyExists )
{
User::Leave( error );
}
*(*propertyValues)[EIconPath] = aWidgetPath;
}
}
// localization of display name
TFileName infoLocFile( aWidgetPath );
infoLocFile.Append( aLproj );
infoLocFile.Append( KLprojExt );
infoLocFile.Append( KBackSlash );
infoLocFile.Append( KInfoPlistStrings );
TInt error = rFile.Open( aFs, infoLocFile, EFileRead );
CleanupClosePushL( rFile );
if ( error == KErrNone )
{
TInt size = 0;
rFile.Size( size );
HBufC8* buffer = HBufC8::NewLC( size );
TPtr8 bufferPtr( buffer->Des() );
error = rFile.Read( bufferPtr );
User::LeaveIfError( error );
// parse the l10n file and localize the bundle display name
iWidgetConfigHandler->ParseInfoLocL(
bufferPtr, aFs, *(*propertyValues)[EBundleDisplayName] );
CleanupStack::PopAndDestroy( ); // buffer
}
CleanupStack::Pop( 2 ); // rfile, propertyValues
rFile.Close();
return propertyValues;
}
EXPORT_C TInt CWidgetInstaller::RenameIconFile(
RFs &aFs,
RPointerArray<CWidgetPropertyValue>* aEntry )
{
// update icon path, depends on Icon.png and mbm versions existing
const TDesC& mainHtml = *(*aEntry)[EMainHTML];
TInt mainHtmlPathEnd = mainHtml.LocateReverse( TChar( '\\' ) );
if ( KErrNotFound == mainHtmlPathEnd )
{
return KErrNotFound;
}
TFileName mainPath( mainHtml.Left( mainHtmlPathEnd + 1 ) );
TFileName iconFile( mainPath );
iconFile.Append( KIconFile );
if ( BaflUtils::FileExists( aFs, iconFile ) )
{
// scan the directory for mbm extension file with processed icon images
TFileName mbmFilename;
TBool mbmFound = SearchByExtL( mainPath,
EDirsLast | EDescending,
KMBMExt,
mbmFilename );
if ( mbmFound )
{
// rename mbm file using supplied UID
// [A-Z]:\private\[WidgetUIUid]\[bundleID]\[zip-root]\[UID].mbm"
TFileName oldMbmFilename( mainPath );
oldMbmFilename.Append( mbmFilename );
TFileName newMbmFilename( mainPath );
TInt uid = *(*aEntry)[EUid];
newMbmFilename.Append( TUid::Uid(uid).Name() );
newMbmFilename.Append( KMBMExt() );
// it's ok that oldMbm and newMbm share the same uid
TInt error = iRfs.Rename( oldMbmFilename, newMbmFilename );
if( error != KErrNone && error != KErrAlreadyExists )
{
return error;
}
*(*aEntry)[EIconPath] = mainPath;
}
}
return KErrNone;
}
// End of File