emailservices/emailstore/message_store/server/src/MessageStoreServer.cpp
changeset 0 8466d47a6819
child 8 e1b6206813b4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/emailservices/emailstore/message_store/server/src/MessageStoreServer.cpp	Thu Dec 17 08:39:21 2009 +0200
@@ -0,0 +1,1275 @@
+/*
+* 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
+    }
+