emailservices/emailstore/message_store/server/src/MessageStoreServer.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 17 Dec 2009 08:39:21 +0200
changeset 0 8466d47a6819
child 8 e1b6206813b4
permissions -rw-r--r--
Revision: 200949 Kit: 200951

/*
* Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies). 
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "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:  Message store server implementation.
*
*/



// ========
// INCLUDES
// ========

#include <e32svr.h>
#include <bautils.h>
#include <driveinfo.h>
#include <s32file.h>

#include "MsgStoreTypes.h"
#include "MsgStorePropertyKeys.h"
#include "EmailStoreUids.hrh"
#include "MessageStoreServer.h"
#include "MessageStoreSession.h"
#include "ContainerStore.h"
#include "PropertiesSerializer.h"
#include "ShutdownServer.h"
//<cmail>
#include "ImsPointsecMonitor.h"
#include "ImsPointsecObserver.h"
#include "emailstorepskeys.h" // Support for on-the-fly upgrade
//</cmail>

// =========
// CONSTANTS
// =========

_LIT8( KNullDescriptor8, "" );

// To support biggest free drive, we have to get rid of hard-coded C: drive
//const TDriveNumber KDbDriveNumber = EDriveC;

_LIT( KDbFilename, "message_store.db" );

const TMsgStoreCounts KInitialCounts = {0, 0};

// This should be more than sufficient.
const TUint KQuickPropertiesMaxLength = 1024;

const TUint KCommandLineSize = 30;

_LIT( KUninstallParameter, "IMS_UNINSTALL" );
_LIT( KImsUninstaller, "IMS Uninstaller" );
_LIT( KUpgradeDetectionFile, "c:\\System\\EsIms\\canary.txt" );
_LIT16( KDriveToUseFile, "db_drive.cfg" );
//_LIT( KMsgStorePrivateDir, "c:\\Private\\200029e1\\" );

//const TChar KColon(':');

// SID list
// This is the list of secure Ids that are allowed to use the message store.  This list must be terminated
// with zero.

/*
const TUint32 KAllowedSecureIds[] =
        {
        0x200029E4,  // bridge process
        0x10274F51,  // IMS process
        0x1000EAEA,  // test automation executable
        0x20009C56,  // another automation test executable
        0x200029E6,  // message store exerciser
        0x20003C0A,
        0x20003C19,
        0x101FD64C,  // Idle server (for active idle plugin)
        0xA0002879,  // Native Composer Screen
        0x101F9A02,	 // DMHostServer1
		0x101F9A03,	 // DMHostServer2
		0x101F9A04,	 // DMHostServer3
		0x101F9A05,	 // DMHostServer4

        // UNIT TESTERS
#ifdef _DEBUG
        0x043ECF25,  // message store unit tester
        0x200029EC,
        0x200029EE,
        0x200029F0,
        0x200029F1,
        0x200029F2,
        0x200029F3,
        0x20004205,
#endif

        0
        }; // end KAllowedSecureIds
*/

// -----------------------------------------------------------------------------
// DoUninstallL
// Remove the files in private directory.
// -----------------------------------------------------------------------------
static void DoUninstallL()
    {
	RFs fs;
    if( fs.Connect() == KErrNone )
        {
        if( ! BaflUtils::FileExists( fs, KUpgradeDetectionFile ) )
            {
        	// This is a full uninstallation because the upgrade detection file
        	// does not exist !!
            TFileName privatePath;
            fs.PrivatePath( privatePath );
            
            CFileMan* fm = CFileMan::NewL( fs );
            CleanupStack::PushL( fm );
            TInt err = fm->RmDir( privatePath );
            CleanupStack::PopAndDestroy( fm );
            } // end if
            
        fs.Close();
        } // end if
}

// ==========================================================================
// Server startup code
// ==========================================================================

// Perform all server initialisation, in particular creation of the
// scheduler and server and then run the scheduler
//
static void RunServerL( TBool aThreadRendezvous )
  {
  // naming the server thread after the server helps to debug panics
  User::LeaveIfError( User::RenameThread( KMsgStoreServerName ) );

  // create and install the active scheduler we need
  CActiveScheduler* scheduler = new (ELeave) CActiveScheduler();
  CleanupStack::PushL( scheduler );
  CActiveScheduler::Install( scheduler );
  
  // Examine the command line parameters, to determine whether to
  // run the server as normal, or to perform the uninstall operation
  // and exit.
  
  TBuf<KCommandLineSize> commandLine;
  if( User::CommandLineLength() > 0 && User::CommandLineLength() < KCommandLineSize )
      {        
      User::CommandLine( commandLine );
      }
  
  // If the executable was launched with the "IMS_UNINSTALL" parameter then do not
  // launch the server, but just performs necessary uninstallation tasks.
  if( commandLine.Length() > 0 && commandLine.Compare( KUninstallParameter ) == 0 )
      {
	  // naming the server thread after the server helps to debug panics
      User::RenameThread( KImsUninstaller );

      // This is an uninstall.  Do not run the server.
      DoUninstallL();
      }
  else
  {
	  // Create the upgrade detection file, if necessary.
      RFs fs;
      if( (fs.Connect() == KErrNone) && !BaflUtils::FileExists( fs, KUpgradeDetectionFile ) )
      {            
    	  TRAP_IGNORE( /*result,*/ BaflUtils::EnsurePathExistsL( fs, KUpgradeDetectionFile ) );
	        
    	  RFile file;
    	  /*result =*/ file.Create( fs, KUpgradeDetectionFile, EFileWrite );
    	  file.Close();

    	  fs.Close();
      } // end if

	  // create the server (leave it on the cleanup stack)
	  CMessageStoreServer* server = CMessageStoreServer::NewLC();
	
	  // Initialisation complete, now signal the client
	  if( aThreadRendezvous )
	    {    
	    RThread::Rendezvous( KErrNone );
	    }
	  else
	    {
	    RProcess::Rendezvous( KErrNone );    
	    }
	  
	  // Ready to run
	  // Start wait loop, will return when server is shutdown
	  CActiveScheduler::Start();
	  
	  // Cleanup the server
	  CleanupStack::PopAndDestroy( server );
  }

  // Cleanup scheduler after shutdown
  CleanupStack::PopAndDestroy( scheduler );

  __LOG_LEAKED_OBJECTS
  
  } // end RunServerL

// ==========================================================================
// Server thread main function
// ==========================================================================
EXPORT_C TInt MessageStoreServerThreadFunction( TAny* aParams )
  {
  __UHEAP_MARK;
  CTrapCleanup* cleanup = CTrapCleanup::New();

  TInt ret = KErrNoMemory;
  if( cleanup )
    {
	// At one time, Message Store runs as a dll with Bridge process, but it is
	// decided that MsgStore running as a seperate process in case Bridge process
	// crashes makes more sense
	// (aParams == NULL) is basically to differentiate dll or exe
    TRAP( ret, RunServerL( (aParams == NULL) ) );
    delete cleanup;
    }
  __UHEAP_MARKEND;

  return ret;
  } // end MessageStoreServerThreadFunction

// ==========================================================================
// RMessagePtr2::Panic() also completes the message. This is:
// (a) important for efficient cleanup within the kernel
// (b) a problem if the message is completed a second time
// ==========================================================================
void PanicClient(const RMessagePtr2& aMessage, TMessageStorePanic aPanic)
  {
  _LIT( KPanic, "MessageStoreServer" );
  aMessage.Panic( KPanic, aPanic);
  } // end PanicClient

// ======================
// METHOD IMPLEMENTATIONS
// ======================

// ==========================================================================
// FUNCTION: NewLC
// ==========================================================================
CMessageStoreServer* CMessageStoreServer::NewLC()
  {
  CMessageStoreServer* self = new (ELeave) CMessageStoreServer();
  CleanupStack::PushL( self );
  self->ConstructL();
  return self;
  } // end NewLC

// ==========================================================================
// FUNCTION: Constructor
// ==========================================================================
CMessageStoreServer::CMessageStoreServer()
  :CPolicyServer( 0, KMessageStorePolicy, ESharableSessions )
  {
  __LOG_CONSTRUCT( "msg", "CMessageStoreServer" )
  
  iLockedByBackupRestore = EFalse;
  iLockedByPointSec = EFalse;
  iLockedByDriveMonitor = EFalse;
  
  } // end constructor

// ==========================================================================
// FUNCTION: ConstructL
// 2nd phase construction - ensure the timer and server objects are running
// ==========================================================================
void CMessageStoreServer::ConstructL()
    {
    __LOG_ENTER( "ConstructL" )

	// Log filename for this process.
	RProcess thisProcess;
    __LOG_BLOCK( TFileName processName = thisProcess.FileName(); )
	__LOG_WRITE_FORMAT1_INFO( "Filename: %S", &processName );
	thisProcess.Close();
	
    StartL( KMsgStoreServerName );

    iShutdown = CShutdownServer::NewL();
    //starts the PointsecMonitor
    iPointsecMonitor = CImsPointsecMonitor::NewL( *this );
    //must check if PointSec has locked the system or not,
    //only create the container store if PointSec is NOT locked.
    
    // This will be used to monitor the drive where message store db is located.
    iStoreDriveMonitor = CStoreDriveMonitor::NewL( *this );
    
    if ( iPointsecMonitor->IsServiceAllowed() )
        {
        //PointSec does NOT have the device locked, so we can start initialization
        // Construct an instance of the container store to be shared across all message server sessions.
        CreateContainerStoreL();
    
        // Create the inbox/outbox/draft/sent folders, if needed.
        CreatePredefinedFoldersIfNeededL();
        }
    else
        {
        __LOG_WRITE_INFO("PointSec has system locked! Database NOT initialized!")
        //PointSec has the device locked, DO NOT initialize the database
        iLockedByPointSec = ETrue;
        }
    // Register to receive backup/restore notifications.
    iBackupRestoreNotifier = CBackupRestoreNotifier::NewL( *this );
    
    // Ensure that the server still exits even if the 1st client fails to connect.
    // This time isn't started until after the code above, so that the shutdown timer isn't
    // running while the database is possibly being created or wiped.
//    iShutdown->Start();

    // Support for on-the-fly upgrade
    // Watch for KProperty_EmailStore_Upgrade property. When set to our UID3/SECUREID,
    // then, this server should stop.
    RProcess process;
	CleanupClosePushL( process ); //+process
    TSecurityPolicy readPolicy( ECapabilityReadDeviceData );
    TSecurityPolicy writePolicy( ECapabilityWriteDeviceData );
    iUpgradePropertyWatcher = CPSIntPropertyWatcher::NewL( this );
    iUpgradePropertyWatcher->StartL( KEmailStoreUpgradePSCategory,
                                     KProperty_EmailStore_Upgrade,
                                     process.SecureId(),
                                     /*ETrue*/EFalse,
                                     readPolicy,
                                     writePolicy ); 
    CleanupStack::PopAndDestroy(); //-process
        
    __LOG_EXIT
    } // end ConstructL

// ==========================================================================
// FUNCTION: Destructor
// ==========================================================================
CMessageStoreServer::~CMessageStoreServer()
  {
  delete iUpgradePropertyWatcher;
  delete iStoreDriveMonitor;
  delete iShutdown;
  delete iMessageStore;
  iMessageStore = NULL;
  delete iBackupRestoreNotifier;
  delete iPointsecMonitor;
  iSessions.ResetAndDestroy();
  __LOG_DESTRUCT
  } // end destructor

// ==========================================================================
// FUNCTION: NewSessionL
// Create a new client session.
// ==========================================================================
CSession2* CMessageStoreServer::NewSessionL(const TVersion& aVersion, const RMessage2&) const
    {
    __LOG_ENTER( "NewSessionL" )

    if( aVersion.iMajor != KMsgStoreMajorVersion ||
        aVersion.iMinor != KMsgStoreMinorVersion ||
        aVersion.iBuild != KMsgStoreBuild )
        {
        __LOG_WRITE_ERROR( "Session version mismatch" )
        User::Leave( KErrNotSupported );
        } // end if

    // Construct a new session, passing it a pointer to the server object.  This function
    // is const, so the const-ness must be cast away from the this pointer.
    CSession2* returnValue = new (ELeave) CMessageStoreSession( *const_cast<CMessageStoreServer*>(this) );

    __LOG_EXIT
    return returnValue;

    } // end NewSessionL

// ==========================================================================
// FUNCTION: AddSession
// A new session is being created
// Cancel the shutdown timer if it was running
// ==========================================================================
void CMessageStoreServer::AddSession( CMessageStoreSession* aSession )
  {
  __LOG_ENTER( "AddSession" )
  iSessions.Append( aSession );

  // A sesssion was added, so the shutdown timer can be stopped.
//  iShutdown->Stop();
  __LOG_EXIT
  } // end AddSession

// ==========================================================================
// FUNCTION: DropSession
// A session is being destroyed
// Start the shutdown timer if it is the last session.
// ==========================================================================
void CMessageStoreServer::DropSession( CMessageStoreSession* aSession )
    {
    __LOG_ENTER( "DropSession" )

    // Find the current session in the list of sessions.
    TInt index = iSessions.Find( aSession );

    if( index == KErrNotFound )
        {
        // This should never happen.
        __LOG_WRITE_ERROR( "ERROR! SESSION NOT FOUND!" );
        }
    else
        {
        // Remove the session from the list of sessions.
        iSessions.Remove( index );
        } // end if

    // If this was the last session to this server then start the shutdown timer with no delay.  This
    // will immediately shut down the server, unless the container store is currently performing
    // background operations.
//    if( iSessions.Count() == 0 )
//        {
//        iShutdown->Start( 0 );
//        } // end if

    __LOG_EXIT
    } // end DropSession



// ==========================================================================
// FUNCTION: CreateContainerStoreL
// ==========================================================================
void CMessageStoreServer::CreateContainerStoreL()
    {
    TDriveNumber drive( EDriveC );
    if ( GetDriveL( drive ) == KErrNotFound )
        {
        FindBiggestCapacityDriveL( drive );
        }
    //set again the drive to be monitored in case is different than previous
    iStoreDriveMonitor->SetDriveL( drive );
    
    if ( iStoreDriveMonitor->IsDrivePresent() )
        {
    iMessageStore = CContainerStore::NewL( KDbFilename,
                                           drive,
                                           *this,
                                           *iShutdown,
                                           Priority() - 1);  // lower than server
        }
    else
        {
        iLockedByDriveMonitor = ETrue;
        }
    
    // monitor the message store drive
    iStoreDriveMonitor->WaitForChange();

    } // end CreateContainerStoreL


// ==========================================================================
// FUNCTION: MessageStoreL
// ==========================================================================
CContainerStore& CMessageStoreServer::MessageStoreL()
  {
  if( !iMessageStore )
    {
    // A backup or restore is in progress.
    // Or PointSec has locked the device.
    User::Leave( KErrInUse );
    } // end if

  return *iMessageStore;
  } // end MessageStoreL

// ==========================================================================
// FUNCTION: CreatePredefinedFoldersIfNeededL
// ==========================================================================
void CMessageStoreServer::CreatePredefinedFoldersIfNeededL()
  {
  __LOG_ENTER( "CreatePredefinedFoldersIfNeededL" )

  // Create predefined folders.
  if ( iMessageStore )
      {
      iMessageStore->CreatePredefinedContainerIfNeededL(
              KMsgStoreRootMailBoxId, EMsgStoreMailBoxBits, KMsgStoreInvalidId,
              KNullDescriptor8 );
      }

  __LOG_EXIT
  } // end CreatePredefinedFoldersIfNeededL

// ==========================================================================
// FUNCTION: QuickPropertiesAndCountsL
// This function traverses the properties, looking for the "quick" properties.
// This is not part of the container store in order to keep the container
// store more generic, without any message-specific logic.
// ==========================================================================
void CMessageStoreServer::QuickPropertiesAndCountsL( TContainerId  aType,
													 const TDesC8& aProperties,
													 RBuf8&        aQuickProperties,
													 TDes8&        aCounts ) const
  {
  __LOG_ENTER( "QuickPropertiesAndCountsL" )

  TMsgStoreCounts counts;

  counts.iMessagesCount = 0;
  counts.iUnreadCount   = 0;
  
    // Quick properties and message counts are for message containers only.
  if( (aType & EMsgStoreContainerMask) == EMsgStoreMessageBits )
    {
    // This container should increment the iMessageCount field of the parent container's child counts.
    counts.iMessagesCount = 1;

    // Iterator through the properties, looking for the quick properties.

    TPropertiesDeserializer propertiesDeserializer( aProperties );
    TPropertiesSerializer   quickPropertiesSerializer( aQuickProperties );

    TBool moreProperties = propertiesDeserializer.First();

    TBool flagsFound       = EFalse;
    TBool completedAtFound = EFalse;
    TBool receivedAtFound  = EFalse;
    TBool replyByFound     = EFalse;
    TBool sizeFound        = EFalse;

    while( moreProperties && !(flagsFound && completedAtFound && receivedAtFound && replyByFound && sizeFound) )
        {
        TBool addProperty = EFalse;

        if( !flagsFound && (propertiesDeserializer.Name().Compare( KMsgStorePropertyFlags ) == 0) )
            {
            addProperty = ETrue;
            flagsFound  = ETrue;

            TPckgBuf<TUint32> flagsPckg;
            flagsPckg.Copy( propertiesDeserializer.Value() );
            
            // The message is considered read if either Read flag is set.
            if ((flagsPckg() & (EMsgStoreFlag_Read_Locally | EMsgStoreFlag_Read) ) == 0)
                {
                // This container should increment the iUnreadCount field of the parent
                // container's child counts.
                __LOG_WRITE_INFO( "is unread" );
                counts.iUnreadCount = 1;
                } // end if
            }
        else if( !completedAtFound && (propertiesDeserializer.Name().Compare( KMsgStorePropertyCompletedAt ) == 0) )
            {
            addProperty      = ETrue;
            completedAtFound = ETrue;
            }
        else if( !receivedAtFound && (propertiesDeserializer.Name().Compare( KMsgStorePropertyReceivedAt ) == 0) )
            {
            addProperty     = ETrue;
            receivedAtFound = ETrue;
            }
        else if( !replyByFound && (propertiesDeserializer.Name().Compare( KMsgStorePropertyReplyBy ) == 0) )
            {
            addProperty  = ETrue;
            replyByFound = ETrue;
            } // end if
        else if( !sizeFound && (propertiesDeserializer.Name().Compare( KMsgStorePropertyMessageSizeOnServer ) == 0) )
            {
            addProperty  = ETrue;
            sizeFound = ETrue;
            } // end if

        if( addProperty )
            {
            // This is a quick property!

            __LOG_WRITE8_FORMAT1_INFO( "found %S", &propertiesDeserializer.Name() );

            quickPropertiesSerializer.AddPropertyL( propertiesDeserializer.Name(),
                                                    propertiesDeserializer.Type(),
                                                    propertiesDeserializer.Value() );

            } // end if

        moreProperties = propertiesDeserializer.Next();

        } // end while

    } // end if

    __LOG_WRITE8_FORMAT2_INFO( "counts total=%i unread=%i", counts.iMessagesCount, counts.iUnreadCount )
    TPckg<TMsgStoreCounts> countsPckg(counts);
    aCounts.Copy( countsPckg );

    __LOG_EXIT

  } // end QuickPropertiesAndCountsL

// ==========================================================================
// FUNCTION: SortableFieldsL
// This function retrieves some sortable fields from a serialized quick properties.
// ==========================================================================
void CMessageStoreServer::SortableFieldsL( const TDesC8&             aQuickProperties, 
                                           const TDesC8&             aProperties,
                                           RMsgStoreSortableFields&  aSortableFields ) const
    {
    __LOG_ENTER( "SortableFieldsL" )
    
    aSortableFields.iFlags = 0;
    aSortableFields.iReceivedDate = 0;
    aSortableFields.iSizeOnServer = 0;
    
    TPropertiesDeserializer qpDeserializer( aQuickProperties );
    
    TBool moreProperties = qpDeserializer.First();

    TBool flagsFound       = EFalse;
    TBool receivedAtFound  = EFalse;
    TBool sizeFound        = EFalse;

    while( moreProperties && !(flagsFound && receivedAtFound && sizeFound) )
        {
        if( !flagsFound && (qpDeserializer.Name().Compare( KMsgStorePropertyFlags ) == 0) )
            {
            flagsFound  = ETrue;
            
            TPckgBuf<TUint32> flagsPckg;
            flagsPckg.Copy( qpDeserializer.Value() );
            
            aSortableFields.iFlags = flagsPckg();
            }
        else if( !receivedAtFound && (qpDeserializer.Name().Compare( KMsgStorePropertyReceivedAt ) == 0) )
            {
            receivedAtFound = ETrue;
            
            TPckgBuf<TInt64> receivedDatePckg;
            receivedDatePckg.Copy( qpDeserializer.Value() );
            
            aSortableFields.iReceivedDate = receivedDatePckg();
            }
        else if( !sizeFound && (qpDeserializer.Name().Compare( KMsgStorePropertyMessageSizeOnServer ) == 0) )
            {
            sizeFound = ETrue;
            
            TPckgBuf<TUint32> sizePckg;
            sizePckg.Copy( qpDeserializer.Value() );
            
            aSortableFields.iSizeOnServer = sizePckg();
            } // end if

        moreProperties = qpDeserializer.Next();
        } // end while
    
    //get the "subject" and "from" the from fields
    TPropertiesDeserializer propDeserializer( aProperties );
    
    moreProperties = propDeserializer.First();

    TBool subjectFound     = EFalse;
    TBool fromFound        = EFalse;
    TBool toFound          = EFalse;

    while( moreProperties && !(subjectFound && fromFound && toFound) )
        {
        if( !subjectFound && (propDeserializer.Name().Compare( KMsgStorePropertySubject ) == 0) )
            {
            RBuf subject;
            CleanupClosePushL( subject );
            
            TPtrC8ToRBuf16L( propDeserializer.Value(), subject );
            
            //get rid of the "Alpha:" "AlphaAlpha:" "AlphaAlphaAlpha:"
            TPtr16 ptr = subject.MidTPtr(0);
            TBool prefixFound = ETrue;
            TChar KColon(':');
            const TUint KPos = 3;
            while ( prefixFound )
                {
                ptr.TrimLeft();
                TInt pos = ptr.Locate( KColon );
       
                if ( pos > 0 && pos <= KPos )
                    {
                    for ( TInt i = 0; i < pos; i++ )
                        {
                        TChar ch = ptr[i];
                        if ( !ch.IsAlpha() )
                            {
                            prefixFound = EFalse;
                            break;
                            }                    
                        }
                    if ( prefixFound )
                        {
                        ptr = ptr.Mid( pos + 1 );
                        __LOG_WRITE_INFO("Prefix ':' found.")
                        }
                    }
                else
                    {
                    prefixFound = EFalse;
                    }
                }
            
            aSortableFields.iSubject.Create( ptr );
            
            __LOG_WRITE_FORMAT1_INFO( "subject=%S", &aSortableFields.iSubject )
            
            CleanupStack::PopAndDestroy( &subject );
            subjectFound = ETrue;
            }
    
    //get the from
        else if( !fromFound && (propDeserializer.Name().Compare( KMsgStorePropertyFrom ) == 0) )
            {
            TPropertiesDeserializer addrDeserializer( propDeserializer.Value() );
            RBuf displayName;
            CleanupClosePushL( displayName );
            if ( addrDeserializer.Find( KMsgStorePropertyDisplayName ) )
                {
                TPtrC8ToRBuf16L( addrDeserializer.Value(), displayName );
                }
            RBuf emailAddr;
            CleanupClosePushL( emailAddr );
            if ( addrDeserializer.Find( KMsgStorePropertyEmailAddress ) )
                {
                TPtrC8ToRBuf16L( addrDeserializer.Value(), emailAddr );
                }
            
            aSortableFields.iFrom.Create( displayName.Length() + emailAddr.Length() );
            aSortableFields.iFrom.Copy( displayName );
            aSortableFields.iFrom.Append( emailAddr );
            
            CleanupStack::PopAndDestroy( &emailAddr );
            CleanupStack::PopAndDestroy( &displayName );
            fromFound = ETrue;
            }
        //get the to
        else if( !toFound && (propDeserializer.Name().Compare( KMsgStorePropertyTo ) == 0) )
            {
            TPropertiesDeserializer addrDeserializer( propDeserializer.Value() );
            RBuf displayName;
            CleanupClosePushL( displayName );
            if ( addrDeserializer.Find( KMsgStorePropertyDisplayName ) )
                {
                TPtrC8ToRBuf16L( addrDeserializer.Value(), displayName );
                }
            RBuf emailAddr;
            CleanupClosePushL( emailAddr );
            if ( addrDeserializer.Find( KMsgStorePropertyEmailAddress ) )
                {
                TPtrC8ToRBuf16L( addrDeserializer.Value(), emailAddr );
                }
            
            aSortableFields.iTo.Create( displayName.Length() + emailAddr.Length() );
            aSortableFields.iTo.Copy( displayName );
            aSortableFields.iTo.Append( emailAddr );
            
            CleanupStack::PopAndDestroy( &emailAddr );
            CleanupStack::PopAndDestroy( &displayName );
            toFound = ETrue;
            }
        moreProperties = propDeserializer.Next();
        }

    __LOG_EXIT
    
    }

// ==========================================================================
// FUNCTION: LengthsL
// ==========================================================================
void CMessageStoreServer::LengthsL( TUint& aCountsLength,
                                    TUint& aQuickPropertiesMaxLength ) const
    {
    aCountsLength = KMsgStoreCountsLength;

    aQuickPropertiesMaxLength = KQuickPropertiesMaxLength;

    } // end LengthsL

// ==========================================================================
// FUNCTION: IncrementParentCounts
// ==========================================================================
void CMessageStoreServer::IncrementParentCounts( TDes8& aParentCounts, const TDesC8& aChildCounts ) const
  {
  __LOG_ENTER_SUPPRESS( "IncrementParentCounts" )
  TMsgStoreCounts*       parentCounts = const_cast<TMsgStoreCounts*>( reinterpret_cast<const TMsgStoreCounts*>( aParentCounts.Ptr() ) );
  const TMsgStoreCounts* childCounts  = reinterpret_cast<const TMsgStoreCounts*>( aChildCounts.Ptr() );

  __LOG_WRITE_FORMAT4_INFO("p.total=%d, p.unread=%d, c.total=%d, c.unread=%d", parentCounts->iMessagesCount, parentCounts->iUnreadCount, childCounts->iMessagesCount, childCounts->iUnreadCount )
  
  parentCounts->iMessagesCount += childCounts->iMessagesCount;
  parentCounts->iUnreadCount   += childCounts->iUnreadCount;
  } // end IncrementParentCounts

// ==========================================================================
// FUNCTION: DecrementParentCounts
// ==========================================================================
void CMessageStoreServer::DecrementParentCounts( TDes8& aParentCounts, const TDesC8& aChildCounts ) const
  {
  TMsgStoreCounts*       parentCounts = const_cast<TMsgStoreCounts*>( reinterpret_cast<const TMsgStoreCounts*>( aParentCounts.Ptr() ) );
  const TMsgStoreCounts* childCounts  = reinterpret_cast<const TMsgStoreCounts*>( aChildCounts.Ptr() );

  parentCounts->iMessagesCount -= childCounts->iMessagesCount;
  parentCounts->iUnreadCount   -= childCounts->iUnreadCount;
  } // end DecrementParentCounts

// ==========================================================================
// FUNCTION: InitializeCounts
// ==========================================================================
void CMessageStoreServer::InitializeCounts( TDes8& aCounts ) const
  {
  TPckg<TMsgStoreCounts> countsPckg( KInitialCounts );
  aCounts.Copy( countsPckg );
  } // end InitializeCounts

  
void CMessageStoreServer::LogCounts( const TDesC& aDescription, const TDesC8& __LOG_BLOCK(aCounts) ) const
    {
    __LOG_ENTER_SUPPRESS( "LogCounts" )
    
    TInt length( aDescription.Length() );                   // noop usage to suppress compilation warning, "INFO" log level not enabled.
    
    __LOG_BLOCK( const TMsgStoreCounts* counts  = reinterpret_cast<const TMsgStoreCounts*>( aCounts.Ptr() ); )
    __LOG_WRITE_FORMAT3_INFO( "%S total=%d unread=%d", &aDescription, counts->iMessagesCount, counts->iUnreadCount )
    }


// ==========================================================================
// FUNCTION: WipeEverythingL
// ==========================================================================
void CMessageStoreServer::WipeEverythingL()
  {
  __LOG_ENTER( "WipeEverythingL" )

  for( TInt i = 0; i < iSessions.Count(); i++ )
    {
    iSessions[i]->ContainerStoreUnavailable();
    } // end if

    if( !iMessageStore )
        {
        // A backup/restore is in progress.  Wipe after it completes.
        iWipeAfterBackupRestore = ETrue;
        }
    else
        {
        delete iMessageStore;
        iMessageStore = NULL;

        // Wipe the message store files in the private area.  (Do not wipe the
        // entire private area since other threads may be storing files in
        // the area.)

        // Note: when the message store server was changed from a process to a
        //       thread, it would have been nice to move all of the message store
        //       files to a specific subdirectory in the private area, but that
        //       change would have been backwards incompatible.
        
        TDriveNumber drive( EDriveC );
        if ( GetDriveL( drive ) == KErrNotFound ) drive = EDriveC;
        TInt result = CContainerStore::WipeEverything( KDbFilename,
                                                       drive );

        TRAP( result, CreateContainerStoreL();
                      CreatePredefinedFoldersIfNeededL() );

        if( result != 0 )
            {
            __LOG_WRITE_ERROR( "failed to recreate message store after wipe" )

            // The server is in a very bad state.  Shut down the server immediately.
            iShutdown->ShutDownNow();
            } // end if

        // Notify observers of wipe event.
        TMsgStoreEvent event;
        event.iType        = EMsgStoreDatabaseWiped;
        event.iId          = KMsgStoreInvalidId;
        event.iParentId    = KMsgStoreInvalidId;
        event.iOtherId     = KMsgStoreInvalidId;
        event.iFlags       = KMsgStoreFlagsNotFound;

        for( TInt i = 0; i < iSessions.Count(); i++ )
            {
            iSessions[i]->ContainerStoreAvailable();
            iSessions[i]->SendEventToObserver( event );
            } // end if

        } // end if

  __LOG_EXIT
  } // end WipeEverythingL

// ==========================================================================
// FUNCTION: BackupOrRestoreInProgress
// ==========================================================================
void CMessageStoreServer::BackupOrRestoreInProgress( TBool /*aIsARestore*/ )
{
    __LOG_ENTER( "BackupOrRestoreInProgress" );
    
    SendSystemLockMessage( EMsgStoreBackupOrRestoreInProgress );
    
    if (( !iLockedByPointSec ) && ( !iLockedByDriveMonitor ) )
        {
        LockSystem();
        }

    __LOG_EXIT
    } // end BackupOrRestoreInProgress

// ==========================================================================
// FUNCTION: BackupOrRestoreCompleted
// ==========================================================================
void CMessageStoreServer::BackupOrRestoreCompleted()
    {
    __LOG_ENTER( "BackupOrRestoreCompleted" );
  
    SendSystemLockMessage( EMsgStoreBackupOrRestoreCompleted );
      
    if (( !iLockedByPointSec ) && ( !iLockedByDriveMonitor ) )
        {
        TRAP_IGNORE( UnlockSystemL() );
        }
    
    __LOG_EXIT
} // end BackupOrRestoreCompleted

// ==========================================================================
// FUNCTION: PointSecLockStarted
// ==========================================================================
void CMessageStoreServer::PointSecLockStarted()
    {
    __LOG_ENTER( "PointSecLockStarted" );
    if ( !iLockedByPointSec )
        {
        iLockedByPointSec = ETrue;
        SendSystemLockMessage( EMsgStorePointSecLockStarted );
        if (( !iLockedByBackupRestore ) && ( !iLockedByDriveMonitor ) )
            {
            LockSystem();
            }
        }
    __LOG_EXIT
    }

// ==========================================================================
// FUNCTION: PointSecLockEnded
// ==========================================================================
void CMessageStoreServer::PointSecLockEnded()
    {
    __LOG_ENTER( "PointSecLockEnded" );
    if ( iLockedByPointSec )
        {
        iLockedByPointSec = EFalse;
        SendSystemLockMessage( EMsgStorePointSecLockEnded );
        if ( ( !iLockedByBackupRestore ) && ( !iLockedByDriveMonitor ) )
            {
            TRAP_IGNORE( UnlockSystemL() );
            }
        }
    __LOG_EXIT
    }

// ==========================================================================
// FUNCTION: LockSystem
// ==========================================================================
void CMessageStoreServer::LockSystem()
    {
    __LOG_ENTER( "LockSystem" );
    
    for( TInt i = 0; i < iSessions.Count(); i++ )
        {
        iSessions[i]->ContainerStoreUnavailable();
        } // end if

    delete iMessageStore;
    iMessageStore = NULL;
    
    __LOG_EXIT
    }

// ==========================================================================
// FUNCTION: UnlockSystemL
// ==========================================================================
void CMessageStoreServer::UnlockSystemL()
    {
    __LOG_ENTER( "UnlockSystemL" );
    
    TRAPD( result,
            CreateContainerStoreL(); CreatePredefinedFoldersIfNeededL(); );

    if( result != 0 )
        {
        __LOG_WRITE_ERROR( "failed to recreate message store after system lock" )

        // The server is in a very bad state.  Shut down the server immediately.
        iShutdown->ShutDownNow();
        }
   else if  ( ( !iLockedByBackupRestore ) 
           && ( !iLockedByDriveMonitor ) 
           && ( !iLockedByPointSec ) )
        {

        for( TInt i = 0; i < iSessions.Count(); i++ )
            {
            iSessions[i]->ContainerStoreAvailable();
            } // end if
        
        if( iWipeAfterBackupRestore )
            {
            iWipeAfterBackupRestore = EFalse;
            WipeEverythingL();
            } // end if

        } // end if
    
    __LOG_EXIT
    }
    
// ==========================================================================
// FUNCTION: FindBiggestCapacityDrive
//           To locate the internal drive with the biggest available space.
// ========================================================================== 
void CMessageStoreServer::FindBiggestCapacityDriveL( TDriveNumber& aDrive )
    {
    __LOG_ENTER( "FindBiggestCapacityDrive" );
    
    RFs fs;
    TInt driveToUse( 2 );  // set default drive to C:
    
    if( fs.Connect() == KErrNone )
        {
        CleanupClosePushL( fs );               //+fs
        TDriveList driveList;
        fs.DriveList( driveList );
        TInt64 highest( 0 );
        for (TInt i=0; i<KMaxDrives; i++)
            {
            if ( driveList[i] != 0 )     // check if drive exits
                {
                TDriveInfo driveInfo;
                fs.Drive( driveInfo, i );
                TVolumeInfo driveVolume;
                fs.Volume( driveVolume, i );
                                            
                TUint drvStatus( 0 );
                User::LeaveIfError( DriveInfo::GetDriveStatus( fs, i, drvStatus ) );
                                            
                if ( (drvStatus & DriveInfo::EDriveUserVisible) &&
                     (drvStatus & DriveInfo::EDriveInternal)  && 
                     (drvStatus & DriveInfo::EDrivePresent) )
                    {
                    if (driveVolume.iFree > highest)
                        {
                        driveToUse = i;
                        highest = driveVolume.iFree;
                        }
                    }
                }
             }             
                            
        __LOG_WRITE_FORMAT2_INFO( "Drive #%d to use, free space=%u", driveToUse, highest );
                                  
        CleanupStack::PopAndDestroy( &fs );    //-fs
        } // end if
    
    aDrive = static_cast<TDriveNumber>( driveToUse );
    SetDriveL( aDrive );   // save it
    
    __LOG_EXIT
    }

// ==========================================================================
// FUNCTION: GetDrive
//           To find the previously-defined drive to use.   KErrNotFound will
//           be returned if not config file found
//
// Note    : the config file will be loacted in C: drive 
//           (C:\Private\2000c8d2\db_drive.cfg)
// ==========================================================================
TInt CMessageStoreServer::GetDriveL( TDriveNumber& aDrive )
    {
    __LOG_ENTER( "GetDrive" );
    
    TInt ret( KErrNotFound );
        
    RFs fs;
    if( fs.Connect() == KErrNone )
        {
        CleanupClosePushL( fs );               //+fs
        TFileName fileName;
        fs.CreatePrivatePath( EDriveC );
        User::LeaveIfError(fs.PrivatePath( fileName ));
        fileName.Append( KDriveToUseFile );
        _LIT( KCDrive, "C:" );
        fileName.Insert( 0, KCDrive() );
        __LOG_WRITE_FORMAT1_INFO( "DriveToUse file=%S", &fileName );
                   
        if( BaflUtils::FileExists( fs, fileName ) )
            {
            RFileReadStream reader;
            if ( reader.Open( fs, fileName, EFileRead ) == KErrNone )
          		{
           		CleanupClosePushL( reader );             //+reader
           		TUint drive = reader.ReadUint32L();
           		__LOG_WRITE_FORMAT1_INFO( "previously-defined drive to use : %d", drive );
                CleanupStack::PopAndDestroy( &reader );  //-reader
           		aDrive = static_cast<TDriveNumber>( drive );
           		ret = KErrNone;
           		}
            }
                   
        CleanupStack::PopAndDestroy( &fs );    //-fs
        } // end if
    
    __LOG_EXIT
    return ret;
    }

// ==========================================================================
// FUNCTION: SetDrive
//           To write the chosen drive info to the config file.
//
// Note    : the config file will be loacted in C: drive 
//           (C:\Private\2000c8d2\db_drive.cfg)
// ==========================================================================
void CMessageStoreServer::SetDriveL( const TDriveNumber aDrive )
    {
    __LOG_ENTER( "SetDrive" );
    
    TUint drive = static_cast<TUint32>( aDrive );
        
    RFs fs;
    if( fs.Connect() == KErrNone )
        {
        CleanupClosePushL( fs );               //+fs
        TFileName fileName;
        User::LeaveIfError(fs.PrivatePath( fileName ));
        fileName.Append( KDriveToUseFile );
        _LIT( KCDrive, "C:" );
        fileName.Insert( 0, KCDrive() );
        __LOG_WRITE_FORMAT1_INFO( "DriveToUse file=%S", &fileName )
                   
        RFileWriteStream writer;
        if ( writer.Replace( fs, fileName, EFileRead ) == KErrNone )
            {
         	CleanupClosePushL( writer );             //+writer
           	writer.WriteUint32L( drive );
           	writer.CommitL();
           	__LOG_WRITE_FORMAT1_INFO( "writing chosen drive #%d to file", drive );
            CleanupStack::PopAndDestroy( &writer );  //-writer
           	}
                 
        CleanupStack::PopAndDestroy( &fs );    //-fs
        } // end if
    
    __LOG_EXIT
    }

// ==========================================================================
// FUNCTION: SendSystemLockMessage
// ==========================================================================
void CMessageStoreServer::SendSystemLockMessage( TInt aEvent )
    {
    TMsgStoreEvent event;
    event.iType        = aEvent;
    event.iId          = KMsgStoreInvalidId;
    event.iParentId    = KMsgStoreInvalidId;
    event.iOtherId     = KMsgStoreInvalidId;
    event.iFlags       = KMsgStoreFlagsNotFound;

    for( TInt i = 0; i < iSessions.Count(); i++ )
        {
        iSessions[i]->SendEventToObserver( event );
        } // end if
    }

// ==========================================================================
// FUNCTION: TPtrC8ToRBuf16L
// ==========================================================================
void CMessageStoreServer::TPtrC8ToRBuf16L( const TPtrC8& aPtr8, RBuf& aBuf ) const
    {
    RBuf8 val8;
    CleanupClosePushL( val8 );
    
    val8.Create( aPtr8 );
    
    const TUint16* valuePtr16 = reinterpret_cast<const TUint16*>( val8.Ptr() );
    TPtrC16 val16( valuePtr16, val8.Length() / 2 );
    
    aBuf.Create( val16 );
    
    CleanupStack::PopAndDestroy( &val8 );
    }

// FROM: MPSPropertyWatcherObserver
// ==========================================================================
// FUNCTION: OnPSValueChangedToRequired
// ==========================================================================
void CMessageStoreServer::OnPSValueChangedToRequired()
    {
    iShutdown->Start( 0 );
    }

// FUNCTIONS TO SUPPORT AUTOMATED UNIT TESTING

#ifdef _DEBUG
// ==========================================================================
// FUNCTION: InjectBackupRestoreEventL
// ==========================================================================
void CMessageStoreServer::InjectBackupRestoreEventL( TUint aEvent )
    {
    iBackupRestoreNotifier->ProcessEvent( aEvent );
    }

// ==========================================================================
// FUNCTION: InjectPointSecEvern
// ==========================================================================
void CMessageStoreServer::InjectPointSecEvent( TBool aLock )
    {
    if ( aLock )
        {
        PointSecLockStarted();
        }
    else
        {
        PointSecLockEnded();
        }
    }

void CMessageStoreServer::Shutdown()
    {
    iShutdown->Start( 0 );
    }

#endif

//from MStoreDriveStateObserver
void CMessageStoreServer::DriveStateChangedL( TBool aState )
    {
    __LOG_ENTER( "DriveStateChangedL" );
    if ( aState )
        {
        //drive mounted
        if ( iLockedByDriveMonitor )
             {
             iLockedByDriveMonitor = EFalse;
             if ( ( !iLockedByBackupRestore ) && ( !iLockedByPointSec ) )
                 {
                 UnlockSystemL();
                 }
             }
        }
    else
        {
        //drive unmounted
        if ( !iLockedByDriveMonitor )
            {
            iLockedByDriveMonitor = ETrue;
            if ( ( !iLockedByBackupRestore ) && ( !iLockedByPointSec ) )
                {
                LockSystem();
                }
            }
        }
    __LOG_EXIT
    }