javatools/javarestoreconverter/src.s60/restoreconvertmidlet.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 27 Apr 2010 16:30:29 +0300
branchRCL_3
changeset 14 04becd199f91
permissions -rw-r--r--
Revision: v2.1.22 Kit: 201017

/*
* Copyright (c) 2009 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: Java platform 2.0 javarestoreconverter process
*
*/


#include <apacmdln.h>
#include <apgcli.h>
#include <driveinfo.h>
#include <e32base.h>
#include <pathinfo.h>
#include <utf.h>
#include <zipfile.h>

#include "restoreconvertmidlet.h"

#include "javacommonutils.h"
#include "javaprocessconstants.h"
#include "javastorage.h"
#include "javastorageentry.h"
#include "javastoragenames.h"
#include "javasymbianoslayer.h"
#include "javauids.h"
#include "logger.h"


using namespace java::storage;
using namespace java::util;

// MIDlet name and vendor max size is 255,
// +1 added for terminating zero needed for logging
const TInt KMaxBufferSize = 256;

// character constants needed when parsing java attributes
const TUint32 HT = 9;  // horizontal tab
const TUint32 LF = 10; // line feed
const TUint32 CR = 13; // carriage return
const TUint32 SP = 32; // space
const TUint32 COLON = 58; // ':'


_LIT(KJarFileNameSuffix, ".jar");
_LIT(KJadFileNameSuffix, ".jad");
_LIT(KFLJarFileNameSuffix, ".dm");
_LIT(KSDJarFileNameSuffix, ".dcf");
_LIT(KSMCBackSplash, "\\");
_LIT(KDriveText, "drive");
_LIT(KMidletName, "MIDlet-Name");
_LIT(KMidletVendor, "MIDlet-Vendor");
_LIT(KManifestEntryName, "META-INF/MANIFEST.MF");

// The directory where the java applications to be converted are searched from
_LIT(KMidletSuiteInstallBasePath, ":\\Private\\102033E6\\MIDlets\\");


/**
 * To create new instance of this class.
 *
 * @param aFs - A reference to the file server.
 * @return Reference to the object of this class.
 * @exception If construction fails.
 *
 */
CRestoreConvertMIDlet* CRestoreConvertMIDlet::NewLC(RFs& aFs)
{
    CRestoreConvertMIDlet* self = new(ELeave) CRestoreConvertMIDlet(aFs);
    CleanupStack::PushL(self);
    self->ConstructL();
    return self;
}

/**
 * To do 1st phase construction for this object.
 *
 * Adds this active object to the scheduler.
 *
 * @param aFs - A reference to the file server.
 */
CRestoreConvertMIDlet::CRestoreConvertMIDlet(RFs& aFs) :
        CActive(EPriorityStandard), iFs(aFs), iNumberOfAppsToInstall(0)
{
    CActiveScheduler::Add(this);
}

/**
 * To do 2nd phase construction for this object.
 *
 * @exception If the method is not able to allocate necessary buffers.
 */
void CRestoreConvertMIDlet::ConstructL()
{
    iConvertServer = new(ELeave) RestoreServer();
    iMIDletName    = HBufC::NewL(KMaxBufferSize);
    iMIDletVendor  = HBufC::NewL(KMaxBufferSize);
}

/**
 * Deletes this object.
 * All allocated resources are released.
 */
CRestoreConvertMIDlet::~CRestoreConvertMIDlet()
{
    Cancel();
    iInstallFiles.ResetAndDestroy();
    iInstallFiles.Close();

    iUninstallUids.clear();

    iDirs.ResetAndDestroy();
    iDirs.Close();

    iIsJad.Reset();

    delete iMIDletName;
    iMIDletName = NULL;

    delete iMIDletVendor;
    iMIDletVendor = NULL;

    delete iConvertServer;
    iConvertServer = NULL;
}

/**
 * To start silent installation.
 */
void CRestoreConvertMIDlet::Start()
{
    iState = EFindOutDeviceDrives;
    CompleteRequest();
}

/**
 * To stop whole preinstaller.
 * Stops the active scheduler.
 */
void CRestoreConvertMIDlet::Exit()
{
    Deque();
    CActiveScheduler::Stop();
}

/**
 * To complete the request for this object.
 *
 * @Postconditions The following conditions are true immediately after
 * returning from this method.
 * - iStatus == KErrNone
 * - IsActive() == ETrue
 */
void CRestoreConvertMIDlet::CompleteRequest()
{
    TRequestStatus *status = &iStatus;
    User::RequestComplete(status, KErrNone);
    if (!IsActive())
    {
        SetActive();
    }
}

/**
 * To run this active object.
 *
 * The state logic is:
 *
 * EFindOutDeviceDrives:
 *    - find out all non-remote drives in the device
 *    - mark externally mountable drives (removable drives)
 *    - mark read only drives (ROM)
 *
 * EGetDriveToBeScanned:
 *    - the drive to be scanned has been given as command line argument,
 *      store it to iDrive
 *
 * EAppsInInstalledDirectories:
 *    - go through all directories in iDirs one by one
 *      and find all java applications in the subdirectories of the directory
 *    - if only .jar/.dcf/.dm file is found it is added to iInstallFiles
 *    - if .jad file is found, it is added to iInstallFiles
 *
 * EUninstallFromOMJ:
 *   - uninstall all OMJ java applications in drive iDrive using Java Installer
 *     command line options 'uninstallall -drive=<iDrive>'
 *   - then for each java application in iInstallFiles try to find it from
 *     Java Storage based on MIDlet suite name and vendor name. If the MIDlet
 *     is still installed to OMJ (to some other drive than iDrive), it must
 *     be uninstalled from OMJ to reset the state of MIDlet (RMS data etc)
 *     to original state. The actual uninstallation is not done in this
 *     state, the uids of the applications to be uninstalled are just added to
 *     iUninstallUids in string format
 *
 * EExecuteRestoreServer:
 *  - give list of uninstall uids and list of install files to restore server
 *  - start restore server in another thread (it will listen to Comms messages)
 *  - start Java Installer in poll mode
 *  - when Java Installer polls for operations to be executed, the restore server
 *    will first ask it to uninstall each Java application in the list of uninstall uids,
 *    then restore server will ask Java Installer to install each .jad or .jar/.dcf/.dm file
 *    in the list of install files.
 *    Restore server will log whether each uninstallation / installation succeeds.
 *  - after all uninstallations and installations have been done, restore server will ask
 *    Java Installer to exit.
 *  - after that this active object moves to next state.
 *
 * EExit: free resources and exit
 *
 */
void CRestoreConvertMIDlet::RunL()
{
    switch (iState)
    {
    case EFindOutDeviceDrives:
    {
        LOG(EJavaConverters, EInfo,
            "CRestoreConvertMIDlet::RunL EFindOutDeviceDrives");
        GetAllDeviceDrivesL();
        iState = EGetDriveToBeScanned;
        CompleteRequest();
    }
    break;

    case EGetDriveToBeScanned:
    {
        LOG(EJavaConverters, EInfo,
            "CRestoreConvertMIDlet::RunL EGetDriveToBeScanned");

        HBufC *pBufCmdLine = HBufC::NewLC(User::CommandLineLength() + 10);
        TPtr args = pBufCmdLine->Des();
        User::CommandLine(args);
        LOG1WSTR(EJavaConverters, EInfo,
                 "Command line arguments of javarestoreconverter.exe are : %s",
                 (wchar_t *)(args.PtrZ()));

        // Check which drive is to be restored now from command line.
        TDriveNumber drive = EDriveC;
        TInt idx = args.Find(KDriveText());
        if (KErrNotFound == idx)
        {
            LOG1WSTR(EJavaConverters, EError,
                     "no -drive<X> argument in javarestoreconveter.exe command line (%s)",
                     (wchar_t *)(args.PtrZ()));
            User::Leave(KErrNotFound);
        }

        TChar driveChar = args[idx + KDriveText().Length()];
        char argLetter = args[idx + KDriveText().Length()];
        if (argLetter >= 'A' && argLetter <= 'J')
        {
            drive = (TDriveNumber)(argLetter - 'A');
        }
        else if (argLetter >= 'a' && argLetter <= 'j')
        {
            drive = (TDriveNumber)(argLetter - 'a');
        }
        else
        {
            User::Leave(KErrArgument);
        }
        CleanupStack::PopAndDestroy(pBufCmdLine);
        iDrive = drive;

        LOG1(EJavaConverters, EInfo, "Drive is %d", drive);

        // forget old search directories
        iDirs.ResetAndDestroy();

        // The specified drive will be scanned for
        // java applications to be converted
        if (iDriveStatuses[drive] & DriveInfo::EDrivePresent)
        {
            (void)iFs.DriveToChar(drive, driveChar);

            // The base Java application install directory in this
            // drive must be scanned.
            // Reserve memory also for drive letter and terminating zero
            // for logging.
            HBufC *baseInstallDir = HBufC::NewLC(KMidletSuiteInstallBasePath().Length() + 2);

            TPtr dirPtr(baseInstallDir->Des());
            dirPtr.Append(driveChar);
            dirPtr.Append(KMidletSuiteInstallBasePath());

            // Add new search directory
            iDirs.AppendL(baseInstallDir);
            CleanupStack::Pop(baseInstallDir);
        }
        else
        {
            // Restoring MIDlets to drive X but drive X is not present
            ELOG1(EJavaConverters,
                  "javarestoreconverter.exe: Drive number %d is not present", drive);
            User::Leave(KErrDisMounted);
        }

        iState = EAppsInInstalledDirectories;
        CompleteRequest();
    }
    break;

    case EAppsInInstalledDirectories:
    {
        LOG(EJavaConverters, EInfo,
            "CRestoreConvertMIDlet::RunL EAppsInInstalledDirectories");

        GetInstallFilesL(iDirs);


        iState = EUninstallFromOMJ;
        CompleteRequest();
    }
    break;

    case EUninstallFromOMJ:
    {
        LOG(EJavaConverters, EInfo,
            "CRestoreConvertMIDlet::RunL EUninstallFromOMJ");

        // Uninstall all Java applications in current drive
        UninstallAllFromDriveL(iDrive);

        // Check all Jad / Jar files in iInstallFiles and
        // add the Uids of those MIDlets still installed into OMJ
        // to iUninstallUids
        FindRemainingMIDletsToBeUninstalledL();

        iState = EExecuteRestoreServer;
        CompleteRequest();
    }
    break;

    case EExecuteRestoreServer:
    {
        LOG(EJavaConverters, EInfo,
            "CRestoreConvertMIDlet::RunL EExecuteRestoreServer");

        if (iNumberOfAppsToInstall > 0)
        {
            // Uninstall all MIDlet suites in iUninstallUids and then
            // install all MIDlet suites in iInstallFiles.

            // Tell RestoreServer what should be uninstalled and what
            // should be converted
            iConvertServer->setOperations(iInstallFiles, iUninstallUids);

            // Start the server
            int err = iConvertServer->start();
            if (0 != err)
            {
                // server cannot be started
                ELOG1(EJavaConverters,
                      "Cannot start convert server, err %d", err);
            }

            // Starts Java Installer, waits until Java Installer exits
            RunJavaInstallerL();

            iState = EExit;
        }
        else
        {
            iState = EExit;
            CompleteRequest();
        }
    }
    break;

    case EExit:
    {
        LOG(EJavaConverters, EInfo, "CRestoreConvertMIDlet::RunL EExit");

        // Stops the server if it is running
        iConvertServer->stop();

        FullCleanup();

        // The whole javaappconverter process is stopped.
        Exit();
    }
    break;
    }
}

/**
 * To handle leave from RunL.
 * This method exits this active object using normal state machine
 * After calling this method this active object will exit.
 *
 * @param aError - A reason of error.
 * @return KErrNone.
 */
TInt CRestoreConvertMIDlet::RunError(TInt aError)
{
    ELOG2(EJavaConverters,
          "CRestoreConvertMIDlet::RunError(%d) from state %d", aError, iState);

    Cancel();

    iState = EExit;
    CompleteRequest();

    return KErrNone;
}

/**
 * To do cancelling for this object.
 *
 */
void CRestoreConvertMIDlet::DoCancel()
{
    ELOG1(EJavaConverters,
          "CRestoreConvertMIDlet::DoCancel from state %d", iState);

    // Check whether restore server must be stopped
    if (iState == EExecuteRestoreServer)
    {
        // Stops the server if it is running
        iConvertServer->stop();
    }
}

/**
 * Add .jad files in the directories specified in aDirs to iInstallFiles.
 * If a directory does not contain a .jad file but it contains a .jar/.dcf/.dm file,
 * add that file to iInstallFiles
 *
 * @param aDirs - An array of directories to be scanned.
 * @exception Unable to alloc memory for the internal buffers.
 * @exception Unable to get directory's contents.
 */
void CRestoreConvertMIDlet::GetInstallFilesL(RPointerArray<HBufC>& aDirs)
{
    // Number of MIDlet installation base directories to scan,
    // one directory per local drive
    const TInt nBaseDirs = aDirs.Count();

    for (TInt i = 0; i < nBaseDirs; i++)
    {
        TFileName suitePath;

        // Form base directory path, MIDlets have been installed
        // to subdirectories of this directory
        const TPtrC& dir = ((HBufC *)(aDirs[i]))->Des();
        TFileName baseSuitePath = dir;

        CDir *entryList;
        CDir *dirList;
        TInt err = iFs.GetDir(baseSuitePath, KEntryAttMatchMask, ESortNone, entryList, dirList);
        if (KErrNone != err)
        {
            WLOG1WSTR(EJavaConverters,
                      "CRestoreConvertMIDlet::GetInstallFilesL Cannot list directory %s",
                      (wchar_t *)(baseSuitePath.PtrZ()));

            // If no S60 Java application has been installed to the drive,
            // the directory does not exist but it is OK
            if (KErrPathNotFound != err)
            {
                User::Leave(err);
            }
            continue;
        }

        // Only midlet installation directory entries are meaningfull,
        // discard other possible entries
        delete entryList;

        TInt   nCount = dirList->Count();
        TEntry dirEntry;

        for (TInt nInd = 0; nInd < nCount; nInd++)
        {
            dirEntry = (*dirList)[nInd];
            // Just to make sure this really is subdirectory
            if (dirEntry.IsDir())
            {
                // Is this midlet suite subdir?
                // Midlet suite subdirs look like this "[102b567B]"
                // Must parse the value of the Uid as unsigned int to avoid
                // overflow
                TUint32 nUid;
                TLex   lexer(dirEntry.iName);
                lexer.Inc();
                err = lexer.Val(nUid, EHex);
                if (KErrNone != err)
                {
                    // Not midlet suite subdir, skip it
                    continue;
                }

                // Does the subdirectory contain any Jar files?
                suitePath = baseSuitePath;
                suitePath.Append(dirEntry.iName);
                suitePath.Append(KSMCBackSplash);

                CDir *suiteDirEntryList;
                err = iFs.GetDir(suitePath, KEntryAttMatchMask, ESortNone, suiteDirEntryList);
                if (KErrNone != err)
                {
                    LOG1WSTR(EJavaConverters, EError,
                             "CRestoreConvertMIDlet::GetInstallFilesL Cannot list content of suite dir %s",
                             (wchar_t *)(suitePath.PtrZ()));
                    User::Leave(err);
                    return;
                }
                // If there is .jad or Jar file in suiteDirEntryList, adds
                // it to iInstallFiles.
                // Recognizes also DRM protected Jar files (.dm, .dcf)
                AddJadJarToInstallFilesL(suitePath, suiteDirEntryList);
                delete suiteDirEntryList;
            }
        } // for - loop all directory entries in a base installation directories
        delete dirList;

    } // for - loop all base installation directories

    if (iInstallFiles.Count() == 0)
    {
        WLOG(EJavaConverters,
             "CRestoreConvertMIDlet::GetInstallFilesL No MIDlets to convert");
    }

    // how many applications to restore
    iNumberOfAppsToInstall = iInstallFiles.Count();
}

/**
 *  For each java application in iInstallFiles try to find it from
 *  Java Storage based on MIDlet suite name and vendor name. If the MIDlet
 *  is still installed to OMJ (to some other drive than iDrive), it must
 *  be uninstalled from OMJ to reset the state of MIDlet (RMS data etc)
 *  to original state. Add the uids of the applications to be uninstalled
 *  to iUninstallUids in string format
 */
void CRestoreConvertMIDlet::FindRemainingMIDletsToBeUninstalledL()
{
    TBool namesParsed;
    for (TInt nInd = 0; nInd < iNumberOfAppsToInstall; nInd++)
    {
        // Get the MIDlet-Name and MIDlet-Vendor from iInstallFiles[nInd]
        if (iIsJad[nInd] == 1)
        {
            namesParsed = ParseJadL(*iInstallFiles[nInd]);
        }
        else
        {
            // If file is .jar file, get the names from Manifest
            namesParsed = ParseJarL(*iInstallFiles[nInd]);
        }

        // Search for the MIDlet suite from Java Storage table APPLICATION_PACKAGE_TABLE
        // If it is found, add the suite Uid in string format to iUninstallUids
        if (namesParsed)
        {
            TPtr suite = iMIDletName->Des();
            TPtr vendor = iMIDletVendor->Des();
            std::wstring suiteUid = IsSuiteInstalled(suite, vendor);
            if (!suiteUid.empty())
            {
                iUninstallUids.push_back(suiteUid);
            }
        }
    }
}

/**
 * Parse MIDlet-Name and MIDlet-Vendor parameters from JAD file.
 * Parameters are stored to iMIDletName and iMIDletVendor
 *
 * @param ETrue if parsing succeeds otherwise EFalse.
 */
TBool CRestoreConvertMIDlet::ParseJadL(const TDesC& aJadFileName)
{
    HBufC *jadContent = NULL;
    // Trap leave thrown if reading jad content fails
    TRAPD(err, jadContent = GetJadContentL(aJadFileName));
    if (KErrNone != err)
    {
        ELOG1(EJavaConverters,
              "CRestoreConvertMIDlet::ParseJadL Reading Jad content failed, error %d",
              err);
        return EFalse;
    }
    CleanupStack::PushL(jadContent);

    HBufC *midletName = ParseAttribute(jadContent, KMidletName);
    if (NULL == midletName)
    {
        ELOG(EJavaConverters,
             "CRestoreConvertMIDlet::ParseJadL Parsing midlet name failed.");
        CleanupStack::PopAndDestroy(jadContent);
        return EFalse;
    }
    // store midlet name to member variable and log it
    TPtr namePtr(iMIDletName->Des());
    namePtr.Copy(*midletName);
    LOG1WSTR(EJavaConverters, EInfo,
             "CRestoreConvertMIDlet::ParseJadL MIDlet-Name %s",
             (wchar_t *)(namePtr.PtrZ()));
    delete midletName;

    HBufC *midletVendor = ParseAttribute(jadContent, KMidletVendor);
    if (NULL == midletVendor)
    {
        ELOG(EJavaConverters,
             "CRestoreConvertMIDlet::ParseJadL Parsing midlet vendor failed.");
        CleanupStack::PopAndDestroy(jadContent);
        return EFalse;
    }
    // store midlet vendor to member variable and log it
    TPtr vendorPtr(iMIDletVendor->Des());
    vendorPtr.Copy(*midletVendor);
    LOG1WSTR(EJavaConverters, EInfo,
             "CRestoreConvertMIDlet::ParseJadL MIDlet-Vendor %s",
             (wchar_t *)(vendorPtr.PtrZ()));
    delete midletVendor;

    CleanupStack::PopAndDestroy(jadContent);
    return ETrue;
}


/**
 * Reads the whole content of the Jad file and returns it in
 * buffer in Symbian Unicode character set.
 * @param[in] aJadFile
 * @return pointer to HBufC that contains the Jad file,
 * ownership is transferred to caller
 * @exception If jad file content cannot be read
 */
HBufC *CRestoreConvertMIDlet::GetJadContentL(const TDesC& aJadFileName)
{
    RFile jadFile;
    TInt err = jadFile.Open(iFs, aJadFileName, EFileRead);
    User::LeaveIfError(err);
    CleanupClosePushL(jadFile);

    // Reserve buffer for Jad in UTF-8 char set
    TInt jadSize = 0;
    err = jadFile.Size(jadSize);
    User::LeaveIfError(err);
    HBufC8 *bufUtf8Jad  = HBufC8::NewL(jadSize);
    CleanupStack::PushL(bufUtf8Jad);

    // Read the content in Utf8 char set
    TPtr8 tmpPtr(bufUtf8Jad->Des());
    err = jadFile.Read(tmpPtr, jadSize);
    User::LeaveIfError(err);

    // Convert to Unicode
    HBufC *bufUnicodeJad =
        CnvUtfConverter::ConvertToUnicodeFromUtf8L(*bufUtf8Jad);

    CleanupStack::PopAndDestroy(bufUtf8Jad);
    CleanupStack::PopAndDestroy(&jadFile);

    // Return to caller
    return bufUnicodeJad;
}


/**
 * Parse MIDlet-Name and MIDlet-Vendor parameters from Manifest inside
 * a .jar file.
 * Parameters are stored to iMIDletName and iMIDletVendor
 *
 * @param ETrue if parsing succeeds otherwise EFalse.
 */
TBool CRestoreConvertMIDlet::ParseJarL(const TDesC& aJarFileName)
{
    HBufC *manifestContent = NULL;
    // Trap leave thrown if reading Manifest content fails
    TRAPD(err, manifestContent = GetManifestContentL(aJarFileName));
    if (KErrNone != err)
    {
        ELOG1(EJavaConverters,
              "CRestoreConvertMIDlet::ParseJarL Reading Manifest failed, error %d",
              err);
        return EFalse;
    }
    CleanupStack::PushL(manifestContent);

    HBufC *midletName = ParseAttribute(manifestContent, KMidletName);
    if (NULL == midletName)
    {
        ELOG(EJavaConverters,
             "CRestoreConvertMIDlet::ParseJarL Parsing midlet name failed.");
        CleanupStack::PopAndDestroy(manifestContent);
        return EFalse;
    }
    // store midlet name to member variable and log it
    TPtr namePtr(iMIDletName->Des());
    namePtr.Copy(*midletName);
    LOG1WSTR(EJavaConverters, EInfo,
             "CRestoreConvertMIDlet::ParseJarL MIDlet-Name %s",
             (wchar_t *)(namePtr.PtrZ()));
    delete midletName;

    HBufC *midletVendor = ParseAttribute(manifestContent, KMidletVendor);
    if (NULL == midletVendor)
    {
        ELOG(EJavaConverters,
             "CRestoreConvertMIDlet::ParseJarL Parsing midlet vendor failed.");
        CleanupStack::PopAndDestroy(manifestContent);
        return EFalse;
    }
    // store midlet vendor to member variable and log it
    TPtr vendorPtr(iMIDletVendor->Des());
    vendorPtr.Copy(*midletVendor);
    LOG1WSTR(EJavaConverters, EInfo,
             "CRestoreConvertMIDlet::ParseJarL MIDlet-Vendor %s",
             (wchar_t *)(vendorPtr.PtrZ()));
    delete midletVendor;

    CleanupStack::PopAndDestroy(manifestContent);
    return ETrue;
}


/**
 * Reads the whole content of the Manifest inside Jar file
 * and returns it in buffer in Symbian Unicode character set.
 * @param[in] aJarFile
 * @return pointer to HBufC that contains the Manifest,
 * ownership is transferred to caller
 * @exception If Manifest content cannot be read
 */
HBufC *CRestoreConvertMIDlet::GetManifestContentL(const TDesC& aJarFileName)
{
    CZipFile* zipFile = CZipFile::NewL(iFs, aJarFileName);
    CleanupStack::PushL(zipFile);

    // Seek manifest file
    CZipFileMember* zippedFile =
        zipFile->CaseSensitiveOrCaseInsensitiveMemberL(KManifestEntryName());
    if (!zippedFile)
    {
        ELOG(EJavaConverters,
             "CRestoreConvertMIDlet::GetManifestContentL Package is missing manifest");
        User::Leave(KErrNotFound);
    }
    CleanupStack::PushL(zippedFile);

    TUint uncompressedSize = zippedFile->UncompressedSize();
    if ((TUint)uncompressedSize >= (KMaxTInt/2))
    {
        ELOG(EJavaConverters, "CRestoreConvertMIDlet::GetManifestContentL Invalid manifest");
        User::Leave(KErrCorrupt);
    }

    // Buffer to read Manifest into
    HBufC8* resultData = HBufC8::NewLC(uncompressedSize);

    RZipFileMemberReaderStream* zippedStream = 0;
    TInt err = zipFile->GetInputStreamL(zippedFile, zippedStream);
    User::LeaveIfError(err);
    CleanupStack::PushL(zippedStream);

    TPtr8 ptr(resultData->Des());
    User::LeaveIfError(zippedStream->Read(ptr, uncompressedSize));

    CleanupStack::PopAndDestroy(zippedStream);

    // Manifest buffer when converted to UCS-2
    HBufC *manifestContent = HBufC16::NewL(uncompressedSize);
    TPtr16 ucsPtr(manifestContent->Des());
    err = CnvUtfConverter::ConvertToUnicodeFromUtf8(ucsPtr, ptr);
    if (KErrNone != err)
    {
        ELOG1(EJavaConverters,
              "CRestoreConvertMIDlet::GetManifestContentL UTF-8 to unicode"
              " conversion failed: %d", err);
        User::Leave(err);
    }
    CleanupStack::PopAndDestroy(resultData);
    CleanupStack::PopAndDestroy(zippedFile);
    CleanupStack::PopAndDestroy(zipFile);

    // Return to caller
    return manifestContent;
}


std::wstring CRestoreConvertMIDlet::IsSuiteInstalled(TPtr& aSuiteName, TPtr& aVendorName)
{
    std::wstring suiteName(desToWstring(aSuiteName));
    std::wstring vendorName(desToWstring(aVendorName));
    std::wstring suiteUid;

    // Find application uid based on names from Java Storage
    JavaStorage *js = JavaStorage::createInstance();

    try
    {
        js->open(JAVA_DATABASE_NAME);

        JavaStorageEntry attribute;
        JavaStorageApplicationEntry_t findPattern;
        JavaStorageApplicationList_t  foundEntries;

        // Get ID from APPLICATION_PACKAGE_TABLE  based on PACKAGE_NAME and VENDOR
        attribute.setEntry(PACKAGE_NAME, suiteName);
        findPattern.insert(attribute);
        attribute.setEntry(VENDOR, vendorName);
        findPattern.insert(attribute);
        attribute.setEntry(ID, L"");
        findPattern.insert(attribute);

        js->search(APPLICATION_PACKAGE_TABLE , findPattern, foundEntries);

        // Anything found?
        if (foundEntries.size() > 0)
        {
            // The application package has been found, get the ID of the package
            suiteUid = foundEntries.front().begin()->entryValue();
            LOG1WSTR(EJavaConverters, EInfo,
                     "CRestoreConvertMIDlet::IsSuiteInstalled: Found suite by name. Uid is %s",
                     suiteUid.c_str());
        }
    }
    catch (JavaStorageException& e)
    {
        ELOG1(EJavaConverters,
              "CRestoreConvertMIDlet::IsSuiteInstalled: Java Storage exception %s", e.what());
    }

    try
    {
        js->close();
    }
    catch (JavaStorageException& e2)
    {
        WLOG1(EJavaConverters,
              "CRestoreConvertMIDlet::IsSuiteInstalled: Java Storage exception "
              "when closing storage %s", e2.what());
    }

    delete js;

    // suiteUid is empty if suite was not found
    return suiteUid;
}


/**
 * Finds the java attribute specified by aAttributeName
 * from aBuf and returns the value of that attribute
 * in HBufC.
 * @param[in] aBuf contents of .jad / Manifest file
 * @param[in] aAttributeName the name of a java attribute
 * @return the value of the attribute. Caller gets the ownership of the
 * returned HBufC.
 * If the attribute is not found, returns NULL
 */
HBufC *CRestoreConvertMIDlet::ParseAttribute(const HBufC *aBuf, const TDesC& aAttributeName)
{
    TInt    nInd(0);
    TBool   fullNameFound(EFalse);
    TUint32 ch;

    // Start parsing from the beginning
    TPtrC parsePtr = aBuf->Mid(nInd);

    do
    {
        // Find attribute name
        nInd = parsePtr.Find(aAttributeName);
        if (nInd < 0)
        {
            // Returns NULL if the attribute cannot be found
            return NULL;
        }

        // Check that the attribute name was preceded by line break or
        // it was at the beginning
        if (nInd == 0)
        {
            fullNameFound = ETrue;
        }
        else
        {
            ch = parsePtr[nInd-1];
            if ((ch == CR) || (ch == LF))
            {
                fullNameFound = ETrue;
            }
            else
            {
                // Name was just a part of longer string (not 'word match')
                fullNameFound = EFalse;
                // Skip to the last character of the found match.
                // We can skip because we are insterested only in 'word' matches
                // so the next cannot start inside the area we are skipping now.
                parsePtr.Set(parsePtr.Mid(nInd + aAttributeName.Length() - 1));
                continue;
            }
        }

        // Check whether buffer ends after attribute name
        if (nInd + aAttributeName.Length() >= parsePtr.Length())
        {
            // Buffer ends immediately after the found
            // attribute name instance. No attribute value
            return NULL;
        }

        // Check that there is a white space character or colon after
        // attribute name
        ch = parsePtr[nInd + aAttributeName.Length()];
        if ((ch == COLON) || (ch == SP) || (ch == HT))
        {
            fullNameFound = ETrue;
        }
        else
        {
            // Name was just a part of longer string (not 'word match')
            fullNameFound = EFalse;
            // Skip to the next character after the found match
            parsePtr.Set(parsePtr.Mid(nInd + aAttributeName.Length()));
            continue;
        }
    }
    while (!fullNameFound);

    // Skip to the end of the attribute name and find ':' after the name.
    // The skipped characters must be white space chacraters, otherwise
    // the attribute name is illegal and Java Installer will not accept
    // the Jad file / Manifest.
    parsePtr.Set(parsePtr.Mid(nInd + aAttributeName.Length() - 1));
    nInd = parsePtr.Locate(COLON);
    if (nInd < 0)
    {
        return NULL;
    }
    nInd++;

    // Parse attribute value (CR or LF ends)
    TInt nEndInd = parsePtr.Locate(CR);
    TInt nTmpInd = parsePtr.Locate(LF);

    if (KErrNotFound == nEndInd)
    {
        nEndInd = parsePtr.Length() - 1;
    }
    if (KErrNotFound == nTmpInd)
    {
        nTmpInd = parsePtr.Length() - 1;
    }

    if (nTmpInd < nEndInd)
    {
        nEndInd = nTmpInd;
    }

    if (nEndInd < nInd)
    {
        return NULL;
    }

    TPtrC attributeValue = parsePtr.Mid(nInd, (nEndInd - nInd));

    // Remove possible white space from the beginning and end of the value
    HBufC *bufValue = attributeValue.Alloc();
    if (NULL == bufValue)
    {
        return NULL;
    }
    TPtr value = bufValue->Des();
    value.Trim();

    return bufValue;
} // parseAttribute


/**
 * Uninstall all Java 2.x MIDlets from drive given in aDrive
 *
 * @param aDrive uninstallation drive
 */
void CRestoreConvertMIDlet::UninstallAllFromDriveL(TDriveNumber &aDrive)
{
    // Execute Java Installer with uninstallall -drive=X command line options

    LOG1(EJavaConverters, EInfo,
         "CRestoreConvertMIDlet::UninstallAllFromDriveL Going to uninstall "
         "MIDlets from drive number %d", aDrive);

    RProcess rJavaInstaller;
    TBuf<256> commandLine;

    // Build command line used to pass all necessary info to Java Installer
    commandLine = _L("javainstallerstarter");
    commandLine.Append(_L(" uninstallall -forceuninstall -captainmsgs=no"));

    // Run installer silently, do not send uninstall notification messages
    commandLine.Append(_L(" -silent -skipotastatus -drive="));

    // Add drive letter
    commandLine.Append((TChar)('A' + aDrive));

    // start JavaInstaller
#ifdef RD_JAVA_S60_RELEASE_5_0_IAD
    TBuf<128> processName = _L("j9midps60");
#else // RD_JAVA_S60_RELEASE_5_0_IAD
    TBuf<128> processName = _L("javamidp");
#endif // RD_JAVA_S60_RELEASE_5_0_IAD

    TInt err = rJavaInstaller.Create(processName, commandLine);
    if (KErrNone == err)
    {
        LOG(EJavaConverters, EInfo, "CRestoreConvertMIDlet::UninstallAllFromDriveL calling Rendezvous");
        // This call will wait until Java Installer exits (or panics)
        TRequestStatus status;
        rJavaInstaller.Logon(status);

        LOG(EJavaConverters, EInfo, "CRestoreConvertMIDlet::UninstallAllFromDriveL calling Resume");
        rJavaInstaller.Resume();

        // now wait until Java Installer exits
        User::WaitForRequest(status);
        if (status.Int() != KErrNone)
        {
            ELOG1(EJavaConverters,
                  "CRestoreConvertMIDlet::UninstallAllFromDriveL Installer exited with error %d",
                  status.Int());
        }
    }
    else
    {
        ELOG1(EJavaConverters,
              "CRestoreConvertMIDlet::UninstallAllFromDriveL Cannot start Installer, error %d", err);
    }

    LOG(EJavaConverters, EInfo, "CRestoreConvertMIDlet::UninstallAllFromDriveL calling RProcess::Close");
    // free resources before returning
    rJavaInstaller.Close();
}


/**
 * Scan the content of one directory entry and add the name of
 * .jad / .jar /.dcf/.dm file to iInstallFiles if
 * the directory contains a valid, installed Java application.
 * Recognizes also DRM protected jar files (.dm, .dcf)
 *
 * @param aSuitePathName -  directory to be scanned.
 * @param aSuiteDirEntryList - contents of the directory
 * @exception Unable to alloc memory for the internal buffers.
 */
void CRestoreConvertMIDlet::AddJadJarToInstallFilesL(
    const TFileName &aSuitePathName,
    const CDir *aSuiteDirEntryList)
{
    if (NULL == aSuiteDirEntryList)
    {
        return;
    }

    TInt nCount = aSuiteDirEntryList->Count();
    if (0 == nCount)
    {
        return;
    }

    TInt        suffixPos;
    TEntry      dirEntry;
    TBool       jarFileInSuiteDir = EFalse;
    TBool       jadFileInSuiteDir = EFalse;
    TFileName   jadFullPathName;
    TFileName   jarFullPathName;

    for (TInt nInd = 0; nInd < nCount; nInd++)
    {
        dirEntry = (*aSuiteDirEntryList)[nInd];
        // Directory cannot be Jar file.
        // Empty file cannot valid Jar file
        if (dirEntry.IsDir() || (dirEntry.iSize == 0))
        {
            continue;
        }
        // get the suffix of the name
        suffixPos = dirEntry.iName.LocateReverse('.');
        if (suffixPos == KErrNotFound)
        {
            // File name does not contain '.' char
            continue;
        }
        TPtrC suffix(dirEntry.iName.Mid(suffixPos));

        // if the name ends with ".jar" the name is current candidate
        // for the name to be added to iInstallFiles list
        if (suffix.CompareF(KJarFileNameSuffix) == 0)
        {
            jarFullPathName = aSuitePathName;
            jarFullPathName.Append(dirEntry.iName);
            jarFileInSuiteDir = ETrue;
        }
        else if (suffix.CompareF(KJadFileNameSuffix) == 0)
        {
            // If .jad file is found, then it will be added
            // to iInstallFiles list
            jadFullPathName = aSuitePathName;
            jadFullPathName.Append(dirEntry.iName);
            jadFileInSuiteDir = ETrue;
        }
        else if (suffix.CompareF(KFLJarFileNameSuffix) == 0)
        {
            // forward locked and combined delivery DRM protected
            // .jar files have suffix ".dm"
            jarFullPathName = aSuitePathName;
            jarFullPathName.Append(dirEntry.iName);
            jarFileInSuiteDir = ETrue;
        }
        else if (suffix.CompareF(KSDJarFileNameSuffix) == 0)
        {
            // separate delivery DRM protected .jar files have suffix ".dcf"
            jarFullPathName = aSuitePathName;
            jarFullPathName.Append(dirEntry.iName);
            jarFileInSuiteDir = ETrue;
        }
    }

    // If directory contains a Jar file, then add something to iInstallFiles
    if (jarFileInSuiteDir)
    {
        // If directory contains also .jad file, add .jad file name to iInstallFiles
        if (jadFileInSuiteDir)
        {
            // Reserve one char for null terminator
            HBufC* path = HBufC::NewLC(jadFullPathName.Length() + 1);
            TPtr pathPtr(path->Des());
            pathPtr.Append(jadFullPathName);

            LOG1WSTR(EJavaConverters, EInfo,
                     "CRestoreConvertMIDlet::AddJadJarToInstallFilesL Adding jad file %s",
                     (wchar_t *)(pathPtr.PtrZ()));
            iInstallFiles.Append(path);
            CleanupStack::Pop(path);
            iIsJad.AppendL(1);
        }
        else
        {
            // Reserve one char for null terminator
            HBufC* path = HBufC::NewLC(jarFullPathName.Length() + 1);
            TPtr pathPtr(path->Des());
            pathPtr.Append(jarFullPathName);

            LOG1WSTR(EJavaConverters, EInfo,
                     "CRestoreConvertMIDlet::AddJadJarToInstallFilesL Adding jar file %s",
                     (wchar_t *)(pathPtr.PtrZ()));
            iInstallFiles.Append(path);
            CleanupStack::Pop(path);
            iIsJad.AppendL(0);
        }
    }
}

/**
 * Start Java Installer in poll mode and then wait until it exits.
 */
void CRestoreConvertMIDlet::RunJavaInstallerL()
{
    LOG(EJavaConverters, EInfo, "CRestoreConvertMIDlet::RunJavaInstaller");

    RProcess rJavaInstaller;
    TFileName fileName;
    TInt err;
    // Max one path name, user name and password and some options ->
    // 1536 is enough
    TBuf<1536> commandLine;

    // Build command line used to pass all necessary info to Java Installer
    std::auto_ptr<HBufC> installerStarterDll(
        stringToDes(java::runtime::JAVA_INSTALLER_STARTER_DLL));
    commandLine = installerStarterDll->Des();
    commandLine.Append(_L(" poll -address=convert"));

    // Run installer silently
    commandLine.Append(_L(" -silent -skipotastatus -silentconversion"));

    // Convert old S60 applications so that applications uids,
    // private data and RMS data are all preserved
    commandLine.Append(_L(" -convert=yes"));

    // Upgrading MIDlets is allowed
    commandLine.Append(_L(" -upgrade=yes"));

    // No OCSP checks for converted MIDlets
    commandLine.Append(_L(" -ocsp=no"));

    // Allow upgrade even if version number has not increased
    commandLine.Append(_L(" -overwrite=yes"));

    // Downloading .jar is not allowed.
    commandLine.Append(_L(" -download=no"));

    // If upgrade install, automatically upgrade also the data
    commandLine.Append(_L(" -upgrade_data=yes"));

    // MIDlets must be restored to the original drive
    if (iDrive > -1)
    {
        TChar targetDrive;
        err = RFs::DriveToChar(iDrive, targetDrive);
        if (KErrNone == err)
        {
            commandLine.Append(_L(" -drive="));
            commandLine.Append(targetDrive);
        }
    }

    // start JavaInstaller
    std::auto_ptr<HBufC> installerProcess(
        stringToDes(java::runtime::JAVA_PROCESS));
    err = rJavaInstaller.Create(installerProcess->Des(), commandLine);
    if (KErrNone == err)
    {
        LOG(EJavaConverters, EInfo, "CRestoreConvertMIDlet::RunJavaInstaller calling Rendezvous");
        // This call will wait until Java Installer exits (or panics)
        rJavaInstaller.Logon(iStatus);

        LOG(EJavaConverters, EInfo, "CRestoreConvertMIDlet::RunJavaInstaller calling Resume");
        rJavaInstaller.Resume();
    }
    else
    {
        ELOG1(EJavaConverters,
              "CRestoreConvertMIDlet::RunJavaInstaller Cannot start Installer, error %d", err);
        // CActive will trap the following leave, execution will go to RunError
        User::Leave(err);
    }

    LOG(EJavaConverters, EInfo, "CRestoreConvertMIDlet::RunJavaInstaller calling RProcess::Close");
    // free resources before returning
    rJavaInstaller.Close();

    // now wait until Java Installer exits
    SetActive();
}

/**
 * To cleanup member variables.
 */
void CRestoreConvertMIDlet::FullCleanup()
{
    iDirs.ResetAndDestroy();
    iInstallFiles.ResetAndDestroy();
    iUninstallUids.clear();
    iIsJad.Reset();
}


/**
 * Checks all local drives in the device and stores the DriveInfo API drive
 * status information for each drive to iDriveStatuses
 * @exception Cannot get drive list.
 */
void CRestoreConvertMIDlet::GetAllDeviceDrivesL()
{
    TDriveList driveList;
    // get all drives
    TInt err = iFs.DriveList(driveList);
    if (KErrNone != err)
    {
        ELOG1(EJavaConverters,
              "CRestoreConvertMIDlet::GetAllDeviceDrives cannot get drive list, err %d", err);
        User::Leave(err);
    }

    // store status of the non-remote, non-substed drives
    TUint  status = 0;
    for (TInt drive = 0; drive < KMaxDrives; drive++)
    {
        iDriveStatuses[drive] = 0;

        if (driveList[drive] == 0)
        {
            // no such drive in this device
            continue;
        }

        err = DriveInfo::GetDriveStatus(iFs, drive, status);
        if (KErrNone != err)
        {
            ELOG2(EJavaConverters,
                  "CRestoreConvertMIDlet::GetAllDeviceDrivesL cannot get drive %d status, err %d",
                  drive, err);
            User::Leave(err);
        }
        // D drive is temporary RAM drive, skip it
        // Drives J to Y are substed or remote drives, skip them
        if ((drive == EDriveD) || ((drive >= EDriveJ) && (drive <= EDriveY)))
        {
            continue;
        }

        iDriveStatuses[drive] = status;
    }
}