/*
* Copyright (c) 2004-2010 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: 
* Implementation of Swi::CUissClientHandler class which is the client-side 
* part of the reverse completion mechanism used by SWI to communicate with 
* the UI
*
*/


/**
 @file
*/
#include "uissclienthandler.h"
#include "uisscommand.h"
#include "uisssession.h"
#include "../source/uiss/server/uissserver.h"
#include "sishelper.h"
#include "sisregistrypackage.h"
#include "writestream.h"
// UI Support Server Commands
#include "commands/installdialog.h"
#include "commands/grantcapabilitiesdialog.h"
#include "commands/languagedialog.h"
#include "commands/applicationsinusedialog.h"
#include "commands/drivedialog.h"
#include "commands/cannotoverwritefiledialog.h"
#include "commands/dependencybreakdialog.h"
#include "commands/deviceincompatibility.h"
#include "commands/missingdependency.h"
#include "commands/errordialog.h"
#include "commands/handlecancellableinstallevent.h"
#include "commands/handleinstallevent.h"
#include "commands/ocspresultdialog.h"
#include "commands/optionsdialog.h"
#include "commands/questiondialog.h"
#include "commands/upgradedialog.h"
#include "commands/uninstalldialog.h"
#include "commands/securitywarningdialog.h"
#include "commands/textdialog.h"
#include "log.h"

#include "cleanuputils.h"
#ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK
#include <usif/sif/sifcommon.h>
const TInt KCompInfoBufferSize=4*1024;
#endif



namespace Swi
{

//Temporary error logging solution.
void LogSwiErrorsToFileL(TInt aErrorCode, const TDesC& aAppName);

//
// A cancel handler
//
class InternalCancelHandler : public MCancelHandler
    {
public:
    InternalCancelHandler(CUissClientHandler& aUissClientHandler);
    void HandleCancel();
private:
    CUissClientHandler& iUissClientHandler;
    };

InternalCancelHandler::InternalCancelHandler(
    CUissClientHandler& aUissClientHandler)
:   iUissClientHandler(aUissClientHandler)
    {
    }

void InternalCancelHandler::HandleCancel()
    {
    iUissClientHandler.CancelOperation();
    }

CUissClientHandler* CUissClientHandler::NewLC(MUiHandler& aUiHandler, TBool aActiveObjectMode)
    {
    CUissClientHandler* self=new(ELeave) CUissClientHandler(aUiHandler, aActiveObjectMode);
    CleanupStack::PushL(self);
    self->ConstructL();
    return self;
    }

CUissClientHandler* CUissClientHandler::NewL(MUiHandler& aUiHandler, TBool aActiveObjectMode)
    {
    CUissClientHandler* self=NewLC(aUiHandler, aActiveObjectMode);
    CleanupStack::Pop(self);
    return self;
    }

CUissClientHandler::CUissClientHandler(MUiHandler& aUiHandler, TBool aActiveObjectMode)
        : CActive(EPriorityStandard),
          iUiHandler(aUiHandler),
          iPtrIntoBuf(0,0),
          iActiveObjectMode(aActiveObjectMode),
          iPtrIntoArgsStream(0,0)
          #ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK
          ,iCompInfoBufPtr(0,0)
          #endif
    {
    if (iActiveObjectMode)
        {
        CActiveScheduler::Add(this);
        }
    }

void CUissClientHandler::WaitForSisHelperShutdown()
    {   
    if(iSisHelper.Handle() > 0)
        {
        TRequestStatus reqStatus;
        iSisHelper.Logon(reqStatus);
        User::WaitForRequest(reqStatus);
        iSisHelper.Close();
        }       
    }
    
CUissClientHandler::~CUissClientHandler()
    {
    //Cancel any outstanding request
    CancelOperation();

    WaitForSisHelperShutdown();
    if (iActiveObjectMode)
        {
        CActive::Cancel(); // Make sure we are cancelled before deletion
        }

    delete iCancelHandler;
    delete iArgsStream;
    iUissSession.Close();
    delete iBuf;
    delete iBufLogger;
    #ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK
    delete iCompInfoBuffer;
    #endif
    }

/**
 * Allocates a buffer for reverse-completion commands. The buffer is going to 
 * be resized in case it is not sufficient for a dialog command.
 */
void CUissClientHandler::ConstructL()
    {
    iCancelHandler=new(ELeave) InternalCancelHandler(*this);
    AllocBufL(KBufSize);// Allocate the initial r/c buffer

	//Logger buffer.
	iBufLogger = KNullDesC8().AllocL();
    User::LeaveIfError(StartUiss()); // Start UISS
    User::LeaveIfError(iUissSession.Connect()); // Connect to UISS
    }

void CUissClientHandler::AllocBufL(TInt aSize)
    {
    HBufC8* buf=HBufC8::NewL(aSize);
    delete iBuf;
    iBuf=buf;
    }

void CUissClientHandler::HandleOverflowL()
    {
    // Reallocate the buffer to the size received in parameter 1
    TInt size=0;
    TPckg<TInt> theSize(size);
    theSize.Copy(iBuf->Left(sizeof(TInt)));
    AllocBufL(size);
    }
    
#ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK
void CUissClientHandler::AllocCompInfoBufL(TInt aSize)
    {
    HBufC8* buf = HBufC8::NewL(aSize);
    delete iCompInfoBuffer;
    iCompInfoBuffer = buf;
    }
#endif

///\short Creates a command handler object for the specified dialog request
CUissCmdHandler* CUissClientHandler::UissCmdHandlerFactoryL(TInt aCommand) const
    {
    switch (aCommand)
        {
        case CUissSession::KMessageApplicationsInUseDialog:
            return new(ELeave) CApplicationsInUseDialogCmdHandler(iUiHandler);
        case CUissSession::KMessageCannotOverwriteFileDialog:
            return new(ELeave) CCannotOverwriteFileDialogCmdHandler(iUiHandler);
        
        case CUissSession::KMessageDependencyBreakDialog:
            return new(ELeave) CDependencyBreakDialogCmdHandler(iUiHandler);
        case CUissSession::KMessageDeviceIncompatibility:
            return new(ELeave) CDeviceIncompatibilityDialogCmdHandler(iUiHandler);
        case CUissSession::KMessageMissingDependency:
            return new(ELeave) CMissingDependencyDialogCmdHandler(iUiHandler);
        case CUissSession::KMessageDriveDialog:
            return new(ELeave) CDriveDialogCmdHandler(iUiHandler);
        case CUissSession::KMessageErrorDialog:
            return new(ELeave) CErrorDialogCmdHandler(iUiHandler);
        case CUissSession::KMessageGrantCapabilitiesDialog:
            return new(ELeave) CGrantCapabilitiesDialogCmdHandler(iUiHandler);  
        case CUissSession::KMessageHandleCancellableInstallEvent:
            return new(ELeave) CHandleCancellableInstallEventCmdHandler(iUiHandler, 
                *iCancelHandler);
        case CUissSession::KMessageHandleInstallEvent:
            return new(ELeave) CHandleInstallEventCmdHandler(iUiHandler);
        case CUissSession::KMessageInstallDialog:
            return new(ELeave) CInstallDialogCmdHandler(iUiHandler);
        case CUissSession::KMessageLanguageDialog:
            return new(ELeave) CLanguageDialogCmdHandler(iUiHandler);
        case CUissSession::KMessageOcspResultDialog:
            return new(ELeave) COcspResultDialogCmdHandler(iUiHandler);
        case CUissSession::KMessageOptionsDialog:
            return new(ELeave) COptionsDialogCmdHandler(iUiHandler);
        case CUissSession::KMessageQuestionDialog:
            return new(ELeave) CQuestionDialogCmdHandler(iUiHandler);
        case CUissSession::KMessageSecurityWarningDialog:
            return new(ELeave) CSecurityWarningDialogCmdHandler(iUiHandler);
        case CUissSession::KMessageUninstallDialog:
            return new(ELeave) CUninstallDialogCmdHandler(iUiHandler);
        
        case CUissSession::KMessageUpgradeDialog:
            return new(ELeave) CUpgradeDialogCmdHandler(iUiHandler);
        
        case CUissSession::KMessageTextDialog:
            return new(ELeave) CTextDialogCmdHandler(iUiHandler);
                    
        default:
            return NULL;
        }
    }

void CUissClientHandler::InitializeArgStreamL(const CInstallPrefs& aInstallPrefs)
    {
    // Stream out install parameters. Cannot do this in UISSCLIENT because 
    // the code is in LAUNCHER which depends on UISSCLIENT.
    delete iArgsStream;
    iArgsStream = 0;
    iArgsStream = CWriteStream::NewL();
    *iArgsStream << aInstallPrefs;
    // Save ptr for args (must persist whilst server is processing)
    iPtrIntoArgsStream.Set(iArgsStream->Ptr()); 
    }
    
void CUissClientHandler::InstallL(const CInstallPrefs& aInstallPrefs, const RArray<TInt>& aDeviceSupportedLanguages, TRequestStatus& aRequestStatus, RThread& aServer)
    {
    iState = KUissClientInstalling;
    iClientStatus = &aRequestStatus;
    
    // Save ptr for data returned by request (must persist whilst server is processing)
    iPtrIntoBuf.Set(iBuf->Des());

    InitializeArgStreamL(aInstallPrefs);
    iArgsStream->Stream().WriteInt32L(aDeviceSupportedLanguages.Count());
    //Streaming set of languages that device supports
    TInt noOfDeviceSupportedLanguages = aDeviceSupportedLanguages.Count();
    for(TInt i=0;i<noOfDeviceSupportedLanguages;i++)
        {
        iArgsStream->Stream().WriteInt32L(aDeviceSupportedLanguages[i]);
        }
    // Save ptr for args (must persist whilst server is processing)
    iPtrIntoArgsStream.Set(iArgsStream->Ptr()); 
        
    // Issue initial request
    iUissSession.Install(iPtrIntoArgsStream, iPtrIntoBuf, iStatus);
    if (iActiveObjectMode)
        {
        SetActive();
        }

    // Update client's TRequestStatus object
    *iClientStatus = KRequestPending;
    iSisHelper = aServer;
    }

#ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK
void CUissClientHandler::GetComponentInfoL(const CInstallPrefs& aInstallPrefs, Usif::CComponentInfo& aComponentInfo, TRequestStatus& aRequestStatus, RThread& aServer)
    {    
    iState = KUissClientGettingCompInfo;
    iClientStatus = &aRequestStatus;
        
    // Store the component info reference to the class reference. So that, the same will be 
    // populated after getting the asynchronous method completed.
    iComponentInfo = &aComponentInfo;
    
    InitializeArgStreamL(aInstallPrefs);
    
    AllocCompInfoBufL(KCompInfoBufferSize); 
    
    // Save the pointer for component info collection buffer
    iCompInfoBufPtr.Set(iCompInfoBuffer->Des());

    // Issue get component info request
    iUissSession.GetComponentInfo(iPtrIntoArgsStream, iCompInfoBufPtr, iStatus);

    // There is no synchronous API for GetComponentInfo
    __ASSERT_ALWAYS(iActiveObjectMode, User::Invariant());
    SetActive();

    // Update client's TRequestStatus object
    *iClientStatus = KRequestPending;
    iSisHelper = aServer;   
    }
#endif

void CUissClientHandler::UninstallL(const CSisRegistryPackage& aPackage, TRequestStatus& aRequestStatus)
    {
    iState = KUissClientUninstalling;
    iClientStatus = &aRequestStatus;

    // Save ptr for data returned by request (must persist whilst server is processing)
    iPtrIntoBuf.Set(iBuf->Des());

    delete iArgsStream;
    iArgsStream = 0;
    iArgsStream = CWriteStream::NewL();
    *iArgsStream << aPackage;
    // Save ptr for args (must persist whilst server is processing)
    iPtrIntoArgsStream.Set(iArgsStream->Ptr());

    // Issue initial request
    iUissSession.Uninstall(iPtrIntoArgsStream, iPtrIntoBuf, iStatus);
    if (iActiveObjectMode)
        {
        SetActive();
        }

    // Update client's TRequestStatus object
    *iClientStatus = KRequestPending;
    }

void CUissClientHandler::CancelOperation()
    {
    if (iState == KUissClientIdle)
        {
        return;
        }
    
    // User called this so must have an outstanding request with us.

    // First tell the Uiss that we want to cancel the current
    // operation.
    //
    // If we have an outstanding Uiss request, this will complete (with
    // KErrCancel) when the operation has terminated.
    //
    // If we are called inside a dialog callback, then there is no
    // outstanding Uiss request at the moment. When the dialog
    // returns, we will issue a request, which will complete (with
    // KErrCancel) when the operation has terminated.
    (void)iUissSession.Cancel();
    }

void CUissClientHandler::WorkUntilCompleteL()
    {
    // Keep processing UISS responses and issuing new requests
    // until we update the client status to non-pending.
    while(iClientStatus && *iClientStatus == KRequestPending) 
        {
        User::WaitForRequest(iStatus);
        TRAPD(err,RunL());
        if(err != KErrNone)
            {   
            RunError(err);
            }
        }
    }

TBool CUissClientHandler::IsBusy()
    {
    return iState != KUissClientIdle;
    }


//
// Code necessary to run UISS in the same process but in a different thread
//
TInt CUissClientHandler::StartUiss()
    {
    const TInt KUissServerStackSize=0x2000;
    const TInt KUissServerInitHeapSize=0x1000;
    const TInt KUissServerMaxHeapSize=0x1000000;
    
    
    TThreadFunction entryPoint=UissThreadFunction;
    //TUiSupportStartParams uiSupportParams(aUiHandler);
    // The owner of the new thread will be the process, otherwise if the 
    // current thread dies, the server thread will die, too.
    RThread server;
    TInt err = KErrNone;
        
    for (TInt retry=0; retry < 2; ++retry)
        {
        err = server.Create(KUissServerName, entryPoint, 
        KUissServerStackSize, KUissServerInitHeapSize, KUissServerMaxHeapSize,
        NULL, EOwnerThread);
        
        if (err == KErrAlreadyExists)
            {
            User::After(30000); 
            }
        else
            {
            break;
            }
        }
        
    if (err==KErrAlreadyExists)
        {
        return KErrServerBusy;
        }
    if (err != KErrNone)
        {
        return err;
        }
    
    // Synchronise with the process to make sure it hasn't died straight away
    TRequestStatus stat;
    server.Rendezvous(stat);
    if (stat != KRequestPending)
        {
        // logon failed - server is not yet running, so cannot have terminated
        server.Kill(0); // Abort startup
        }
    else
        {
        // logon OK - start the server
        server.Resume();
        }
    // Wait to synchronise with server - if it dies in the meantime, it
    // also gets completed
    User::WaitForRequest(stat); 
    // We can't use the 'exit reason' if the server panicked as this
    // is the panic 'reason' and may be '0' which cannot be distinguished
    // from KErrNone
    TInt r=(server.ExitType()==EExitPanic) ? KErrGeneral : stat.Int();
    server.Close();
    return r;
    }

// Entry point for the thread the UISS runs in
TInt CUissClientHandler::UissThreadFunction(TAny *)
    {
    __UHEAP_MARK;
    CTrapCleanup* cleanup = CTrapCleanup::New(); // get clean-up stack
    
    CActiveScheduler* scheduler=new(ELeave) CActiveScheduler;
    CActiveScheduler::Install(scheduler);
    CUissServer* server=NULL;
    
    TRAPD(err, server=CUissServer::NewL());
    if (err==KErrNone)
        {
        RThread::Rendezvous(KErrNone);
        scheduler->Start();
        }
    
    delete server;
    
    CActiveScheduler::Install(NULL);
    delete scheduler;
    delete cleanup; // destroy clean-up stack
    __UHEAP_MARKEND;
    return KErrNone;
    }

void CUissClientHandler::RunL()
    {
    TInt err = iStatus.Int();
    iPtrIntoBuf.Set(iBuf->Des()); // Get ptr to our return buffer
    
	DEBUG_PRINTF2(_L8("Sis Helper - UISS Client Handler, RunL(). Status: %d."), err);
    if(err > 0)
        {
		// For Logging purpose, store the buffer to retrieve 
		// application info later.
        delete iBufLogger;
        iBufLogger = 0;
        iBufLogger = iBuf->AllocL();
        }
    
    if (err==KErrOverflow 
#ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK  
        && iState != KUissClientGettingCompInfo // We don't support overflow management for component info
#endif
        )
        {
        // Grow the respective buffer buffer and re-issue "request".
        // There should now be space for the UISS server to copy in its dialogue message.
        HandleOverflowL();
        iPtrIntoBuf.Set(iBuf->Des());
        iUissSession.BufferReallocated(iPtrIntoBuf, iStatus);

        if (iActiveObjectMode)
            {
            SetActive();
            }
        return;
        }
    else
        {
        if (err>CUissSession::KMsgSeparatorMinimumSwisMessage && 
            err<CUissSession::KMsgSeparatorMaximumSwisMessage)
            {
            // this is a dialog request, unmarshal parameters and display 
            // the dialog
            CUissCmdHandler* cmdHandler=UissCmdHandlerFactoryL(err);
            if (!cmdHandler)
                {               
                User::Leave(KErrNotSupported);
                }
            
            CleanupStack::PushL(cmdHandler);
            // Note that the callback might call CancelOperation which
            // would update iState...
            cmdHandler->HandleMessageL(iPtrIntoBuf, iPtrIntoBuf);
            CleanupStack::PopAndDestroy(cmdHandler);
            }
        else
            {
            #ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK
            // Request has been completed successfully. So, now construct the 
            // component info from the buffer which is populated from the SWI server.
            if (err == KErrNone  && iState == KUissClientGettingCompInfo)
                {
                ConstructCompInfoFromBufferL();
                }           
            #endif
            // Either KErrNone or some sort of error status - in any case the processing has finished
            iState = KUissClientIdle;           
            }
        }
    
    // Re-issue request, if we are still installing/uninstalling
    switch(iState)
        {
    case KUissClientInstalling:
    case KUissClientUninstalling:
        iUissSession.CompleteDialog(KErrNone, iPtrIntoBuf, iStatus);
        if (iActiveObjectMode)
            {
            SetActive();
            }
        return;
            
    case KUissClientIdle:
        // All done, or failed...
        delete iArgsStream;
        iArgsStream = 0;
        //Wait for the death of SisHelper
        WaitForSisHelperShutdown(); 
        // Complete user request (also sets iClientStatus to 0)
        ASSERT(iClientStatus);
        User::RequestComplete(iClientStatus, err);
        
        // Logging the error to a file 
        // Temporary solution, should be removed once instrumentation is fully operational.
        if(err != KErrNone)
            {
			TRAP_IGNORE(
            RDesReadStream readStream(iBufLogger->Des());
            CleanupClosePushL(readStream);
			
			//Retrieve package name.
            CAppInfo* appInfo=CAppInfo::NewLC(readStream);
            LogSwiErrorsToFileL(err, appInfo->AppName());
            
            CleanupStack::PopAndDestroy(2, &readStream);
			);
            }
        return;
        }
    ASSERT(false);
    }


TInt CUissClientHandler::RunError(TInt aError)
	{
	DEBUG_PRINTF2(_L8("Sis Helper - UISS Client Handler, RunError. Error: %d."), aError);
	// Pass failure code on to our client.
	iPtrIntoBuf.Zero();
	iUissSession.CompleteDialog(aError, iPtrIntoBuf, iStatus);
    if (iActiveObjectMode)
        {
        SetActive();
        }
    return KErrNone; // Do not crash the CActiveScheduler.
    }

void CUissClientHandler::DoCancel()
    {
    DEBUG_PRINTF(_L8("Sis Helper - UISS Client Handler, Cancelling."));
    
	// Normally NEVER called because the application should have
	// waited for the original request to complete!
	
	// We can NOT simply call CancelOperation, because when we return
	// into the framework Cancel function it will block on our
	// iStatus, which would stop the active scheduler and hence stop
	// the CancelOperation from being actioned.

    // Do an emergency abort.....

    // First kill our helper threads
    
    // SIS helper thread/server
    CSisHelperServer::Abort();
    
    // UI helper thread/server
    TFullName fullName = RProcess().FullName();
    fullName.Append(':');
    fullName.Append(':');
    fullName.Append(KUissServerName);

    RThread server;
    TInt err = server.Open(fullName);
    if (err == KErrNone)
        {
        server.Terminate(KErrAbort);
        server.Close();
        }
    
    // Now complete any client request
    if (iClientStatus)
        {
        User::RequestComplete(iClientStatus, err);
        }
    }
    
#ifdef SYMBIAN_UNIVERSAL_INSTALL_FRAMEWORK
void CUissClientHandler::ConstructCompInfoFromBufferL()
    {    
    // create a stream based on the buffer
    RDesReadStream stream(*iCompInfoBuffer);
    CleanupClosePushL(stream);
    
    CNativeComponentInfo* nativeCompInfo = CNativeComponentInfo::NewLC();
    nativeCompInfo->InternalizeL(stream);
        
    // UISS and SWI cannot use Usif::CComponentInfo directly, as it is implemented in a non-TCB DLL. For this reason, a private structure maintained (CNativeComponentInfo),
    // which is returned by SWI and is converted here to the CComponentInfo according to the USIF interface
    Usif::CComponentInfo::CNode* rootNode = MapToComponentInfoL(*nativeCompInfo);
    iComponentInfo->SetRootNodeL(rootNode);

    CleanupStack::PopAndDestroy(nativeCompInfo);
    CleanupStack::PopAndDestroy(&stream);
    }

Usif::CComponentInfo::CNode* CUissClientHandler::MapToComponentInfoL(CNativeComponentInfo& aNativeComponentInfo)
    {
    // Create the array to store the children nodes.
    RPointerArray<Usif::CComponentInfo::CNode> children;
    CleanupResetAndDestroyPushL(children);
    
    // If there is any child for the current node, call this method with that child object.
    // Continue this (recursively) until we get the leaf node in the embedded tree (with no children)
    // and add the resultant node as one of the children and pass it to create the parent node further.
    TInt count = aNativeComponentInfo.iChildren.Count();
    for (TInt i = 0; i < count; ++i)
        {
        Usif::CComponentInfo::CNode* node = MapToComponentInfoL(*aNativeComponentInfo.iChildren[i]);
        CleanupStack::PushL(node);
        children.AppendL(node);
        CleanupStack::Pop(node);
        }
        
    // Create the CNode object using the appropriate parameters.
    // children for leaf nodes (bottom most nodes in the embedded tree) will be null.
    Usif::CComponentInfo::CNode* node = Usif::CComponentInfo::CNode::NewLC(
                                        Usif::KSoftwareTypeNative(),
                                        *(aNativeComponentInfo.iComponentName), 
                                        *(aNativeComponentInfo.iVersion),
                                        *(aNativeComponentInfo.iVendor),
                                        static_cast<Usif::TScomoState>(aNativeComponentInfo.iScomoState),
                                        static_cast<Usif::TInstallStatus>(aNativeComponentInfo.iInstallStatus),
                                        aNativeComponentInfo.iComponentId,
                                        *(aNativeComponentInfo.iGlobalComponentId),
                                        static_cast<Usif::TAuthenticity>(aNativeComponentInfo.iAuthenticity),
                                        aNativeComponentInfo.iUserGrantableCaps,
                                        aNativeComponentInfo.iMaxInstalledSize,
                                        aNativeComponentInfo.iHasExe,
                                        &children);
    CleanupStack::Pop(node);
    CleanupStack::Pop(&children);
    children.Close();
    return (node);
    }
#endif


void LogSwiErrorsToFileL(TInt aErrorCode, const TDesC& aAppName)
    {
    _LIT(KErrorString, "%S : Failed with %d");
    _LIT(KLogFile, "c:\\data\\swilog.log");
    
    //Format Output string
    HBufC* outputString = HBufC::NewLC(aAppName.Length()+ 30);
    outputString->Des().Format(KErrorString, &aAppName, aErrorCode);
    
	//Maximum number of log file entries.
    const TUint KLogFileSize = 10;
    
    RFs fs;
    User::LeaveIfError(fs.Connect());
    CleanupClosePushL(fs);
    
    RFile logFile;
    TInt err = logFile.Open(fs, KLogFile, EFileWrite);
    if(err != KErrNone)
        {
		//Attempt to create the file.
        err = logFile.Create(fs, KLogFile, EFileWrite);
        User::LeaveIfError(err);
        }
    CleanupClosePushL(logFile);   
    

	//Read the entire log file.
    RPointerArray<HBufC> linesArray;
    CleanupResetAndDestroyPushL(linesArray);
    
    TFileText logFileManipulator;
    logFileManipulator.Set(logFile);

    TBuf<200>buffer;
    err = logFileManipulator.Read(buffer);
    TInt count(0);
    while(err == KErrNone && ++count <= KLogFileSize)
        {
		HBufC* line = buffer.AllocLC();
        linesArray.AppendL(line);
		CleanupStack::Pop(line);
        err = logFileManipulator.Read(buffer);   
        }
    
	// If the log file contains less than KLogFileSize entries,
	// write the new log entry.
    if(count < KLogFileSize)
        {
        logFileManipulator.Seek(ESeekEnd);
        logFileManipulator.Write(*outputString);
        }
    else
        {
		// Contains KLogFileSize entries. 
		// Replicate the last KLogFileSize - 1 entries and add the
		// new log at the end.
        logFile.Close();
        logFile.Replace(fs, KLogFile, EFileWrite);
        logFileManipulator.Set(logFile);
        for(TInt i = linesArray.Count()- KLogFileSize +1; i<linesArray.Count(); ++i )
            {
            logFileManipulator.Write(*linesArray[i]);
            }

		// New Entry.
        logFileManipulator.Write(*outputString);   
        }
    logFile.Close();
    CleanupStack::PopAndDestroy(4, outputString);    
    }
} // namespace Swi
