messagingfw/msgsrvnstore/server/src/CMsvCopyStoreOperation.cpp
changeset 0 8e480a14352b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/messagingfw/msgsrvnstore/server/src/CMsvCopyStoreOperation.cpp	Mon Jan 18 20:36:02 2010 +0200
@@ -0,0 +1,425 @@
+// Copyright (c) 2004-2009 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:
+//
+
+#include "MSVSERV.H"
+#include "MSVAPI.H"
+#include <cmsvcopystoreoperation.h>
+
+// the amount of space used, even if empty (an approximation)
+const TInt KMinimumSpace=400;
+
+_LIT(KIndex, "Index");
+
+CMsvCopyStoreOperation::CMsvCopyStoreOperation(const RMessage2& aMessage, CMsvServer& aServer)
+: CMsvServerOperation(aMessage, aMessage.Int0(), KUidMsvLocalServiceMtm, KMsvLocalServiceIndexEntryId, -1), iDrive(TDriveUnit (aMessage.Int1() )), iServer(aServer)
+	{
+	CActiveScheduler::Add(this);
+	}
+
+CMsvCopyStoreOperation* CMsvCopyStoreOperation::NewL(const RMessage2& aMessage, CMsvServer& aServer)
+	{
+	CMsvCopyStoreOperation* self=new(ELeave)CMsvCopyStoreOperation(aMessage, aServer);
+	return self;
+	}
+
+CMsvCopyStoreOperation::~CMsvCopyStoreOperation()
+	{
+	Cancel();
+	delete iDir;
+	delete iFileMan;
+	}
+
+
+void CMsvCopyStoreOperation::StartL()
+	{
+	__ASSERT_DEBUG(!IsActive() , PanicServer(EMsvCopyStoreReset));
+
+	iProgress().iState=TMsvCopyProgress::ENotYetStarted;
+	iPos=0;
+
+	iFileMan = CFileMan::NewL(iServer.FileSession(), this);
+
+	CompleteSelf();
+	}
+
+/* Function to retrieve progress of the Copy operation
+**/
+const TDesC8& CMsvCopyStoreOperation::Progress()
+	{
+	 return iProgress;
+	}
+
+void CMsvCopyStoreOperation::DoCancel()
+	{
+	iCopyCancel = ETrue;
+	Completed(KErrCancel);
+	}
+
+
+/* Run different operations like LockStore, CopyStore
+   UnlockStore and Completed */
+
+void CMsvCopyStoreOperation::RunL()
+	{
+	User::LeaveIfError(iStatus.Int());
+	switch(iProgress().iState)
+		{
+		case TMsvCopyProgress::ENotYetStarted:
+			LockMailStoreL();
+			iProgress().iState=TMsvCopyProgress::ELock;
+			break;
+		case TMsvCopyProgress::ELock:
+			InitCopyStoreL();
+			iProgress().iState=TMsvCopyProgress::EInitCopy;
+			break;
+
+		case TMsvCopyProgress::EInitCopy:
+			iProgress().iState=TMsvCopyProgress::ECopyStore;
+		//drop through the next case statement
+		case TMsvCopyProgress::ECopyStore:
+			if (iPos<iProgress().iTotal)
+				{
+				CopySourceL();
+				}
+			else
+			   	{
+				FinishCopyStoreL();
+				iProgress().iState=TMsvCopyProgress::EUnlock;
+			   	}
+			break;
+		case TMsvCopyProgress::EUnlock:
+			Completed(KErrNone);
+			iProgress().iState=TMsvCopyProgress::ECompleted;
+			break;
+		default:
+			__ASSERT_DEBUG(EFalse,PanicServer(EMsvCopyStoreReset));
+			Completed(KErrUnknown);
+			iProgress().iState=TMsvCopyProgress::ECompleted;
+			break;
+		}
+	}
+
+
+/* Lock the Mail store so other operations dont use it while
+   the copy event is in progress
+*/
+void CMsvCopyStoreOperation::LockMailStoreL()
+	{
+	User::LeaveIfError(iServer.Context().IndexAdapter()->ErrorState());
+	iServer.NotifyChanged(EMsvMediaUnavailable, KMsvNullIndexEntryId, iServer.Drive());
+	iServer.SetStartupState(EMsvMediaUnavailable);
+	iServer.Context().IndexAdapter()->SetErrorState(KErrNone);
+	//Complete yourself
+	CompleteSelf();
+	}
+
+
+/* Initialise the copy process by setting the destination path,etc
+*/
+void CMsvCopyStoreOperation::InitCopyStoreL()
+	{
+	//Check if the drive already contains a store,
+	//if so then unlock the index file and complete
+	if(MessageServer::DriveContainsStore(iServer.FileSession(),iDrive))
+		{
+		User::Leave(KErrAlreadyExists);
+		}
+	else
+		{
+		//Create the destination path
+		iDest = iDrive.Name();
+		iDest.Append(iServer.Context().MessageFolder());
+
+		//Delete the drive letters from Messagefolder path
+		iDest.Delete(2,2);
+
+		//check if the target disk has enough disk space
+		CheckDiskSpaceL();
+
+		//Get the directory structure of the source path
+		delete iDir;
+		iDir=NULL;
+    	User::LeaveIfError(iServer.FileSession().GetDir(iServer.Context().MessageFolder(), KEntryAttDir|KEntryAttNormal, ESortNone, iDir));
+    	iProgress().iTotal = iDir->Count();
+
+ 		//Check if there is any broken store already existing on iDest
+ 		TUint unused;
+		TInt error=iServer.FileSession().Att(iDest,unused);
+		if(error == KErrNone)
+			{
+			//delete any files that may exist
+			User::LeaveIfError(iFileMan->RmDir(iDest,iStatus));
+			SetActive();
+			}
+		else
+			{
+		    //Complete
+   			CompleteSelf();
+            }
+		}
+	}
+
+
+/** Copy Message Store directories from source to destination
+	except for the Index file, which is copied in the end after
+	all goes well.
+*/
+void CMsvCopyStoreOperation::CopySourceL()
+	{
+	iCopyCancel = EFalse;
+	TEntry entry;
+	TFileName dest=iDest;
+	entry = (*iDir)[iPos];
+
+	if(entry.iName==KIndex)
+    	{
+    	//complete yourself
+	   	CompleteSelf();
+    	}
+    else
+    	{
+	    iSrc.Zero();
+    	iSrc.Append(iServer.Context().MessageFolder());
+       	iSrc.Append(entry.iName);
+       	dest.Append(entry.iName);
+
+       	// Check if the entry is a directory
+    	if(entry.IsDir())
+    		{
+    		dest.Append('\\');
+    		iSrc.Append('\\');
+
+      		// Get the contents of this directory
+      		CDir *dirList=NULL;
+      		TInt err = iServer.FileSession().GetDir(iSrc, KEntryAttDir, ESortByName, dirList);
+
+      		// Get the count to check if the directory is empty
+      		TInt count = dirList->Count();
+      		delete dirList;
+
+      		if(err == KErrNone)
+      			{
+      			// Create an empty directory
+      			User::LeaveIfError(iServer.FileSession().MkDirAll(dest));
+
+      			// Copy the contents over
+      			if( count != 0 )
+       	     		{
+       	     		User::LeaveIfError(iFileMan->Copy(iSrc,dest,CFileMan::ERecurse,iStatus));
+    				SetActive();
+       	     		}
+       	     	// Directory empty nothing needed
+       	    	else
+       				{
+       				CompleteSelf();
+       				}
+      			}
+      		else
+      			{
+       			User::Leave(err);
+      			}
+    		}
+    	else
+    		{
+    		if(iPos==0)
+    			{
+    			User::LeaveIfError(iServer.FileSession().MkDirAll(dest));
+    			}
+    		User::LeaveIfError(iFileMan->Copy(iSrc,dest,CFileMan::EOverWrite,iStatus));
+    		SetActive();
+    		}
+    	}
+    dest.Zero();
+    iProgress().iCurrent = iPos++;
+    }
+
+
+/** Finish the Copy Process by copying over the index
+*/
+void CMsvCopyStoreOperation::FinishCopyStoreL()
+	{
+	TDriveUnit drive = MessageServer::CurrentDriveL(iServer.FileSession());
+	
+	TParse parse;
+	
+	TPtrC currDrive(drive.Name());
+	parse.Set(KMsvDbFile, &currDrive, NULL);
+	TFileName src = parse.FullName();
+	
+	TPtrC newDrive(iDrive.Name());
+	parse.Set(KMsvDbFile, &newDrive, NULL);
+	TFileName dest = parse.FullName();
+	
+#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
+	TRAP_IGNORE(iServer.Context().IndexAdapter()->GetDbAdapter()->DetachDBL(KCurrentDriveId));
+#else
+	delete iServer.Context().IndexAdapter()->GetDbAdapter();
+#endif
+	
+	RSqlDatabase::Copy(src, dest);
+
+#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
+	// Update the drive status of the target 
+	// drive in the preferred drive list.
+	iServer.UpdateDriveStatusL(iDrive, EMsvMessageStoreAvailableStatus);
+#endif
+
+	CompleteSelf();
+	}
+
+
+/** Unlock the Index file so other functions can
+    use it once again
+*/
+void CMsvCopyStoreOperation::UnlockMailStore()
+	{
+#if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
+	TMsvId maxId;
+	TDriveNumber driveNum = CMsvPreferredDriveList::GetDriveList()->CurrentDriveNumber();
+#if (defined SYMBIAN_MESSAGESTORE_HEADER_BODY_USING_SQLDB)
+	TRAP_IGNORE(iServer.Context().IndexAdapter()->GetDbAdapter()->AttachDBL(driveNum, KCurrentDriveId, maxId, &iServer.MessageDBAdapter()));
+#else
+	TRAP_IGNORE(iServer.Context().IndexAdapter()->GetDbAdapter()->AttachDBL(driveNum, KCurrentDriveId, maxId));
+#endif
+#else
+	TDriveUnit driveUnit;
+	TRAP_IGNORE(driveUnit = MessageServer::CurrentDriveL(iServer.FileSession()));
+	TPtrC drive(driveUnit.Name());
+	TParse parse;
+	parse.Set(KMsvDbFile, &drive, NULL);
+	TFileName dest = parse.FullName();
+	TRAP_IGNORE(iServer.Context().IndexAdapter()->OpenclosedL(dest));
+#endif		// #if (defined SYMBIAN_MSGS_ENHANCED_REMOVABLE_MEDIA_SUPPORT)
+
+	iServer.NotifyChanged(EMsvMediaAvailable, KMsvNullIndexEntryId, iServer.Drive());
+	iServer.Context().IndexAdapter()->SetErrorState(KErrNone);
+	iServer.SetStartupState(EMsvNullNotification);
+	}
+
+
+/** Complete the unlock process and Set yourself active
+*/
+void CMsvCopyStoreOperation::CompleteSelf()
+	{
+	TRequestStatus *status=&iStatus;
+	iStatus=KRequestPending;
+	SetActive();
+	User::RequestComplete(status,KErrNone);
+	}
+
+
+TInt CMsvCopyStoreOperation::RunError(TInt aError)
+	{
+
+	//Delete any broken store on the target disk
+	TUint unused;
+	if (iDest.Length() > 0)
+		{
+		if(iServer.FileSession().Att(iDest,unused) == KErrNone)
+			{
+			iFileMan->RmDir(iDest);	// ignore error - don't care if fails
+			}
+		}
+
+	Completed(aError);
+	return(KErrNone);
+	}
+
+/* Completed */
+void CMsvCopyStoreOperation::Completed(TInt aError)
+	{
+	iProgress().iError=aError;
+	if(iProgress().iState >= TMsvCopyProgress::ELock)
+		{
+		 UnlockMailStore();
+		}
+	SetState(EMsvOperationCompleted);
+	iMessage.Complete(KErrNone);
+	}
+
+/* Check the target disk space */
+void CMsvCopyStoreOperation::CheckDiskSpaceL()
+	{
+	RFs& fs = iServer.FileSession();
+	TInt64 spaceNeed=KMinimumSpace;
+	CDirScan* dirScan = CDirScan::NewLC(fs);
+	CDir* dirList = NULL;
+    
+	TVolumeIOParamInfo volumeIOInfo;
+	User::LeaveIfError(fs.VolumeIOParam(TInt(iDrive), volumeIOInfo));
+	TInt clusterSize = volumeIOInfo.iClusterSize;
+	dirScan->SetScanDataL(iServer.Context().MessageFolder(), KEntryAttNormal, ESortByName, CDirScan::EScanDownTree);
+
+	do{
+		dirScan->NextL(dirList);
+		CleanupStack::PushL(dirList);
+		if(dirList)
+			{
+			for(TInt item = 0; item < dirList->Count(); ++item)
+				{
+				const TEntry& entry = (*dirList)[item];
+				spaceNeed+=entry.iSize+clusterSize;
+				}
+			}
+		else
+			{
+			spaceNeed+=KMinimumSpace;
+			}
+         
+		CleanupStack::PopAndDestroy(dirList);
+		} while(dirList); 
+
+	 CleanupStack::PopAndDestroy(dirScan);
+	 TVolumeInfo volumeInfo;
+	 User::LeaveIfError(iServer.FileSession().Volume(volumeInfo, iDrive));
+	 if(volumeInfo.iFree<spaceNeed)
+	 	{
+	 	User::Leave(KErrDiskFull);
+	 	}
+	 }
+/**
+NotifyFileManOperation is called by the Fileman observer framework after/before each
+file operation.
+@param None.
+@return MFileManObserver::TControl
+        ECancel If the operation is cancelled for one entry.
+        EContinue If the operation needs to be continued.
+*/
+MFileManObserver::TControl CMsvCopyStoreOperation::NotifyFileManOperation()
+	{
+	if(iCopyCancel)
+		{
+		return MFileManObserver::ECancel;
+		}
+	return MFileManObserver::EContinue;
+	}
+
+/**
+NotifyFileManEnded is called by the Fileman observer framework to inform the observer
+that an operation is complete.
+@param None.
+@return MFileManObserver::TControl
+        EAbort If the bulk operation is cancelled.
+        EContinue If the operation needs to be continued.
+
+*/
+MFileManObserver::TControl CMsvCopyStoreOperation::NotifyFileManEnded()
+	{
+	if(iCopyCancel)
+		{
+		return MFileManObserver::EAbort;
+		}
+	return MFileManObserver::EContinue;
+	}