mobilemessaging/smsmtm/clientmtm/src/smut.cpp
changeset 0 72b543305e3a
child 34 84197e66a4bd
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mobilemessaging/smsmtm/clientmtm/src/smut.cpp	Thu Dec 17 08:44:11 2009 +0200
@@ -0,0 +1,955 @@
+// Copyright (c) 1999-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:
+// smut.cpp
+//
+#include <smut.h>
+#include <gsmuieoperations.h>
+
+#include <e32std.h>
+#include <gsmumsg.h>
+#include <msvstd.h>
+#include <cntdb.h>
+#include <cntfield.h>
+#include <cntitem.h>
+#include <cntfldst.h>
+#include <msventry.h>
+#include <msvuids.h>
+#include <msvapi.h>
+#include <txtetext.h>
+#include <gsmubuf.h>
+#include <bautils.h>
+#include <barsc.h>
+#include <smss.rsg>
+#include <smss.hrh>
+#include <msvutils.h>
+#include "SMCMMAIN.H"
+#include "SMUTSET.H"
+
+// Used by ValidGsmNumber(). The characters that may appear at the start of a
+// valid GSM number
+_LIT(KSmsValidGsmNumberFirstChar, "+");
+
+// Used by ValidGsmNumber(). The characters that may appear after the first
+// character of a valid GSM number
+_LIT(KSmsValidGsmNumberOtherChar, "#*");
+
+const TInt KSmsValidGsmNumberMinLength = 2;
+
+/**
+Finds and returns all the Service IDs for the specified MTM.
+
+A Service ID is the entry ID for an service-type entry. The first Service ID
+for the specified MTM is returned.
+
+If the complete set of Service IDs for the MTM is required then the caller
+should provide a valid CMsvEntrySelection object in aServiceIds. The Service
+Ids are appended to this object. If the complete set is not required then the
+input/output argument aServiceIds should be set to NULL.
+
+@param	aEntry
+A server message entry that can be used by this function.
+
+@param	aFirstId
+An output argument with the first Service ID.
+
+@param	aMtm
+The specified MTM. This has a default value of KUidMsgTypeSMS.
+
+@param	aServiceIds
+An input/output argument with the complete selection of Service IDs.  This has
+a default value of NULL.
+
+@leave	KErrNotFound
+A service entry could not be found for the specified MTM.
+*/
+EXPORT_C void TSmsUtilities::ServiceIdL(CMsvServerEntry& aEntry, TMsvId& aFirstId, TUid aMtm, CMsvEntrySelection* aServiceIds)
+	{
+	aFirstId = KMsvNullIndexEntryId;
+
+	CMsvEntrySelection* selection = new (ELeave) CMsvEntrySelection();
+	CleanupStack::PushL(selection);
+
+	User::LeaveIfError(aEntry.SetEntry(KMsvRootIndexEntryId));
+
+	TMsvSelectionOrdering order;
+	order.SetShowInvisibleEntries(ETrue);
+	aEntry.SetSort(order);
+
+	// Get the children on the Root Index Entry
+	User::LeaveIfError(aEntry.GetChildrenWithType(KUidMsvServiceEntry, *selection));
+
+	TInt count = selection->Count();
+
+	// Find an entry for MTM aMtm
+	for (TInt curChild = 0; curChild < count && (aFirstId == KMsvNullIndexEntryId || aServiceIds); ++curChild)
+		{
+		User::LeaveIfError(aEntry.SetEntry(selection->At(curChild)));
+		CompareEntryL(aEntry.Entry(), aMtm, aFirstId, aServiceIds);
+		}
+
+	// Leave if no Service Entry found for MTM aMtm
+	if (aFirstId == KMsvNullIndexEntryId)
+		User::Leave(KErrNotFound);
+
+	CleanupStack::PopAndDestroy(selection);
+	}
+
+/**
+Finds and returns all the Service IDs for the specified MTM.
+
+A Service ID is the entry ID for an service-type entry. The first Service ID
+for the specified MTM is returned.
+
+If the complete set of Service IDs for the MTM is required then the caller
+should provide a valid CMsvEntrySelection object in aServiceIds. The Service
+Ids are appended to this object. If the complete set is not required then the
+input/output argument aServiceIds should be set to NULL.
+
+@param	aSession
+A message server session.
+
+@param	aFirstId
+An output argument with the first Service ID.
+
+@param	aMtm
+The specified MTM. This has a default value of KUidMsgTypeSMS.
+
+@param	aServiceIds
+An input/output argument with the complete selection of Service IDs.  This has
+a default value of NULL.
+
+@leave	KErrNotFound
+A service entry could not be found for the specified MTM.
+*/
+EXPORT_C void TSmsUtilities::ServiceIdL(CMsvSession& aSession, TMsvId& aFirstId, TUid aMtm, CMsvEntrySelection* aServiceIds)
+ 	{
+ 	TMsvSelectionOrdering order;
+ 	order.SetShowInvisibleEntries(ETrue);
+
+ 	CMsvEntry* root = CMsvEntry::NewL(aSession, KMsvRootIndexEntryId, order);
+ 	CleanupStack::PushL(root);
+
+ 	ServiceIdL(*root, aFirstId, aMtm, aServiceIds);
+
+ 	CleanupStack::PopAndDestroy(root);
+ 	}
+
+/**
+Finds and returns all the Service IDs for the specified MTM.
+
+A Service ID is the entry ID for an service-type entry. The first Service ID
+for the specified MTM is returned.
+
+If the complete set of Service IDs for the MTM is required then the caller
+should provide a valid CMsvEntrySelection object in aServiceIds. The Service
+Ids are appended to this object. If the complete set is not required then the
+input/output argument aServiceIds should be set to NULL.
+
+@param	aEntry
+A message entry that can be used by this function.
+
+@param	aFirstId
+An output argument with the first Service ID.
+
+@param	aMtm
+The specified MTM. This has a default value of KUidMsgTypeSMS.
+
+@param	aServiceIds
+An input/output argument with the complete selection of Service IDs.  This has
+a default value of NULL.
+
+@leave	KErrNotFound
+A service entry could not be found for the specified MTM.
+*/
+EXPORT_C void TSmsUtilities::ServiceIdL(CMsvEntry& aEntry, TMsvId& aFirstId, TUid aMtm, CMsvEntrySelection* aServiceIds)
+	{
+	aFirstId = KMsvNullIndexEntryId;
+
+ 	TMsvSelectionOrdering order(aEntry.SortType());
+ 	if (!order.ShowInvisibleEntries())
+ 		{
+ 		order.SetShowInvisibleEntries(ETrue);
+ 		aEntry.SetSortTypeL(order);
+ 		}
+
+   	aEntry.SetEntryL(KMsvRootIndexEntryId);
+ 	const TInt count = aEntry.Count();
+
+	// Find an entry for MTM aMtm
+	for (TInt curChild = 0; curChild < count && (aFirstId == KMsvNullIndexEntryId || aServiceIds != NULL); ++curChild)
+		{
+		CompareEntryL(aEntry[curChild], aMtm, aFirstId, aServiceIds);
+		}
+
+	// Leave if no Service Entry found for MTM aMtm
+	if (aFirstId == KMsvNullIndexEntryId)
+		User::Leave(KErrNotFound);
+	}
+
+/**
+Populates a message index.
+
+The input data is used to set the fields in supplied message index. The affected
+fields are the entry type, MTM, entry date, Service ID and error fields. The
+date field is set from the time information in the aMessage argument.
+
+This function can be used as part of the process when creating SMS messages in
+the message store.
+
+@param	aEntry
+An input/output argument with the index entry to populate.
+
+@param	aMessage
+The SMS message object for the index entry.
+
+@param	aServiceId
+The Service ID for the message.
+
+@param	aMtm
+The specified MTM. This has a default value of KUidMsgTypeSMS.
+*/
+EXPORT_C void TSmsUtilities::PopulateMsgEntry(TMsvEntry& aEntry, const CSmsMessage& aMessage, TMsvId aServiceId, TUid aMtm)
+	{
+	aEntry.iType		= KUidMsvMessageEntry;
+	aEntry.iMtm			= aMtm;
+	aEntry.iDate		= aMessage.Time();
+	aEntry.iServiceId	= aServiceId;
+	aEntry.iError		= KErrNone;
+	}
+
+/**
+Populates a message index.
+
+The input data is used to set the fields in supplied message index. The affected
+fields are the entry type, MTM, entry date, Service ID and error fields. The
+date field is either set from the time information in the aMessage argument or from
+the service center timestamp in the PDU depending on the associated SMS setting.
+
+This function can be used as part of the process when creating SMS messages in
+the message store.
+
+@param	aEntry
+An input/output argument with the index entry to populate.
+
+@param	aMessage
+The SMS message object for the index entry.
+
+@param	aServiceId
+The Service ID for the message.
+
+@param	aSettings
+The settings for the SMS account.
+
+@param	aMtm
+The specified MTM. This has a default value of KUidMsgTypeSMS.
+*/
+EXPORT_C void TSmsUtilities::PopulateMsgEntry(TMsvEntry& aEntry, const CSmsMessage& aMessage, TMsvId aServiceId, const CSmsSettings& aSettings, TUid aMtm)
+	{
+	TSmsUtilities::PopulateMsgEntry(aEntry, aMessage, aServiceId, aMtm);
+
+	if (aSettings.UseServiceCenterTimeStampForDate())
+		{
+		const CSmsPDU& pdu = aMessage.SmsPDU();
+
+		TTime time = 0;
+		TInt gmtOffset = 0;
+
+		if (pdu.Type() == CSmsPDU::ESmsDeliver)
+			{
+			CSmsDeliver& smsDeliver =
+				const_cast<CSmsDeliver&>(static_cast<const CSmsDeliver&>(pdu));
+			smsDeliver.ServiceCenterTimeStamp(time, gmtOffset);
+			}
+		else if (pdu.Type() == CSmsPDU::ESmsStatusReport)
+			{
+			CSmsStatusReport& smsStatusReport =
+				const_cast<CSmsStatusReport&>(static_cast<const CSmsStatusReport&>(pdu));
+			smsStatusReport.ServiceCenterTimeStamp(time, gmtOffset);
+			}
+
+		if (time > 0)
+			{
+			aEntry.iDate = time;
+			}
+		}
+	}
+
+/**
+Get the SMS message recipient/sender details.
+
+The recipient/sender telephone number is extracted from the supplied message
+object. If the recipient/sender telephone number appears uniquely in the contacts
+database then the family name and given name contact details are set into the
+output argument aDetails in the format specified by the resource item
+R_SENDER_NAME_FORMAT. The buffer limit specified by aMaxLength is observed.
+
+If there is not a unique contact entry for the recipient/sender telephone number
+then aDetails will contain the orginally telephone number.
+
+@param	aFs
+A connected file server session.
+
+@param	aMessage
+The message object with the recipient/sender telephone number.
+
+@param	aDetails
+The output argument to contain the message details.
+
+@param	aMaxLength
+The maximum length of the supplied buffer in aDetails.
+
+@return
+KErrNotSupported if the message is not of type SMS-SUBMIT, SMS-DELIVER or SMS-STATUS-REPORT.
+KErrArgument if the telephone number is invalid.
+KErrNotFound if a contact could not be found.
+KErrAlreadyExists if more than one contact entry found.
+KErrNone if details is obtained successfully.
+*/
+EXPORT_C TInt TSmsUtilities::GetDetails(RFs& aFs, const CSmsMessage& aMessage, TDes& aDetails, TInt aMaxLength)
+	{
+	__ASSERT_DEBUG( aMaxLength <= aDetails.MaxLength(), User::Invariant() );
+
+	if (aMaxLength > aDetails.MaxLength())
+		{
+		aMaxLength = aDetails.MaxLength();
+		}
+
+	aDetails.Zero();
+
+	TPtrC fromAddress;
+
+	switch (aMessage.SmsPDU().Type())
+		{
+		case CSmsPDU::ESmsSubmit:
+		case CSmsPDU::ESmsDeliver:
+		case CSmsPDU::ESmsStatusReport:
+			fromAddress.Set(aMessage.SmsPDU().ToFromAddress());
+			break;
+		default:
+			return KErrNotSupported;
+		}
+
+	return GetDetails(aFs, fromAddress, aDetails, aMaxLength);
+	}
+
+/**
+Get the SMS message recipient/sender details.
+
+The recipient/sender telephone number is searched for in the contacts database.
+If a unique match is found then the family name and given name contact details
+are set into the output argument aDetails in the format specified by the
+resource item R_SENDER_NAME_FORMAT. The buffer limit specified by aMaxLength is
+observed.
+
+If a unique match is not found or the supplied telephone number is invalid, then
+aDetails will contain the orginally telephone number.
+
+@param	aFs
+A connected file server session.
+
+@param	aFromAddress
+The recipient/sender telephone number.
+
+@param	aDetails
+The output argument to contain the message details.
+
+@param	aMaxLength
+The maximum length of the supplied buffer in aDetails.
+
+@return
+KErrArgument if aFromAddress is not a valid GSM number.
+KErrNotFound if a contact could not be found.
+KErrAlreadyExists if more than one contact entry found.
+KErrNone if details is obtained successfully.
+*/
+EXPORT_C TInt TSmsUtilities::GetDetails(RFs& aFs, const TDesC& aFromAddress, TDes& aDetails, TInt aMaxLength)
+	{
+	__ASSERT_DEBUG( aMaxLength <= aDetails.MaxLength(), User::Invariant() );
+
+	if (aMaxLength > aDetails.MaxLength())
+		{
+		aMaxLength = aDetails.MaxLength();
+		}
+
+	TRAPD(err, DoGetDetailsL(aFs, aFromAddress, aDetails, aMaxLength));
+
+	if ( (err != KErrNone) || (aDetails.Length() == 0) )
+		{
+		if (aFromAddress.Length() <= aMaxLength)
+			{
+			aDetails = aFromAddress;
+			aDetails.Trim();
+			}
+		else
+			{
+			// Truncate aFromAddress so that it fits into aDetails.
+			aDetails = aFromAddress.Left(aMaxLength);
+			aDetails.Trim();
+			}
+		}
+
+	return KErrNone;
+	}
+
+/**
+Get the SMS message description.
+
+If the message is Special Message Indication SMS then the description will
+contain the appropriate localised text for the indication.
+
+If the message is a Status Report then the description will contain the
+appropriate localised text for a Status Report.
+
+If the message is a standard text message the description will contain the
+beginning section of the SMS message text itself.
+
+In all cases the buffer limit specified by aMaxLength is observered.
+
+@param	aMessage
+The SMS message object.
+
+@param	aDescription
+The output argument for the description.
+
+@param	aMaxLength
+The maximum length of the supplied buffer in aDescription.
+
+@return
+An error code if the localised text for a SMS-STATUS-REPORT message could not be
+obtained. Otherwise KErrNone is returned.
+*/
+EXPORT_C TInt TSmsUtilities::GetDescription(const CSmsMessage& aMessage, TDes& aDescription, TInt aMaxLength)
+	{
+	__ASSERT_DEBUG( aMaxLength <= aDescription.MaxLength(), User::Invariant() );
+
+	if (aMaxLength > aDescription.MaxLength())
+		{
+		aMaxLength = aDescription.MaxLength();
+		}
+
+	aDescription.Zero();
+
+	TBool gotDescription = EFalse;
+	TRAPD(err, gotDescription = DoGetDescriptionL(aMessage, aDescription, aMaxLength));
+	if(err != KErrNone || !gotDescription)
+		ExtractDescriptionFromMessage(aMessage, aDescription, aMaxLength);
+
+	return KErrNone;
+	}
+
+/**
+Opens and returns the SMS client MTM resource file.
+
+It is the responsibility of the caller to ensure that the resource file is
+closed once it is no longer required.
+
+@param	aFs
+A connected file server session.
+
+@return
+The opened resource file.
+*/
+EXPORT_C RResourceFile TSmsUtilities::OpenResourceFileL(RFs& aFs)
+	{
+	TFileName fileName(KSmsResourceFile);
+	MsvUtils::AddPathAndExtensionToResFileL(fileName);
+ 	BaflUtils::NearestLanguageFile(aFs, fileName);
+
+	RResourceFile resFile;
+	resFile.OpenL(aFs, fileName);
+	return resFile;
+	}
+
+/**
+Reads the resource specified by aResourceId from the supplied resource file.
+
+The resource is returned in the output argument aString. The supplied resource
+file must be open or this function will leave.
+
+@param	aResourceFile
+The opened resource file to read the resource from.
+
+@param	aResourceId
+The ID of the resource that is required.
+
+@param	aString
+An output buffer into which the read resource is placed.
+
+@leave	KErrOverflow
+The length of the resource string is greater than the maximum allowed.
+*/
+EXPORT_C void TSmsUtilities::ReadResourceStringL(RResourceFile aResourceFile, TInt aResourceId, TDes& aString)
+
+	{
+	HBufC8* buf = aResourceFile.AllocReadLC(aResourceId);
+	TResourceReader reader;
+	reader.SetBuffer(buf);
+	
+	TPtrC resString = reader.ReadTPtrC();
+
+	if (resString.Length() <= aString.MaxLength())
+		{ 
+		aString.Copy(resString);
+		}
+	else
+		{
+		User::Leave(KErrOverflow);
+		}
+
+	CleanupStack::PopAndDestroy(buf);
+	}
+
+void TSmsUtilities::CompareEntryL(const TMsvEntry& aEntry, TUid aMtm, TMsvId& aFirstId, CMsvEntrySelection* aServiceIds)
+	{
+	if (aEntry.iType == KUidMsvServiceEntry && aEntry.iMtm == aMtm)
+		{
+		const TMsvId id = aEntry.Id();
+
+		if (aFirstId == KMsvNullIndexEntryId)
+			aFirstId = id;
+
+		if (aServiceIds)
+			aServiceIds->AppendL(id);
+		}
+	}
+
+void TSmsUtilities::DoGetDetailsL(RFs& aFs, const TDesC& aFromAddress, TDes& aDetails, TInt aMaxLength)
+	{
+	__UHEAP_MARK;
+
+	// Check that aFromAddress is a valid GSM telephone number
+	if (!ValidGsmNumber(aFromAddress))
+		User::Leave(KErrArgument);
+
+	aDetails.Zero();
+
+	CContactDatabase* db = CContactDatabase::OpenL();
+	CleanupStack::PushL(db);
+
+	// Lookup the telephone number (aFromAddress) in the contact database
+	CContactIdArray* contactId = db->MatchPhoneNumberL(aFromAddress, KLowerSevenDigits);
+	CleanupStack::PushL(contactId);
+	
+	// Add the name if there is one and only one match in contacts. If there's more than
+	// one then wouldn't know which one to choose	
+	if (contactId->Count() <= 0)
+		{
+		//The telephone number (aFromAddress) was not found in the contact database.
+		User::Leave(KErrNotFound);
+		}
+	else if (contactId->Count() > 1)
+		{
+		//There's more than one telephone number match in contacts.	
+		User::Leave(KErrAlreadyExists);
+		}
+	
+	CContactItem* newContact = db->ReadMinimalContactL((*contactId)[0]);
+	CleanupStack::PushL(newContact);
+
+	CContactItemFieldSet& fieldSet = newContact->CardFields();
+
+	TInt count		= fieldSet.Count();
+
+	HBufC* family	= HBufC::NewLC(aMaxLength);
+	HBufC* given	= HBufC::NewLC(aMaxLength);
+	TPtr familyPtr(family->Des());
+	TPtr givenPtr(given->Des());
+
+	// Find the Given and First Name of the contact
+	// Order important
+	for (TInt curField = 0; curField < count && !(familyPtr.Length() && givenPtr.Length()); curField++)
+		{
+		CContactItemField& field = fieldSet[curField];
+
+		if (!familyPtr.Length())
+			GetName(field, KUidContactFieldFamilyName, familyPtr);
+
+		if (!givenPtr.Length())
+			GetName(field, KUidContactFieldGivenName, givenPtr);
+		}
+
+	familyPtr.Trim();
+	givenPtr.Trim();
+
+	TInt familyLen	= familyPtr.Length();
+	TInt givenLen	= givenPtr.Length();
+
+	if (!familyLen && !givenLen)
+		{
+		//Leave if no family nor given name found
+		User::Leave(KErrNotFound);
+		}
+	else if (givenLen == 0)
+		{
+		// The maximum length of familyPtr may be greater than
+		// aMaxLength, so need to check its length before copying.
+		if (familyPtr.Length() > aMaxLength)
+  			{
+  			familyPtr.Set(familyPtr.LeftTPtr(aMaxLength));
+  			}
+
+		aDetails = familyPtr;
+		}
+	else if (familyLen == 0)
+		{
+		// The maximum length of givenPtr may be greater than
+		// aMaxLength, so need to check its length before copying.
+		if (givenPtr.Length() > aMaxLength)
+  			{
+  			givenPtr.Set(givenPtr.LeftTPtr(aMaxLength));
+  			}
+
+		aDetails = givenPtr;
+		}
+	else
+		{
+		RResourceFile resFile = OpenResourceFileL(aFs);
+		CleanupClosePushL(resFile);
+		ReadResourceStringL(resFile, R_SENDER_NAME_FORMAT, aDetails);
+		CleanupStack::PopAndDestroy(&resFile);
+
+		TBuf<8> givenPlaceHolder = L_SMS_GIVEN_NAME;
+		TBuf<8> familyPlaceHolder = L_SMS_FAMILY_NAME;
+		TInt minLength = aDetails.Length() - givenPlaceHolder.Length() - familyPlaceHolder.Length();
+
+		if ((familyLen + givenLen + minLength) > aMaxLength)
+			{
+			// The maximum length of familyPtr may be greater than
+	   		// aMaxLength, so need to check its length before copying.
+  			if (familyPtr.Length() > aMaxLength)
+   				{
+   				familyPtr.Set(familyPtr.LeftTPtr(aMaxLength));
+   				}
+			aDetails = familyPtr;
+			}
+		else
+			{
+			Replace(givenPlaceHolder, givenPtr, aDetails);
+			Replace(familyPlaceHolder, familyPtr, aDetails);
+			}
+		}
+
+	//Remove leading and trailing spaces
+	aDetails.Trim();
+
+	CleanupStack::PopAndDestroy(5, db);
+
+	__UHEAP_MARKEND;
+	}
+
+TBool TSmsUtilities::ValidGsmNumber(const TDesC& aTelephone)
+	{
+	// Returns ETrue if
+	//	aTelephone is not zero length; and
+	//	aTelephone[0] is a digit or "+"; and
+	//	aTelephone[1..] is a digit or "*" or "#".
+	//	aTelephone has at least 2 valid characters
+
+	//Note: Returns EFalse if aTelephone contains any alpha character
+	//      All spaces are ignored
+
+	const TInt length = aTelephone.Length();
+
+	if (length < KSmsValidGsmNumberMinLength)
+		return EFalse;
+
+	TPtrC KFirstChar(KSmsValidGsmNumberFirstChar);
+	TPtrC KOtherChar(KSmsValidGsmNumberOtherChar);
+	TBool validTel = ETrue;
+	TInt validCharsFound = 0; //must be >= KSmsValidGsmNumberMinLength by the end
+
+	const TText* first = aTelephone.Ptr(); //Points to the first character in aTelephone
+	const TText* last = first + length - 1; //Points to the last character in aTelephone
+
+	//Check each character in the telephone number
+	while (validTel && first <= last)
+		{
+		TChar ch(*first);
+
+		if (!ch.IsSpace()) //ignore whitespace
+			{
+			if (!ch.IsDigit())
+				{
+				//Need to create TPtrC because TDesC::Find() does not take a TChar
+				TPtrC telCharacter(first, 1);
+
+				if (validCharsFound)
+					{
+					//Check the remaining characters of the telephone number
+					validTel = (KOtherChar.Find(telCharacter) != KErrNotFound);
+					}
+				else //validCharsFound == 0
+					{
+					//Check the first character of the telephone number
+					validTel = (KFirstChar.Find(telCharacter) != KErrNotFound);
+					}
+				}
+
+			validCharsFound++;
+			}
+
+		first++; //move to the next character
+		}
+
+	return validTel && validCharsFound >= KSmsValidGsmNumberMinLength;
+	}
+
+void TSmsUtilities::GetName(CContactItemField& aField, TUid aFieldType, TDes& aName)
+	{
+	__UHEAP_MARK;
+	if (aField.ContentType().ContainsFieldType(aFieldType))
+		{
+		TPtrC name = aField.TextStorage()->Text();
+		aName = name.Left(Min(aName.MaxLength(), name.Length()));
+		}
+	__UHEAP_MARKEND;
+	}
+
+TBool TSmsUtilities::DoGetDescriptionL(const CSmsMessage& aMessage, TDes& aDescription, TInt aMaxLength)
+// this function returns EFalse if aMessage has no special message indication data and is not an SMS_STATUS_REPORT,
+// i.e. more needs to be done to extract the description from the message
+// otherwise returns ETrue
+	{
+	TInt resourceId = 0;
+	TBuf<KSmsDescriptionLength> format;
+	TSmsMessageIndicationType messageIndicationType;
+	TExtendedSmsIndicationType extendedType;
+	TSmsMessageProfileType messageProfileType;
+	TBool toStore=EFalse;
+	TUint totalIndicationCount=0;
+	TUint totalMessageCount=0;	
+	
+	//check if the messae contains an enhanced voice mail indication
+	CSmsEnhancedVoiceMailOperations& enhancedVoiceMailOperations = STATIC_CAST(CSmsEnhancedVoiceMailOperations&,aMessage.GetOperationsForIEL(CSmsInformationElement::ESmsEnhanceVoiceMailInformation));
+	
+	if(enhancedVoiceMailOperations.ContainsEnhancedVoiceMailIEL())
+		{
+		//get a copy of the indication
+		CEnhancedVoiceMailBoxInformation* retrievedNotification=enhancedVoiceMailOperations.CopyEnhancedVoiceMailIEL();
+		TVoiceMailInfoType typeInfo=retrievedNotification->Type();
+		//check its type
+		if(typeInfo==EGsmSmsVoiceMailNotification)
+			{
+			//increment the indication count
+			++totalIndicationCount;
+			resourceId = R_MESSAGE_INDICATION_ENHANCED_VOICEMAIL_ONE;	
+			}
+		
+		TUint8 messageCount=retrievedNotification->NumberOfVoiceMessages();
+		//add the message count to the running total
+		totalMessageCount+=messageCount;
+		//if there is more that one message of this type then set the resouce id to 'many'
+		if(messageCount!=1)
+			{
+			++resourceId;	
+			}
+		
+		delete retrievedNotification;
+		}  
+		
+	//check for special message waiting indications
+	CSmsSpecialSMSMessageOperations& operations = STATIC_CAST(CSmsSpecialSMSMessageOperations&,aMessage.GetOperationsForIEL(CSmsInformationElement::ESmsIEISpecialSMSMessageIndication));
+	TUint specialMessageIndicationCount=operations.GetCountOfSpecialMessageIndicationsL();	
+	
+	if(specialMessageIndicationCount!=0)
+		{
+		//add special message indications to out indication count
+		totalIndicationCount+=specialMessageIndicationCount;	
+		
+		if(totalIndicationCount>1) 
+			{
+			//set the resource id to R_MESSAGE_INDICATION_OTHER_ONE
+			resourceId = R_MESSAGE_INDICATION_OTHER_ONE;
+			//get the total number of messages from the indicatations
+			TUint8 messageCount=0;
+			for(TInt loopCount=0;loopCount<specialMessageIndicationCount;loopCount++)
+				{
+				operations.GetMessageIndicationIEL(loopCount,toStore,messageIndicationType,extendedType,messageProfileType,messageCount);
+				totalMessageCount+=messageCount;						
+				}
+			}
+		else
+			{
+			//there is only one indication, get it's type and the number of messages it holds.
+			TUint8 messageCount=0;
+			operations.GetMessageIndicationIEL(0,toStore,messageIndicationType,
+											extendedType,messageProfileType,messageCount);	
+		
+			//add the message count to the running total
+			totalMessageCount+=messageCount;
+		
+			switch (messageIndicationType)
+				{
+				case EGsmSmsVoiceMessageWaiting:
+					resourceId = R_MESSAGE_INDICATION_VOICEMAIL_ONE;
+					break;
+				
+				case EGsmSmsFaxMessageWaiting:
+					resourceId = R_MESSAGE_INDICATION_FAX_ONE;
+					break;
+				
+				case EGsmSmsElectronicMailMessageWaiting:
+					resourceId = R_MESSAGE_INDICATION_EMAIL_ONE;
+					break;
+					
+				case EGsmSmsExtendedMessageTypeWaiting:
+					//get the extended indications type
+					if(extendedType==EGsmSmsVideoMessageWaiting)
+						{
+						resourceId = R_MESSAGE_INDICATION_VIDEOMESSAGE_ONE;	
+						}
+					else
+						{
+						resourceId = R_MESSAGE_INDICATION_OTHER_ONE;	
+						}
+					break;
+					
+				default:
+					resourceId = R_MESSAGE_INDICATION_OTHER_ONE;
+					break;
+				}	
+			}
+		//if there is more that one message waiting append 'many' to the id.
+		if(totalMessageCount!=1)
+			{
+			resourceId++;	
+			}
+		}
+
+	const CSmsPDU& smsPDU= aMessage.SmsPDU();
+	// If no Special Msg Indication found in the User Data,
+	// then check the DataCodingScheme.
+	if (totalIndicationCount==0 && smsPDU.DataCodingSchemePresent())
+		{
+		TInt bits7to4 = smsPDU.Bits7To4();
+
+		switch (bits7to4)
+			{
+			case TSmsDataCodingScheme::ESmsDCSMessageWaitingIndication7Bit:
+			case TSmsDataCodingScheme::ESmsDCSMessageWaitingIndicationUCS2:
+				{
+				if (smsPDU.IndicationState() == TSmsDataCodingScheme::ESmsIndicationActive)
+					{
+					totalIndicationCount = 1;
+
+					switch (smsPDU.IndicationType())
+						{
+						case TSmsDataCodingScheme::ESmsVoicemailMessageWaiting:
+							resourceId = R_MESSAGE_INDICATION_VOICEMAIL_ONE;
+							break;
+						case TSmsDataCodingScheme::ESmsFaxMessageWaiting:
+							resourceId = R_MESSAGE_INDICATION_FAX_ONE;
+							break;
+						case TSmsDataCodingScheme::ESmsElectronicMailMessageWaiting:
+							resourceId = R_MESSAGE_INDICATION_EMAIL_ONE;
+							break;
+						case TSmsDataCodingScheme::ESmsFaxOtherMessageWaiting:
+						default:
+							resourceId = R_MESSAGE_INDICATION_OTHER_ONE;
+							break;
+						} //end switch
+					} //end if
+				} //end case
+			default:
+				{
+				break; //do nothing
+				}
+			}
+		}
+	
+	if (totalIndicationCount!=0)
+		{
+		//Special message found.
+		//Look up the resourceID in the SMSS resource file
+
+		RFs fs;
+		User::LeaveIfError(fs.Connect());
+		CleanupClosePushL(fs);
+		RResourceFile resFile = OpenResourceFileL(fs);
+		CleanupClosePushL(resFile);
+		ReadResourceStringL(resFile, resourceId, format);
+		CleanupStack::PopAndDestroy(2, &fs);
+
+		if (totalMessageCount == 1)
+			{
+			if (format.Length() <= aMaxLength)
+				{
+				aDescription = format;
+				}
+			else
+				{
+				// Truncate format so that it fits into aDescription.
+				aDescription = format.Left(aMaxLength);
+				}
+			}
+		else if (format.Length() < aMaxLength)
+			{
+			aDescription.Zero();
+			aDescription.AppendFormat(format, totalMessageCount);
+			}
+		return ETrue;
+		}
+	else
+		{
+		if(aMessage.Type() == CSmsPDU::ESmsStatusReport)
+			{
+			// for SMS_STATUS_REPORT messages, if we cannot read the string in, then
+			// we do not attempt to extract the description from the message: return EFalse
+			RFs fs;
+			User::LeaveIfError(fs.Connect());
+			CleanupClosePushL(fs);
+			RResourceFile resFile = OpenResourceFileL(fs);
+			CleanupClosePushL(resFile);
+			ReadResourceStringL(resFile, R_TYPE_STATUS_REPORT, aDescription);
+			CleanupStack::PopAndDestroy(2, &fs);
+			return ETrue;
+			}
+		else
+			{
+			return EFalse;
+			}
+		}
+	}
+
+
+void TSmsUtilities::ExtractDescriptionFromMessage(const CSmsMessage& aMessage, TDes& aDescription, TInt aMaxLength)
+	{
+	if(aMessage.Type() != CSmsPDU::ESmsStatusReport)
+		{
+		aMessage.Buffer().Extract(aDescription, 0, Min(aMaxLength, aMessage.Buffer().Length()));
+
+		TInt length = aDescription.Length();
+
+		//replace paragraphs with spaces.
+		while(length--)
+			{
+			TText& text = aDescription[length];
+			const TChar ch(text);
+			if (ch.IsSpace() || text == CEditableText::EParagraphDelimiter)
+				text = ' ';
+			}
+
+		aDescription.TrimAll(); //removes leading trailing and multiple internal whitespace (spaces, line feeds etc)
+		}
+	}
+
+void TSmsUtilities::Replace(const TDesC& aOld, const TDesC& aNew, TDes& aString)
+	{
+	TInt find = aString.Find(aOld);
+
+	if (find != KErrNotFound)
+		{
+		aString.Replace(find, aOld.Length(), aNew);
+		}
+	}