/*
* 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:  Manages list of widget entries.
*
*/


#include <e32base.h>
#include <f32file.h>
#include <s32file.h>
#include <s32mem.h>
#include <bautils.h>

#include "WidgetEntry.h"
#include "WidgetRegistry.h"
#include "WidgetInstaller.h"
#include <libxml2_globals.h>
#include <libc\stdlib.h>
#include <libxml2_parser.h>
#include <libxml2_tree.h>
#include <xmlengxestd.h>
#include "WidgetMMCHandler.h"
#include "UidAllocator.h"
#if defined( BRDO_WRT_SECURITY_MGR_FF )
#include <RTSecManager.h>
#endif
#include <e32property.h>

// CONSTANTS

static const TInt KDefaultWidgetCount = 20; // used for initial RArray size
static const TInt KAppArchTimeout = 10000000; // 10 seconds in microseconds
static const TInt KAppArchDelayInterval = 500000; // 500 milliseonds in microseconds

enum
    {
    EAppListFlagEntry = 1
    };

enum
    {
    EInstallListFlagEntry = 1
    };

// Be sure that length of literal pathname strings is less than
// KWidgetRegistryMaxPathName in header file.
//
// drive letter a: is used as a placeholder, it will be set as needed
_LIT( KWidgetInstallPath,
      "a:\\private\\10282822\\" );
_LIT( KWidgetEntryStoreBinaryFile,
      "a:\\private\\10282f06\\WidgetEntryStore.dat" );
_LIT( KWidgetEntryStoreXmlFile,
      "a:\\private\\10282f06\\WidgetEntryStore.xml" );
_LIT( KWidgetEntryStoreXmlTempFile,
      "a:\\private\\10282f06\\WidgetEntryStoreTemp.xml" );
_LIT( KWidgetDirFile, "widget_lproj.xml" );
_LIT( KWidgetAccessPolicy, "WidgetAccessPolicy.xml" );
_LIT( KWidgetPolicyIdFile, "WidgetPolicyId.dat" );

_LIT( KWidgetDefaultLangDir, "en" );
_LIT8( KLangID, "LangID" );
_LIT8( KLangDir, "LangDir" );

// for exteranlize
_LIT( KXmlHeader,
      "<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"yes\" ?>" );
_LIT( KXmlRootStart, "<widgetregistry>" );
_LIT( KXmlRootEnd, "</widgetregistry>" );
_LIT( KWidgetEntryStart, "<entry>" );
_LIT( KWidgetEntryEnd, "</entry>" );
_LIT( KXmlNewline, "\x0D\x0A" ); // DOS/Symbian style works with XML parsers

// for internalize
_LIT8( KWidgetRegistry, "widgetregistry" );
_LIT8( KEntry, "entry" );

LOG_NAMES( "widreg", "widreg.txt" )

void XmlDocFree( TAny* aPtr )
    {
    __ASSERT_DEBUG( aPtr, User::Invariant() );
    xmlDocPtr ptr( (xmlDocPtr)(aPtr) );
    xmlFreeDoc( ptr );
    }

// ============================================================================
// Traverse to the next Node
//
// @param aNode: current node
// @since 3.1
// @return next node
// ============================================================================
//
xmlNode* 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;
    }
// ============================================================================
// Changes the Publish & Subscribe key value
// ============================================================================
//

static void NotifyWidgetAltered()
    {
    const TUid KMyPropertyCat = { 0x10282E5A };
    enum TMyPropertyKeys { EMyPropertyAltered = 110 };
    TInt altered( 1 );
    RProperty::Set( KMyPropertyCat, EMyPropertyAltered , altered );
    }
// ============================================================================
// CWidgetRegistry::NewL()
// two-phase constructor
//
// @since 3.1
// ============================================================================
//
CWidgetRegistry* CWidgetRegistry::NewL( RFs& aFs )
    {
    CWidgetRegistry* widgetRegistry = new ( ELeave ) CWidgetRegistry( aFs );
    CleanupStack::PushL( widgetRegistry );
    TRAP_IGNORE( widgetRegistry->ConstructL() );
    CleanupStack::Pop( widgetRegistry );
    return widgetRegistry;
    }

// ============================================================================
// CWidgetRegistry::CWidgetRegistry()
// C++ constructor
//
// @since 3.1
// ============================================================================
//
CWidgetRegistry::CWidgetRegistry( RFs& aFs ):
    iFs( aFs ),
    iWidgetInstallPath( KWidgetInstallPath ),
    iRegistryBinaryFileName( KWidgetEntryStoreBinaryFile ),
    iRegistryXmlFileName( KWidgetEntryStoreXmlFile ),
    iRegistryXmlTempFileName( KWidgetEntryStoreXmlTempFile ),
    iPolicyId( 0 )
    {
    }

// ============================================================================
// CWidgetRegistry::~CWidgetRegistry()
// destructor
//
// @since 3.1
// ============================================================================
//
CWidgetRegistry::~CWidgetRegistry()
    {
    iEntries.ResetAndDestroy();
    iUsedUids.Close();
    // iFs not owned
    iAppArch.Close();
    delete iInstaller;
    iLangDirList.ResetAndDestroy();
    delete iMMCHandler;
    delete iXmlProcessor;

    iFs.Close();
    LOG_DESTRUCT;
    }

// ============================================================================
// CWidgetRegistry::ConstructL()
// Symbian second phase constructor
//
// @since 3.1
// ============================================================================
//
void CWidgetRegistry::ConstructL()
    {
    LOG_CONSTRUCTL;
    LOG_OPEN;
    LOG( "CWidgetRegistry::ConstructL internalize" );

    User::LeaveIfError( iFs.Connect() );
    iFs.CreatePrivatePath( EDriveC );
    iFs.SetSessionToPrivate( EDriveC );
    User::LeaveIfError( iAppArch.Connect() );
    iInstaller = CWidgetInstaller::NewL();

    // If internalizing leaves, then the registry will be out-of-sync
    // with the installed widgets and apparc.  But that's okay since
    // it should be detected and corrected once the resource limit
    // that caused the leave is removed.
    TBool dirtyFlag = EFalse;
    iXmlProcessor = CWidgetRegistryXml::NewL();
    TRAP_IGNORE( InternalizeL( dirtyFlag ) );
    if ( dirtyFlag )
        {
        // Basically same reason to ignore leaves here.
        TRAP_IGNORE( ExternalizeL() );
        }

    LOG( " ctor internalize done" );

#if defined( BRDO_WRT_SECURITY_MGR_FF )
    FetchSecurityPolicyIdL();
#endif

    LOG1( "ConstructL internalize done, registry count %d",
              iEntries.Count() );
    LOG_CLOSE;

    iMMCHandler = CWidgetMMCHandler::NewL( *this, iFs );
    iMMCHandler->Start();
    }

// ============================================================================
// CWidgetRegistry::FetchSecurityPolicyIdL()
// Get policyId from security manager
//
// @since 5.0
// ============================================================================
//
TInt CWidgetRegistry::FetchSecurityPolicyIdL()
    {
#if defined( BRDO_WRT_SECURITY_MGR_FF )
    if ( !iPolicyId )
        {
        if ( KErrNone == iFs.ShareProtected() )
            {
            TFileName secPolicyFileName;
            RFile secPolicyFile;
            iFs.PrivatePath( secPolicyFileName );
#ifdef __WINSCW__
            secPolicyFileName.Insert( 0, _L( "C:" ));
#else
            secPolicyFileName.Insert( 0, _L( "Z:" ));
#endif
            secPolicyFileName.Append( KWidgetAccessPolicy );

            if ( KErrNone == secPolicyFile.Open( iFs, secPolicyFileName, EFileShareAny ) )
                {
                CleanupClosePushL( secPolicyFile );
                CRTSecManager* secMgr = CRTSecManager::NewL();
                iPolicyId = secMgr->SetPolicy( secPolicyFile );
                CleanupStack::PopAndDestroy( &secPolicyFile );

                CleanupStack::PushL( secMgr );
                // read old policyId, unset old policy file; store new policyId
                RFile policyIdFile;
                TFileName policyIdFileName;
                iFs.PrivatePath( policyIdFileName );
                policyIdFileName.Insert( 0, _L( "C:" ));
                policyIdFileName.Append( KWidgetPolicyIdFile );

                TInt error = KErrNotFound;
                error = policyIdFile.Open( iFs, policyIdFileName, EFileShareAny );
                // policy Id file exists
                if ( error == KErrNone )
                    {
                    CleanupClosePushL( policyIdFile );
                    RFileReadStream readStream;
                    CleanupClosePushL( readStream );
                    readStream.Attach( policyIdFile );

                    TInt oldPolicyId = 0;
                    TRAP_IGNORE( oldPolicyId = readStream.ReadInt32L() );
                    // clean the old policy file
                    if ( oldPolicyId > 0 )
                        {
                        secMgr->UnSetPolicy( oldPolicyId );
                        }

                    CleanupStack::PopAndDestroy( &readStream );
                    }
                // create policy Id file
                else if ( error == KErrNotFound )
                    {
                    User::LeaveIfError( policyIdFile.Create( iFs, policyIdFileName, EFileShareAny ) );
                        CleanupClosePushL( policyIdFile );
                    }

                // we should have a good policyIdFile by now, store iPolicyId there
                    RFileWriteStream writeStream;
                    CleanupClosePushL( writeStream );
                    writeStream.Replace( iFs, policyIdFileName, EFileShareAny );

                    TRAP_IGNORE( writeStream.WriteInt32L( iPolicyId ) );

                CleanupStack::PopAndDestroy( 2, &policyIdFile );// writeStream, policyIdFile

                CleanupStack::PopAndDestroy( secMgr );
                }
            }
        }
#else
	iPolicyId = KErrNotSupported;
#endif    
    return iPolicyId;
    }

// ============================================================================
// CWidgetRegistry::GetWidgetEntryL()
// Get widget entry by UID or leave with KErrNotFound
//
// @since 3.1
// ============================================================================
//
CWidgetEntry& CWidgetRegistry::GetWidgetEntryL( const TUid& aUid ) const
    {
    for( TInt i = 0; i < iEntries.Count(); i++)
        {
        CWidgetEntry* entry = iEntries[i];
        if ( TUid::Uid( (*entry)[EUid] ) == aUid )
            {
            return *entry;
            }
        }
    User::Leave( KErrNotFound );
    return *iEntries[0];  // just for compiler
    }

// ============================================================================
// CWidgetRegistry::GetWidgetEntry()
// Get the widget entry
//
// @since 3.1
// ============================================================================
//
// TODO use leaving version GetWidgetEntryL everywhere and delete this version
TInt CWidgetRegistry::GetWidgetEntry(
    const TUid& aUid,
    CWidgetEntry*& aEntry) const
    {
    for(TInt i = 0;i < iEntries.Count();i++)
        {
        CWidgetEntry* entry = iEntries[i];
        if ( TUid::Uid( (*entry)[EUid] ) == aUid )
            {
            aEntry = entry;
            return i;
            }
        }
    return -1;
    }

// ============================================================================
// CWidgetRegistry::GetWidgetEntry()
// Get the widget entry
//
// @since 3.1
// ============================================================================
//
TInt CWidgetRegistry::GetWidgetEntry(
    const TDesC& aBundleId,
    CWidgetEntry*& aEntry) const
    {
    for(TInt i = 0;i < iEntries.Count();i++)
        {
        CWidgetEntry* entry = iEntries[i];
        const TDesC& widgetBundleId = (*entry)[EBundleIdentifier];
        if ( widgetBundleId.CompareF( aBundleId ) == 0 )
            {
            aEntry = entry;
            return i;
            }
        }
    return -1;
    }

// ============================================================================
// CWidgetRegistry::GetWidgetEntry()
// Returns the WidgetEntry at a particular index
//
// @since 3.1
// ============================================================================
//
const CWidgetEntry* CWidgetRegistry::GetWidgetEntry( const TInt& aPos )
    {
    return iEntries[aPos];
    }


// ============================================================================
// CWidgetRegistry::InsertL()
// Insert the widget entry into the list if BundleID is not already in
// the list.
//
// @since 3.1
// ============================================================================
//
TInt CWidgetRegistry::InsertL( CWidgetEntry* aEntry )
    {
    CWidgetEntry* entry = NULL;
    TDesC bundleID = (*aEntry)[EBundleIdentifier];
    TInt pos  = GetWidgetEntry( bundleID, entry );
    if ( pos == KErrNotFound )
        {
        iUsedUids.AppendL( TUid::Uid( (*aEntry)[EUid] ) );
        TInt error = iEntries.Append( aEntry );
        if ( KErrNone != error )
            {
            TInt upos = iUsedUids.FindReverse( TUid::Uid( (*aEntry)[EUid] ) );
            iUsedUids.Remove( upos );
            User::Leave( error );
            }
        }
    return pos;
    }

// ============================================================================
// CWidgetRegistry::Remove()
// Remove the widget entry from the list
//
// @since 3.1
// ============================================================================
//
void CWidgetRegistry::Remove( const TDesC& aBundleId )
    {
    CWidgetEntry* entry = NULL;
    TInt pos  = GetWidgetEntry( aBundleId, entry );

    if ( pos != -1 )
        {
        TInt upos = iUsedUids.Find( TUid::Uid( (*entry)[EUid] ) );
        if ( upos >= 0 )
            {
            iUsedUids.Remove( upos );
            }

        iEntries.Remove( pos );

        delete entry;
        }
    }

// ============================================================================
// CWidgetRegistry::Remove()
// Remove the widget entry from the list
//
// @since 3.1
// ============================================================================
//
void CWidgetRegistry::Remove( const TUid& aUid )
    {
    CWidgetEntry* entry = NULL;
    TInt pos  = GetWidgetEntry( aUid, entry );

    if ( pos != -1 )
        {
        TInt upos = iUsedUids.Find( TUid::Uid( (*entry)[EUid] ) );
        if ( upos >= 0 )
            {
            iUsedUids.Remove( upos );
            }

        iEntries.Remove( pos );
        delete entry;
        }

    }


// ============================================================================
// CWidgetRegistry::InternalizeL()
// Read entry info from data file into memory
//
// @since 3.1
// ============================================================================
//
void CWidgetRegistry::InternalizeL( TBool& aDirtyFlag )
    {
    LOG_OPEN;
    LOG( "Internalize" );

    // prepare for consistency enforcement
    RArray<TUid> appArchList( KDefaultWidgetCount );
    RArray<TInt> appArchListFlags( KDefaultWidgetCount );
    // default is internalization just imports files without
    // modification, if dirty flag gets set to true, do externalize
    // after internalize to bring files into sync with modified
    // registry contents
    aDirtyFlag = EFalse;

    // internal dirty flag, will be copied to arg dirty flag at
    // end if no leave occurs
    TBool dirtyFlag = EFalse;

    // empty the registry
    iEntries.ResetAndDestroy();
    iUsedUids.Reset();

    CleanupClosePushL( appArchList );
    CleanupClosePushL( appArchListFlags );
    TBool doConsistency = AppArchWidgets( appArchList, appArchListFlags );
    if ( doConsistency )
        {

        // UIDs are the key differentiator of apps in app arch.  The
        // widget implementation used BundleID to differentiate
        // widgets on installation (overwrite is keyed by BundleID).
        // But once widgets are installed, they are assigned a unique
        // UID.  For UID allocation purposes, it is important that
        // UIDs already known to app arch be reserved.
        for ( TInt i = 0; i < appArchList.Count(); i++ )
            {
            if ( KErrNone != iUsedUids.Append( (appArchList)[i] ) )
                {
                // no recovery possible
                doConsistency = EFalse;
                break;
                }
            }
        }
    LOG2( " iUsedUids %d and doConsistency %d",
          (TInt)(iUsedUids.Count()), (TInt)doConsistency );

    TRAPD( error, GetLprojNameL( iLprojName ) );
    if ( KErrNone != error )
        {
        // on error use english
        iLprojName = _L("en");
        }

    // List all drives in the system
    TDriveList driveList;
    User::LeaveIfError( iFs.DriveList(driveList) );

    // Check all drives but Z and D.  Scan from Y to A because that is
    // the order that the loader and AppArch follow when "shadowing"
    // UID identical apps. Drive Z is ignored because it is used for
    // the phone ROM image and is not an installation location for
    // widgets. Drive D is ignored because it is a temporary RAM disk
    // and not a widget install location.
    for ( TInt driveNumber = EDriveY; driveNumber >= EDriveA; driveNumber-- )
        {
        // The drives that will be filtered out are the same ones that
        // WidgetInstaller filters out in CWidgetUIHandler::SelectDriveL()
        if ( (EDriveD == driveNumber)
             || !driveList[driveNumber] )
            {
            // EDriveD is a temporary drive usually a RAM disk
            continue;
            }

        TVolumeInfo volInfo;
        if ( iFs.Volume( volInfo, driveNumber ) != KErrNone )
            {
            // volume is not usable (e.g. no media card inserted)
            continue;
            }
        if ( (volInfo.iDrive.iType == EMediaNotPresent) ||
             (volInfo.iDrive.iType == EMediaRom) ||
             (volInfo.iDrive.iType == EMediaRemote) ||
             (volInfo.iDrive.iDriveAtt & KDriveAttRom) ||
             (volInfo.iDrive.iDriveAtt & KDriveAttSubsted) )
            {
            // not a suitable widget install drive
            continue;
            }

        // found a usable drive
        TDriveUnit driveUnit( driveNumber );
        LOG1( " Drive %c", (TUint)(driveUnit.Name()[0]) );

        // prepare for consistency enforcement
        CDir* installedListForDrive = NULL;
        RArray<TInt> installedListForDriveFlags;
        if ( doConsistency )
            {
            doConsistency = InstallDirWidgets( driveUnit,
                                               installedListForDrive,
                                               installedListForDriveFlags );
            }
        LOG1( " after InstallDirWidgets doConsistency %d",
                  (TInt)doConsistency );

        // direct install path to this drive
        iWidgetInstallPath[0] = driveUnit.Name()[0];

        // which persistent data files exist?
        iRegistryBinaryFileName[0] = driveUnit.Name()[0];
        TBool binaryExists = BaflUtils::FileExists( iFs,
                                                    iRegistryBinaryFileName );

        iRegistryXmlFileName[0] = driveUnit.Name()[0];
        TBool xmlExists = BaflUtils::FileExists( iFs,
                                                 iRegistryXmlFileName );

        // Here internalizing a single drive.  If internalizing
        // leaves, then the registry will be out-of-sync with the
        // installed widgets and apparc.  But that's okay since it
        // should be detected and corrected once the resource limit
        // that caused the leave is removed.
        if ( xmlExists )
            {
            TRAP_IGNORE( InternalizeXmlL( iRegistryXmlFileName,
                                          driveUnit,
                                          doConsistency,
                                          appArchList,
                                          appArchListFlags,
                                          installedListForDrive,
                                          installedListForDriveFlags,
                                          dirtyFlag ) );
            }
        else if ( binaryExists )
            {
            TRAP_IGNORE( InternalizeBinaryL( iRegistryBinaryFileName,
                                             driveUnit,
                                             doConsistency,
                                             appArchList,
                                             appArchListFlags,
                                             installedListForDrive,
                                             installedListForDriveFlags,
                                             dirtyFlag ) );
            }

        if ( doConsistency )
            {
            InstallDirConsistency( installedListForDrive,
                                   installedListForDriveFlags,
                                   dirtyFlag );
            }
        delete installedListForDrive;
        installedListForDriveFlags.Close();
        } // for
    if ( doConsistency )
        {
        AppArchListConsistency( appArchList, appArchListFlags );
        }    
    CleanupStack::PopAndDestroy( 2, &appArchList );//appArchListFlags, appArchList

    aDirtyFlag = dirtyFlag;
    LOG1( "Internalize done, dirty flag %d", (TInt)dirtyFlag );
    LOG_CLOSE;
    }

// ============================================================================
// CWidgetRegistry::InternalizeBinaryL()
// Read entry info from data file into memory
//
// @since 3.1
// ============================================================================
//
void CWidgetRegistry::InternalizeBinaryL( const TDesC& aFileName,
                                          const TDriveUnit& aDriveUnit,
                                          TBool aDoConsistency,
                                          RArray<TUid>& aAppArchList,
                                          RArray<TInt>& aAppArchListFlags,
                                          const CDir* aInstalledListForDrive,
                                          RArray<TInt>& aInstalledListForDriveFlags,
                                          TBool& aDirtyFlag )
    {
    LOG( "InternalizeBinaryL" );
    RFile file;
    User::LeaveIfError( file.Open( iFs, aFileName, EFileRead ) );
    CleanupClosePushL( file );

    RFileReadStream readStream;
    CleanupClosePushL( readStream );
    readStream.Attach( file );

    TInt error = KErrNone;
    TInt entryCount = 0;
    TRAP( error, entryCount = readStream.ReadInt32L() );
    // TODO should limit entryCount to something like 1024
    // for each entry in the registry file
    for ( TInt i = 0 ; i < entryCount; i++ )
        {
        CWidgetEntry* entry = CWidgetEntry::NewL();
        CleanupStack::PushL( entry );

        // extract one entry
        TRAP( error,
              entry->InternalizeBinaryL( readStream ) );
        if ( KErrNone == error )
            {
            CWidgetEntry* res( NULL );
            if ( aDoConsistency )
                {
                res = EntryConsistency( entry,
                                          aAppArchList,
                                          aAppArchListFlags,
                                          aInstalledListForDrive,
                                          aInstalledListForDriveFlags,
                                          aDriveUnit,
                                          aDirtyFlag );
                }
            if ( NULL != res )
                {
                TRAP( error, InsertL( entry ) );
                if ( KErrNone != error )
                    {
                    CleanupStack::PopAndDestroy( entry );
                    }
                else
                    {
                    __ASSERT_DEBUG( res == entry, User::Invariant() );
                    // Entry was inserted successfully.
                    CleanupStack::Pop( entry );
                    // add uid to AppArchList if not there,
                    // this can happend due to UID
                    // reallocation for UID collision resolution
                    TInt uidInt = (*entry)[EUid];
                    if ( aDoConsistency &&
                         ( KErrNotFound
                           == aAppArchList.Find(TUid::Uid(uidInt)) ) )
                        {
                        User::LeaveIfError( aAppArchList.Append( TUid::Uid(uidInt) ) );
                        User::LeaveIfError( aAppArchListFlags.Append( EAppListFlagEntry ) );
                        }
                    LOG2( " entry 0x%x (%d) added to registry",
                              uidInt, uidInt );
                    }
                }
            }
        else
            {
            // entry error
            CleanupStack::PopAndDestroy( entry );
            }
        } // for

    CleanupStack::PopAndDestroy( 2, &file ); // readStream, file
    }

// ============================================================================
// CWidgetRegistry::InternalizeXmlL()
// Read entry info from data file into memory
//
// @since 5.0
// ============================================================================
//
void CWidgetRegistry::InternalizeXmlL( const TDesC& aFileName,
                                       const TDriveUnit& aDriveUnit,
                                       TBool aDoConsistency,
                                       RArray<TUid>& aAppArchList,
                                       RArray<TInt>& aAppArchListFlags,
                                       const CDir* aInstalledListForDrive,
                                       RArray<TInt>& aInstalledListForDriveFlags,
                                       TBool& aDirtyFlag )
    {
    LOG( "InternalizeXmlL" );
    RFile file;
    User::LeaveIfError( file.Open( iFs, aFileName, 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; // resulting document tree

    doc = xmlReadMemory( (const char *)bufPtr.Ptr(), bufPtr.Length(),
                         NULL, // no base URL
                         NULL, // get encoding from doc
                         0); // options

    if ( !doc )
        {
        LOG( " leaving: parse failed XML corrupt" );
        User::Leave( KErrCorrupt );
        }
    
    //TCleanupItem item( XmlDocFree, doc );
    //CleanupStack::PushL( item );
    xmlNode* rootElement = xmlDocGetRootElement( doc );
    TPtrC8 rootTag( rootElement->name );
    if ( 0 != rootTag.Compare( KWidgetRegistry() ) )
    {
        LOG( " leaving: XML root element mismatch" );
        User::Leave( KErrCorrupt );
    }

    for ( xmlNode* n = rootElement->children;
          n;
          n = n->next )
        {
        if ( n->type == XML_ELEMENT_NODE )
            {
            TPtrC8 element( n->name );

            if ( 0 == element.Compare( KEntry() ) )
                {
                if ( NULL == n->children )
                    {
                    // malformed? should we require entry to have
                    // some minimal info?
                    continue;
                    }
                CWidgetEntry* entry = CWidgetEntry::NewL();
                CleanupStack::PushL( entry );

                // extract one entry
                TRAPD( error,
                       entry->InternalizeXmlL( iFs, doc, n->children,
                                               iXmlProcessor ) );
                LOG2( " entry 0x%x read from XML with error %d",
                          (error == KErrNone)? (*entry)[EUid] : 0,
                          error );
                if ( KErrNone == error )
                    {
                    if ( aDoConsistency )
                        {
                        entry = EntryConsistency( entry,
                                                  aAppArchList,
                                                  aAppArchListFlags,
                                                  aInstalledListForDrive,
                                                  aInstalledListForDriveFlags,
                                                  aDriveUnit,
                                                  aDirtyFlag );
                        }
                    if ( NULL != entry )
                        {
                        TRAP( error, InsertL( entry ) );
                        if ( KErrNone != error )
                            {
                            delete entry;
                            }
                        else
                            {
                            entry->SetBlanketPermission((*entry)[EBlanketPermGranted] );
                            // add uid to AppArchList if not there,
                            // this can happend due to UID
                            // reallocation for UID collision resolution
                            TInt uidInt = (*entry)[EUid];
                            if ( aDoConsistency &&
                                 ( KErrNotFound
                                   == aAppArchList.Find(TUid::Uid(uidInt)) ) )
                                {
                                aAppArchList.Append( TUid::Uid(uidInt) );
                                aAppArchListFlags.Append( EAppListFlagEntry );
                                }
                            LOG2( " entry 0x%x (%d) added to registry",
                                      uidInt, uidInt );
                            }
                        }
                    }
                else
                    {
                    // entry error
                    delete entry;
                    LOG( " entry internalize failed" );
                    }

                CleanupStack::Pop(); //entry
                } // if <entry>
            } // if n is element
        } // for

    xmlFreeDoc( doc );
    xmlCleanupParser();

    CleanupStack::PopAndDestroy( 2, &file ); // buf, file
    }

// ============================================================================
// CWidgetRegistry::ExternalizeL()
// Write entry info in memory into data file
//
// @since 3.1
// ============================================================================
//
void CWidgetRegistry::ExternalizeL()
    {
    // in order to have a list of all entries for a particular drive,
    // create a hash map from the entries with the key as drive name
    // and value as an array of entry indices for that drive
    RPtrHashMap< TInt, CArrayFixFlat<TInt> > driveEntryHashMap;
    CleanupClosePushL( driveEntryHashMap );

    for (TInt i = 0 ;i < iEntries.Count(); i++)
        {
        CWidgetEntry* entry = iEntries[i];
        const TDesC& driveName = (*entry)[EDriveName];
        TDriveUnit driveUnit( driveName );
        CArrayFixFlat<TInt>* array =
            driveEntryHashMap.Find( driveUnit );
        if( !array )
           {
           TInt* driveNo = new (ELeave) TInt( driveUnit );
           CArrayFixFlat<TInt>* drArray = new (ELeave) CArrayFixFlat<TInt>(1);
            CleanupStack::PushL( drArray );
           drArray->AppendL(i);
           driveEntryHashMap.Insert(driveNo, drArray);
           }
        else
           {
           array->AppendL(i);
           }
        }

    // List all drives in the system
    TDriveList driveList;
    User::LeaveIfError( iFs.DriveList(driveList) );

    // Check all drives
    for ( TInt driveNumber = EDriveA; driveNumber <= EDriveZ; driveNumber++ )
        {
        // The drives that will be filtered out are the same ones that
        // WidgetInstaller filters out in CWidgetUIHandler::SelectDriveL()
        if ( (EDriveD == driveNumber)
             || !driveList[driveNumber] )
            {
            // EDriveD is a temporary drive, usually a RAM disk
            continue;
            }

        TVolumeInfo volInfo;
        if ( iFs.Volume( volInfo, driveNumber ) != KErrNone )
            {
            // The volume is not usable (e.g. no media card inserted)
            continue;
            }
        if ( (volInfo.iDrive.iType == EMediaNotPresent) ||
             (volInfo.iDrive.iType == EMediaRom) ||
             (volInfo.iDrive.iType == EMediaRemote) ||
             (volInfo.iDrive.iDriveAtt & KDriveAttRom) ||
             (volInfo.iDrive.iDriveAtt & KDriveAttSubsted) )
            {
            continue;
            }

        // found a usable drive
        TDriveUnit driveUnit( driveNumber );

        // redirect paths to this drive
        iRegistryBinaryFileName[0] = driveUnit.Name()[0];
        iRegistryXmlFileName[0] = iRegistryBinaryFileName[0];
        iRegistryXmlTempFileName[0] = iRegistryBinaryFileName[0];

        const CArrayFixFlat<TInt>* indices =
            driveEntryHashMap.Find( driveNumber );
        if ( NULL == indices )
            {
            // not in hash map, delete any existing versions
            BaflUtils::DeleteFile( iFs, iRegistryBinaryFileName );
            BaflUtils::DeleteFile( iFs, iRegistryXmlFileName );
            BaflUtils::DeleteFile( iFs, iRegistryXmlTempFileName );
            }
        else
            {
            iFs.CreatePrivatePath( driveUnit );

            // a transactional file update to protect against
            // disk full, etc: overwrite temp then rename temp to original

            TRAPD( error,
                   ExternalizeXmlL( iRegistryXmlTempFileName, indices ) );
            if ( KErrNone == error )
                {
                // last steps in transactional update
                BaflUtils::DeleteFile( iFs, iRegistryXmlFileName );
                BaflUtils::RenameFile( iFs,
                                       iRegistryXmlTempFileName,
                                       iRegistryXmlFileName );
                }
            else // handle leave by deleting temp file
                {
                BaflUtils::DeleteFile( iFs, iRegistryXmlTempFileName );
                }
            }
        }

    for ( TInt i = 0; i < driveEntryHashMap.Count(); i++ )
        {
        CleanupStack::Pop();
        }
    CleanupStack::Pop( &driveEntryHashMap );
    driveEntryHashMap.ResetAndDestroy();
    driveEntryHashMap.Close();
    }

// ============================================================================
// CWidgetRegistry::ExternalizeBinaryL
// Externalize Binary file
//
// @since 3.1
// ============================================================================
//
void CWidgetRegistry::ExternalizeBinaryL( const TDesC& aFilename,
                                          const CArrayFixFlat<TInt>* aIndices )
    {
    RFileWriteStream writeStream;
    CleanupClosePushL( writeStream );

    RFile file;
    User::LeaveIfError( file.Replace( iFs, aFilename, EFileWrite ) );
    CleanupClosePushL( file );
    writeStream.Attach( file );
    writeStream.WriteInt32L( aIndices->Count() );
    for ( TInt i = 0; i < aIndices->Count() ; i++ )
        {
        TInt pos = (*aIndices)[i];
        CWidgetEntry* entry = iEntries[pos];
        if(entry)
            {
            TRAPD( error, entry->ExternalizeBinaryL( writeStream ) );
            if ( KErrNone != error )
                {
                // TODO how to recover from error?
                continue;
                }
            }
        }
    writeStream.CommitL();
    CleanupStack::PopAndDestroy(2);
    }

// ============================================================================
// CWidgetRegistry::ExternalizeXmlL
//
// @since 3.1
// ============================================================================
//
void CWidgetRegistry::ExternalizeXmlL( const TDesC& aFilename,
                                       const CArrayFixFlat<TInt>* aIndices )
    {
    // sequence: write XML header, write each entry including
    // unrecognized XML, write closing XML

    RFileWriteStream writeStream;
    CleanupClosePushL( writeStream );

    RFile file;
    User::LeaveIfError( file.Replace( iFs, aFilename, EFileWrite ) );
    CleanupClosePushL( file );
    writeStream.Attach( file );

    // write XML header
    TInt bom = 0xfeff; // byte-order mark
    writeStream.WriteInt16L( bom );
    writeStream.WriteL( KXmlHeader );
    writeStream.WriteL( KXmlNewline );
    writeStream.WriteL( KXmlRootStart );
    writeStream.WriteL( KXmlNewline );

    for ( TInt i = 0; i < aIndices->Count() ; i++ )
        {
        TInt pos = (*aIndices)[i];
        CWidgetEntry* entry = iEntries[pos];
        if ( entry )
            {
            writeStream.WriteL( KWidgetEntryStart );
            writeStream.WriteL( KXmlNewline );
            // TODO handle unrecognized XML from internalize
            TRAPD( error, entry->ExternalizeXmlL( writeStream,
                                                  iXmlProcessor,
                                                  iFs) );
            writeStream.WriteL( KWidgetEntryEnd );
            writeStream.WriteL( KXmlNewline );
            if ( KErrNone != error )
                {
                // TODO how to recover from error?
                continue;
                }
            }
        }

    writeStream.WriteL( KXmlRootEnd );
    writeStream.WriteL( KXmlNewline );
    writeStream.CommitL();

    CleanupStack::PopAndDestroy( 2 ); // file, writeStream
    }

// ============================================================================
// CWidgetRegistry::RegisterWidget()
// Creates CWidgetEntry
//
// @since 3.1
// ============================================================================
//
void CWidgetRegistry::RegisterWidgetL( RReadStream& aStream )
    {
    CWidgetEntry* entry = CWidgetEntry::NewL();
    CleanupStack::PushL( entry );

    entry->InternalizeBinaryL( aStream );
    InsertL( entry );

    CleanupStack::Pop(); //entry
    ExternalizeL();

    //Notify WRT Harvester that widget registry has changed
    NotifyWidgetAltered();
    }

// ============================================================================
// CWidgetRegistry::IsWidget()
// Returns true if the Uid falls within the range specified for widgets
//
// @since 3.1
// ============================================================================
//
TBool CWidgetRegistry::IsWidget( const TUid& aUid ) const
    {
    if ( ( aUid.iUid >= KWidgetUidLowerBound ) &&
        ( aUid.iUid <= KWidgetUidUpperBound ) )
        {
        return ETrue;
        }
    else
        {
        return EFalse;
        }

    }

// ============================================================================
// CWidgetRegistry::WidgetExists()
// Returns true if the widget is installed
//
// @since 3.1
// ============================================================================
//
TBool CWidgetRegistry::WidgetExists( const TDesC& aWidgetId ) const
    {
    CWidgetEntry* entry = NULL;
    TInt pos = GetWidgetEntry( TPtrC( aWidgetId ), entry );
    return ( pos != -1 )?  ETrue : EFalse;
    }

// ============================================================================
// CWidgetRegistry::IsWidgetRunning()
// Returns true if the widget is running
//
// @since 3.1
// ============================================================================
//
TBool CWidgetRegistry::IsWidgetRunning( const TUid& aUid ) const
    {
    TBool active = EFalse;
    CWidgetEntry* entry = NULL;

    GetWidgetEntry( aUid, entry );
    if ( entry && entry->ActiveL() )
        {
        active = ETrue;
        }
    return active;
    }

// ============================================================================
// CWidgetRegistry::WidgetSapiAccessState()
// Returns sapi widget access state
//
// @since 5.0
// ============================================================================
//
TInt CWidgetRegistry::WidgetSapiAccessState( const TUid& aUid ) const
    {
    CWidgetEntry* entry = NULL;

    GetWidgetEntry( aUid, entry );
    if ( entry)
        {
        return entry->SapiAccessState();
        }
    return -1;
    }

// ============================================================================
// CWidgetRegistry::IsWidgetInMiniView()
// Returns true if the widget is in miniview
//
// @since 5.0
// ============================================================================
//
TBool CWidgetRegistry::IsWidgetInMiniView( const TUid& aUid ) const
    {
    TBool state = EFalse;
    CWidgetEntry* entry = NULL;

    GetWidgetEntry( aUid, entry );
    if ( entry && entry->GetMiniViewState())
        {
        state = ETrue;
        }
    return state;
    }

// ============================================================================
// CWidgetRegistry::IsWidgetInFullView()
// Returns true if the widget is in miniview
//
// @since 5.0
// ============================================================================
//
TBool CWidgetRegistry::IsWidgetInFullView( const TUid& aUid ) const
    {
    TBool state = EFalse;
    CWidgetEntry* entry = NULL;

    GetWidgetEntry( aUid, entry );
    if ( entry && entry->GetFullViewState())
        {
        state = ETrue;
        }
    return state;
    }

TBool CWidgetRegistry::IsBlanketPermGranted( const TUid& aUid ) const
    {
    TBool state = EFalse;
    CWidgetEntry* entry = NULL;

    GetWidgetEntry( aUid, entry );
    if ( entry && entry->GetBlanketPermGranted())
        {
        state = ETrue;
        }
    return state;
    }

// ============================================================================
// CWidgetRegistry::InstalledWidgets()
// Returns widget info for all the installed widgets
//
// @since 3.1
// ============================================================================
//
void CWidgetRegistry::InstalledWidgetsL( RWidgetInfoArray& aWidgetInfoArr )
    {
    for ( TInt pos = 0; pos < iEntries.Count(); pos++ )
        {
        CWidgetEntry* entry = iEntries[pos];
        if(entry)
            {
            CWidgetInfo *info = new ( ELeave ) CWidgetInfo();
            info->iUid = TUid::Uid( (*entry)[EUid] );
            info->iFileSize = (*entry)[EFileSize];
            *(info->iBundleName) = (*entry)[EBundleName];
            *(info->iDriveName) = (*entry)[EDriveName];
            aWidgetInfoArr.AppendL( info );
            }
        }
    }

// ============================================================================
// CWidgetRegistry::RunningWidgetsL()
// Returns widget info for all the running widgets
//
// @since 3.1
// ============================================================================
//
void CWidgetRegistry::RunningWidgetsL( RWidgetInfoArray& aWidgetInfoArr )
    {
    for ( TInt pos = 0; pos < iEntries.Count(); pos++ )
        {
        CWidgetEntry* entry = iEntries[pos];

        if ( entry && entry->ActiveL() )
            {
            CWidgetInfo* info = new ( ELeave ) CWidgetInfo();
            info->iUid = TUid::Uid( (*entry)[EUid] );
            info->iFileSize = (*entry)[EFileSize];
            *(info->iBundleName) = (*entry)[EBundleName];
            *(info->iDriveName) = (*entry)[EDriveName];
            aWidgetInfoArr.AppendL( info );
            }
        }
    }

// ============================================================================
// CWidgetRegistry::DeRegisterWidgetL()
// Deregister the widget
//
// @since 3.1
// ============================================================================
//
void CWidgetRegistry::DeRegisterWidgetL( const TUid& aUid )
    {
    Remove( aUid );
    ExternalizeL( );
    //Notify WRT Harvester that widget registry has changed
    NotifyWidgetAltered();
    }

// ============================================================================
// CWidgetRegistry::GetWidgetBundleId()
// Returns bundleId of the widget with a particular UId.
//
// @since 3.1
// ============================================================================
//
void CWidgetRegistry::GetWidgetBundleId( const TUid& aUid, TDes& aBundleId )
    {
    CWidgetEntry* entry = NULL;
    TInt pos = GetWidgetEntry( aUid, entry );

    if ( pos != -1 )
        {
        const TDesC& widgetBundleId = (*entry)[EBundleIdentifier];
        aBundleId.Copy( widgetBundleId );
        aBundleId.SetLength( widgetBundleId.Length() );
        }
    else
        {
        aBundleId.SetLength( 0 );
        }
    }

// ============================================================================
// CWidgetRegistry::GetWidgetBundleName()
// Returns bundle display name of the widget with a particular UId.
//
// @since 3.1
// ============================================================================
//
void CWidgetRegistry::GetWidgetBundleName( const TUid& aUid, TDes& aBundleName )
    {
    CWidgetEntry* entry = NULL;
    TInt pos = GetWidgetEntry( aUid, entry );

    if ( pos != -1 )
        {
        const TDesC& widgetBundleName = (*entry)[EBundleName];
        aBundleName.Copy( widgetBundleName );
        aBundleName.SetLength( widgetBundleName.Length() );
        }
    else
        {
        aBundleName.SetLength( 0 );
        }
    }

// ============================================================================
// CWidgetRegistry::GetWidgetPropertyValueL()
// return serialized value of property aId for the widget aUid or leave
//
// @since 3.1
// ============================================================================
//
CBufFlat* CWidgetRegistry::GetWidgetPropertyValueL(
    const TUid& aUid,
    TWidgetPropertyId aId,
    TInt aMaxLength )
    {
    CWidgetEntry& entry = GetWidgetEntryL( aUid );
    CBufFlat* buf = CBufFlat::NewL( aMaxLength );
    CleanupStack::PushL( buf );
    RBufWriteStream stream( *buf ); // stream over the buffer
    CleanupClosePushL( stream );

    // TBD safe array indexing (leave if out of range)
    entry[aId].SerializeL( stream ); // serialize

    CleanupStack::PopAndDestroy( &stream );
    CleanupStack::Pop(); // buf
    return buf;
    }

// ============================================================================
// CWidgetRegistry::GetWidgetPath()
// Returns path of the widget with a particular UId.
//
// @since 3.1
// ============================================================================
//
void CWidgetRegistry::GetWidgetPath( const TUid& aUid, TDes& aPath )
    {
    CWidgetEntry* entry = NULL;
    TInt pos = GetWidgetEntry( aUid, entry );

    if ( pos != -1 )
        {
        const TDesC& widgetBasePath = (*entry)[EBasePath];
        aPath.Copy( widgetBasePath );
        aPath.SetLength( widgetBasePath.Length() );
        }
    else
        {
        aPath.SetLength( 0 );
        }
    }

// ============================================================================
// CWidgetRegistry::GetLprojName()
// Returns lproj name
//
// @since 3.1
// ============================================================================
//
void CWidgetRegistry::GetLprojNameL( TDes& aPath )
    {
    if ( !iLangDirList.Count() )
        {
        CreateLangDirListL();
        }
    HBufC8* lprojName = iLangDirList.Find( User::Language() );
    if ( lprojName )
        {
        TPtr8 lprojNamePtr = lprojName->Des();
        aPath.Copy( lprojNamePtr );
        }
    else
        {
        aPath.Copy( KWidgetDefaultLangDir );
        }
    }

// ============================================================================
// CWidgetRegistry::GetWidgetUid()
// Returns UId of the widget with a particular bundle identifier
//
// @since 3.1
// ============================================================================
//
TUid CWidgetRegistry::GetWidgetUid( const TDesC& aBundleId ) const
    {
    CWidgetEntry* entry = NULL;
    TInt pos = GetWidgetEntry( aBundleId, entry );

    if ( pos != -1 )
        {
        return TUid::Uid( (*entry)[EUid] );
        }
    else
        {
        return KNullUid;
        }
    }

// ============================================================================
// CWidgetRegistry::GetWidgetUidForUrl()
// Returns uid of the widget with a patricular html path
//
// @since 3.1
// ============================================================================
//
TUid CWidgetRegistry::GetWidgetUidForUrl( const TDesC& aUrl ) const
    {
    for( TInt i = 0; i < iEntries.Count(); i++)
        {
        CWidgetEntry* entry = iEntries[i];

        if ( aUrl.CompareF( (*entry)[EMainHTML] ) == 0 )
            {
            return TUid::Uid( (*entry)[EUid] );
            }
        }
    return KNullUid;
    }

// ============================================================================
// CWidgetRegistry::UsedUids()
// Returns array of used UIds
//
// @since 3.1
// ============================================================================
//
void CWidgetRegistry::UsedUidsL( RUidArray& aUsedUids )
    {
    for( TInt i = 0; i < iUsedUids.Count(); i++ )
        {
        aUsedUids.AppendL( iUsedUids[i] );
        }
    }

// ============================================================================
// CWidgetRegistry::GetAvailableUidL()
// Get next availble uid from the pool using a random generation
//
// @since 3.1
// ============================================================================
//
TUid CWidgetRegistry::GetAvailableUidL( TInt aDriveLetter )
    {
    TUidAllocator uidAllocator;
    return TUid::Uid( uidAllocator.AllocateL( iUsedUids, aDriveLetter ) );
    }

// ============================================================================
// CWidgetRegistry::SetActive()
// Set/Reset active status of the widget with a particular UId
//
// @since 3.1
// ============================================================================
//
void CWidgetRegistry::SetActive( TUid aUid, TInt aStatus )
    {
    CWidgetEntry* entry = NULL;
    TInt pos = GetWidgetEntry( aUid, entry );
    if ( pos != -1 )
      {
        entry->SetActive( aStatus );
      }
    }

// ============================================================================
// CWidgetRegistry::SetMiniView()
// Set/Reset Widget status for launched in MiniView
//
// @since 5.0
// ============================================================================
//
void CWidgetRegistry::SetMiniView( TUid aUid, TInt aStatus )
    {
    CWidgetEntry* entry = NULL;
    TInt pos = GetWidgetEntry( aUid, entry );
    if ( pos != -1 )
        {
        entry->SetMiniView( aStatus );
        }
    }

// ============================================================================
// CWidgetRegistry::SetFullView()
// Set/Reset Widget status in FullView
//
// @since 5.0
// ============================================================================
//
void CWidgetRegistry::SetFullView( TUid aUid, TInt aStatus )
    {
    CWidgetEntry* entry = NULL;
    TInt pos = GetWidgetEntry( aUid, entry );
    if ( pos != -1 )
        {
        entry->SetFullView( aStatus );
        }
    }

// ============================================================================
// CWidgetRegistry::SetBlanketPermissionL()
// Set/Reset Widget Blanket Permission
//
// @since 5.0
// ============================================================================
//
void CWidgetRegistry::SetBlanketPermissionL( TUid aUid, TInt aStatus )
    {
    CWidgetEntry* entry = NULL;
    TInt pos = GetWidgetEntry( aUid, entry );
    if ( pos != -1 )
        {
        entry->SetBlanketPermission( aStatus );
        }
    ExternalizeL();
    }

// ============================================================================
// CWidgetRegistry::CreateLangDirListL()
// create the mapping table for language and lproj dir
//
// @since 3.1
// ============================================================================
//
void CWidgetRegistry::CreateLangDirListL()
    {
    TLanguage defaultLanguage = User::Language();
    RFile file;
    TFileName langDirFile;
    iFs.PrivatePath( langDirFile );
#ifdef __WINSCW__
    langDirFile.Insert( 0, _L( "C:" ));
#else
    langDirFile.Insert( 0, _L( "Z:" ));
#endif
    langDirFile.Append( KWidgetDirFile );
    User::LeaveIfError( file.Open( iFs, langDirFile, EFileRead ) );
    CleanupClosePushL( file );

    TInt size;
    User::LeaveIfError( file.Size( size ) );
    HBufC8* buf = HBufC8::NewLC( size );
    TPtr8 bufPtr( buf->Des() );
    User::LeaveIfError( file.Read( bufPtr ) );
    CleanupStack::Pop( buf );
    CleanupStack::PopAndDestroy( &file );
    CleanupStack::PushL( buf );

    // initialize the parser and check compiled code matches lib version
    LIBXML_TEST_VERSION

    xmlDocPtr doc; // resulting document tree

    doc = xmlReadMemory( (const char *)bufPtr.Ptr(), bufPtr.Length(),
                         NULL, // no base URL
                         NULL, // get encoding from doc
                         0); // options

    if ( !doc )
          {
          User::Leave( KErrCorrupt );
          }

    xmlNode* rootElement = xmlDocGetRootElement( doc );

    TInt* langID = NULL;
    xmlNode* n;

    for ( n = rootElement; n; n = TraverseNextNode( n ) )
        {
        HBufC8* langDir;
        if ( n->type == XML_ELEMENT_NODE )
            {
            TPtrC8 element( n->name );

            if ( element.Compare( KLangID() ) == 0 )
                {
                if ( n->children && n->children->type == XML_TEXT_NODE )
                    {
                    TPtrC8 content( n->children->content );
                    langID = new TInt( atoi( (const char *)content.Ptr() ) );
                    CleanupStack::PushL( langID );
                    }
                }
            if ( element.Compare( KLangDir() ) == 0 )
                {
                if ( n->children && n->children->type == XML_TEXT_NODE )
                    {
                        TPtrC8 content( n->children->content );
                    langDir = HBufC8::NewLC( content.Length() );
                    langDir->Des().Copy( content );
                    if ( *langID <= 0 )
                        {
                        User::Leave( KErrCorrupt );
                        }
                    TInt* currentLangID = new TInt(*langID);
                    CleanupStack::Pop(langDir);
                    iLangDirList.Insert( currentLangID, langDir );
                    *langID = 0;
                    CleanupStack::PopAndDestroy(langID);
                    langID = NULL;
                    }
                }
            }   // if n is element

        }   // for

    xmlFreeDoc(doc);
    xmlCleanupParser();
    XmlEngineCleanup();
    CleanupStack::PopAndDestroy( buf );
    }

TBool CWidgetRegistry::InstallDirWidgets(
    const TDriveUnit& aDriveUnit,
    CDir*& aInstalledListForDrive,
    RArray<TInt>& aInstalledListForDriveFlags )
    {
    LOG( "InstallDirWidgets" );
    TBool doConsistency = ETrue;
    aInstalledListForDrive = NULL;
    aInstalledListForDriveFlags.Reset();
    iWidgetInstallPath[0] = aDriveUnit.Name()[0];
    TInt error = iFs.GetDir( iWidgetInstallPath,
                             KEntryAttDir | KEntryAttMatchExclusive,
                             ESortNone,
                             aInstalledListForDrive );
    if ( KErrPathNotFound == error )
        {
        LOG( "install path not found" );
        delete aInstalledListForDrive;
        aInstalledListForDrive = NULL;
        return doConsistency;
        }
    if ( KErrNone != error || NULL == aInstalledListForDrive )
        {
        delete aInstalledListForDrive;
        aInstalledListForDrive = NULL;
        doConsistency = EFalse;
        LOG( "error listing install directory, doConsistency 0" );
        }
    else
        {
        TInt count =
            ( (NULL == aInstalledListForDrive) ?
              0 : aInstalledListForDrive->Count() );
        if ( count )
            {
                for ( TInt i = 0; i < aInstalledListForDrive->Count(); i++ )
                    {
                error = aInstalledListForDriveFlags.Append( 0 );
                    if ( KErrNone != error )
                        {
                        break;
                        }
                    }
            if ( KErrNone != error )
                {
                doConsistency = EFalse;
                aInstalledListForDriveFlags.Reset();
                
                delete aInstalledListForDrive;
                aInstalledListForDrive = NULL;
                }
            }
        }
    LOG_CODE( if ( aInstalledListForDrive ) )
    LOG2( "InstallDirWidgets done count %d doConsistency %d",
              aInstalledListForDrive->Count(),
              (TInt)doConsistency );
    LOG_CODE( else )
    LOG1( "InstallDirWidgets return NULL list doConsistency %d",
              (TInt)doConsistency );
    return doConsistency;
    }

// a routine to build an array of widgets according to app arch app list
// returns NULL on error, otherwise a UID list (possibly with no entries)
TInt CWidgetRegistry::AppArchWidgetUids( RArray< TUid >& aUids )
    {
    LOG( "AppArchWidgetUids" );
    TInt error = iAppArch.GetAllApps();
    aUids.Reset();
    if ( KErrNone != error )
        {
        LOG ( "AppArchWidgetUids done error NULL list" );
        
        return error;
        }
    TInt appArchWaitTotal = 0;
    TApaAppInfo info;
    do
        {
        error = iAppArch.GetNextApp( info );
        if ( KErrNone == error )
            {
            if ( TUidAllocator::IsWidget( info.iUid ) )
                {
                LOG2( " widget uid 0x%x (%d)",
                          (TUint)(info.iUid.iUid), info.iUid.iUid );
                error = aUids.Append( info.iUid );
                if ( KErrNone !=  error )
                    {
                    break;
                    }
                }
            continue;
            }
        else if ( RApaLsSession::EAppListInvalid == error )
            {
            if ( appArchWaitTotal > KAppArchTimeout )
                {
                LOG( " appArchWaitTotal > KAppArchTimeout" );
                break;
                }
            appArchWaitTotal += KAppArchDelayInterval;
            LOG1( " appArchWaitTotal %d", appArchWaitTotal );
            User::After( KAppArchDelayInterval );
            continue;
            }
        } while ( RApaLsSession::ENoMoreAppsInList != error );
    if ( RApaLsSession::ENoMoreAppsInList != error )
        {
        aUids.Reset();
        }
    LOG_CODE( if ( aUids.Count() ) )
    LOG1( "AppArchWidgetUids done widget count %d",
              aUids.Count() );
    LOG_CODE( else )
    LOG( "AppArchWidgetUids done error NULL list" );

    return error;
    }

// if primary return bool is T then arg lists will be non NUll, else
// return bool will be F and arg lists will be NULL (error condition)
TBool CWidgetRegistry::AppArchWidgets( RArray<TUid>& aAppArchList,
                                       RArray<TInt>& aAppArchListFlags )
    {
    TBool doConsistency = ETrue;
    TInt error = AppArchWidgetUids( aAppArchList );
    aAppArchListFlags.Reset();
    // NULL means AppArchWidgetUids encountered an error
    if ( error )
        {
        // doConsistency flase means there is an unrecoverable error
        // in consistency logic
        doConsistency = EFalse;
        }
    else
        {
        TInt error = KErrNone;
        for ( TInt i = 0 ; i < aAppArchList.Count(); i++ )
                {
            error = aAppArchListFlags.Append( 0 );
                if ( KErrNone != error )
                    {
                    break;
                    }
                }
    
        if ( KErrNone != error )
            {
            doConsistency = EFalse;
            aAppArchListFlags.Reset();
            aAppArchList.Reset();
            }
        }
    LOG_CODE( if ( aAppArchList.Count() ) )
    LOG2( "AppArchWidgets done widgets count %d doConsistency %d",
              aAppArchListFlags.Count(), (TInt)doConsistency );
    LOG_CODE( else )
    LOG1( "AppArchWidgets done error doConsistency %d",
              (TInt)doConsistency );
    return doConsistency;
    }

// when called: aAppArchList and Flags must not be
// NULL. aInstalledListForDrive and Flags may be NULL
CWidgetEntry* CWidgetRegistry::EntryConsistency(
    CWidgetEntry* aEntry,
    const RArray<TUid>& aAppArchList,
    RArray<TInt>& aAppArchListFlags,
    const CDir* aInstalledListForDrive,
    RArray<TInt>& aInstalledListForDriveFlags,
    const TDriveUnit& aDriveUnit,
    TBool& aDirtyFlag )
    {
    LOG( "EntryConsistency" );
    TInt error;
    TBool inAppList;
    TUid uid;

    if ( IsEntryInstalled( aEntry, aInstalledListForDrive,
                           aInstalledListForDriveFlags ) )
        {
        error = IsEntryInAppList( aEntry,
                                  aAppArchList,
                                  aAppArchListFlags,
                                  inAppList,
                                  uid );
        if ( ( inAppList == EFalse ) || ( KErrNone != error ) )
            {
            if ( KErrNone == error )
                {
                LOG( " entry not in AppArch" );
                // not in AppArch but may be using a UID already in
                // use (UID conflict)
                error =
                    InternalizeEntryNewUidIfNeeded( aEntry, aDriveUnit );
                if ( KErrNone == error )
                    {
                    iInstaller->RenameIconFile( iFs, aEntry->Properties() );
                    TInt uidInt = (*aEntry)[EUid];
                    TRAP( error,
                          iInstaller->RegisterWidgetL(
                              (*aEntry)[EMainHTML],
                              (*aEntry)[EBundleDisplayName],
                              (*aEntry)[EIconPath],
                              (*aEntry)[EDriveName],
                              TUid::Uid( uidInt ) ) );
                    if ( KErrNone == error )
                        {
                        LOG2( " registered widget 0x%x (%d)",
                                  uidInt, uidInt );
                        LOG( " registry dirty flag set" );
                        aDirtyFlag = ETrue;
                        }
                    }
                }
            // catches errors from several prior lines
            if ( KErrNone != error )
                {
                // allocated uid hasn't been added to iUsedUid yet so
                // just delete entry

                // on error don't put entry in registry
                LOG( " error entry deleted" );
                delete aEntry;
                aEntry = NULL;
                }
            }
        }
    else
        {
        LOG( " entry is not installed" );
        // entry is not installed, clean up app list if needed
        error = IsEntryInAppList( aEntry,
                                  aAppArchList,
                                  aAppArchListFlags,
                                  inAppList,
                                  uid );
        if ( ( (TInt)ETrue == inAppList ) && ( KErrNone == error ) )
            {
            LOG( " removing from AppArch" );
            // there is nothing useful to do on error
            TRAP_IGNORE( iInstaller->DeregisterWidgetL( uid ) );
            }

        // don't put entry in registry
        LOG( " entry deleted" );
        delete aEntry;
        aEntry = NULL;
        aDirtyFlag = ETrue;
        }
    LOG( "EntryConsistency done " );
    return aEntry;
    }

// aInstalledListForDrive and Flags may be NULL (meaning no installed widgets)
TBool CWidgetRegistry::IsEntryInstalled(
    CWidgetEntry* aEntry,
    const CDir* aInstalledListForDrive,
    RArray<TInt>& aInstalledListForDriveFlags )
    {
    TBool result = EFalse;
    // NULL is an expected input due to GetDir producing it for empty lists
    if ( NULL != aInstalledListForDrive )
        {
        const TDesC& bundleIdentifier = (*aEntry)[EBundleIdentifier];
        for ( TInt i = 0; i < aInstalledListForDrive->Count(); i++ )
            {
            if ( !((aInstalledListForDriveFlags)[i] & EInstallListFlagEntry ))
                {
                TEntry dirEntry = (*aInstalledListForDrive)[i];
                if ( 0 == bundleIdentifier.Compare( dirEntry.iName ) )
                    {
                    (aInstalledListForDriveFlags)[i] |= EInstallListFlagEntry;
                    result = ETrue;
                    break;
                    }
                }
            }
        }
    LOG1( "IsEntryInstalled result %d", (TInt)result );
    return result;
    }

// aAppArchList and Flags must not be NULL but may be empty (0 count)
TInt CWidgetRegistry::IsEntryInAppList( CWidgetEntry* aEntry,
                                        const RArray<TUid>& aAppArchList,
                                        RArray<TInt>& aAppArchListFlags,
                                        TBool& aInAppList,
                                        TUid& uid )
     {
     // entry has uid and EMainHTML, appInfo has uid and iFullName,
     // both should match exactly
     TInt error = KErrNone;
     aInAppList = EFalse;
     for ( TInt i = 0; i < aAppArchList.Count(); i++ )
         {
         if ( !( (aAppArchListFlags)[i] & EAppListFlagEntry ) )
             {
             uid = aAppArchList[i];
             TInt uidInt = (*aEntry)[EUid];
             TUid uid2 = TUid::Uid( uidInt );
             if ( uid == uid2 )
                 {
                 TApaAppInfo appInfo;
                 error = iAppArch.GetAppInfo( appInfo, uid );
                 if ( KErrNone == error )
                     {
                     const TDesC& mainHtml = (*aEntry)[EMainHTML];
                     if ( 0 == mainHtml.Compare( appInfo.iFullName ) )
                         {
                         aAppArchListFlags[i] |= EAppListFlagEntry;
                         aInAppList = ETrue;
                         break;
                         }
                     }
                 else
                     {
                     break;
                     }
                 }
             }
         }
     LOG2( "IsEntryInAppList done %d error %d",
           (TInt)aInAppList, error );
     return error;
     }

// This will assign a new UID if the UID in the entry appears either
// in iUsedUids or a call to GetAppInfo returns some info (something
// is using the UID).  The new UID will *not* be placed in iUsedUids and
// GetAppInfo using the new UID will return KErrNotFound.
TInt CWidgetRegistry::InternalizeEntryNewUidIfNeeded(
    CWidgetEntry* aEntry,
    const TDriveUnit& aDriveUnit )
    {
    LOG( "InternalizeEntryNewUidIfNeeded" );
    // entry has a uid but it may already be in use by another widget
    // according to app arch or iUsedUids
    TUidAllocator uidAllocator;
    TInt upos = iUsedUids.Find( TUid::Uid( (*aEntry)[EUid] ) );
    TApaAppInfo appInfo;
    TInt error = iAppArch.GetAppInfo( appInfo, TUid::Uid( (*aEntry)[EUid] ) );
    if ( KErrNotFound != upos || KErrNotFound != error )
        {
        LOG( " widget needs a new UID" );
        LOG_CLOSE;
        LOG_OPEN;
        do
            {
            TInt uidInt = 0;
            TRAP( error,
                  uidInt = uidAllocator.AllocateL( iUsedUids,
                                                   aDriveUnit.Name()[0] ) );
            if ( KErrNone != error )
                {
                LOG(
    "InternalizeEntryNewUidIfNeeded done error failed to allocate a new UID" );
                return KErrNotFound;
                }
            TUid uid = TUid::Uid( uidInt );
            error = iAppArch.GetAppInfo( appInfo, uid );
            if ( KErrNotFound == error )
                {
                // use new uid (do icon mbm file rename elsewhere) and
                // don't add it to iUsedUids yet as that will be done
                // when entry is finally inserted in registry
                (*aEntry)[EUid] = uidInt;
                LOG2(
  "InternalizeEntryNewUidIfNeeded done new uid is 0x%x (%d)", uidInt, uidInt );
                return KErrNone;
                }
            else if ( KErrNone == error )
                {
                // don't allocate this uid because AppArch thinks it
                // is in use
                error = iUsedUids.Append( uid );
                }
            } while ( KErrNone == error );
        LOG(
    "InternalizeEntryNewUidIfNeeded done error failed to allocate a new UID" );
        return KErrNotFound;
        }
    LOG( "InternalizeEntryNewUidIfNeeded done widget already has a unique ID" );    LOG_CLOSE;
    LOG_OPEN;
    return KErrNone;
    }


// this examines the installed widget list after the persistent
// registry entries have been processed for any installed widgets that
// don't yet have a registry entry.  An entry is generated and the
// widget is registered with app arch.
void CWidgetRegistry::InstallDirConsistency(
    const CDir* aInstalledListForDrive,
    RArray<TInt>& aInstalledListForDriveFlags,
    TBool& aDirtyFlag )
    {
    LOG( "InstallDirConsistency" );
    if ( NULL == aInstalledListForDrive
         || ( ( NULL != aInstalledListForDrive)
              && ( 0 == aInstalledListForDrive->Count() ) ) )
        {
        LOG( " nothing is installed" );
        return;
        }
    TInt error = KErrNone;
    TUidAllocator uidAllocator;
    for ( TInt i = 0; i < aInstalledListForDrive->Count(); i++ )
        {
        if ( !( aInstalledListForDriveFlags[i] & EInstallListFlagEntry ) )
            {
            TFileName widgetPath( iWidgetInstallPath );
            widgetPath.Append( (*aInstalledListForDrive)[i].iName );
            widgetPath.Append( _L("\\") );
            // assume directory under this is widget top-level dir
            CDir *listing = NULL;
            error = iFs.GetDir( widgetPath,
                                KEntryAttDir | KEntryAttMatchExclusive,
                                ESortNone,
                                listing );
            if ( ( KErrNone != error )
                 || ( 1 != listing->Count() ) )
                {
                // skip this widget
                LOG( " rejecting a directory that has unexpected contents" );
                delete listing;
                continue;
                }
            LOG( " found inconsistency" );
            widgetPath.Append( ((*listing)[0]).iName );
            widgetPath.Append( _L("\\") );
            delete listing;

            TInt uidInt = 0;
            TRAP( error, uidInt =
                  uidAllocator.AllocateL( iUsedUids,
                                          iWidgetInstallPath[0] ) );
            if ( KErrNone != error )
                {
                LOG1( " uid allocate error %d", error );
                continue; // skip this widget
                }

            LOG( " getting properties from installed widget" );
            LOG_CLOSE;
            LOG_OPEN;
            RPointerArray<CWidgetPropertyValue>* tmp;
            TRAP( error,
                  tmp =
                  iInstaller->WidgetPropertiesFromInstalledWidgetL(
                      iFs, widgetPath, iLprojName, TUid::Uid( uidInt ) ) );
            if ( KErrNone != error || !tmp )
                {
                LOG1( " props from installed widget error %d", error );
                continue; // skip this widget
                }
            LOG( " got properties from installed widget" );
            // transfer ownership of tmp
            CWidgetEntry* entry = NULL;
            TRAP( error, entry = CWidgetEntry::NewL( &tmp ) );
            if ( KErrNone != error )
                {
                LOG1( " new entry copy ctor error %d", error );
                if ( tmp )
                    {
                    tmp->ResetAndDestroy();
                    }
                continue; // skip this widget
                }
            // app arch may have this widget under another uid
            // but if we assign a new uid and register the
            // widget then the duplicate app arch entry will
            // be removed when app arch list is made
            // consistent by removing entries not in the
            // registry by uid

            // check that uid is unknown to app arch
            TDriveUnit driveUnit( widgetPath );
            error = InternalizeEntryNewUidIfNeeded( entry, driveUnit );
            if ( KErrNone != error )
                {
                LOG1( " new uid if needed error %d", error );
                delete entry;
                continue; // skip this widget
                }
            LOG_CODE( TInt uidLog = (*entry)[EUid] );
            LOG1( " uid 0x%x", uidLog );
            LOG_CODE( const TDesC& logDes = (*entry)[EMainHTML] );
            LOG1( " mainHTML %S", &logDes );
            LOG_CODE( const TDesC& logDes2 = (*entry)[EBundleDisplayName] );
            LOG1( " bundleDiplayname %S", &logDes2 );

            TRAP( error,
                  iInstaller->RegisterWidgetL(
                      (*entry)[EMainHTML],
                      (*entry)[EBundleDisplayName],
                      (*entry)[EIconPath],
                      (*entry)[EDriveName],
                      TUid::Uid( (*entry)[EUid]) ) );

            if ( KErrNone == error )
                {
                LOG2( " registered widget 0x%x (%d)",
                          uidLog, uidLog );
                TRAP( error,
                      InsertL( entry ) );
                if ( KErrNone != error )
                    {
                    LOG1( " insert entry error %d", error );
                    delete entry;
                    continue; // skip this widget
                    }
                LOG2( " entry 0x%x (%d) added to registry",
                          uidLog, uidLog );
                LOG( "registry dirty flag set" );
                aDirtyFlag = ETrue;
                }
            else
                {
                // allocated uid hasn't been added to iUsedUid yet so
                // just delete entry
                delete entry;
                LOG( "failed to recover an installed widget" );
                continue; // skip this widget
                }
            }
        }
    LOG( "InstallDirConsistency done" );
    }

void CWidgetRegistry::AppArchListConsistency( const RArray<TUid>& aAppArchList,
                                              RArray<TInt>& aAppArchListFlags )
    {
    LOG( "AppArchListConsistency" );
    for ( TInt i = 0; i < aAppArchList.Count(); i++ )
        {
        if ( !( aAppArchListFlags[i] & EAppListFlagEntry ) )
            {
            LOG_CODE( TInt uidIntLog = aAppArchList[i].iUid );
            LOG2( " deregistered widget 0x%x (%d)",
                      uidIntLog, uidIntLog );
            TRAP_IGNORE( iInstaller->DeregisterWidgetL( aAppArchList[i] ) );
            }
        }
    LOG( "AppArchListConsistency done" );
    }


// End of File
