--- /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
+ }
+