diff -r 000000000000 -r 72b543305e3a mobilemessaging/smsmtm/clientmtm/src/smut.cpp --- /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 +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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(static_cast(pdu)); + smsDeliver.ServiceCenterTimeStamp(time, gmtOffset); + } + else if (pdu.Type() == CSmsPDU::ESmsStatusReport) + { + CSmsStatusReport& smsStatusReport = + const_cast(static_cast(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 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