messagingfw/msgsrvnstore/server/src/CMsvPlainBodyText.cpp
changeset 0 8e480a14352b
child 36 e7635922c074
child 39 e5b3a2155e1a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/messagingfw/msgsrvnstore/server/src/CMsvPlainBodyText.cpp	Mon Jan 18 20:36:02 2010 +0200
@@ -0,0 +1,1143 @@
+// Copyright (c) 2007-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:
+// CMsvPlainBodyText.cpp
+//
+
+#include <cmsvplainbodytext.h>
+#include "cmsvbodytext.h"
+#include <mmsvstoremanager.h>
+#include "MSVPANIC.H"
+#include <txtrich.h>
+#include <utf.h>
+#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS  
+#include "msvconsts.h"
+#endif
+
+/**
+The number of characters that will be converted to Unicode at any one time.
+*/
+const TInt KMaxDecodeUnicodeLength = 20;
+
+/**
+The NewL factory function for Write operation.
+@param aMsvStoreManager	 The MMsvStoreManager reference to call Store related RFile APIs
+@param aStore 			 The CMsvStore object.
+@param aIs8Bit			 TBool indicating whether to store bdy text as 8/16 bit.
+@param aCharsetId		 The charset of the body part.
+@param aDefaultCharsetId The default charset of the system.
+@param aMessageId		 The Id of the message that is to be stored.
+@param aFs				 The RFs for handling RFile related operations.
+@return CMsvPlainBodyText.
+*/
+CMsvPlainBodyText* CMsvPlainBodyText::NewL(MMsvStoreManager& aMsvStoreManager, CMsvStore& aStore, TBool aIs8Bit, TUint aCharsetId, TUint aDefaultCharsetId, TMsvId aMessageId, RFs& aFs)
+	{
+	CMsvPlainBodyText* self = new(ELeave)CMsvPlainBodyText(aMsvStoreManager, aStore, aIs8Bit, aCharsetId, aDefaultCharsetId, aMessageId, aFs);
+	CleanupStack::PushL(self);
+	self->ConstructWriteL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+/**
+Overloaded NewL for Read operation.
+@param aMsvStoreManager	 The MMsvStoreManager reference to call Store related RFile APIs
+@param aStore			 The CMsvStore object.
+@param aMessageId		 The Id of the message that is to be stored.
+@param aFs				 The RFs for handling RFile related operations.
+@param aChunkLength		 The length of the chunk that will be stored/restored in single operaion
+@return CMsvPlainBodyText.
+*/
+CMsvPlainBodyText* CMsvPlainBodyText::NewL(MMsvStoreManager& aMsvStoreManager, CMsvStore& aStore, TMsvId aMessageId, RFs& aFs, TInt aChunkLength)
+	{
+	CMsvPlainBodyText* self = new(ELeave)CMsvPlainBodyText(aMsvStoreManager, aStore, aMessageId, aFs, aChunkLength);
+	CleanupStack::PushL(self);
+	self->ConstructReadL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+/**
+Default Constructor for write operation.
+*/
+CMsvPlainBodyText::CMsvPlainBodyText(MMsvStoreManager& aMsvStoreManager, CMsvStore& aStore, TBool aIs8Bit, TUint aCharsetId, TUint aDefaultCharsetId, TMsvId aMessageId,RFs& aFs)
+: CMsgActive(EPriorityNormal), iMsvStoreManager(aMsvStoreManager), iStore(aStore), iIs8Bit(aIs8Bit), iCharsetId(aCharsetId), iDefaultCharsetId(aDefaultCharsetId), 
+  iMessageId(aMessageId), iFSession(aFs), iMsvFileStatus(EMsvFileWriting)
+  	{
+  	iAvailable = CCnvCharacterSetConverter::ENotAvailable;
+    CActiveScheduler::Add(this);
+	}
+
+/**
+Default Constructor called for Read operation..
+*/
+CMsvPlainBodyText::CMsvPlainBodyText(MMsvStoreManager& aMsvStoreManager, CMsvStore& aStore, TMsvId aMessageId, RFs& aFs, TInt aChunkLength)
+: CMsgActive(EPriorityNormal), iMsvStoreManager(aMsvStoreManager), iStore(aStore), iMessageId(aMessageId), iFSession(aFs), iPos(0), iChunkMaxLength(aChunkLength), 
+   iStartPosOfNextChunk(0),  iMsvFileStatus(EMsvFileReading)
+	{
+	iAvailable = CCnvCharacterSetConverter::ENotAvailable;
+	CActiveScheduler::Add(this);
+	}
+
+/**
+ConstructWriteL for creating and writing to the plain bodytext file.
+*/
+void CMsvPlainBodyText::ConstructWriteL()
+	{
+	RMsvWriteStream out;
+
+	if(iIs8Bit)
+		{
+		out.AssignLC(iStore, KMsvPlainBodyText8);
+		out.WriteUint32L(iCharsetId);
+		out.WriteUint32L(iDefaultCharsetId);
+		}
+	else
+		{
+		out.AssignLC(iStore, KMsvPlainBodyText16);
+		}
+	// 	iIsCommitted will be EFalse at this point.
+	out.WriteUint32L(iIsCommitted);
+
+    TBuf<KFileNameFixedWidth> fileName;
+	fileName.NumFixedWidth(iMessageId, EHex, KFileNameFixedWidth);
+	
+	out << (TDes&)fileName;
+	out.CommitL();
+	out.Close();
+	iStore.CommitL();
+
+	iMsvStoreManager.CreatePlainTextFileL(iFile, iMessageId);
+	
+	iRemainingConvertedData.Create(KMaxDecodeUnicodeLength);
+	CleanupStack::PopAndDestroy(&out);
+	}
+
+/**
+ConstructL for Read operation.
+*/
+void CMsvPlainBodyText::ConstructReadL()
+	{
+	// Check whether the data is stored in old-style non-chunk storage mechanism.
+	if(iStore.IsPresentL(KMsvEntryRichTextBody) || iStore.IsPresentL(KMsv8BitEncodedBodyData))
+		{
+		iParaLayer = CParaFormatLayer::NewL();
+		iCharLayer = CCharFormatLayer::NewL();
+		iRichText = CRichText::NewL(iParaLayer, iCharLayer);
+		iStore.RestoreBodyTextL(*iRichText);
+		}
+	else // Data is stored using the new chunk storage mechanism.
+		{
+		RMsvReadStream in;
+		if(iStore.IsPresentL(KMsvPlainBodyText8))
+			{
+			in.OpenLC(iStore, KMsvPlainBodyText8);
+			iCharsetId = in.ReadUint32L();
+			iDefaultCharsetId = in.ReadUint32L();
+			iIs8Bit = ETrue;
+			}
+		else if(iStore.IsPresentL(KMsvPlainBodyText16))
+			{
+			in.OpenLC(iStore, KMsvPlainBodyText16);
+			}
+		else
+			{
+			// The stream is not found.
+			User::Leave(KErrNotFound);
+			}
+		iIsCommitted = in.ReadUint32L();
+		HBufC* filename = HBufC::NewLC(in,KMaxFileName);
+		iFilePath = HBufC::NewL(KMaxFileName);
+		
+		TPtr16 fileNamePtr = iFilePath->Des();
+		iMsvStoreManager.BodyTextFilePathL(iMessageId, fileNamePtr);
+		fileNamePtr.Append(*filename);
+		iMsvStoreManager.OpenBodyTextFileForReadL(iFile, iMessageId, *iFilePath);
+		
+		iRemainingConvertedData.Create(KMaxDecodeUnicodeLength);
+		CleanupStack::PopAndDestroy(2,&in);// filename, in
+		}
+	}
+
+/**
+Destructor
+*/
+EXPORT_C CMsvPlainBodyText::~CMsvPlainBodyText()
+	{
+	delete iConverter;
+	delete iParaLayer;
+	delete iCharLayer;
+	delete iRichText;
+	delete iFilePath;
+	iTempChunk16.Close();
+	iRemainingConvertedData.Close();
+
+	iStartPosOfEachChunkInFile.Close();
+	iRfileReadBuf8.Close();
+	
+	// Cannot leave in destructor, so ignore any error.
+	// iFile will be closed by a call to RevertL method.
+	TRAP_IGNORE(RevertL());
+	}
+
+/**
+Store the plain body text part in chunks.
+@param aChunk. 				The 8 bit chunk that is to be stored.
+@param aStatus. 			The TRequestStatus parameter for this request.
+@leave KErrAccessDenied.	If CMsvStore was opened in Read mode or
+							If CommitL is already called.
+@leave Other 				Standard system-wide error codes.
+@return void.
+*/
+EXPORT_C void CMsvPlainBodyText::StoreChunkL(const TDesC8& aChunk, TRequestStatus& aStatus)
+	{
+	iTempChunk16.Close();
+	if(iIsCommitted || iMsvFileStatus == EMsvFileReading)
+		{
+		// Leave if CommitL is already called or CMsvStore is opened in Read mode.
+		User::Leave(KErrAccessDenied);
+		}
+
+	if(iIs8Bit)
+		{
+		iFile.Write(aChunk, aStatus);
+		}
+	// convert to 16..........
+	else
+		{
+		ConvertChunkToUnicodeForStoreL(aChunk);
+		iFile.Write(iRfileWritePtr8, aStatus);
+		}
+	}
+
+/**
+Store the plain body text part in chunks,synchronous version.
+@param aChunk. 				The 8 bit chunk that is to be stored.
+@leave KErrAccessDenied.	If CMsvStore was opened in Read mode or
+							If CommitL is already called.
+@leave Other 				Standard system-wide error codes.
+@return void.
+*/
+EXPORT_C void CMsvPlainBodyText::StoreChunkL(const TDesC8& aChunk)
+	{
+	iTempChunk16.Close();
+	if(iIsCommitted || iMsvFileStatus == EMsvFileReading)
+		{
+		// Leave if CommitL is already called or CMsvStore is opened in Read mode.
+		User::Leave(KErrAccessDenied);
+		}
+	if(iIs8Bit)
+		{
+		User::LeaveIfError(iFile.Write(aChunk));
+		}
+	else
+		{
+		ConvertChunkToUnicodeForStoreL(aChunk);
+		User::LeaveIfError(iFile.Write(iRfileWritePtr8));
+		}
+	}
+
+/**
+Converts the 8 bit chunk to Unicode.
+@param aChunk The 8bit chunk to be converted.
+@return void.
+*/
+void CMsvPlainBodyText::ConvertChunkToUnicodeForStoreL(const TDesC8& aChunk)
+	{
+	TInt chunkLength = aChunk.Length();
+	TPtrC8 chunkPtr8(aChunk);
+
+	iTempChunk16.Create(chunkLength);
+
+	PrepareToConvertL();
+	TInt charsetState = CCnvCharacterSetConverter::KStateDefault;
+
+	if (iAvailable == CCnvCharacterSetConverter::ENotAvailable)
+		{
+		// Copy data if character convertor not available
+		iTempChunk16.Copy(chunkPtr8);
+		}
+	else
+		{
+		TInt rem = iConverter->ConvertToUnicode(iTempChunk16, chunkPtr8, charsetState);
+		if (rem < 0)
+			{
+			// Copy the data without conversion,if there was error while converting.
+			iTempChunk16.Copy(aChunk);
+			}
+		else if(rem > 0)
+			{
+			// Some of the characters were not converted because the output buffer
+			// was not long enough, so reallocate the output buffer.
+			do
+				{
+				TBuf<KMaxDecodeUnicodeLength> buf;
+				chunkPtr8.Set(chunkPtr8.Right(rem));
+				rem = iConverter->ConvertToUnicode(buf, chunkPtr8, charsetState);
+				if(iTempChunk16.Length() + buf.Length() > iTempChunk16.MaxLength())
+					{
+					iTempChunk16.ReAllocL(iTempChunk16.Length() + buf.Length());
+					}
+				iTempChunk16.Append(buf);
+				}while(rem > 0);
+			}
+		}
+		TPtrC8 convertedChunk8((TUint8*)(iTempChunk16.Ptr()), iTempChunk16.Length()*2);
+		iRfileWritePtr8.Set(convertedChunk8);
+	}
+
+/**
+Store the body part in chunks(16 bit version).
+@param aChunk 			 The 16 bit chunk that is to be stored.
+@param aStatus	 		 The TRequestStatus parameter for this request.
+@leave KErrNotSupported  If the 8-bit storage was enabled.
+@leave KErrAccessDenied  If CMsvStore was opened in Read mode or
+						 IfCommitL is already called.
+@leave Other 			 Standard system-wide error codes.
+@return void
+*/
+EXPORT_C void CMsvPlainBodyText::StoreChunkL(const TDesC16& aChunk, TRequestStatus& aStatus)
+	{
+	if(iIs8Bit)	
+		{
+		User::Leave(KErrNotSupported);
+		}
+	if(iIsCommitted || iMsvFileStatus == EMsvFileReading)
+		{
+		// Leave if CommitL is already called or CMsvStore is opened in Read mode.
+		User::Leave(KErrAccessDenied);
+		}	
+
+	TPtrC8 inFileBuf8((TUint8*)(aChunk.Ptr()), aChunk.Length()*2);
+	iFile.Write(inFileBuf8, aStatus);
+	}
+
+/**
+Store the body part in chunks(16 bit synchronous version).
+@param aChunk 			 The 16 bit chunk that is to be stored.
+@leave KErrNotSupported  If the 8-bit storage was enabled.						 
+@leave KErrAccessDenied  If CMsvStore was opened in Read mode or
+						 If CommitL is already called.
+@leave Other 			 Standard system-wide error codes.
+@return void
+*/
+EXPORT_C void CMsvPlainBodyText::StoreChunkL(const TDesC16& aChunk)
+	{
+	if(iIs8Bit)	
+		{
+		User::Leave(KErrNotSupported);
+		}
+	if(iIsCommitted || iMsvFileStatus == EMsvFileReading)
+		{
+		// Leave if CommitL is already called or CMsvStore is opened in Read mode.
+		User::Leave(KErrAccessDenied);
+		}
+
+	TPtrC8 inFileBuf8((TUint8*)(aChunk.Ptr()), aChunk.Length()*2);
+	User::LeaveIfError(iFile.Write(inFileBuf8));
+	}
+
+/**
+Returns the size of body text in bytes.
+@param None.
+@return TInt  The size of the plain body text stored in bytes.
+*/
+EXPORT_C TInt CMsvPlainBodyText::Size()
+	{
+	TInt size=0;
+	if(iRichText)
+		{
+		size = iRichText->DocumentLength() * 2;// Since Unicode multiply by 2.
+		}
+	else
+		{
+		iFile.Size(size);
+		}
+	return size;
+	}
+
+/**
+Converts and stores the CRichText contents to a plain text.
+@param aRichText 		 The CRichText object that will be stored as plain body text.
+@leave KErrNotSupported  If CMsvStore was opened in Read mode or
+						 If CommitL is already called.
+@leave Other 			 Standard system-wide error codes.
+@return void.
+*/
+EXPORT_C void CMsvPlainBodyText::StoreRichTextAsPlainTextL(CRichText& aRichText)
+	{
+	if(iIs8Bit)	
+		{
+		User::Leave(KErrNotSupported);
+		}
+	if(iIsCommitted || iMsvFileStatus == EMsvFileReading)
+		{
+		// Leave if CommitL is already called or CMsvStore is opened in Read mode.
+		User::Leave(KErrAccessDenied);
+		}
+  	// create a buffer of 512 bytes to extract data from a rich text instead of extracting
+  	// the entire richtext so that 2 copies of the mail is not created in RAM.
+	TBuf<KMsvDecodeChunkLength> richBuf;
+	TInt currentPos = 0;
+	TInt richTextLen = aRichText.DocumentLength();
+	TInt lengthToExtract = richTextLen;
+	while(richTextLen > 0)
+	 	{
+	 	if(lengthToExtract > KMsvDecodeChunkLength)
+	 		{
+	 		lengthToExtract = KMsvDecodeChunkLength;
+	 		}
+	 	aRichText.Extract(richBuf,currentPos,lengthToExtract);
+	 	TPtrC8 richTextPtr1((TUint8*)(richBuf.Ptr()), richBuf.Length() * 2);
+	 	User::LeaveIfError(iFile.Write(richTextPtr1));
+	  	currentPos += lengthToExtract;
+	  	richTextLen -=lengthToExtract;
+	   	if(richTextLen <= lengthToExtract)
+	 		{
+	 		lengthToExtract = richTextLen;
+	 		}
+	 	 }
+	}
+
+/**
+Retrieve the next chunk of the plain body text.
+@param aChunk 			 The output parameter contains the requested chunk.
+@param aStatus.			 The TRequestStatus for this request.
+@leave KErrAccessDenied  If CMsvStore was opened in Write mode.
+@leave KErrNotSupported  If 16-bit storage is enabled.
+@leave KErrUnderflow 	 If aChunk MaxLength is less than iChunkMaxLength.
+@leave Other 				Standard system-wide error codes.
+@return void
+*/
+EXPORT_C void CMsvPlainBodyText::NextChunkL(TDes8& aChunk, TRequestStatus& aStatus)
+	{
+	if(!iIs8Bit)
+		{
+		// If 16-bit storage is enabled.
+		User::Leave(KErrNotSupported);
+		}
+	if(iMsvFileStatus == EMsvFileWriting)
+		{
+		// Leave if CMsvStore is opened in Write mode.
+		User::Leave(KErrAccessDenied);
+		}	
+	if(aChunk.MaxLength() < iChunkMaxLength)
+		{
+		User::Leave(KErrUnderflow);
+		}
+	
+	iChunk8 = &aChunk;	
+	iChunk8->SetLength(0);
+	
+	iRetrieving8bit = ETrue;
+	
+	//store the current position of the file so that if request cancels and for next 
+	// call to NextChunkL() will attempt to read the same chunk that was cancelled
+	iCurrentFilePos = 0;
+	User::LeaveIfError(iFile.Seek(ESeekCurrent, iCurrentFilePos));
+	
+	Queue(aStatus);
+	iFile.Read(aChunk, iChunkMaxLength, iStatus);
+	SetActive();
+	}
+
+/**
+Retrieve the next chunk of the plain body text.
+@param aChunk 			 The output parameter contains the requested chunk.
+@leave KErrNotSupported  If CMsvStore was opened in Write mode.
+@leave KErrNotSupported  If 16-bit storage is enabled.
+@leave KErrUnderflow	 If aChunk MaxLength is less than iChunkMaxLength.
+@leave Other 			 Standard system-wide error codes.
+@return void
+*/
+EXPORT_C void CMsvPlainBodyText::NextChunkL(TDes8& aChunk)
+	{
+	if(!iIs8Bit)
+		{
+		// If 16-bit storage is enabled.
+		User::Leave(KErrNotSupported);
+		}
+	if(iMsvFileStatus == EMsvFileWriting)
+		{
+		// Leave if CMsvStore is opened in Write mode.
+		User::Leave(KErrAccessDenied);
+		}
+	if(aChunk.MaxLength() < iChunkMaxLength)
+		{
+		User::Leave(KErrUnderflow);
+		}
+	aChunk.SetLength(0);
+	User::LeaveIfError(iFile.Read(aChunk, iChunkMaxLength));
+
+	if(aChunk.Length()>0)
+		{
+		iReadChunkLength = aChunk.Length();
+		}
+	}
+
+/**
+Retrieve  the next chunk of the plain body text. If body is stored as 8bit, the chunk is
+converted to unicode.Unconverted bytes will be converted while reading the next chunk.
+@param aChunk 			 The output parameter contains the requested chunk.If no more data is in Store this
+						 contains NULL descriptor.
+@param aStatus  		 The TRequestStatus for this request.
+@leave KErrAccessDenied  If CMsvStore was opened in Write mode.
+@leave KErrUnderflow	 If aChunk MaxLength is less than iChunkMaxLength.
+@leave Other 			 Standard system-wide error codes.
+@return void
+*/
+EXPORT_C void CMsvPlainBodyText::NextChunkL(TDes16& aChunk, TRequestStatus& aStatus)
+	{
+	if(iMsvFileStatus == EMsvFileWriting)
+		{
+		// Leave if CMsvStore is opened in Write mode.
+		User::Leave(KErrAccessDenied);
+		}
+
+	if(aChunk.MaxLength() < iChunkMaxLength)
+		{
+		User::Leave(KErrUnderflow);
+		}
+
+	if(iRichText)
+ 		{
+ 		ExtractNextChunkFromRichText(aChunk);
+	 	TRequestStatus *status=&aStatus;
+		User::RequestComplete(status, KErrNone);
+ 		}
+	else
+		{
+		iRfileReadBuf8.Close();
+		iChunk16 = &aChunk;	
+		iChunk16->SetLength(0);
+		
+		//store the current position of the file so that if request cancels and for next 
+		// call to NextChunkL() will attempt to read the same chunk that was cancelled
+		iCurrentFilePos = 0;
+		User::LeaveIfError(iFile.Seek(ESeekCurrent, iCurrentFilePos));
+	
+		Queue(aStatus);
+		if(iIs8Bit)
+			{
+			// if there is any converted data then append to aChunk
+			if(iRemainingConvertedData.Length() != 0)
+				{
+				iChunk16->Append(iRemainingConvertedData);
+				iRemainingConvertedData.SetLength(0);
+				}
+				
+			iRfileReadBuf8.Create(iChunkMaxLength);
+			// 8-bit data read will be converted to Unicode in DoRunL.
+			iFile.Read(iRfileReadBuf8, iChunkMaxLength, iStatus);
+			SetActive();
+			}
+		else
+			{
+			iRfileReadBuf8.Create(iChunkMaxLength*2);
+			iFile.Read(iRfileReadBuf8, iChunkMaxLength*2, iStatus);
+			SetActive();
+			}
+		}
+	}
+	
+/**
+Retrieve the next chunk of the plain body text. If body is stored as 8bit, convert the chunk to Unicode.
+Unconverted bytes will be converted while reading the next chunk.
+@param aChunk			 The output parameter contains the requested chunk.If no more data is in Store this
+						 contains NULL descriptor.
+@leave KErrAccessDenied  If CMsvStore was opened in Write mode.
+@leave KErrUnderflow	 If aChunk MaxLength is less than iChunkMaxLength.
+@leave Other 			 Standard system-wide error codes.
+@return void
+*/
+EXPORT_C void CMsvPlainBodyText::NextChunkL(TDes16& aChunk)
+	{
+	if(iMsvFileStatus == EMsvFileWriting)
+		{
+		// Leave if CMsvStore is opened in Write mode.
+		User::Leave(KErrAccessDenied);
+		}
+
+	if(aChunk.MaxLength() < iChunkMaxLength)
+		{
+		User::Leave(KErrUnderflow);
+		}
+
+	if(iRichText)
+ 		{
+ 		ExtractNextChunkFromRichText(aChunk);
+ 		}
+ 	else if(iIs8Bit)
+ 		{
+ 		iRfileReadBuf8.Close();
+ 		iChunk16 = &aChunk;	
+	
+ 		iChunk16->SetLength(0);
+ 		
+		TInt numOfBytesInCurrentChunk = 0;
+		// if there is any converted data then append to aChunk
+		if(iRemainingConvertedData.Length() != 0)
+			{
+			iChunk16->Append(iRemainingConvertedData);
+			iRemainingConvertedData.SetLength(0);
+			}
+
+	 	PrepareToConvertL();
+	 	if(iAvailable != CCnvCharacterSetConverter::EAvailable)
+	 		{
+	 		User::Leave(KErrNotSupported);
+	 		}
+
+ 		iIsLastChunk = EFalse;
+ 		while(iChunk16->Length() < iChunkMaxLength && !iIsLastChunk)
+			{
+			iRfileReadBuf8.Create(iChunkMaxLength);
+			iFile.Read(iRfileReadBuf8, iChunkMaxLength);
+			// iRfileReadBuf8 will be closed when ConvertChunkToUnicodeForRestoreL returns.
+ 			numOfBytesInCurrentChunk = ConvertChunkToUnicodeForRestoreL();
+			}
+
+		// Push start position of current chunk in to the stack.
+		iStartPosOfEachChunkInFile.Append(iStartPosOfNextChunk);
+		iStartPosOfNextChunk += numOfBytesInCurrentChunk;
+		iReadChunkLength = numOfBytesInCurrentChunk;
+		}
+	else // KMsvPlainBodyText16
+		{
+		HBufC8* inBuffer = HBufC8::NewLC(iChunkMaxLength * 2);
+		TPtr8 inPtr16(inBuffer->Des());
+		iFile.Read(inPtr16, iChunkMaxLength * 2); // Multiply by 2 to get no of bytes.
+		TPtrC16 ptr16((TUint16*)(inBuffer->Ptr()), inBuffer->Length() / 2);
+		aChunk.Copy(ptr16);
+		CleanupStack::PopAndDestroy(inBuffer);
+		if(aChunk.Length())
+			{
+			iReadChunkLength = aChunk.Length();
+			}
+		
+		}
+	}
+
+/**
+Extracts a chunk of iChunkMaxLength or less, from the CRichText object.
+*/
+void CMsvPlainBodyText::ExtractNextChunkFromRichText(TDes16& aChunk)
+	{
+	TInt docLength = iRichText->DocumentLength();
+	// If we are at the end of the body text.
+	if(iPos >= docLength)
+		{
+		aChunk.Copy(KNullDesC16);
+		}
+	else
+		{
+		if(iPos+iChunkMaxLength <= docLength)
+			{
+			iRichText->Extract(aChunk, iPos, iChunkMaxLength);
+			iPos += iChunkMaxLength;
+			iReadChunkLength = iChunkMaxLength;
+			}
+		else // If it is the last chunk.
+			{
+			iRichText->Extract(aChunk, iPos, docLength-iPos);
+			iReadChunkLength = docLength-iPos;
+			iPos = docLength;
+			}
+		}
+	}
+
+/**
+Retrieve the previous chunk of the plain body text asynchronously.
+@param aChunk 			 The output parameter containing the requested chunk on completion.
+@param aStatus  		 The TRequestStatus for this request.
+@leave KErrNotSupported  If 16-bit storage is enabled.
+@leave KErrAccessDenied  If CMsvStore was opened in Write mode.
+@leave KErrUnderflow	 If aChunk MaxLength is less than iChunkMaxLength.
+@leave Other 			 Standard system-wide error codes.
+@return void
+*/
+EXPORT_C void CMsvPlainBodyText::PreviousChunkL(TDes8& aChunk, TRequestStatus& aStatus)
+	{
+	TInt currentPos = 0;
+	User::LeaveIfError(iFile.Seek(ESeekCurrent, currentPos));
+	
+	// point to the position from where previous chunk was read.
+	currentPos -= iReadChunkLength;
+	// point to the position from where the new chunk is to be read.
+	currentPos -= iChunkMaxLength;
+
+	if(currentPos >= 0)
+		{
+		iFile.Seek(ESeekStart, currentPos);
+		NextChunkL(aChunk, aStatus);
+		}
+	else
+		{
+		aChunk.Copy(KNullDesC8);
+		TRequestStatus *status = &aStatus;
+		User::RequestComplete(status, KErrNone);
+		}
+	}
+
+/**
+Retrieve the previous chunk of the plain body text.
+@param aChunk 			 The output parameter containing the requested chunk.
+@leave KErrNotSupported  If 16-bit storage is enabled.
+@leave KErrAccessDenied  If CMsvStore was opened in Write mode. 
+@leave KErrUnderflow	 If aChunk MaxLength is less than iChunkMaxLength.
+@leave Other 			 Standard system-wide error codes.
+@return void
+*/
+EXPORT_C void CMsvPlainBodyText::PreviousChunkL(TDes8& aChunk)
+	{
+	TInt currentPos = 0;
+	iFile.Seek(ESeekCurrent, currentPos);
+
+	// point to the position from where previous chunk was read.
+	currentPos -= iReadChunkLength;
+	 // point to the position from where the new chunk is to be read.
+	currentPos -= iChunkMaxLength;
+
+	if(currentPos >= 0)
+		{
+		iFile.Seek(ESeekStart, currentPos );
+		NextChunkL(aChunk);
+		}
+	else
+		{
+		aChunk.Copy(KNullDesC8);
+		}
+	}
+
+/**
+Retrieve the previous chunk of the plain body text asynchronously.DoRunl converts the chunk to unicode.
+@param aChunk 			 The output parameter containing the requested chunk on completion.
+@param aStatus  		 The TRequestStatus for this request.
+@leave KErrNotSupported  If CMsvStore was opened in Write mode.
+@leave KErrUnderflow	 If aChunk MaxLength is less than iChunkMaxLength.
+@leave Other 			 Standard system-wide error codes.
+@return void
+*/
+EXPORT_C void CMsvPlainBodyText::PreviousChunkL(TDes16& aChunk, TRequestStatus& aStatus)
+	{
+	iPrevChunk = ETrue;
+	if(iRichText)
+ 		{
+ 		ExtractPreviousChunkFromRichText(aChunk);
+ 		TRequestStatus *status = &aStatus;
+		User::RequestComplete(status, KErrNone);
+ 		}
+	else 
+		{
+		if(iIs8Bit && iStartPosOfEachChunkInFile.Count() >= 2)
+			{
+			delete iRemainingUnConvertedData;
+			iRemainingUnConvertedData = NULL;
+			TInt count = iStartPosOfEachChunkInFile.Count() - 1;
+			TInt numOfBytes = iStartPosOfEachChunkInFile[count] - iStartPosOfEachChunkInFile[count - 1];
+			iFile.Seek(ESeekStart, iStartPosOfEachChunkInFile[count - 1] );
+			iReadChunkLength = numOfBytes;
+			NextChunkL(aChunk,aStatus);	
+			}
+		else
+			{
+			TInt startPos = 0;
+			TInt currentPos = 0;
+			User::LeaveIfError(iFile.Seek(ESeekCurrent, currentPos));
+			TInt prevChunkPos = 0;
+			if(currentPos > 0)
+				{
+				startPos = currentPos - iReadChunkLength * 2;
+				if(startPos > 0)
+					{
+					prevChunkPos = startPos - iChunkMaxLength * 2;
+					if(prevChunkPos >= 0)
+						{
+						iFile.Seek(ESeekStart, prevChunkPos);
+						NextChunkL(aChunk,aStatus);	
+						}
+					else
+						{
+						iChunk16->Copy(KNullDesC16);
+						TRequestStatus *status = &aStatus;
+						User::RequestComplete(status, KErrNone);
+						}
+					}
+				else
+					{
+					aChunk.Copy(KNullDesC16);
+					TRequestStatus *status = &aStatus;
+					User::RequestComplete(status, KErrNone);
+					}
+				}
+			else
+				{
+				aChunk.Copy(KNullDesC16);
+				TRequestStatus *status = &aStatus;
+				User::RequestComplete(status, KErrNone);
+				}
+			}
+		}	
+	}
+
+/**
+Retrieve the Previouschunk of the plain body text. If body is stored as 8bit, convert the chunk to Unicode.
+@param aChunk 			 The output parameter contains the requested chunk.
+@leave KErrNotSupported  If CMsvStore was opened in Write mode.
+@leave KErrUnderflow	 If aChunk MaxLength is less than iChunkMaxLength.
+@leave Other 			 Standard system-wide error codes.
+@return void
+*/
+EXPORT_C void CMsvPlainBodyText::PreviousChunkL(TDes16& aChunk)
+	{
+	if(iRichText)
+ 		{
+ 		ExtractPreviousChunkFromRichText(aChunk);
+ 		}
+	else 
+		{
+		if(iIs8Bit && iStartPosOfEachChunkInFile.Count() >= 2)
+			{
+			delete iRemainingUnConvertedData;
+			iRemainingUnConvertedData = NULL;
+
+			TInt count = iStartPosOfEachChunkInFile.Count() - 1;
+
+			iFile.Seek(ESeekStart, iStartPosOfEachChunkInFile[count - 1] );
+			TInt numOfBytes = iStartPosOfEachChunkInFile[count] - iStartPosOfEachChunkInFile[count - 1];
+			iReadChunkLength = numOfBytes;
+			// Pop one position from iStartPosOfEachChunkInFile
+			iStartPosOfNextChunk = iStartPosOfEachChunkInFile[count];
+			iStartPosOfEachChunkInFile.Remove(count);
+			NextChunkL(aChunk);
+			}
+		else
+			{
+			TInt startPos = 0;
+			TInt currentPos = 0;
+			iFile.Seek(ESeekCurrent, currentPos);
+			TInt prevChunkPos = 0;
+
+			if(currentPos > 0)
+				{
+				startPos = currentPos - iReadChunkLength * 2;// Multiply by 2 to get no of bytes.
+				if(startPos > 0)
+					{
+					prevChunkPos = startPos - iChunkMaxLength * 2;// Multiply by 2 to get no of bytes.
+					if(prevChunkPos >= 0)
+						{
+						iFile.Seek(ESeekStart, prevChunkPos );
+						NextChunkL(aChunk);
+						}
+					else
+						{
+						aChunk.Copy(KNullDesC16);
+						}
+					}
+				}
+			}
+		}	
+	}
+
+/**
+Extracts a chunk of iChunkMaxLength or less, from the CRichText object.
+*/
+void CMsvPlainBodyText::ExtractPreviousChunkFromRichText(TDes16& aChunk)
+	{
+	iPos -= iReadChunkLength;
+	if(iPos > 0)
+		{
+		iPos -= iChunkMaxLength;
+		if(iPos >= 0)
+			{
+			iRichText->Extract(aChunk, iPos, iChunkMaxLength);
+			iPos += iChunkMaxLength;
+			iReadChunkLength = iChunkMaxLength;
+			}
+		}
+	}
+
+/**
+Asynchronously retrives the body text chunks.If body is stored as 8bit, converts the chunk to Unicode.
+@param None.
+@return
+*/
+void CMsvPlainBodyText::DoRunL()
+	{
+	User::LeaveIfError(iStatus.Int());
+	if(iRetrieving8bit)
+		{
+		if(iChunk8->Length())
+			{
+			iReadChunkLength = iChunk8->Length();
+			}
+		Complete(iStatus.Int());
+		iRetrieving8bit = EFalse;
+		}
+	else
+		{
+		iIsLastChunk = EFalse;
+		if (iIs8Bit)
+	 		{
+	 		PrepareToConvertL();
+		 	if(iAvailable != CCnvCharacterSetConverter::EAvailable)
+		 		{
+		 		User::Leave(KErrNotSupported);
+		 		}
+	 		TInt numOfBytesInCurrentChunk = ConvertChunkToUnicodeForRestoreL();
+	 		if(iChunk16->Length() < iChunkMaxLength && !iIsLastChunk)
+				{
+				// iRfileReadBuf8 will be closed when ConvertChunkToUnicodeForRestoreL returns.
+				iRfileReadBuf8.Create(iChunkMaxLength);
+				//store the current position of the file so that if request cancels and for next 
+				// call to NextChunkL() will attempt to read the same chunk that was cancelled
+				iCurrentFilePos = 0;
+				User::LeaveIfError(iFile.Seek(ESeekCurrent, iCurrentFilePos));
+				iFile.Read(iRfileReadBuf8, iChunkMaxLength, iStatus);
+				SetActive();
+				}
+			else
+				{
+				if(iPrevChunk)
+					{
+					//Pop one position from iStartPosOfEachChunkInFile
+					TInt count = iStartPosOfEachChunkInFile.Count() - 1;
+					iStartPosOfNextChunk = iStartPosOfEachChunkInFile[count];
+					iStartPosOfEachChunkInFile.Remove(count);
+					iPrevChunk = EFalse;
+					}
+				else
+					{
+					// Push Start position of current chunk in to the stack.
+					iStartPosOfEachChunkInFile.Append(iStartPosOfNextChunk);
+					iStartPosOfNextChunk += numOfBytesInCurrentChunk;
+					}
+				iReadChunkLength = numOfBytesInCurrentChunk;
+				Complete(iStatus.Int());
+				}
+			}
+		else
+			{
+			TPtrC16 ptr16((TUint16*)(iRfileReadBuf8.Ptr()), iRfileReadBuf8.Length()/2);
+			iChunk16->Copy(ptr16);
+			iRfileReadBuf8.Close();
+			Complete(iStatus.Int());
+			iReadChunkLength = ptr16.Length();
+			}
+		}
+	}
+
+/**
+Converts the 8 bit chunk to unicode.When this is called iRfileReadBuf8 will have the 8-bit  
+data that needs to be converted to unicode.8 bit data that is not converted is copied to 
+iRemainingUnConvertedData, which is used in conversion when this is called again.
+@param  None.
+@return TInt Number of bytes in Current Chunk.
+*/
+TInt CMsvPlainBodyText::ConvertChunkToUnicodeForRestoreL()
+	{
+	TBufC16<KMaxDecodeUnicodeLength> outBuf;
+	TPtr16 outPtr = outBuf.Des();
+ 	TInt charsetState = CCnvCharacterSetConverter::KStateDefault;
+
+ 	TInt numOfBytesInCurrentChunk = 0;
+	TInt bytesUnconverted = 0;
+	// Check if we are processing the last chunk.
+	if(iRfileReadBuf8.Length() == 0)
+		{
+		iIsLastChunk = ETrue;
+		}
+	// Check if there are data that are yet to be converted.
+	if(iRemainingUnConvertedData)
+		{
+		if(iIsLastChunk)
+			{
+			TPtrC8 remPtr(*iRemainingUnConvertedData);
+			// If it is the lastchunk then convert the data, need not insert to input buffer.
+			if(iIsLastChunk)
+				{
+				while(remPtr.Length() > 0)
+					{
+					bytesUnconverted = iConverter->ConvertToUnicode(outPtr, remPtr, charsetState);
+					User::LeaveIfError(bytesUnconverted);
+					iChunk16->Append(outPtr);
+					remPtr.Set(remPtr.Right(bytesUnconverted));
+					}
+				}
+			}
+		else // If there are data that are yet to be converted insert it at the front of the input buffer.
+			{
+			iRfileReadBuf8.ReAllocL(iRfileReadBuf8.Length() + iRemainingUnConvertedData->Length());
+			iRfileReadBuf8.Insert(0, *iRemainingUnConvertedData);
+			}
+		delete iRemainingUnConvertedData;
+		iRemainingUnConvertedData = NULL;
+		}
+	
+	TPtrC8 remainingUnconvertedData(iRfileReadBuf8);
+	// This loop will convert the data to unicode till iChunk16 is filled with the converted data.
+	while(remainingUnconvertedData.Length() >= KMaxDecodeUnicodeLength || (iIsLastChunk && remainingUnconvertedData.Length() > 0))
+		{
+		bytesUnconverted = iConverter->ConvertToUnicode(outPtr, remainingUnconvertedData, charsetState);
+		User::LeaveIfError(bytesUnconverted);
+		TInt bytesConverted = remainingUnconvertedData.Length()-bytesUnconverted;
+
+		if (iChunk16->Length() + outPtr.Length() <= iChunk16->MaxLength())
+			{
+			iChunk16->Append(outPtr);
+			numOfBytesInCurrentChunk += bytesConverted;
+			}
+		else
+			{
+			// Copy remaining unconverted data to iRemainingConvertedData,
+			// if outBuf.Length() is greater than the space remains in aChunk
+			TInt availableSpaceOnChunk = iChunk16->MaxLength() - iChunk16->Length();
+			iChunk16->Append(outPtr.Left(availableSpaceOnChunk));
+			
+			numOfBytesInCurrentChunk += bytesConverted - iRemainingConvertedData.Length();
+			iRemainingConvertedData.Append(outPtr.Mid(availableSpaceOnChunk));
+			remainingUnconvertedData.Set(remainingUnconvertedData.Right(bytesUnconverted));
+			break;
+			}
+		remainingUnconvertedData.Set(remainingUnconvertedData.Right(bytesUnconverted));
+		}
+
+	// Copy remaining unconverted data to iRemainingUnConvertedData
+	if(remainingUnconvertedData.Length())
+		{
+		__ASSERT_DEBUG(iRemainingUnConvertedData == NULL, PanicServer(EMsvBufferNotEmpty));
+		iRemainingUnConvertedData = remainingUnconvertedData.AllocL();
+		}
+	iRfileReadBuf8.Close();	
+	return numOfBytesInCurrentChunk;
+	}
+
+/**
+Creates the character converter and specifies the character set to convert to or from.
+Leaves if the specified or default character set is not available.
+@param   None.
+@return  void.
+*/
+void CMsvPlainBodyText::PrepareToConvertL()
+	{
+	if(!iConverter)
+		{
+		iConverter = CCnvCharacterSetConverter::NewL();
+	 	if (iCharsetId != 0)
+		 	{
+		 	iAvailable = iConverter->PrepareToConvertToOrFromL(iCharsetId, iFSession);
+		 	}
+		if (iAvailable == CCnvCharacterSetConverter::ENotAvailable)
+			{
+			iAvailable = iConverter->PrepareToConvertToOrFromL(iDefaultCharsetId, iFSession);
+			}
+		}
+ 	}
+
+/**
+Set iCharsetId to aCharset if body text of the message was downloaded as 8 bit.
+This can be used to override the charset with which a message was downloaded, when
+the body text is opened for reading.
+@param aCharset		The new charset with which it needs to be converted.
+@return void.
+*/
+EXPORT_C void CMsvPlainBodyText::SetCharacterSetL(const TUint aCharset)
+	{
+	if(iStore.IsPresentL(KMsvPlainBodyText8))
+		{
+		iCharsetId = aCharset;
+		}
+	}
+
+/**
+Returns the charset for the plain text part
+@param  None.
+@return TUint
+*/
+EXPORT_C TUint CMsvPlainBodyText::CharacterSet()
+	{
+	return iCharsetId ;
+	}
+
+/**
+Returns the default charset for the plain text part
+@param  None.
+@return TUint
+*/
+EXPORT_C TUint CMsvPlainBodyText::DefaultCharacterSet()
+	{
+	return iDefaultCharsetId;
+	}
+
+/**
+Commit the file and the store after it is written and and also closes the file.
+@param
+@return void
+*/
+EXPORT_C void CMsvPlainBodyText::CommitL()
+	{
+	iFile.Close();
+	RMsvWriteStream out;
+	if(iIs8Bit)
+		{
+		out.AssignLC(iStore, KMsvPlainBodyText8);
+		out.WriteUint32L(iCharsetId);
+		out.WriteUint32L(iDefaultCharsetId);
+		}
+	else
+		{
+		out.AssignLC(iStore, KMsvPlainBodyText16);
+		}
+	iIsCommitted = ETrue;
+	out.WriteUint32L(iIsCommitted);
+
+	TBuf<KFileNameFixedWidth> fileName;
+	fileName.NumFixedWidth(iMessageId, EHex, KFileNameFixedWidth);
+	out << (TDes&)fileName;
+
+	out.CommitL();
+	out.Close();
+	iStore.CommitL();
+	iMsvStoreManager.ReplacePlainTextFileL(iMessageId);
+	CleanupStack::PopAndDestroy(&out);
+	}
+
+/**
+Revert/Delete the file if it is not committed.
+@param  None.
+@return void.
+*/
+void CMsvPlainBodyText::RevertL()
+	{
+	iFile.Close();
+	if(!iIsCommitted)
+		{
+		iMsvStoreManager.DeletePlainTextFileL(iMessageId);
+		}
+	}
+	
+/**
+Cancel the file read operation and set the filepointer to the current position of the file 
+so that next call to NextChunkL() will attempt to read the same chunk that was cancelled
+@param  None.
+@return void.
+*/	
+void CMsvPlainBodyText::DoCancel()
+	{
+	if(iMsvFileStatus == EMsvFileReading)
+		{
+		iFile.ReadCancel();
+		iFile.Seek(ESeekStart, iCurrentFilePos);
+		}
+	CMsgActive::DoCancel();
+	}