messagingappbase/smsmtm/servermtm/src/SmssSimUtils.cpp
changeset 23 238255e8b033
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/messagingappbase/smsmtm/servermtm/src/SmssSimUtils.cpp	Fri Apr 16 14:56:15 2010 +0300
@@ -0,0 +1,1169 @@
+// Copyright (c) 2000-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:
+//
+
+#ifdef _DEBUG
+#undef _MSG_NO_LOGGING
+#endif
+
+#include <smutset.h>
+#include <txtrich.h>
+#include <smuthdr.h>
+#include <msventry.h>
+#include <msvuids.h>
+#include <mmlist.h>
+#include <smutsimparam.h>
+#include <biodb.h>
+#include <biouids.h>
+#include <csmsaccount.h>
+
+#include "SmssSimUtils.h"
+#include "SMSSPAN.H"
+
+//Logging constants
+
+#ifndef _MSG_NO_LOGGING
+_LIT(KDeleteFromSimLog, "DeleteFromSim.txt");
+_LIT(KEnumerateSimLog, "EnumerateSim.txt");
+_LIT(KCopyToSimLog, "CopyToSim.txt");
+#endif
+
+//CopyToSim constants
+
+
+CSmsSimUtils* CSmsSimUtils::NewL(CMsvServerEntry& aServerEntry, RFs& aFs, TMsvId aSmsServiceId)
+	{
+	CSmsSimUtils* self = new (ELeave) CSmsSimUtils(aServerEntry, aFs, aSmsServiceId);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+    return self;
+	}
+
+void CSmsSimUtils::ConstructL()
+	{
+	iSettings = CSmsSettings::NewL();
+		
+	// load ECOM interface used to create SMS details and description values.
+	iGetDetDesc = CSmsGetDetDescInterface::NewL();
+	}
+
+CSmsSimUtils::~CSmsSimUtils()
+	{
+	Cancel();
+
+	iWriteStream.Close();
+	iReadStream.Close();
+
+	iSocket.Close();
+	iSocketServ.Close();
+
+	delete iSelection;
+	delete iCompletedSelection;
+
+	delete iHeader;
+	delete iBody;
+	delete iCharFormat;
+	delete iParaFormat;
+
+	delete iBioDb;
+	delete iSettings;
+
+	delete iGetDetDesc;
+	REComSession::FinalClose();
+	}
+
+void CSmsSimUtils::ReadSimParamsL(TRequestStatus& aStatus)
+/**
+	Reads the SMS parameters from the SIM
+
+	@since			7.0
+	@param			aStatus		Indicates the completion status of a request made to 
+								a service provider. 
+	@pre 			None
+	@post			Starts the reading async operation.
+ */
+	{
+	SMSSLOG(FLogFormat(_L8("Reading SMS SIM Parameters")));
+
+	Queue(aStatus);
+
+	iProgress = TSmsProgress();
+	iProgress.iType = TSmsProgress::ESmsProgressTypeReadSimParams;
+
+	ConnectL();
+
+	iSocket.Ioctl(KIoctlReadSmsParams, iStatus, NULL, KSolSmsProv);
+	
+	SetActive();
+	iReadingSimParams = ETrue;
+	}
+
+void CSmsSimUtils::WriteSimParamsL(TRequestStatus& aStatus)
+/**
+	Writes the SMS parameters to the SIM
+
+	@since			7.0
+	@param			aStatus		
+	@pre 			The parameters are stored in the message store.
+	@post			The parameters are retrived form the message store and the writing
+					async operation is started.
+ */
+	{
+	SMSSLOG(FLogFormat(_L8("Writing SMS SIM Parameters")));
+
+	Queue(aStatus);
+
+	iProgress = TSmsProgress();
+	iProgress.iType = TSmsProgress::ESmsProgressTypeWriteSimParams;
+
+	CMobilePhoneSmspList* smspList = CMobilePhoneSmspList::NewL();
+	CleanupStack::PushL(smspList);
+
+	User::LeaveIfError(iServerEntry.SetEntry(iSmsServiceId));
+
+	CMsvStore* store = iServerEntry.ReadStoreL();
+	CleanupStack::PushL(store);
+
+	CSmsSimParamOperation::RestoreSimParamsL(*store, *smspList);
+
+	TInt count = 0;
+	count = smspList->Enumerate();
+
+	if (!count)
+		User::Leave(KErrNotFound);
+
+	ConnectL();
+	
+	iWriteStream << *smspList;
+	iWriteStream.CommitL(); 
+
+	CleanupStack::PopAndDestroy(2);  //smspList, store
+	User::LeaveIfError(iServerEntry.SetEntry(KMsvNullIndexEntryId));
+
+	iSocket.Ioctl(KIoctlWriteSmsParams, iStatus, NULL, KSolSmsProv);
+
+	SetActive();
+	}
+
+void CSmsSimUtils::EnumerateL(const TDesC8& aParameter, TRequestStatus& aStatus)
+	{
+	SMSSLOG(FLogFormat(_L8("Enumerate messages in SIM [param=%d]"), aParameter.Length()));
+
+	//Enumerate messages on the SIM.
+	//First obtains a count of messages on the SIM.
+
+	Queue(aStatus);
+	iProgress = TSmsProgress();
+	iProgress.iType = TSmsProgress::ESmsProgressTypeEnumeratingPhoneStores;
+
+	ConstructHeaderAndBodyL();
+	SetAndCleanEnumerateFolderL(aParameter);
+	SMSSLOG(FLogFormat(_L8("\tUsing folder %d"), iProgress.iEnumerateFolder, aParameter.Length()));
+	
+	//Obtain a count of the messages on the SIM
+	CountMessagesInPhoneStoresL();
+	}
+
+void CSmsSimUtils::SetAndCleanEnumerateFolderL(const TDesC8& aParameter)
+/**
+ * 
+ *
+ * @param aParameter Optionally contains the ID of the folder to store the phone store messages
+ */
+	{
+	iProgress.iEnumerateFolder = KErrNotFound;
+
+	//Restore the iSimUtilsData.
+	//This should be moved to a more general ContructL() if other functions other than EnumerateL() need to use this data
+	RestoreSimUtilsDataL();
+
+	//Delete messages under the last folder used to store phone-side messages
+	const TInt err = iServerEntry.SetEntry(iSimUtilsData.iLastEnumerateFolder);
+	if (err == KErrNotFound)
+		{
+		iSimUtilsData.iLastEnumerateFolder = KErrNotFound;
+		}
+	else
+		{
+		//Clean the last folder used to store phone-side messages
+		User::LeaveIfError(err);
+		DeleteChildrenL(iSimUtilsData.iLastEnumerateFolder);
+		}
+
+	if (aParameter.Length() != 0)
+		{
+		//Use the TMsvId packaged in aParameter as the folder in which to store phone-side messages
+		TPckgC<TMsvId> enumFolder(KErrNotFound);
+		enumFolder.Set(aParameter);
+		iProgress.iEnumerateFolder = enumFolder();
+		SMSSLOG(FLogFormat(_L8("\tAsked to use folder %d [last=%d]"), iProgress.iEnumerateFolder, iSimUtilsData.iLastEnumerateFolder));
+
+		switch (iProgress.iEnumerateFolder)
+			{
+			case KMsvRootIndexEntryId:
+			case KMsvLocalServiceIndexEntryId:
+			case KMsvGlobalInBoxIndexEntryId:
+			case KMsvGlobalOutBoxIndexEntryId:
+			case KMsvDraftEntryId:
+			case KMsvSentEntryId:
+				User::Leave(KErrArgument); //cannot store phone-side messages under a standard folder
+				break;
+			default:
+				break;
+			}
+
+		//Check the folder exists and delete all its children
+		User::LeaveIfError(iServerEntry.SetEntry(iProgress.iEnumerateFolder));
+		DeleteChildrenL(iProgress.iEnumerateFolder);
+		}
+	else
+		{
+		//else use the last folder used to store phone-side messages
+		iProgress.iEnumerateFolder = iSimUtilsData.iLastEnumerateFolder;
+		}
+
+	if (iProgress.iEnumerateFolder == KErrNotFound)
+		{
+		//Create a new folder if last is not found AND aParameter,Length() == 0
+		//Note: CreateNewEnumerateFolderL() sets iSimUtilsData.iLastEnumerateFolder
+		iProgress.iEnumerateFolder = CreateNewEnumerateFolderL();
+		}
+
+	if (iProgress.iEnumerateFolder != iSimUtilsData.iLastEnumerateFolder)
+		{
+		//Delete the last enumerate folder if it is under the SMS service
+		DeleteEnumerateFolderL(iSimUtilsData.iLastEnumerateFolder);
+
+		//Store the enumerate folder for next time
+		iSimUtilsData.iLastEnumerateFolder = iProgress.iEnumerateFolder;
+		StoreSimUtilsDataL();
+		}
+	}
+
+void CSmsSimUtils::DeleteEnumerateFolderL(TMsvId aId)
+/**
+ * Deletes entry aId if its parent is iSmsServiceId
+ */
+	{
+	SMSSLOG(FLogFormat(_L8("\tAttempting to delete enumerate folder %d"), aId));
+	const TInt err = iServerEntry.SetEntry(aId);
+	
+	if (err != KErrNotFound)
+		{
+		User::LeaveIfError(aId);
+		
+		if (iServerEntry.Entry().Parent() == iSmsServiceId)
+			{
+			SMSSLOG(FLogFormat(_L8("\tDeleting enumerate folder %d"), aId));
+			User::LeaveIfError(iServerEntry.SetEntry(iSmsServiceId));
+			User::LeaveIfError(iServerEntry.DeleteEntry(aId));
+			}
+		}
+	}
+
+void CSmsSimUtils::DeleteChildrenL(TMsvId aId)
+/**
+ * Deletes all messages under parent aId
+ */
+	{
+	SMSSLOG(FLogFormat(_L8("\tDeleting children from %d"), aId));
+
+	User::LeaveIfError(iServerEntry.SetEntry(aId));
+
+	TMsvSelectionOrdering order;
+	order.SetShowInvisibleEntries(ETrue);
+	iServerEntry.SetSort(order);
+
+	CMsvEntrySelection* sel = new (ELeave) CMsvEntrySelection();
+	CleanupStack::PushL(sel);
+	User::LeaveIfError(iServerEntry.GetChildren(*sel));
+
+	if (sel->Count() != 0)
+		{
+		User::LeaveIfError(iServerEntry.DeleteEntries(*sel));
+		}
+
+	CleanupStack::PopAndDestroy(sel);
+	}
+
+TMsvId CSmsSimUtils::CreateNewEnumerateFolderL()
+/**
+ * Creates a new folder under the SMS service where phone-side messages will be stored
+ * iSimUtilsData.iLastEnumerateFolder is set to the new folder's ID then stored against the SMS service
+ *
+ * @return the new folder's ID
+ */
+	{
+	SMSSLOG(FLogFormat(_L8("\tCreating new folder...")));
+	User::LeaveIfError(iServerEntry.SetEntry(iSmsServiceId));
+
+	TMsvEntry entry;
+
+	//Create a new invisible folder under the local service
+	entry.SetVisible(EFalse);
+	entry.iType = KUidMsvFolderEntry;
+	entry.iMtm = KUidMsgTypeSMS;
+	entry.iServiceId = iSmsServiceId;
+	User::LeaveIfError(iServerEntry.CreateEntry(entry));
+	iSimUtilsData.iLastEnumerateFolder = entry.Id();
+
+	TRAPD(err, StoreSimUtilsDataL());
+	
+	if (err != KErrNone)
+		{
+		iServerEntry.DeleteEntry(entry.Id()); //remove the folder if StoreSimUtilsDataL() leaves
+		User::Leave(err);
+		}
+
+	return iSimUtilsData.iLastEnumerateFolder;
+	}
+
+void CSmsSimUtils::CountMessagesInPhoneStoresL()
+	{
+	SMSSLOG(FLogFormat(_L8("\tCounting Messages on SIM. Calling Ioctl KIoctlEnumerateSmsMessages")));
+
+	User::LeaveIfError(iServerEntry.SetEntry(KMsvNullIndexEntryId));
+	ConnectL();
+	CreateBioDbL();
+	iSocket.Ioctl(KIoctlEnumerateSmsMessages, iStatus, &iEnumerateCountBuf, KSolSmsProv);
+	SetActive();
+	}
+
+void CSmsSimUtils::CreateBioDbL()
+	{
+	if (iBioDb == NULL)
+		iBioDb = CBIODatabase::NewL(iFs);
+	}
+
+TBool CSmsSimUtils::GetDefaultSendBearerL(TInt aBioType, TBioMsgIdType aBearer, TBioMsgId& rBioMsgId) const
+	{
+	TInt index = 0;
+	TUid uid;
+	uid.iUid = aBioType;
+	TBool found = EFalse;
+	TRAPD(ret,iBioDb->GetBioIndexWithMsgIDL(uid, index)); //leaves with KErrNotFound if aBioType does not exist in bdb
+	if (ret==KErrNone)
+		{
+		const CArrayFix<TBioMsgId>* bioIDs = iBioDb->BIOEntryLC(index);
+		const TInt count = bioIDs->Count();
+
+		for (TInt i = 0; !found && i < count; i++) //order important
+			{
+			rBioMsgId = bioIDs->At(i);
+			found = (rBioMsgId.iType == aBearer);
+			}
+
+		CleanupStack::PopAndDestroy(); // bioIDs
+		}
+	else if (ret!=KErrNotFound)
+		User::LeaveIfError(ret);
+	return found;
+	}
+
+TUid CSmsSimUtils::DecodeBioMessageL(TBioMsgId& rId)
+	{
+	TUid biomsguid=KNullUid;
+	rId.iType=EBioMsgIdNbs;
+	// Take text off front of message
+	CSmsMessage& smsmessage=iHeader->Message();
+	const TInt length=smsmessage.Buffer().Length();
+	smsmessage.Buffer().Extract(rId.iText,0,length<KMaxBioIdText? length: KMaxBioIdText);
+	TInt originator;
+	TInt destination;
+	// Has the message got application port addressing?
+	if (smsmessage.SmsPDU().ApplicationPortAddressing(destination,originator))
+		{
+		rId.iPort=STATIC_CAST(TUint16,destination);
+		rId.iType=EBioMsgIdWap;
+		// Is it WAP?
+		if (iBioDb->IsBioMessageL(rId,biomsguid)==KErrNotFound)
+			{
+			rId.iType=EBioMsgIdWapSecure;
+			// Is it WAP secure?
+			if (iBioDb->IsBioMessageL(rId,biomsguid)==KErrNotFound)
+				{
+				rId.iType=EBioMsgIdWsp;
+				// Is it WSP?
+				if(iBioDb->IsBioMessageL(rId,biomsguid)==KErrNotFound)
+					{
+					rId.iType=EBioMsgIdWspSecure;
+					// Is it WSP secure?
+					iBioDb->IsBioMessageL(rId,biomsguid);
+					}
+				}
+			}
+		}
+	else
+		{
+		// Is it an NBS style text message?
+		TInt index=rId.iText.Locate('\r');
+		if (index==KErrNotFound)
+			index=rId.iText.Locate('\n');
+		// Look for text match in BIO database
+		if ((index>0) && (iBioDb->IsBioMessageL(EBioMsgIdNbs,rId.iText.Left(index),0,biomsguid) != KErrNotFound))
+			smsmessage.Buffer().DeleteL(0,index+1);  // Remove BIO text
+		}
+	iHeader->SetBioMsgIdType(rId.iType);
+	return biomsguid;
+	}
+
+void CSmsSimUtils::EncodeBioMessageL()
+	{
+	CSmsMessage& smsmessage=iHeader->Message();
+	TBioMsgId bioMsgId;
+	//  Is it a BIO message?  
+		if (GetDefaultSendBearerL(iServerEntry.Entry().iBioType, iHeader->BioMsgIdType(), bioMsgId))
+		{
+		switch (bioMsgId.iType)
+			{
+			case EBioMsgIdNbs:
+				{
+				// Put text on NBS message
+				TBioMsgIdText text;
+				CSmsBufferBase& buffer=smsmessage.Buffer();
+				TInt textlength=bioMsgId.iText.Length();
+				buffer.Extract(text,0,textlength<buffer.Length()? textlength: 0);
+				if (bioMsgId.iText.CompareF(text))
+					{
+					_LIT(KSmsNewLine,"\n");
+					buffer.InsertL(0,KSmsNewLine);
+					buffer.InsertL(0,bioMsgId.iText);
+					}
+				break;
+				}
+			case EBioMsgIdWap:
+			case EBioMsgIdWapSecure:
+			case EBioMsgIdWsp:
+			case EBioMsgIdWspSecure:
+				{
+				// Put port addressing on binary messages 
+				smsmessage.SmsPDU().SetApplicationPortAddressingL(ETrue,bioMsgId.iPort,bioMsgId.iPort,bioMsgId.iPort>255);
+				smsmessage.SmsPDU().SetAlphabet(TSmsDataCodingScheme::ESmsAlphabet8Bit);
+				}
+			default:
+				break;
+			}
+		}
+	}
+
+void CSmsSimUtils::SetupMoveDeleteL(const CMsvEntrySelection& aSelection, TRequestStatus& aStatus)
+	{
+	__ASSERT_DEBUG(aSelection.Count(), Panic(KSmssPanicNoMessagesInSelection));
+
+	Queue(aStatus);
+	iProgress = TSmsProgress();
+
+	if (iSelection != &aSelection)
+		{
+		delete iSelection;
+		iSelection = NULL;
+		iSelection = aSelection.CopyL();
+		iSelection->Delete(0);  // Remove the SMS service entry id
+		}
+
+	iProgress.iMsgCount = iSelection->Count();
+	}
+
+void CSmsSimUtils::DeleteEachMessageFromPhoneStoreL()
+	{
+	iState = ESimUtilsStateOther;
+
+	if (iProgress.iMsgDone < iProgress.iMsgCount)
+		{
+		ConstructHeaderAndBodyL();
+
+		const TMsvId id = iSelection->At(iProgress.iMsgDone);
+		const TInt err = iServerEntry.SetEntry(id);
+
+		if (err == KErrNone)
+			{
+			CMsvStore* store = iServerEntry.ReadStoreL();
+			CleanupStack::PushL(store);
+
+			store->RestoreBodyTextL(*iBody);
+			iHeader->RestoreL(*store);
+			CleanupStack::PopAndDestroy(); //store
+
+			iProgress.iError = KErrNone;
+
+			SMSSLOG(FLogMessage(iServerEntry.Entry(), iHeader->Message(), iHeader->BioMsgIdType(), KDeleteFromSimLog));
+			SMSSLOG(FLogFormat(_L8("\tDeleting %d from SIM (%d/%d)"), id, iProgress.iMsgDone+1, iProgress.iMsgCount));
+
+			iWriteStream << iHeader->Message();
+			iWriteStream.CommitL();
+			iSocket.Ioctl(KIoctlDeleteSmsMessage, iStatus, NULL, KSolSmsProv);
+			iState = ESimUtilsDeleteEachMessage;
+			}
+		else
+			{
+			SMSSLOG(FLogFormat(_L8("\tIgnoring %d, Error=%d"), id, err));
+			RequestComplete(&iStatus, KErrNone);
+			}
+
+		SetActive();
+		iProgress.iMsgDone++;
+		User::LeaveIfError(iServerEntry.SetEntry(KMsvNullIndexEntryId));
+		}
+	else
+		{
+		iState = ESimUtilsDeletedLastMessage;
+		}
+	}
+
+void CSmsSimUtils::ConstructHeaderAndBodyL()
+	{
+	if (!iParaFormat)
+		iParaFormat = CParaFormatLayer::NewL();
+
+	if (!iCharFormat)
+		iCharFormat = CCharFormatLayer::NewL();
+
+	if (!iBody)
+		iBody = CRichText::NewL(iParaFormat, iCharFormat);
+
+	if (!iHeader)
+		iHeader = CSmsHeader::NewL(CSmsPDU::ESmsDeliver, *iBody);
+	}
+
+void CSmsSimUtils::RestoreSimUtilsDataL()
+/**
+ * Restore iSimUtilsData from the SMS service entry
+ * Does not attempt to restore if the stream is not present
+ */
+	{
+	User::LeaveIfError(iServerEntry.SetEntry(iSmsServiceId));
+	CMsvStore* store = iServerEntry.ReadStoreL();
+	CleanupStack::PushL(store);
+
+	if (iSimUtilsData.IsPresentL(*store))
+		{
+		iSimUtilsData.RestoreL(*store);
+		}
+
+	CleanupStack::PopAndDestroy(store);
+	}
+
+void CSmsSimUtils::StoreSimUtilsDataL()
+/**
+ * Store iSimUtilsData against the SMS service entry
+ */
+	{
+	User::LeaveIfError(iServerEntry.SetEntry(iSmsServiceId));
+	CMsvStore* store = iServerEntry.EditStoreL();
+	CleanupStack::PushL(store);
+	iSimUtilsData.StoreL(*store);
+	store->CommitL();
+	CleanupStack::PopAndDestroy(store);
+	}
+
+void CSmsSimUtils::DeleteFromPhoneStoreL(const CMsvEntrySelection& aSelection, TRequestStatus& aStatus)
+	{
+	SMSSLOG(FLogFormat(_L8("Deleting %d messages from SIM"), aSelection.Count()));
+
+	SetupMoveDeleteL(aSelection, aStatus);
+	DoDeleteFromPhoneStoreL();
+	}
+
+void CSmsSimUtils::DoDeleteFromPhoneStoreL()
+	{
+	iProgress.iType = TSmsProgress::ESmsProgressTypeDeleteFromPhoneStore;
+	
+	ConnectL();
+	RequestComplete(&iStatus, KErrNone, ETrue);
+	}
+
+void CSmsSimUtils::DoDeleteThenMoveFromPhoneStoreL()
+	{
+	iProgress.iType = TSmsProgress::ESmsProgressTypeMoveFromPhoneStore;
+	
+	ConnectL();
+	RequestComplete(&iStatus, KErrNone, ETrue);
+	}
+
+void CSmsSimUtils::LoadClass2FolderIdL() 
+	{
+	RestoreSmsSettingsL();
+	iClass2Folder = iSettings->Class2Folder();
+	}
+
+void CSmsSimUtils::RestoreSmsSettingsL()
+	{
+	CSmsAccount* account = CSmsAccount::NewLC();
+	// just v2
+	account->LoadSettingsL(*iSettings);
+	CleanupStack::PopAndDestroy(account);    
+	}
+
+void CSmsSimUtils::CopyToPhoneStoreL(const CMsvEntrySelection& aSelection, const TDesC8& /*aParameter*/, TRequestStatus& aStatus)
+	{
+	SMSSLOG(FLogFormat(_L8("Copying messages to SIM")));
+	
+	__ASSERT_DEBUG(aSelection.Count(), Panic(KSmssPanicNoMessagesInSelection));
+	
+	SetupMoveDeleteL(aSelection,aStatus);
+	iProgress.iType = TSmsProgress::ESmsProgressTypeCopyToPhoneStore;
+	iProgress.iMsgCount = iSelection->Count();
+	iProgress.iMsgDone = -1;
+	iState=EWritingToSIM;
+	iRecipientCount = 0;
+	iRecipientIndex = 0;
+
+	LoadClass2FolderIdL();
+
+	ConnectL();
+	ConstructHeaderAndBodyL();
+	CreateBioDbL();
+
+	DoCopyToPhoneStoreL();
+	}
+
+void CSmsSimUtils::MoveToPhoneStoreL(const CMsvEntrySelection& aSelection, const TDesC8& /*aParameter*/, TRequestStatus& aStatus)
+	{
+	SMSSLOG(FLogFormat(_L8("Moving messages to SIM")));
+	
+	__ASSERT_DEBUG(aSelection.Count(), Panic(KSmssPanicNoMessagesInSelection));
+	
+	SetupMoveDeleteL(aSelection,aStatus);
+	iProgress.iType = TSmsProgress::ESmsProgressTypeMoveToPhoneStore;
+	iProgress.iMsgCount = iSelection->Count();
+	iProgress.iMsgDone = -1;
+	iState=EWritingToSIM;
+	iRecipientCount = 0;
+	iRecipientIndex = 0;
+	
+	LoadClass2FolderIdL();
+	
+	ConnectL();
+	ConstructHeaderAndBodyL();
+	CreateBioDbL();
+	
+	DoCopyToPhoneStoreL();
+	}
+
+
+void CSmsSimUtils::DoCopyToPhoneStoreL()
+	{
+	User::LeaveIfError(iServerEntry.SetEntry(KMsvNullIndexEntryId));
+	if(iState==EWritingToSIM)
+		{
+		if (iRecipientCount == 0)
+			++iProgress.iMsgDone;
+
+		if (iProgress.iMsgDone < iProgress.iMsgCount)
+			{
+			User::LeaveIfError(iServerEntry.SetEntry(iSelection->At(iProgress.iMsgDone)));
+
+			CMsvStore* store = iServerEntry.ReadStoreL();
+			CleanupStack::PushL(store);
+
+			iHeader->RestoreL(*store);
+			iBody->Reset();
+			store->RestoreBodyTextL(*iBody);
+
+			CleanupStack::PopAndDestroy(); //store
+
+			// Set correct address on the CSmsMessage if it out going
+			CSmsMessage& smsmessage=iHeader->Message();
+			if (smsmessage.Type()==CSmsPDU::ESmsSubmit || (smsmessage.Type()==CSmsPDU::ESmsCommand))
+				{
+				// Get the count of recipients for the current message
+				if (iRecipientCount == 0)
+					{
+					iRecipientCount = iHeader->Recipients().Count();
+					iRecipientIndex = 0;
+					}
+				// Create separate copy of message for each recipient
+				if (iRecipientCount > 0)
+					{
+					CSmsNumber& rcpt = *iHeader->Recipients().At(iRecipientIndex);
+					iHeader->Message().SetToFromAddressL(rcpt.Address());
+					}
+				++iRecipientIndex;
+				// Check if all the recipients are processed
+				if (iRecipientIndex >= iRecipientCount)
+					{
+					// Yes all are done
+					iRecipientIndex = 0;
+					iRecipientCount = 0;
+					}
+				}
+			SMSSLOG(FLogMessage(iServerEntry.Entry(), iHeader->Message(), iHeader->BioMsgIdType(), KCopyToSimLog));
+
+			smsmessage.SetStorage(CSmsMessage::ESmsSIMStorage);	//Set to store on SIM
+
+			if (iServerEntry.Entry().Unread())
+				{
+				smsmessage.SetStatus(NMobileSmsStore::EStoredMessageUnread);
+				}
+			else
+				{
+				smsmessage.SetStatus(NMobileSmsStore::EStoredMessageRead);
+				}	
+
+			//  This may be a BIO message, so test and set up SMS message correctly!
+			if ((smsmessage.Type()==CSmsPDU::ESmsSubmit) || (smsmessage.Type()==CSmsPDU::ESmsDeliver))
+				EncodeBioMessageL();
+
+			iWriteStream << iHeader->Message();
+			iWriteStream.CommitL();
+			iSocket.Ioctl(KIoctlWriteSmsMessage, iStatus, &iSlotBuffer, KSolSmsProv);
+			SetActive();
+
+			// if we have a class 2 folder, and its not the current parent
+			// then update the class 2 folder with the new message one the sim
+			if(iClass2Folder!=KMsvNullIndexEntryId &&
+				iClass2Folder != iServerEntry.Entry().Parent())
+				iState=EUpdatingClass2;
+			else
+				iState=EWritingToSIM;
+			}
+		}
+	else
+		{
+		__ASSERT_DEBUG(iState==EUpdatingClass2,Panic(ESmssBadState));
+		__ASSERT_DEBUG(iClass2Folder!=KMsvNullIndexEntryId,Panic(ESmssNoClass2Folder));
+		TMsvId toCopy=iSelection->At(iProgress.iMsgDone);
+		User::LeaveIfError(iServerEntry.SetEntry(toCopy));
+
+		// Update the slot array of the CSmsMessage so that
+		// it can be deleted at a later time without the need for a re-enumeration
+		TMsvEntry entry = iServerEntry.Entry();
+		TBool wasReadOnly = entry.ReadOnly();
+
+		if (wasReadOnly)
+			{
+			entry.SetReadOnly(EFalse);
+			iServerEntry.ChangeEntry(entry);
+			}
+
+		CMsvStore* store = iServerEntry.EditStoreL();
+		CleanupStack::PushL(store);
+		iHeader->RestoreL(*store);
+		CSmsMessage& smsmessage=iHeader->Message();
+
+		smsmessage.UpdateSlotsL(iSlotBuffer);
+		smsmessage.SetStorage(CSmsMessage::ESmsSIMStorage);
+		iHeader->StoreL(*store);
+		store->CommitL();
+
+		if (wasReadOnly)
+			{
+			entry.SetReadOnly(ETrue);
+			iServerEntry.ChangeEntry(entry);
+			}
+
+		CleanupStack::PopAndDestroy(store);
+		User::LeaveIfError(iServerEntry.SetEntry(iServerEntry.Entry().Parent()));
+
+		if(iProgress.iType == TSmsProgress::ESmsProgressTypeMoveToPhoneStore) 
+			iServerEntry.MoveEntryL(toCopy,iClass2Folder,iStatus);
+		else
+			iServerEntry.CopyEntryL(toCopy,iClass2Folder,iStatus);
+
+		SetActive();
+		iState=EWritingToSIM;		
+		}
+	}
+
+void CSmsSimUtils::CopyFromPhoneStoreL(const CMsvEntrySelection& aSelection, const TDesC8& aParameter, TRequestStatus& aStatus)
+	{
+	SMSSLOG(FLogFormat(_L8("Copying messages from phone store")));
+	__ASSERT_DEBUG(aSelection.Count(), Panic(KSmssPanicNoMessagesInSelection));
+
+	SetupMoveDeleteL(aSelection, aStatus);
+	iProgress.iType = TSmsProgress::ESmsProgressTypeCopyFromPhoneStore;
+	SetDestination(aParameter);
+	MoveEntriesL(*iSelection, ETrue);
+	}
+
+void CSmsSimUtils::MoveEntriesL(const CMsvEntrySelection& aSelection, TBool aCopy)
+	{
+	// Uses the enumeration folder under the service to copy from
+	User::LeaveIfError(iServerEntry.SetEntry(aSelection[0]));
+	User::LeaveIfError(iServerEntry.SetEntry(iServerEntry.Entry().Parent()));
+	if (aCopy)
+		{
+		if (iCompletedSelection)
+			iCompletedSelection->Reset();
+		else
+			iCompletedSelection = new(ELeave) CMsvEntrySelection;
+
+		iServerEntry.CopyEntriesL(aSelection, iDestination, *iCompletedSelection, iStatus);
+		}
+	else
+		iServerEntry.MoveEntriesL(aSelection, iDestination, iStatus);
+	SetActive();
+	}
+
+void CSmsSimUtils::MoveFromPhoneStoreL(const CMsvEntrySelection& aSelection, const TDesC8& aParameter, TRequestStatus& aStatus)
+	{
+	SMSSLOG(FLogFormat(_L8("Moving messages from phone store")));
+	__ASSERT_DEBUG(aSelection.Count(), Panic(KSmssPanicNoMessagesInSelection));
+
+	SetupMoveDeleteL(aSelection, aStatus);
+	iProgress.iType = TSmsProgress::ESmsProgressTypeMoveFromPhoneStore;
+	SetDestination(aParameter);
+
+	DoDeleteThenMoveFromPhoneStoreL();
+	}
+
+void CSmsSimUtils::SetDestination(const TDesC8& aParameter)
+	{
+	TPckgBuf<TMsvId> pkg;
+	pkg.Copy(aParameter);
+	iDestination = pkg();
+	__ASSERT_DEBUG(iDestination, Panic(KSmssPanicDestinationFolderNotSet));
+	}
+
+void CSmsSimUtils::SetLocalStorageInfoL(const CMsvEntrySelection& aSelection)
+	{
+	// After a copy/move from phone store the iSlotArray and Storage members
+	// need to be reset to reflect the local storage.
+	__ASSERT_DEBUG(aSelection.Count(), Panic(KSmssPanicNoMessagesInSelection));
+	TInt count = aSelection.Count();
+	ConstructHeaderAndBodyL();
+	TBool wasReadOnly = EFalse;
+
+	while (count--)
+		{
+		// RetrieveMessageFromPhoneStoreL() may have set the TMsvEntry
+		// to read only if the message is a CSmsPDU::ESmsSubmit
+		iServerEntry.SetEntry(aSelection.At(count));
+		TMsvEntry entry = iServerEntry.Entry();
+		wasReadOnly = entry.ReadOnly();
+
+		if (wasReadOnly)
+			{
+			entry.SetReadOnly(EFalse);
+			iServerEntry.ChangeEntry(entry);
+			}
+
+		CMsvStore* store = iServerEntry.EditStoreL();
+		CleanupStack::PushL(store);
+		iHeader->RestoreL(*store);
+		CSmsMessage& smsmessage = iHeader->Message();
+
+		// Set the destination attributes
+		smsmessage.SetStorage(CSmsMessage::ESmsPhoneStorage);
+		smsmessage.iSlotArray.Reset();
+
+		iHeader->StoreL(*store);
+		store->CommitL();
+		CleanupStack::PopAndDestroy(store);
+
+		if (wasReadOnly)
+			{
+			entry.SetReadOnly(ETrue);
+			iServerEntry.ChangeEntry(entry);
+			}
+		}
+	}
+
+void CSmsSimUtils::ConnectL()
+	{
+	ConnectL(iSocketServ, iSocket, ESmsAddrLocalOperation);
+	}
+
+void CSmsSimUtils::ConnectL(RSocketServ& arSocketServ, RSocket& arSocket, TSmsAddrFamily aSmsAddrFamily)
+	{
+	if (!arSocketServ.Handle())
+		{
+		User::LeaveIfError(arSocketServ.Connect());
+		}
+
+	TProtocolDesc protoinfo;
+	TProtocolName protocolname(KSmsDatagram);
+	User::LeaveIfError(arSocketServ.FindProtocol(protocolname,protoinfo));
+
+	if (!arSocket.SubSessionHandle())
+		{
+		User::LeaveIfError(arSocket.Open(arSocketServ,protoinfo.iAddrFamily,protoinfo.iSockType,protoinfo.iProtocol));
+		}
+
+	TSmsAddr smsaddr;
+	smsaddr.SetSmsAddrFamily(aSmsAddrFamily);
+	User::LeaveIfError(arSocket.Bind(smsaddr));
+	}
+
+
+CSmsSimUtils::CSmsSimUtils(CMsvServerEntry& aServerEntry, RFs& aFs, TMsvId aSmsServiceId)
+	: CSmssActive(aFs, aServerEntry), 
+	iState(ESimUtilsStateOther), 
+	iReadStream(iSocket),
+	iSmsServiceId(aSmsServiceId),
+	iWriteStream(iSocket)
+	{
+	CActiveScheduler::Add(this);
+	}
+
+void CSmsSimUtils::DoRunReadSimParamsL()
+/**
+	Retrive and store the parameters to the message store. 
+
+	@since			7.0
+	@leave 		KErrNotFound
+	@pre 			The reading async operation is complete.
+	@post			The parameters are retrived and stored in the message store.
+ */
+	{
+	iProgress.iMsgDone = iSmsServiceId;
+	
+	CMobilePhoneSmspList* smspList = CMobilePhoneSmspList::NewL();
+	CleanupStack::PushL(smspList);
+		
+	iReadStream >> *smspList;
+	
+	iProgress.iMsgCount = smspList->Enumerate();
+
+	if (!iProgress.iMsgCount)
+		User::Leave(KErrNotFound);
+
+	User::LeaveIfError(iServerEntry.SetEntry(iSmsServiceId));
+
+	CMsvStore* store = iServerEntry.EditStoreL();
+	CleanupStack::PushL(store);
+
+	CSmsSimParamOperation::StoreSimParamsL(*store, *smspList);
+
+	CleanupStack::PopAndDestroy(2); //smspList, store
+	User::LeaveIfError(iServerEntry.SetEntry(KMsvNullIndexEntryId));
+	}
+
+void CSmsSimUtils::DoRunL()
+/**
+	Handles completed async operations
+
+	@since			7.0
+	@leave		ESmscPanicUnexpectedCommand, KErrNone
+	@pre 			The async operation is complete
+	@post			The operation is complete
+ */
+	{
+	switch (iProgress.iType)
+		{
+		case TSmsProgress::ESmsProgressTypeCopyFromPhoneStore:
+			{
+			__ASSERT_DEBUG(iCompletedSelection, Panic(KSmssPanicNoMessagesInSelection));
+			SetLocalStorageInfoL(*iCompletedSelection);
+			break;
+			}
+		case TSmsProgress::ESmsProgressTypeWriteSimParams:
+			{
+			break;
+			}
+		case TSmsProgress::ESmsProgressTypeReadSimParams:
+			{
+			if (iReadingSimParams)
+				{
+				iReadingSimParams = EFalse;
+				TRAP(iProgress.iError, DoRunReadSimParamsL());
+				iSocket.Ioctl(KIoctlCompleteReadSmsParams, iStatus, NULL, KSolSmsProv);
+				SetActive();
+				}
+			break;
+			}
+		case TSmsProgress::ESmsProgressTypeEnumeratingPhoneStores:
+			{
+			iProgress.iMsgCount = iEnumerateCountBuf();
+			RetrieveMessageFromPhoneStoreL();
+			break;
+			}
+		case TSmsProgress::ESmsProgressTypeDeleteFromPhoneStore:
+			{
+			DeleteEachMessageFromPhoneStoreL();
+			break;
+			}
+		case TSmsProgress::ESmsProgressTypeMoveFromPhoneStore:
+			{
+			DeleteEachMessageFromPhoneStoreL();
+
+			if (iState == ESimUtilsDeletedLastMessage)
+				{
+				// We have finished deleting the messages from the SIM so set
+				// the Storage & iSlotArray then move the entries
+				SetLocalStorageInfoL(*iSelection);
+				iState = ESimUtilsStateOther;
+				iProgress.iType = TSmsProgress::ESmsProgressTypeMovingEntries;
+				MoveEntriesL(*iSelection, EFalse);
+				}
+			break;
+			}
+		case TSmsProgress::ESmsProgressTypeCopyToPhoneStore:
+		case TSmsProgress::ESmsProgressTypeMoveToPhoneStore:
+			{
+			DoCopyToPhoneStoreL();
+			break;
+			}
+		case TSmsProgress::ESmsProgressTypeMovingEntries:
+			break;
+		default:
+			{
+			Panic(KSmssPanicUnexpectedState);
+			}
+		}
+	}
+
+void CSmsSimUtils::RetrieveMessageFromPhoneStoreL()
+	{
+	// If one message fails to be restored the rest fail 
+	if (iProgress.iMsgDone < iProgress.iMsgCount)
+		{
+		CSmsMessage& smsmessage=iHeader->Message();
+		iReadStream >> smsmessage;
+		
+		TUid biomsguid=KNullUid;
+		TBioMsgId id;
+		if ((smsmessage.Type()==CSmsPDU::ESmsSubmit) || (smsmessage.Type()==CSmsPDU::ESmsDeliver))
+			biomsguid=DecodeBioMessageL(id);
+
+		TMsvEntry entry;
+		RestoreSmsSettingsL();
+		TInt length = iSettings->DescriptionLength();
+		HBufC* buf = HBufC::NewLC(length);
+		TPtr description = buf->Des();	
+
+		if (biomsguid != KNullUid)
+			{
+			TSmsUtilities::PopulateMsgEntry(entry, smsmessage, iSmsServiceId, *iSettings, KUidBIOMessageTypeMtm);
+			
+			// BioficateEntry!!!
+			// Set up all the needed ids
+			entry.iBioType = biomsguid.iUid;
+			entry.iServiceId = KMsvLocalServiceIndexEntryId; 
+			entry.iMtm = KUidBIOMessageTypeMtm;
+
+			// Look up and set the description
+			TInt index;
+			iBioDb->GetBioIndexWithMsgIDL(biomsguid, index);
+			description.Copy(iBioDb->BifReader(index).Description().Left(length));
+			}
+		else
+			{
+			// Set the details
+			TSmsUtilities::PopulateMsgEntry(entry, smsmessage, iSmsServiceId, *iSettings);
+
+			iGetDetDesc->GetDescription(smsmessage, description, length);
+			}
+		
+		entry.iDescription.Set(description);
+
+		if (smsmessage.Status() == NMobileSmsStore::EStoredMessageUnread)
+			entry.SetUnread(ETrue);
+		else
+			entry.SetUnread(EFalse);
+				
+		TBuf<KSmsDetailsLength> details;	
+		if (iGetDetDesc->GetDetails(iFs, smsmessage, details) == KErrNone)
+			entry.iDetails.Set(details);
+
+		User::LeaveIfError(iServerEntry.SetEntry(iProgress.iEnumerateFolder));
+		User::LeaveIfError(iServerEntry.CreateEntry(entry));
+		User::LeaveIfError(iServerEntry.SetEntry(entry.Id()));
+		CMsvStore* store = iServerEntry.EditStoreL();
+		CleanupStack::PushL(store);
+		store->StoreBodyTextL(*iBody);
+		iHeader->StoreL(*store);
+		store->CommitL();
+				
+		if (iHeader->Type() != CSmsPDU::ESmsSubmit)
+			{
+			entry.SetReadOnly(ETrue);
+			}
+		
+		entry.iSize = store->SizeL();
+		CleanupStack::PopAndDestroy(store);
+		User::LeaveIfError(iServerEntry.ChangeEntry(entry));
+
+		SMSSLOG(FLogMessage(entry, iHeader->Message(), iHeader->BioMsgIdType(), KEnumerateSimLog));
+		
+		User::LeaveIfError(iServerEntry.SetEntry(KMsvNullIndexEntryId));
+
+		iSocket.Ioctl(KIoctlReadMessageSucceeded, iStatus, NULL, KSolSmsProv);
+		SetActive();
+			
+		iProgress.iMsgDone++;
+			
+		SMSSLOG(FLogFormat(_L8("\tRead %d from SIM (%d/%d)"), entry.Id(), iProgress.iMsgDone, iProgress.iMsgCount));
+			
+		CleanupStack::PopAndDestroy(buf);
+		}
+	}
+
+void CSmsSimUtils::DoComplete(TInt& aStatus)
+	{
+	if (iState == ESimUtilsDeleteEachMessage && aStatus == KErrNotFound)
+		{
+#ifndef _MSG_NO_LOGGING
+		const TMsvId id = iServerEntry.Entry().Id();
+		_LIT(KLogTxt, "\tERROR: %d NOT deleted from the SIM. Error=KErrNotFound");
+		SMSSLOG(FLogFormat(KDeleteFromSimLog, KLogTxt, id));
+		SMSSLOG(FLogFormat(KLogTxt, id));
+#endif
+
+		TRAP(iProgress.iError, DeleteEachMessageFromPhoneStoreL());
+		}
+	else if (aStatus != KErrNone)
+		{
+		iServerEntry.SetEntry(KMsvNullIndexEntryId);
+		iProgress.iError = aStatus;
+		}
+
+#ifndef _MSG_NO_LOGGING
+	if (!IsActive())
+		SMSSLOG(FLogFormat(_L8("SmsSimUtils completed. Error %d, MsgCount %d, MsgDone %d"), iProgress.iError, iProgress.iMsgCount, iProgress.iMsgDone));
+#endif
+
+	aStatus = KErrNone;
+	}
+
+void CSmsSimUtils::DoSmssCancel()
+	{
+	if (iSocket.SubSessionHandle())
+		iSocket.CancelIoctl();
+	}
+
+
+/**
+ * TSmsSimUtilsData
+ */
+
+void TSmsSimUtilsData::StoreL(CMsvStore& aStore) const
+	{
+	RMsvWriteStream writeStream;
+	writeStream.AssignLC(aStore, KSmsSimUtilsDataUid);
+	ExternalizeL(writeStream);
+	writeStream.CommitL();
+	CleanupStack::PopAndDestroy(&writeStream);
+	}
+
+void TSmsSimUtilsData::RestoreL(const CMsvStore& aStore)
+	{
+	RMsvReadStream readStream;
+	readStream.OpenLC(aStore, KSmsSimUtilsDataUid);
+	InternalizeL(readStream);
+	CleanupStack::PopAndDestroy(&readStream);
+	}
+
+void TSmsSimUtilsData::ExternalizeL(RWriteStream& aStream) const
+	{
+	aStream.WriteInt16L(KSmsSimUtilsDataVersion);
+	aStream.WriteInt32L(iLastEnumerateFolder);
+	}
+
+void TSmsSimUtilsData::InternalizeL(RReadStream& aStream)
+	{
+	aStream.ReadInt16L(); // version, not used until version > 1
+	iLastEnumerateFolder = aStream.ReadInt32L();
+	}