// 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:
// Contains the implementation of the CSmsMessage class
// 
//

/**
 @file
*/

#include <gsmumsg.h>
#include "Gsmumain.h"
#include <gsmubuf.h>
#include <gsmuset.h>
#include <gsmusar.h>
#include "gsmumsgadditionalattributes.h"
#include <gsmuieoperations.h>
#include <gsmunonieoperations.h>

#include <logwrap.h> //  Used for KLogNullId only
#include <e32uid.h>
#include <etelmm.h>
#include <logwraplimits.h>

#include <emsinformationelement.h>
#include <emsformatie.h>
#include <emsuserpromptie.h>
#include <emsobjectdistributionie.h>
#include "smsstackutils.h"

/**
 *  Allocates and creates a CSmsMessage instance from a TGsmSms.
 *  
 *  The type of SMS-XXXX message to construct is defined by the first byte of
 *  the TGsmSms TPDU header.
 *  
 *  @param aFs Reference handle to the file system
 *  @param aGsmSms Reference to TGsmSms
 *  @param aBuffer The Unicode text for the message. The object takes ownership
 *  of aBuffer.
 *  @param aIsRPError Set to true if the message contains Reply Path Error. Default
 *  is false.
 *  @param aIsMobileTerminated Set to True if the message is Mobile Terminated.
 *  Default is false.
 *  @return New CSmsMessage object
 *  @capability None
 */
EXPORT_C CSmsMessage* CSmsMessage::NewL(RFs& aFs, const TGsmSms& aGsmSms,CSmsBufferBase* aBuffer, TBool aIsRPError,TBool aIsMobileTerminated)
	{
	LOGGSMU1("CSmsMessage::NewL()");
	
	CleanupStack::PushL(aBuffer);
	CSmsMessage* smsmessage=new(ELeave) CSmsMessage(aFs, aBuffer);
	CleanupStack::Pop();
	CleanupStack::PushL(smsmessage);
	smsmessage->ConstructL(aGsmSms, aIsRPError,aIsMobileTerminated);
	CleanupStack::Pop();
	return smsmessage;
	} // CSmsMessage::NewL


/**
 *  Allocates and creates a CSmsMessage, specifying the SMS-XXX message type with
 *  a CSmsPDU::TSmsPduType.
 *  
 *  @param aFs Reference handle to the file system
 *  @param aType The PDU type
 *  @param aBuffer The Unicode text for the message. The object takes ownership
 *  of aBuffer.
 *  @param aIsRPError Set to true if the message contains Reply Path Error. Default
 *  is false.
 *  @return New CSmsMessage object
 *  @capability None
 */
EXPORT_C CSmsMessage* CSmsMessage::NewL(RFs& aFs, CSmsPDU::TSmsPDUType aType,CSmsBufferBase* aBuffer,TBool aIsRPError)
	{
	LOGGSMU1("CSmsMessage::NewL()");

	CleanupStack::PushL(aBuffer);
	CSmsMessage* smsmessage=new(ELeave) CSmsMessage(aFs, aBuffer);
	CleanupStack::Pop();
	CleanupStack::PushL(smsmessage);
	smsmessage->ConstructL(aType,aIsRPError);
	CleanupStack::Pop();
	return smsmessage;
	} // CSmsMessage::NewL


/**
 *  Destructor, frees resources.
 *  @capability None
 */
EXPORT_C CSmsMessage::~CSmsMessage()
	{
	LOGGSMU1("CSmsMessage::~CSmsMessage()");

	delete iSmsPDU;
	delete iBuffer;
	delete iCharacterSetConverter;

	if (iInformationElementArray)
		{
		iInformationElementArray->ResetAndDestroy();
		delete iInformationElementArray;
		}

	delete iAdditionalInfo;
	} // CSmsMessage::NewL


/**
 *  Internalises all object data except for the CSmsBufferBase.
 *  
 *  This is used when the buffer is stored elsewhere.
 *  
 *  @param aStream Stream to read from
 *  @capability None
 */
EXPORT_C void CSmsMessage::InternalizeWithoutBufferL(RReadStream& aStream)
	{
	LOGGSMU1("CSmsMessage::InternalizeWithoutBufferL()");

	InternalizeWithoutBufferAndVersionL(aStream);
	InternalizeVersionL(aStream);

	iAdditionalInfo->ResetAttributesL();

	if (iVersion > ESmsMessageV0)
        {
        iAdditionalInfo->InternalizeL(aStream, iVersion);
		}
	} // CSmsMessage::InternalizeWithoutBufferL


/**
 *  Externalises all object data except for the CSmsBufferBase.
 *  
 *  This is used when the buffer is stored elsewhere.
 *  
 *  @param aStream Stream to write to
 *  @capability None
 */
EXPORT_C void CSmsMessage::ExternalizeWithoutBufferL(RWriteStream& aStream) const
	{
	LOGGSMU1("CSmsMessage::ExternalizeWithoutBufferL()");

	ExternalizeWithoutBufferAndVersionL(aStream);
	ExternalizeVersionL(aStream);

	if (iVersion > ESmsMessageV0)
		{
		iAdditionalInfo->ExternalizeL(aStream, iVersion);
		}
	} // CSmsMessage::ExternalizeWithoutBufferL


/**
 *  Internalises all object data.
 *  
 *  @param aStream Stream to read from
 *  @capability None
 */
EXPORT_C void CSmsMessage::InternalizeL(RReadStream& aStream)
	{
	LOGGSMU1("CSmsMessage::InternalizeL()");

	InternalizeWithoutBufferAndVersionL(aStream);
	InternalizeBufferL(aStream);
	InternalizeVersionL(aStream);

	iAdditionalInfo->ResetAttributesL();
	if (iVersion > ESmsMessageV0)
		{
		iAdditionalInfo->InternalizeL(aStream, iVersion);
		}
	} // CSmsMessage::InternalizeL


/**
 *  Externalises all object data.
 *  
 *  @param aStream Stream to write to
 *  @capability None
 */
EXPORT_C void CSmsMessage::ExternalizeL(RWriteStream& aStream) const
	{
	LOGGSMU1("CSmsMessage::ExternalizeL()");

	ExternalizeWithoutBufferAndVersionL(aStream);
	ExternalizeBufferL(aStream);
	ExternalizeVersionL(aStream);

	if (iVersion > ESmsMessageV0)
		{
		iAdditionalInfo->ExternalizeL(aStream, iVersion);
		}
	} // CSmsMessage::ExternalizeL


/**
 *  Tests if the message contains text.
 *  
 *  @return True if the message contains text
 *  @capability None
 */
EXPORT_C TBool CSmsMessage::TextPresent() const
	{
	LOGGSMU1("CSmsMessage::TextPresent()");

	CSmsPDU::TSmsPDUType pdutype=SmsPDU().Type();
	return (pdutype==CSmsPDU::ESmsSubmit) ||
	       (pdutype==CSmsPDU::ESmsDeliver) ||
	     (((pdutype==CSmsPDU::ESmsSubmitReport) || (pdutype==CSmsPDU::ESmsDeliverReport) ||
	      ((pdutype==CSmsPDU::ESmsStatusReport) && ((CSmsStatusReport&) SmsPDU()).ParameterIndicatorPresent())) &&
	      SmsPDU().UserDataPresent());
	} // CSmsMessage::TextPresent


/**
 *  Gets the number of PDU's required to encode the complete message.
 *  
 *  @leave KErrOverflow Leaves if the number of required PDUs exceeds the maximum
 *  or the message cannot be encoded.
 *  @return Number of PDU's
 *  @capability None
 */
EXPORT_C TInt CSmsMessage::NumMessagePDUsL()
	{
	LOGGSMU1("CSmsMessage::NumMessagePDUsL()");

	TInt nummessagepdus=1;
	if (IsDecoded())
		{
		if (TextPresent())
			{
			nummessagepdus=NumMessageEmsPDUsL();
			TInt maxnummessagepdus=SmsPDU().TextConcatenated()? 0xFF: 1;
 			if (nummessagepdus>maxnummessagepdus)
				{
 				User::Leave(KErrOverflow);
				}
			}

		}
	else if (TextPresent() && SmsPDU().TextConcatenated())
		{
		nummessagepdus=SmsPDU().NumConcatenatedMessagePDUs();
		}

	LOGGSMU2("CSmsMessage::NumMessagePDUsL() returns %d", nummessagepdus);

	return nummessagepdus;
	} // CSmsMessage::NumMessagePDUsL


/**
 *  Gets the maximum message length possible with the current settings.
 *  
 *  If Text is compressed, this returns the maximum number of bytes available
 *  with text compressed.
 *  
 *  @return Maximum message length
 *  @capability None
 */
EXPORT_C TInt CSmsMessage::MaxMessageLength() const
	{
	__ASSERT_DEBUG(TextPresent(),Panic(KGsmuPanicTextNotPresent));
	TInt maxmessagelength=SmsPDU().UserData().MaxBodyLengthInChars();
	if (SmsPDU().TextConcatenated())
		{
		maxmessagelength=maxmessagelength*0xFF;
		}

	LOGGSMU2("CSmsMessage::MaxMessageLength() returns %d", maxmessagelength);

	return maxmessagelength;
	} // CSmsMessage::MaxMessageLength


/**
 *  
 *  @return The converted buffer length
 *  @note Use with care - Expensive operation
 */
TInt CSmsMessage::ConvertedBufferLengthL(const CSmsBufferBase& aBuffer)
    {
    // Ignore in code coverage - not used in SMS stack and not exported
    // but cannot be removed as impacts public header.
    BULLSEYE_OFF    
    LOGGSMU1("CSmsMessage::ConvertedBufferLengthL()");
    
    TInt convertedBufferLength=0;
    CSmsAlphabetConverter* converter=CSmsAlphabetConverter::NewLC(*iCharacterSetConverter,iFs,SmsPDU().Alphabet(),BinaryData());
    CSmsBufferSegmenter* segmenter=CSmsBufferSegmenter::NewLC(*converter,aBuffer);
    convertedBufferLength=segmenter->TotalConvertedLengthL(iAdditionalInfo->Alternative7bitEncoding());
    CleanupStack::PopAndDestroy(2, converter);
    
    LOGGSMU2("CSmsMessage::ConvertedBufferLengthL() returns %d", convertedBufferLength);
    
    return convertedBufferLength;
    BULLSEYE_RESTORE
    }

/**
 *  Gets the message length.
 *  
 *  In the case where the message is compressed, this function compresses the
 *  text and returns the number of bytes of the compressed buffer.
 *  
 *  The function calls ConvertedBufferLengthL(), and is therefore an expensive
 *  operation.
 *  
 *  @return Message length
 *  @capability None
 */
EXPORT_C TInt CSmsMessage::MessageLengthL()
	{
	LOGGSMU1("CSmsMessage::MessageLengthL()");

	TInt messagelength=0;
	if (!SmsPDU().TextCompressed())
		{
		messagelength=iBuffer->Length();
		if(SmsPDU().Alphabet() == TSmsDataCodingScheme::ESmsAlphabetUCS2)messagelength*=2 ;
		}
	else
		{
		User::Leave(KErrNotSupported);
		}
	return messagelength;
	} // CSmsMessage::MessageLengthL


EXPORT_C void CSmsMessage::GetEncodingInfoL(TInt& aPdus, TInt& aUnconvertedChars,
		                                    TInt& aDowngradedChars, TInt& aFreeUDUnitsInLastPDU)
	{
	LOGGSMU1("CSmsMessage::GetEncodingInfoL()");

	aPdus                 = 1;
	aUnconvertedChars     = 0;
	aDowngradedChars      = 0;
	aFreeUDUnitsInLastPDU = 0;

	if (TextPresent())
		{
		if (IsDecoded())
			{
			//
			// Clear the concatenated flag, EncodeBufferL() will add it if needed.
			//
			SmsPDU().SetTextConcatenatedL(EFalse, EFalse);

			//
			// Attempt to encode to a single PDU, and if that fails then attempt to
			// encode to a set of PDUs.
			//
			CArrayFixFlat<TGsmSms>*  tmpArray = new (ELeave) CArrayFixFlat<TGsmSms>(8);
			CleanupStack::PushL(tmpArray);
			
			//
			// Encode the message...
			//
			if (!EncodeIntoSinglePDUL(*tmpArray, aUnconvertedChars,
                                      aDowngradedChars, aFreeUDUnitsInLastPDU))
				{
				EncodeBufferL(*tmpArray, 0, *iBuffer, aUnconvertedChars,
						      aDowngradedChars, aFreeUDUnitsInLastPDU, EFalse);

				aPdus = iAdditionalInfo->SmsPDUArray().Count();
				if (aPdus > 255)
					{
	 				User::Leave(KErrOverflow);
					}
				}

			CleanupStack::PopAndDestroy(tmpArray);
			}
		else if (SmsPDU().TextConcatenated())
			{
			aPdus                 = SmsPDU().NumConcatenatedMessagePDUs();
			aUnconvertedChars     = 0;
			aDowngradedChars      = 0;
			aFreeUDUnitsInLastPDU = 0;
			}
		}

	LOGGSMU2("CSmsMessage::GetEncodingInfoL(): aPdus=%d", aPdus);
	LOGGSMU2("CSmsMessage::GetEncodingInfoL(): aUnconvertedChars=%d", aUnconvertedChars);
	LOGGSMU2("CSmsMessage::GetEncodingInfoL(): aDowngradedChars=%d", aDowngradedChars);
	LOGGSMU2("CSmsMessage::GetEncodingInfoL(): aFreeUDUnitsInLastPDU=%d", aFreeUDUnitsInLastPDU);
	} // CSmsMessage::GetEncodingInfoL


/**
 *  Gets the User Data Settings.
 *  
 *  @param aSettings User Data Settings
 *  @capability None
 */
EXPORT_C void CSmsMessage::UserDataSettings(TSmsUserDataSettings& aSettings) const
	{
	LOGGSMU1("CSmsMessage::UserDataSettings()");

	__ASSERT_DEBUG(TextPresent(),Panic(KGsmuPanicTextNotPresent));
	aSettings.SetAlphabet(SmsPDU().Alphabet());
	aSettings.SetTextCompressed(SmsPDU().TextCompressed());
	TBool is16bit;
	TBool concatenated=SmsPDU().TextConcatenated(&is16bit);
	aSettings.SetTextConcatenated(concatenated,is16bit);
	} // CSmsMessage::UserDataSettings


/**
 *  Sets the User Data Settings.
 *  
 *  @param aSettings User Data Settings
 *  @capability None
 */
EXPORT_C void CSmsMessage::SetUserDataSettingsL(const TSmsUserDataSettings& aSettings)
	{
	LOGGSMU1("CSmsMessage::SetUserDataSettingsL()");

	__ASSERT_DEBUG(TextPresent(),Panic(KGsmuPanicTextNotPresent));
	SmsPDU().SetAlphabet(aSettings.Alphabet());
	SmsPDU().SetTextCompressed(aSettings.TextCompressed());
	TBool is16bit;
	TBool concatenated=aSettings.TextConcatenated(&is16bit);
	SmsPDU().SetTextConcatenatedL(concatenated,is16bit);
	} // CSmsMessage::SetUserDataSettingsL


/**
 *  Optimizes the user data settings.
 *  
 *  The alphabet flag causes an alphabet to be chosen which preserves information
 *  in the message and makes the number of PDUs as small as possible.
 *  
 *  The compression settings flag is not supported.
 *  
 *  The compression flag causes compression to be switched on if the resultant
 *  number of PDUs is smaller.
 *  
 *  If user explicitly defines alphabet as UCS2 or 8-bit coding, this setting is preserved;
 *  otherwise, if 7-bit (default) alphabet can not support current text of message UCS2
 *  alphabet (Unicode) is used.
 *  
 *  The two concatenation flags are mutually exclusive as they deal with 8-bit
 *  and 16-bit referenced concatenation. Both flags cause compression to be switched
 *  off and if the message length is greater than the maximum message length,
 *  concatenation is switched on.
 *  
 *  @param aOptions Combination of TSmsOptimizationFlags
 *  @capability None
 */
EXPORT_C void CSmsMessage::OptimizeSettingsL(TInt aOptions)
	{
	LOGGSMU1("CSmsMessage::OptimizeSettingsL()");

	__ASSERT_DEBUG(TextPresent(),Panic(KGsmuPanicTextNotPresent));
	__ASSERT_DEBUG(IsDecoded(),Panic(KGsmuPanicNotDecoded));

	if (aOptions&ESmsFlagOptimizeAlphabet && (SmsPDU().Alphabet()==TSmsDataCodingScheme::ESmsAlphabet7Bit))
		{
		TBool isSupported=ETrue;
		TInt numOfUnconvertibleChars;
		TInt indexOfFirstUnconvertibleChar;

		TInt size=iBuffer->Length();
		const TInt bufsize=128;
		TBuf<bufsize> buf;
		TInt extracted=0;
		TInt remaining=0;
		TInt toExtract=0;
		TInt pos=0;

		// Defect Fix. Previous for loop caused an access violation as extract size was set
		// to size of ibuffer not bufsize and caused a access violation when over 128. Implemented
		// fix copies the ibuffer in 128 chunks until its all done.

		while((pos<size) && isSupported)
			{
			remaining=(size - extracted);

			if(remaining>bufsize)
				toExtract=bufsize;
			else
				toExtract=remaining;

			iBuffer->Extract(buf,pos,toExtract);
			isSupported=IsSupportedL(buf,numOfUnconvertibleChars,indexOfFirstUnconvertibleChar);
			extracted=(extracted+toExtract);
			pos=extracted;
			}

		if(!isSupported)
			SmsPDU().SetAlphabet(TSmsDataCodingScheme::ESmsAlphabetUCS2);
		}
	} // CSmsMessage::OptimizeSettingsL


/**
 *  This function returns the currently requested alternative encoding method.
 * 
 *  In the case of incoming SMS messages received on the device, it will often
 *  be TSmsEncodingNone unless the Information Elements imply that an
 *  alternative encoding has been used.
 * 
 *  @return The currently selected encoding method.
 * 
 *  @capability None
 */
EXPORT_C TSmsEncoding CSmsMessage::Alternative7bitEncoding() const
	{
	LOGGSMU1("CSmsMessage::Alternative7bitEncoding()");

	return iAdditionalInfo->Alternative7bitEncoding();
	} // CSmsMessage::Alternative7bitEncoding


/**
 *  This function allows the client to specify an alternative encoding method
 *  incase the default GSM encoding cannot encode the message completely
 *  without data loss.
 * 
 *  @param aEncoding  Encoding method to select.
 * 
 *  @return A Symbian system wide error code.  This function will return
 *          KErrArgument if the encoding enum is invalid and will return
 *          KErrNotSupported if the required plug-ins are not present.
 * 
 *  @capability None
 */
EXPORT_C TInt CSmsMessage::SetAlternative7bitEncoding(TSmsEncoding aEncoding)
	{
	LOGGSMU2("CSmsMessage::SetAlternative7bitEncoding(%d)", aEncoding);

	//
	// Get the encoders that would be used for this encoding method.
	// The function will also check that the required encoder is present.
	//
	TRAPD(err,
		{
		CSmsAlphabetConverter*  converter = CSmsAlphabetConverter::NewLC(*iCharacterSetConverter, iFs,
																	     TSmsDataCodingScheme::ESmsAlphabet7Bit,
																	     EFalse);
		converter->ConfirmAlternativeEncoderL(aEncoding);
		CleanupStack::PopAndDestroy(converter);
		});
	
	//
	// Set the variable if the encoders are available...
	//
	if (err == KErrNone)
		{
		iAdditionalInfo->SetAlternative7bitEncoding(aEncoding);
		}
	
	return err;
	} // CSmsMessage::SetAlternative7bitEncoding


/**
 *  Takes an encoding setting (received from a PDU) and merges it into the
 *  current setting (where possible).
 *  
 *  @param aEncoding  Encoding setting to merge.
 */
void CSmsMessage::MergeAlternative7bitEncoding(TSmsEncoding aEncoding) const
	{
	LOGGSMU3("CSmsMessage::MergeAlternative7bitEncoding(): aEncoding=%d (currently %d)",
			 aEncoding, iAdditionalInfo->Alternative7bitEncoding());

	switch (iAdditionalInfo->Alternative7bitEncoding())
		{
		case ESmsEncodingNone:
			{
			// Anything is can be merge with this...
			iAdditionalInfo->SetAlternative7bitEncoding(aEncoding);
			}
			break;
		
		case ESmsEncodingTurkishSingleShift:
			{
			// Only Turkish locking shift can merge...
			if (aEncoding == ESmsEncodingTurkishLockingShift)
				{
				iAdditionalInfo->SetAlternative7bitEncoding(ESmsEncodingTurkishLockingAndSingleShift);
				}
			}
			break;
		
		case ESmsEncodingTurkishLockingShift:
			{
			// Only Turkish single shift can merge...
			if (aEncoding == ESmsEncodingTurkishSingleShift)
				{
				iAdditionalInfo->SetAlternative7bitEncoding(ESmsEncodingTurkishLockingAndSingleShift);
				}
			}
			break;
		
		case ESmsEncodingPortugueseSingleShift:
			{
			// Only Portuguese locking shift can merge...
			if (aEncoding == ESmsEncodingPortugueseLockingShift)
				{
				iAdditionalInfo->SetAlternative7bitEncoding(ESmsEncodingPortugueseLockingAndSingleShift);
				}
			}
			break;
		
		case ESmsEncodingPortugueseLockingShift:
			{
			// Only Portuguese single shift can merge...
			if (aEncoding == ESmsEncodingPortugueseSingleShift)
				{
				iAdditionalInfo->SetAlternative7bitEncoding(ESmsEncodingPortugueseLockingAndSingleShift);
				}
			}
			break;
		
		case ESmsEncodingTurkishLockingAndSingleShift:
		case ESmsEncodingSpanishSingleShift:
		case ESmsEncodingPortugueseLockingAndSingleShift:
		default:
			{
			// NOP - Cannot merge anything into these...
			}
		};

	LOGGSMU2("CSmsMessage::MergeAlternative7bitEncoding(): New encoding=%d",
			 iAdditionalInfo->Alternative7bitEncoding());
	} // CSmsMessage::MergeAlternative7bitEncoding


/**
 *  Tests if a buffer can be encoded without loss of information.
 *  
 *  @param aDes                                 The buffer to test for encoding
 *  @param aNumberOfUnconvertibleCharacters     On return, the number of 
 *                                              unconvertible characters
 *  @param aIndexOfFirstUnconvertibleCharacter  On return, the index of the
 *                                              first unconvertible character
 * 
 *  @return True if aDes can be encoded without loss of information
 * 
 *  @capability None
 */
EXPORT_C TBool CSmsMessage::IsSupportedL(const TDesC& aDes, TInt& aNumberOfUnconvertibleCharacters,
		                                 TInt& aIndexOfFirstUnconvertibleCharacter)
	{
	LOGGSMU1("[1] CSmsMessage::IsSupportedL()");

	__ASSERT_DEBUG(TextPresent(), Panic(KGsmuPanicTextNotPresent));

	aNumberOfUnconvertibleCharacters    = 0;
	aIndexOfFirstUnconvertibleCharacter = aDes.Length();

	if (SmsPDU().TextCompressed())
		{
		return EFalse;
		}

	return SmsPDU().UserData().IsSupportedL(aDes, aNumberOfUnconvertibleCharacters,
                                            aIndexOfFirstUnconvertibleCharacter);
	} // CSmsMessage::IsSupportedL


/**
 *  Tests if a buffer can be encoded without loss of information.
 *  
 *  @param aDes                                 The buffer to test for encoding
 *  @param aNumberOfUnconvertibleCharacters     On return, the number of 
 *                                              unconvertible characters
 *  @param aNumberOfDowngradedCharacters        On return, the number of 
 *                                              characters downgraded
 *  @param aNumberRequiringAlternativeEncoding  On return, the number of 
 *                                              characters needing alternative
 *                                              encoding support
 *  @param aIndexOfFirstUnconvertibleCharacter  On return, the index of the
 *                                              first unconvertible character
 * 
 *  @return True if aDes can be encoded without loss of information
 * 
 *  @capability None
 */
EXPORT_C TBool CSmsMessage::IsSupportedL(const TDesC& aDes, TInt& aNumberOfUnconvertibleCharacters,
		                                 TInt& aNumberOfDowngradedCharacters,
		                                 TInt& aNumberRequiringAlternativeEncoding,
		                                 TInt& aIndexOfFirstUnconvertibleCharacter) const
	{
	LOGGSMU1("[2] CSmsMessage::IsSupportedL()");

	__ASSERT_DEBUG(TextPresent(), Panic(KGsmuPanicTextNotPresent));

	aNumberOfUnconvertibleCharacters    = 0;
	aNumberOfDowngradedCharacters       = 0;
	aNumberRequiringAlternativeEncoding = 0;
	aIndexOfFirstUnconvertibleCharacter = aDes.Length();

	if (SmsPDU().TextCompressed())
		{
		return EFalse;
		}

	return SmsPDU().UserData().IsSupportedL(aDes, iAdditionalInfo->Alternative7bitEncoding(),
											aNumberOfUnconvertibleCharacters,
	                                        aNumberOfDowngradedCharacters,
                                            aNumberRequiringAlternativeEncoding,
                                            aIndexOfFirstUnconvertibleCharacter);
	} // CSmsMessage::IsSupportedL


/**
 *  Encodes message PDUs as an array of TGsmSms objects.
 *  
 *  Note, this function should only be called after EncodeIntoSinglePDUL() as EncodeBufferL() now
 *  automatically prepares PDUs for concatenation.
 *  
 *  @param aSmsArray (In) an empty array. On return, one or more encoded TGsmSms.
 *  @param aReference Sets a Concatenated Message Reference (default 0)
 *  @capability None
 */
EXPORT_C void CSmsMessage::EncodeMessagePDUsL(CArrayFix<TGsmSms>& aSmsArray, TInt aReference)
	{
	LOGGSMU2("CSmsMessage::EncodeMessagePDUsL(): aReference=%d", aReference);

	__ASSERT_DEBUG((aSmsArray.Count()==0),Panic(KGsmuPanicSmsArrayNotEmpty));
	
	if (TextPresent())
		{
		if (SmsPDU().TextCompressed())
			{
			User::Leave(KErrNotSupported);
			}
		else
			{
			TInt  unconvertedChars, downgradedChars, freeUDUnitsInLastPDU;
				
			EncodeBufferL(aSmsArray, aReference, *iBuffer, unconvertedChars,
					      downgradedChars, freeUDUnitsInLastPDU);
			}
		}
	else
		{
        if(SmsPDU().Type()==CSmsPDU::ESmsCommand)
            {
            //Commands never contain text, so commands only encoded
            // in this branch.
            //
            // However  Commands don't support information
            // elements (per 23.040 6.5) and the CSmsIEOperation class
            // do not allow control information elements to be added
            // to Commands, hence the next line is commented out.
            // PrepareCommandMessageL();
            }

 		TGsmSms sms;
 		SmsPDU().EncodeMessagePDUL(sms);
 		aSmsArray.AppendL(sms);
		}
	} // CSmsMessage::EncodeMessagePDUsL


/**
 *  @internalComponent
 *  
 *  This method copies information elements from the collections contained in
 *  CSmsMessageAdditionalAttributes into the working PDU.
 *  
 *  Command Messages comprise only 1 PDU. Therefore all information
 *  elements must be present in the working PDU when encoding takes place.
 *  If there is not enough space in the PDU, the message encoding will fail.
 *  
 *  The 23.040 has not defined any information elements that are contained in
 *  Command PDUs. This method has been provided to maintain consistency with
 *  previous GSMU implementations.
 *  
 *  @leave KErrAlreadyExists or KErrAlreadyExists
 */
void CSmsMessage::PrepareCommandMessageL()
    {
    LOGGSMU1("CSmsMessage::PrepareCommandMessageL()");

    if (SmsPDU().Type()==CSmsPDU::ESmsCommand)
        {
        CSmsCommand& command = static_cast<CSmsCommand&>(SmsPDU());

        for (TUint8 category = 0; category < TSmsInformationElementCategories::ENumberOfCategories; category++)
            {
            switch(category)
                {
                case TSmsInformationElementCategories::ECtrlMandatoryIn1stPDUOnly:
                case TSmsInformationElementCategories::ECtrlSingleInstanceOnly:
                case TSmsInformationElementCategories::ECtrlMultipleInstancesAllowed:
                    {
                    for (TUint j = 0; j < iAdditionalInfo->NumberOfControlInformationElements((CSmsMessageAdditionalAttributes::TCategory) category); j++)
                        {
                        CSmsInformationElement& informationElement = iAdditionalInfo->GetControlInformationElementL( ( (CSmsMessageAdditionalAttributes::TCategory) category), j);
                        TPtr8 data = informationElement.Data();
                        command.AddInformationElementL(informationElement.Identifier(), data);
                        }
                    break;
                    }
                default:
                LOGGSMU2("CSmsMessage::PrepareCommandMessageL,default switch category = %d, id = %d", category);
                    break;
                }
            }
        }
    } // CSmsMessage::PrepareCommandMessageL


/**
 *  Decodes message PDUs from an array of TGsmSms objects.
 *  
 *  The array contains a concatenated message, and must have the correct number
 *  of PDUs.
 *  
 *  @param aSmsArray Concatenated message
 *  @capability None
 */
EXPORT_C void CSmsMessage::DecodeMessagePDUsL(const CArrayFix<TGsmSms>& aSmsArray)
	{
    LOGGSMU2("CSmsMessage::DecodeMessagePDUsL(): PDUs=%d", aSmsArray.Count());

	TInt count=aSmsArray.Count();
	SetIsComplete(ETrue);
	CArrayPtrFlat<CSmsPDU>* smspduarray=new(ELeave) CArrayPtrFlat<CSmsPDU>(8);
	CleanupStack::PushL(smspduarray);
	// With CleanupResetAndDestroyPushL, only ResetAndDestroy method of smspduarray is invoked. 
	// smspduarray object is actually deleted with the first push. 
	// coverity[double_push]
	CleanupResetAndDestroyPushL(*smspduarray);  // Don't forget to destroy what the array elements point to.

	TInt i=0;
	TBool ismobileterminated=(Type()==CSmsPDU::ESmsDeliver) || (Type()==CSmsPDU::ESmsSubmitReport) || (Type()==CSmsPDU::ESmsStatusReport);
	for (; i<count; i++)
		{
		CSmsPDU* smspdu=CSmsPDU::NewL(aSmsArray[i],*iCharacterSetConverter,iFs, EFalse,ismobileterminated);
		CleanupStack::PushL(smspdu);
		TInt j=0;
		for (; (j<smspduarray->Count()) && (smspdu->ConcatenatedMessagePDUIndex()>(*smspduarray)[j]->ConcatenatedMessagePDUIndex()); j++)
			{
			}
		smspduarray->InsertL(j,smspdu);
		CleanupStack::Pop();
		}
	if (SmsPDU().TextCompressed())
		User::Leave(KErrNotSupported);
	else
		DecodeBufferL(*smspduarray,*iBuffer);

	CleanupStack::PopAndDestroy(2);  //  smspduarray elements (Reset and Destroy), smspduarray
	SetIsDecoded(ETrue);
	SmsPDU().UserData().SetBodyL(KNullDesC8);
	} // CSmsMessage::DecodeMessagePDUsL

/**
 *  Decodes partial complete message PDUs from an array of TGsmSms objects.
 *  
 *  The array contains a concatenated incomplete message, but the array of
 *  TGsmSms object must be in sequence.
 *  
 *	NOTE:
 *	It will not fully decode EMS and Control Information elements if it needs 
 *	to decode a partially complete class 0 SMS message. The assumption is that 
 *	it is necessary to display a partially complete message then only the raw 
 *	text will be displayed on the UI without any animation elements. The 
 *	rational for this design is that these information elements are normally 
 *	specified as being located at a specific position in a message. It will 
 *	complicate the design of client app if it has to re-calculate the position 
 *	of these elements when it receives a partially complete message under 
 *	Out Of Memory Conditions.
 
 *  @param aSmsArray Concatenated message
 *	@param aLastPartialCompleteMsg boolean value indicating this is the last
 *			incomplete message for a particular concatenated message.
 *  @capability None
 */
EXPORT_C void CSmsMessage::DecodePartialCompleteMessagePDUsL(const CArrayFix<TGsmSms>& aSmsArray, TBool aLastPartialCompleteMsg)
	{
	LOGGSMU2("CSmsMessage::DecodePartialCompleteMessagePDUsL(): PDUs=%d", aSmsArray.Count());

	TInt count=aSmsArray.Count();
	SetIsComplete(EFalse);
	CArrayPtrFlat<CSmsPDU>* smspduarray=new(ELeave) CArrayPtrFlat<CSmsPDU>(8);
	CleanupStack::PushL(smspduarray);
	// With CleanupResetAndDestroyPushL, only ResetAndDestroy method of smspduarray is invoked. 
	// smspduarray object is actually deleted with the first push. 
	// coverity[double_push]
	CleanupResetAndDestroyPushL(*smspduarray);  // Don't forget to destroy what the array elements point to.

	TBool isMobileTerminated=(Type()==CSmsPDU::ESmsDeliver);
	/*
	The loop below goes through all the sms's received & check whether the received PDUs are of Class 0
	or not. The number of PDUs received must be less than the number of concatenated PDUs constitute this message.
	Then it order the PDUs in sequence for decoding purpose.
	*/
	for (TInt i=0; i<count; i++)
		{
		CSmsPDU* smspdu=CSmsPDU::NewL(aSmsArray[i],*iCharacterSetConverter,iFs, EFalse,isMobileTerminated);
		CleanupStack::PushL(smspdu);

		/*
		Check all the PDUs must be class 0 messages & also the number of received PDUs
		must be less than the number of concatenated PDUs constitute this message
		*/
		TSmsDataCodingScheme::TSmsClass  msgClass;
		if (smspdu->DataCodingSchemePresent()  &&  smspdu->Class(msgClass))
			{
			if (msgClass != TSmsDataCodingScheme::ESmsClass0)
				{
				User::Leave(KErrNotSupported);
				}
			__ASSERT_DEBUG((count < smspdu->NumConcatenatedMessagePDUs()),Panic(KGsmuPanicWrongNumberOfMessagePDUs));
			}
		else
			{
			User::Leave(KErrNotSupported);
			}
		//The below for statement finds the position in smspduarray where the PDU should be inserted.
		//The PDUs are put in sequence for decoding purpose.
		TInt j;
		for (j=0; (j<smspduarray->Count()) && (smspdu->ConcatenatedMessagePDUIndex()>(*smspduarray)[j]->ConcatenatedMessagePDUIndex()); j++)
			{
			}
		smspduarray->InsertL(j,smspdu);
		CleanupStack::Pop();
		}

	if (SmsPDU().TextCompressed())
		{
		User::Leave(KErrNotSupported);
		}
	else
		{
		DecodeOnlyTextL(*smspduarray,*iBuffer);
		}

	iVersion = CSmsMessage::ESmsIncompleteClass0MessageV;

	TInt startPDU = smspduarray->At(0)->ConcatenatedMessagePDUIndex();
	TInt endPDU = smspduarray->At(smspduarray->Count()-1)->ConcatenatedMessagePDUIndex();
	AddIncompleteMessageInfoL(startPDU, endPDU, aLastPartialCompleteMsg);

	CleanupStack::PopAndDestroy(2);  //  smspduarray elements (Reset and Destroy), smspduarray
	SetIsDecoded(ETrue);
	SmsPDU().UserData().SetBodyL(KNullDesC8);	
	}

CSmsMessage::CSmsMessage(RFs& aFs, CSmsBufferBase* aBuffer):
    iFlags(0),
    iStatus(NMobileSmsStore::EStoredMessageUnread),
    iLogServerId(KLogNullId),
    iBuffer(aBuffer),
    iFs(aFs),
	iSlotArray(8),
	iVersion(ESmsMessageV4),
	iAdditionalInfo(NULL)
    {
    iTime.UniversalTime();

    TBool result = SetUTCOffset(User::UTCOffset());
    __ASSERT_DEBUG(result, Panic(KGSMUPanicUserTimeZoneOffsetOutOfRange));
    } // NMobileSmsStore::EStoredMessageUnread

void CSmsMessage::ConstructL(const TGsmSms& aGsmSms, TBool aIsRPError,TBool aIsMobileTerminated)
	{
    LOGGSMU1("CSmsMessage::ConstructL()");

	iCharacterSetConverter=CCnvCharacterSetConverter::NewL();
	iInformationElementArray = new (ELeave) RPointerArray<CEmsInformationElement>(8);

	iAdditionalInfo = CSmsMessageAdditionalAttributes::NewL();
	CreateControlIEOperationsClassesL();
	CreateControlNonIEOperationsClassesL();

	iSmsPDU=CSmsPDU::NewL(aGsmSms,*iCharacterSetConverter,iFs, aIsRPError,aIsMobileTerminated);
	SetIsComplete(NumMessagePDUsL()==1);
	if (IsComplete() && TextPresent())
		{
		TSmsEncoding  encodingUsedInSegment = SmsPDU().NationalLanguageEncoding();
		
		CSmsAlphabetConverter* converter=CSmsAlphabetConverter::NewLC(*iCharacterSetConverter,iFs,SmsPDU().Alphabet(),BinaryData());
		TPtrC nativeChars(converter->ConvertToNativeL(SmsPDU().UserData().Body(),
													  encodingUsedInSegment));
		MergeAlternative7bitEncoding(encodingUsedInSegment);
		
		if (SmsPDU().TextCompressed())
			User::Leave(KErrNotSupported);
		else
			iBuffer->InsertL(iBuffer->Length(),nativeChars);
		CleanupStack::PopAndDestroy(converter);
		CSmsPDU::TSmsPDUType pdutype= SmsPDU().Type();
		if(pdutype==CSmsPDU::ESmsCommand)
			{
			// This branch is never executed as TextPresent() never
			// returns true when pduType == command
            User::Leave(KErrArgument);
			}
		else if(SmsPDU().UserDataPresent())
			{
			TInt smscIndex(0);

			if(SmsPDU().UserData().InformationElementIndex(CSmsInformationElement::ESmsIEISMSCControlParameters,smscIndex))
				{
				iAdditionalInfo->SetStatusReportSchemeL(EControlParametersScheme);
				}
			else
				{
				iAdditionalInfo->SetStatusReportSchemeL(EDefaultScheme);	
				}
				
            InstallControlInformationElementsL(SmsPDU().UserData(), 0);
			InstallEmsInformationElementsL(SmsPDU().UserData(),0);
			UpdateUserPromptAndODIElementsStartPosition();
			}
		}
	SetIsDecoded(IsComplete());

	if (TextPresent())
		SmsPDU().UserData().SetBodyL(KNullDesC8);
	} // CSmsMessage::ConstructL


void CSmsMessage::ConstructL(CSmsPDU::TSmsPDUType aType,TBool aIsRPError)
	{
    LOGGSMU3("CSmsMessage::ConstructL(): aType=%d, aIsRPError=%d", (TInt) aType,
    		 aIsRPError);

	iCharacterSetConverter=CCnvCharacterSetConverter::NewL();
	iInformationElementArray = new (ELeave) RPointerArray<CEmsInformationElement>(2);

	iAdditionalInfo = CSmsMessageAdditionalAttributes::NewL();
	CreateControlIEOperationsClassesL();
	CreateControlNonIEOperationsClassesL();

	iSmsPDU=CSmsPDU::NewL(aType,*iCharacterSetConverter,iFs,aIsRPError);
	SetIsComplete(ETrue);
	SetIsDecoded(ETrue);
	} // CSmsMessage::ConstructL


/**
 *  Counts the number of PDUs that this message will take. To do this the
 *  message will attempt to be encoded into 1 message, and if not into
 *  multiple messages. Due to the processing, the function should be used
 *  sparingly.
 * 
 *  @return  Number of PDUs required.
 */
TInt CSmsMessage::NumMessageEmsPDUsL()
	{
	LOGGSMU1("CSmsMessage::NumMessageEmsPDUsL()");

	//
	// Clear the concatenated flag, EncodeBufferL() will add it if needed.
	//
	SmsPDU().SetTextConcatenatedL(EFalse, EFalse);

	//
	// Attempt to encode to a single PDU, and if that fails then attempt to
	// encode to a set of PDUs.
	//
	CArrayFixFlat<TGsmSms>*  tmpArray = new (ELeave) CArrayFixFlat<TGsmSms>(8);
	CleanupStack::PushL(tmpArray);
	TInt numMsgs = 1;
	
	if (!EncodeIntoSinglePDUL(*tmpArray))
		{
		TInt  unconvertedChars, downgradedChars, freeUDUnitsInLastPDU;
		
		EncodeBufferL(*tmpArray, 0, *iBuffer, unconvertedChars,
			          downgradedChars, freeUDUnitsInLastPDU, EFalse);

		numMsgs = iAdditionalInfo->SmsPDUArray().Count();
		}

	CleanupStack::PopAndDestroy(tmpArray);
	
    LOGGSMU2("CSmsMessage::NumMessageEmsPDUsL() returns %d", numMsgs);

	return numMsgs;
	} // CSmsMessage::NumMessageEmsPDUsL


/**
 *  @internalComponent
 *  
 *  This method removes all non mandatory information elements from the
 *  working PDU.
 *  
 *  The working PDU is the PDU which is currently being encoded or decoded.
 *  
 *  @leave KErrAlreadyExists or KErrAlreadyExists
 */
void CSmsMessage::ResetWorkingPDUL()
	{
	LOGGSMU1("CSmsMessage::ResetWorkingPDUL()");
	
	CSmsUserData& uData = SmsPDU().UserData();
	//remove non-mandatory EMS information elements
	for (TInt a=uData.NumInformationElements(); a>0; --a)
        {
        const CSmsInformationElement& ie = uData.InformationElement(a-1);

         TSmsInformationElementCategories::TInformationElementCategory category;

        if (TSmsInformationElementCategories::GetCategoryDefinition(ie.Identifier(), category)  &&
        	(category !=  TSmsInformationElementCategories::ECtrlMandatoryInEveryPDUAndWithIdenticalValues)    &&
            (category !=  TSmsInformationElementCategories::ECtrlMandatoryInEveryPDUButWithValueSpecificToPDU) &&
            (category !=  TSmsInformationElementCategories::ECtrlMandatoryInEveryPDUMultipleInstancesPerPDU))
            {
            uData.RemoveInformationElement(a-1);
            }
        }
    // reset user body
    uData.SetBodyL(KNullDesC8);
    } // CSmsMessage::ResetWorkingPDUL


void CSmsMessage::CorrectFormattingL(TUint aCharsAddedToCurrentPDU,
									 RPointerArray<CEmsInformationElement>& aCorrectedFormattingIEArray,
									 TUint aCharsAlreadyAdded)
	{
	LOGGSMU3("CSmsMessage::CorrectFormattingL(): aCharsAddedToCurrentPDU=%d, aCharsAlreadyAdded=%d",
			 aCharsAddedToCurrentPDU, aCharsAlreadyAdded);

	CSmsUserData& uData = SmsPDU().UserData();
	for (TInt a= 0; a < uData.NumInformationElements(); a++)
		{
		CSmsInformationElement& ie = uData.InformationElement(a);
		if (CSmsInformationElement::ESmsEnhancedTextFormatting==ie.Identifier())
			{
			CEmsFormatIE& formatIE=static_cast<CEmsFormatIE&>(ie);

			TUint oldFormatLen=formatIE.FormatLength(); //
			if(aCharsAddedToCurrentPDU < formatIE.StartPosition() + oldFormatLen)
				{
				TUint newFormatLen=aCharsAddedToCurrentPDU - formatIE.StartPosition();
				formatIE.SetFormatLength(newFormatLen);

				// Now, re-encode the information element - this is because we have
				// changed a message in the UserData which is using EMS elements as
				// SMS IEs. Encoding is NOT automatic
				formatIE.EncodeInformationElementL();

				if((TInt)(aCharsAlreadyAdded+aCharsAddedToCurrentPDU) < iBuffer->Length())
					{
					CEmsFormatIE* newie=static_cast<CEmsFormatIE*>(formatIE.DuplicateL());
					CleanupStack::PushL(newie);
					newie->SetFormatLength(oldFormatLen - newFormatLen);
					newie->SetStartPosition(aCharsAlreadyAdded+aCharsAddedToCurrentPDU);
					LOGGSMU2("CSmsMessage::CorrectFormattingL",aCorrectedFormattingIEArray.Count());
					aCorrectedFormattingIEArray.Append(newie);
					CleanupStack::Pop(newie);
					}
				}
			}
		}
	} // CSmsMessage::CorrectFormattingL


void CSmsMessage::CorrectFormattingInSinglePDUL()
	{
	LOGGSMU1("CSmsMessage::CorrectFormattingInSinglePDUL()");

	CSmsUserData& uData = SmsPDU().UserData();
	for (TInt a= 0; a < uData.NumInformationElements(); a++)
		{
		CSmsInformationElement& ie = uData.InformationElement(a);
		if (CSmsInformationElement::ESmsEnhancedTextFormatting==ie.Identifier())
			{
			CEmsFormatIE& formatIE=static_cast<CEmsFormatIE&>(ie);
			TUint oldFormatLen=formatIE.FormatLength(); //
			if(iBuffer->Length() < (TInt)(formatIE.StartPosition()+ oldFormatLen))
				{
				TUint newFormatLen=iBuffer->Length() - formatIE.StartPosition();
				formatIE.SetFormatLength(newFormatLen);

				// reencode
				formatIE.EncodeInformationElementL();
				}
			}
		}
	} // CSmsMessage::CorrectFormattingInSinglePDUL


/**
 *  Adds a copy of the current PDU to the PDU array used to record all the
 *  PDUs being encoded in a concatentated message.
 * 
 *  @param  aDoEncode  If true the PDU is added and updated. If false only a
 *                     NULL placeholder is stored. This allows the number of
 *                     PDUs to be quickly counted if the encoded PDUs are of
 *                     no further use.
 */
void CSmsMessage::AddCurrentPDUToPDUArrayL(TBool aDoEncode)
	{
	LOGGSMU2("CSmsMessage::AddCurrentPDUToPDUArrayL(): Adding PDU number %d",
			 iAdditionalInfo->SmsPDUArray().Count() + 1);
	
	//
	// Maximum number of PDU is 255, so if we have that already then we cannot
	// continue.
	//
	TInt  numPDUs = iAdditionalInfo->SmsPDUArray().Count();

	if (numPDUs >= 255)
		{
		User::Leave(KErrOverflow);
		}
	
	//
	// We only do most of the work if we are actually encoding.
	//
	if (aDoEncode)
		{
		//
		// Update the Concatenated Message PDU numbers in the current PDU. This
		// means that at least the last PDU has the correct values (to update
		// all PDUs when a new PDU is added would be too slow, so we do that
		// only once at the end).
		//
		if (numPDUs > 0)
			{
			SmsPDU().SetConcatenatedMessagePDUIndex(numPDUs+1);
			SmsPDU().SetNumConcatenatedMessagePDUs(numPDUs+1);
			}
	
		//
		// Create copy of the current PDU and store it in the array...
		//
		CSmsPDU*  newPDU = SmsPDU().DuplicateL();
		CleanupStack::PushL(newPDU);
		iAdditionalInfo->SmsPDUArray().AppendL(newPDU);
		CleanupStack::Pop(newPDU);
		}
	else
		{
		//
		// Otherwise just append a NULL value to allow the data PDUs to be
		// counted...
		//
		iAdditionalInfo->SmsPDUArray().AppendL(NULL);
		}
	} // CSmsMessage::AddCurrentPDUToPDUArrayL


TBool CSmsMessage::AddIEToUserDataL(CEmsInformationElement* aIE, TInt aCharsAlreadyAdded,TUint& aCharsAddedToCurrentPDU,CSmsEMSBufferSegmenter& aSeg)
	{
	LOGGSMU1("CSmsMessage::AddIEToUserDataL()");

	TBool ieAdded=EFalse;
	if (SmsPDU().UserData().EmsInformationElementWillFitL(aIE,aSeg,aCharsAddedToCurrentPDU))
		{
		CEmsInformationElement* newIE=aIE->DuplicateL();
		newIE->SetStartPosition(newIE->StartPosition()-aCharsAlreadyAdded);
	    CleanupStack::PushL(newIE);
		SmsPDU().UserData().AddEmsInformationElementL(newIE);
		CleanupStack::Pop(newIE);
		ieAdded=ETrue;
		}
	return ieAdded;
	} // CSmsMessage::AddIEToUserDataL


/**
 *  Fills the current PDU with the number of specified native chars.
 *  
 *  @param aSeg Buffer containing the native chars
 *  @param aNumChars number of native characters to add.
 *  @param aEncoding  SMS Encoding if required.
 * 
 *  @return TInt Number of native characters added
 */
TInt CSmsMessage::FillPduL(CSmsEMSBufferSegmenter& aSeg, TInt aNumChars, TSmsEncoding aEncoding)
	{
	LOGGSMU1("CSmsMessage::FillPduL()");

	TUint maxUDUnitsREmaining=SmsPDU().UserData().MaxPackedUDUnitsInBodyRemaining();

	if (aNumChars==0 || maxUDUnitsREmaining==0)
		{
		return 0;
		}

	HBufC8* buf=HBufC8::NewMaxLC(2*aNumChars);
	TPtr8 ptr(buf->Des());
	TInt numberOfUnconvertibleCharacters(0); 
	TInt numberOfDowngradedCharacters(0);

	TInt nativeLength = aSeg.SegmentL(ptr, aNumChars, maxUDUnitsREmaining,
			                          numberOfUnconvertibleCharacters,
			                          numberOfDowngradedCharacters,
			                          aEncoding);

	SmsPDU().UserData().AppendBodyL(ptr);

	CleanupStack::PopAndDestroy(buf);
	return nativeLength;
	} // CSmsMessage::FillPduL


/**
 *  This method copies control information elements from the specified
 *  collection into the working PDU.
 *  
 *  @param aCategory        The category of control information element to be
 *                          added to the PDU.
 *  @param aMandatoryInPDU  Specifies whether the specified category of information
 *                          element must be added to this PDU.
 *  @param aDoEncode        Flag indicating if encoding is really wanted, or just the
 *                          calculation of the number of PDUs.
 *
 *  @leave KErrArgument     If the category is mandatory but one or more elements
 *                          cannot be added then the encoding will fail.
 *  @leave KErrOverFlow     If the information element is too big to be encoded into
 *                          a PDU that contains only the mandatory elements.
 */
void CSmsMessage::AddControlInformationElementsToMultiSegmentMessageL(TSmsInformationElementCategories::TInformationElementCategory aCategory,
			                                                          TBool aMandatoryInPDU, TBool aDoEncode)
    {
    LOGGSMU1("CSmsMessage::AddControlInformationElementsToMultiSegmentMessageL()");

    TUint numberOfInformationElements = iAdditionalInfo->NumberOfControlInformationElements(aCategory);

    for (TInt i = 0; i < numberOfInformationElements; i++)
        {
        CSmsInformationElement& informationElement = iAdditionalInfo->GetControlInformationElementL( aCategory, i);

        CSmsInformationElement* cloneInformationElement = CSmsInformationElement::NewL(informationElement.Identifier(),informationElement.Data());
        CleanupStack::PushL(cloneInformationElement);

        if (SmsPDU().UserData().ControlInformationElementWillFitL(cloneInformationElement))
            {
            SmsPDU().UserData().UpdateInformationElementArrayL(informationElement.Identifier(),informationElement.Data());
            }
        else if (aMandatoryInPDU)
                {
                // Error Scenario, cannot fit the mandatory PDU into the User Data
                User::Leave(KErrArgument);
                }
             else
                {
                // Close off PDU and transfer to output array
                AddCurrentPDUToPDUArrayL(aDoEncode);
                ResetWorkingPDUL();

                // Now test whether the information element will fit into a PDU at all.
                // It is possible to make Enhanced Voice Mail Information Elements
                // bigger than the available space in the PDU.
                TBool canFit = SmsPDU().UserData().ControlInformationElementWillFitL(cloneInformationElement);
                if (canFit == EFalse)
                    {
                    LOGGSMU1("CSmsMessage::AddControlInformationElementsToMultiSegmentMessage, IE too bit to fit in any PDUL");
                    User::Leave(KErrArgument);
                    }
                i--;
                }

        CleanupStack::PopAndDestroy(cloneInformationElement);
        }
    } // CSmsMessage::AddControlInformationElementsToMultiSegmentMessageL


/**
 *  This method copies control information elements from the collections of
 *  control information elements contained in CSmsAdditionalAttributes into the
 *  working PDU.
 *  
 *  @param aDoEncode  Flag indicating if encoding is really wanted, or just the
 *                    calculation of the number of PDUs.
 *
 *  @leave KErrArgument  If the category is mandatory but one or more elements
 *                       cannot be added then the encoding will fail.
 */
void CSmsMessage::AddControlInformationElementsToMultiSegmentMessageL(TBool aDoEncode)
    {
    LOGGSMU1("CSmsMessage::AddControlInformationElementsToMultiSegmentMessageL() 1");

    TBool mandatoryInEachPDU = ETrue;
    AddControlInformationElementsToMultiSegmentMessageL(TSmsInformationElementCategories::ECtrlMandatoryIn1stPDUOnly,
    													mandatoryInEachPDU, aDoEncode);
    mandatoryInEachPDU = EFalse;
    AddControlInformationElementsToMultiSegmentMessageL(TSmsInformationElementCategories::ECtrlSingleInstanceOnly,
    													mandatoryInEachPDU, aDoEncode);
    AddControlInformationElementsToMultiSegmentMessageL(TSmsInformationElementCategories::ECtrlMultipleInstancesAllowed,
    													mandatoryInEachPDU, aDoEncode);
    } // CSmsMessage::AddControlInformationElementsToMultiSegmentMessageL


/**
 *  Attempts to add EMS IE to the current PDU.
 *  
 *  @param aSegmenter                  Reference to buffer segmenter.
 *  @param aCharsAddedToCurrentPDU     Returned number of characters added to current PDU.
 *  @param aDoEncode                   Flag indicating if encoding is really wanted,
 *                                     or just the calculation of the number of PDUs.
 *  @param aEncoding                   SMS 7bit encoding if appropriate.
 *  @param aCorrectedFormatingIEArray  Array of IEs that have to be placed in following
 *                                     PDUs (e.g. corrected).
 *  @param aCurrEMSIEno                Current IE number.
 *  @param aCharsAlreadyAdded          Returned number of characters added.
 *
 *  @return Whether EMS IE has beenn added, in singleMode returns true if there are no more than one PDU.
 */
TBool CSmsMessage::AddEMSInformationElementsToMultiSegmentMessageL(CSmsEMSBufferSegmenter& aSegmenter,
																   TUint& aCharsAddedToCurrentPDU,
																   TBool aDoEncode,
																   TSmsEncoding& aEncoding,
																   RPointerArray<CEmsInformationElement>& aCorrectedFormatingIEArray,
																   TUint& aCurrEMSIEno,
																   TUint& aCharsAlreadyAdded)
	{
	LOGGSMU1("CSmsMessage::AddEMSInformationElementsToMultiSegmentMessageL()");

	TUint startPosition=0;

	 // number of chars added to the current PDU
	TUint no=iInformationElementArray->Count();
	LOGGSMU2("CSmsMessage::AddEMSInformationElementsToMultiSegmentMessageL no of IE %d",no);
	CEmsInformationElement* ie = NULL;
	TUint msgLen=iBuffer->Length();
	TUint filledChars=0;

	while(aCurrEMSIEno < no || aCorrectedFormatingIEArray.Count() > 0)
	{
		ie = NULL;
		TBool correction=EFalse;
		if(aCurrEMSIEno < no)
			{
			ie = (*iInformationElementArray)[aCurrEMSIEno];
			startPosition=ie->StartPosition();
			}

		if(ie == NULL || ( startPosition != aCharsAlreadyAdded) )
		{
			if(aCorrectedFormatingIEArray.Count() > 0)
			{
				correction=ETrue;
				ie = aCorrectedFormatingIEArray[0];
				startPosition=ie->StartPosition();
			}
		}

		__ASSERT_ALWAYS(ie !=NULL, User::Leave(KErrCorrupt));

		if(startPosition <= msgLen)
			{
			__ASSERT_DEBUG(startPosition>=aCharsAlreadyAdded, User::Leave(KErrUnderflow));
			startPosition -= aCharsAlreadyAdded; // startPosition now relative to current PDU.

			// Add all chars upto startposition.
			filledChars=0;

			if(startPosition > aCharsAddedToCurrentPDU)
				{
				filledChars = FillPduL(aSegmenter, startPosition-aCharsAddedToCurrentPDU, aEncoding);
				aCharsAddedToCurrentPDU+=filledChars;
				}

			LOGGSMU2("CSmsMessage::AddEMSInformationElementsToMultiSegmentMessageL: filled %d chars", filledChars);

			if (aCharsAddedToCurrentPDU==startPosition)
				{
				// Try adding the IE.
				if (AddIEToUserDataL(ie, aCharsAlreadyAdded,aCharsAddedToCurrentPDU,aSegmenter))
					{
					if(correction)
					{
						aCorrectedFormatingIEArray.Remove(0);
						// aCorrectedFormatingIEArray[0] has been removed. In next loop, ie will point another element. So there is no double free.
						// coverity[double_free]
						delete ie;
						correction=EFalse;
					}
					else aCurrEMSIEno++;
					}
				else
					{
					// Information Element will not fit send PDU
					LOGGSMU1("CSmsMessage::AddEMSInformationElementsToMultiSegmentMessageL: ie will not fit send Message");
					CorrectFormattingL(aCharsAddedToCurrentPDU,aCorrectedFormatingIEArray,aCharsAlreadyAdded);

					aCharsAlreadyAdded += aCharsAddedToCurrentPDU;
					aCharsAddedToCurrentPDU=0;

					AddCurrentPDUToPDUArrayL(aDoEncode);
					ResetWorkingPDUL();

					//
					// Find the encoding method for the next segment...
					//
					//aEncoding = ESmsEncodingNone;
					//TRAP_IGNORE(aEncoding = aSegmenter->FindBestAlternativeEncodingL(iAdditionalInfo->iAlternative7bitEncoding,
					//												                   maxBodyLength));
					SmsPDU().SetNationalLanguageEncodingL(aEncoding);
					}
				}
			else
				{
				// native chars upto start position will not fit send PDu.
				LOGGSMU1("CSmsMessage::AddEMSInformationElementsToMultiSegmentMessageL: PDU is filled with chars sending");

				CorrectFormattingL(aCharsAddedToCurrentPDU,aCorrectedFormatingIEArray,aCharsAlreadyAdded);

				aCharsAlreadyAdded += aCharsAddedToCurrentPDU;
				aCharsAddedToCurrentPDU=0;

				AddCurrentPDUToPDUArrayL(aDoEncode);
				ResetWorkingPDUL();

				//
				// Find the encoding method for the next segment...
				//
				//aEncoding = ESmsEncodingNone;
				//TRAP_IGNORE(aEncoding = aSegmenter->FindBestAlternativeEncodingL(iAdditionalInfo->iAlternative7bitEncoding,
				//												                   maxBodyLength));
				SmsPDU().SetNationalLanguageEncodingL(aEncoding);
				}
			}
		else
			{
			aCurrEMSIEno++;
			}
		}		// end of while loop for all IEs

	return ETrue;
	} // CSmsMessage::AddEMSInformationElementsToMultiSegmentMessageL


/**
 *  Attempts to add EMS IE to the current PDU
 *  
 *  @param aSegmenter
 *  @return TBool whether EMS IEs have been added to segment
 */
TBool CSmsMessage::AddEMSInformationElementsToSingleSegmentMessageL(CSmsEMSBufferSegmenter& aSegmenter,
																	TSmsEncoding aEncoding)
	{
	LOGGSMU1("CSmsMessage::AddEMSInformationElementsToSingleSegmentMessageL()");

	TUint charsAddedToCurrentPDU=0;
	TUint numOfEmsIE=iInformationElementArray->Count();
	TUint currEmsIEindex=0;
	CEmsInformationElement* ie = NULL;
	TUint startPosition=0;
	TUint filledChars=0;

	while(currEmsIEindex < numOfEmsIE)
		{
		ie = (*iInformationElementArray)[currEmsIEindex];
		startPosition=ie->StartPosition();

		if (startPosition > charsAddedToCurrentPDU)
			{
			filledChars = FillPduL(aSegmenter, startPosition-charsAddedToCurrentPDU, aEncoding);
			charsAddedToCurrentPDU+=filledChars;
			}

		if (charsAddedToCurrentPDU != startPosition ||
			!AddIEToUserDataL(ie, 0,charsAddedToCurrentPDU,aSegmenter))
			{
			return EFalse;
			}

		++currEmsIEindex;
		}

	return ETrue;
	} // CSmsMessage::AddEMSInformationElementsToSingleSegmentMessageL


/**
 *  Encode the PDU into an TGsmSms array. The Text and the EMS objects are
 *  laid out with respect to start position locations and encoded into the
 *  correct TGsmSms object.
 *  
 *  @leave KErrUnderflow if the Ems objects are not in Start position order.
 *
 *  @param aSmsArray               Returned array of newly created TSms objects
 *                                 containing the encoded message.
 *  @param aReference              Unique reference number to be given to the
 *                                 TGsmSms objects.
 *  @param aBuffer                 Body Text buffer of the message.
 *  @param aUnconvertedChars       Exit param for the number of characters not converted.
 *  @param aDowngradedChars        Exit param for the number of characters downgraded.
 *  @param aFreeUDUnitsInLastPDU   Exit param for the number of characters free
 *                                 in the last PDU.
 *  @param aDoEncode               Flag indicating if encoding is really wanted,
 *                                 or just the calculation of the number of PDUs.
 */
void CSmsMessage::EncodeBufferL(CArrayFix<TGsmSms>& aSmsArray, TInt aReference,
								const CSmsBufferBase& aBuffer,
								TInt& aUnconvertedChars, TInt& aDowngradedChars,
						        TInt& aFreeUDUnitsInLastPDU, TBool aDoEncode)
	{
	LOGGSMU1("CSmsMessage::EncodeBufferL()");

	aUnconvertedChars     = 0;
	aDowngradedChars      = 0;
	aFreeUDUnitsInLastPDU = 0;
	
	//
	// Reset the working array of PDUs...
	//
	iAdditionalInfo->SmsPDUArray().ResetAndDestroy();

	TUint currEMSIEno(0);
	
	TInt smscIndex(0);
	TBool smscPresent(EFalse);
	
	TInt emailOverallHeaderLength(0);
	TInt emailIndex(0);
	TBool emailPresent(EFalse);
	
	if(SmsPDU().UserData().InformationElementIndex(CSmsInformationElement::ESmsIEISMSCControlParameters,smscIndex))
		{
		smscPresent=ETrue;
		}
	if(SmsPDU().UserData().InformationElementIndex(CSmsInformationElement::ESmsIEIRFC822EmailHeader,emailIndex))
		{
		emailPresent=ETrue;
		emailOverallHeaderLength=SmsPDU().UserData().InformationElement(emailIndex).Data()[0];
		}

	//
	// Create Array for corrected format elements. These will be elements that
	// have to be moved into subsequent PDUs due to lack of space in the
	// current PDU.
	//
	RPointerArray<CEmsInformationElement>  correctedFormatingIEArray(2);
	CleanupResetAndDestroyPushL(correctedFormatingIEArray);

	//
	// Reset the working PDU. This safes time be keeping fields we need and
	// droping any data from previous PDUs.
	//
	ResetWorkingPDUL();

	//
	// Automatically prepare for concatenation (any single PDU messages would
	// have been handled by EncodeIntoSinglePDUL() previously).
	//
	SmsPDU().SetTextConcatenatedL(ETrue,iIs16BitConcatenation);

	TInt maxBodyLength=SmsPDU().UserData().MaxPackedUDUnitsInBodyRemaining();

	//
	// Add any elements that are required to be present, and ensure they
	// have correct data stored (for example if we are recycling the working PDU).
	//
	AddControlInformationElementsToMultiSegmentMessageL(aDoEncode);

	//
	// Create EMS Segmenter and Buffer Converter...
	//
	CSmsAlphabetConverter*  converter = CSmsAlphabetConverter::NewLC(*iCharacterSetConverter, iFs,SmsPDU().Alphabet(),
			                                                         BinaryData());
	CSmsEMSBufferSegmenter*  segmenter = CSmsEMSBufferSegmenter::NewLC(*converter, aBuffer, maxBodyLength);
	TBool  isUnicode = (SmsPDU().Alphabet() == TSmsDataCodingScheme::ESmsAlphabetUCS2);

	TBool informationToSend     = ETrue;
	TUint charsAdded2CurrentPDU = 0;
	TUint charsAlreadyAdded     = 0;

	//
	// Find the best alternative encoding for the current segment. This will be
	// set at the beginning of each PDU using the list of encoders.
	//
	TSmsEncoding  encodingToUse = ESmsEncodingNone;

	TRAP_IGNORE(encodingToUse = segmenter->FindBestAlternativeEncodingL(iAdditionalInfo->Alternative7bitEncoding(),
													                    aBuffer.Length()));
	SmsPDU().SetNationalLanguageEncodingL(encodingToUse);
	
	//
	// If there are information elements to add, add them to the first PDU (or
	// to the correct formatting array for later PDUs if more appropriate).
	//
	if(iInformationElementArray->Count() >0)
		{
		AddEMSInformationElementsToMultiSegmentMessageL(*segmenter, charsAdded2CurrentPDU,
														aDoEncode, encodingToUse,
														correctedFormatingIEArray,
														currEMSIEno, charsAlreadyAdded);
		}

	//
	// Allocate a temporary buffer to work with...
	//
	HBufC8* buf=HBufC8::NewMaxLC(maxBodyLength);
	TPtr8 ptr(buf->Des());

	//
	// While there is still data to encode see how much space is left in this
	// PDU and attempt to encode something into it...
	//
	while (segmenter->MoreL())
			{
			LOGGSMU1("CSmsMessage::EncodeBufferL - there is MoreL");
			
			//
			// Calculate the space left to use in this PDU...
			//
			TInt size = SmsPDU().UserData().MaxPackedUDUnitsInBodyRemaining();
			LOGGSMU2("CSmsMessage::EncodeBufferL - remaining size in PDU is %d",size);

			//
			// While there is no space, correct the formatting (which may
			// introduce any un-placed EMS Elements into the corrected format
			// array) and then start a new PDU...
			//
			while (size==0)
				{
				//
				// Store any elements not yet placed into the array for later...
				//
				CorrectFormattingL(charsAdded2CurrentPDU, correctedFormatingIEArray, charsAlreadyAdded);

				//
				// Store this PDU, reset the working PDU and get ready to
				// continue with the next PDU...
				//
				aFreeUDUnitsInLastPDU = SmsPDU().UserData().MaxPackedUDUnitsInBodyRemaining();
				AddCurrentPDUToPDUArrayL(aDoEncode);
				ResetWorkingPDUL();
				informationToSend=EFalse;
				charsAlreadyAdded+=charsAdded2CurrentPDU;
				charsAdded2CurrentPDU=0;

				//
				// Find the encoding method for the next segment...
				//
				//encodingToUse = ESmsEncodingNone;
				//TRAP_IGNORE(encodingToUse = segmenter->FindBestAlternativeEncodingL(iAdditionalInfo->iAlternative7bitEncoding,
				//												                    maxBodyLength));
				SmsPDU().SetNationalLanguageEncodingL(encodingToUse);

				//
				// Add any elements that can be placed now (from previous
				// PDUs and above)... 
				//
				LOGGSMU3("CSmsMessage::EncodeBufferL: IE count  %d corrected  count %d",iInformationElementArray->Count(),correctedFormatingIEArray.Count() );
				if ((TUint)iInformationElementArray->Count() > currEMSIEno  ||
					correctedFormatingIEArray.Count() > 0)
					{
					AddEMSInformationElementsToMultiSegmentMessageL(*segmenter,charsAdded2CurrentPDU,
																	aDoEncode, encodingToUse,
																	correctedFormatingIEArray,
																	currEMSIEno,charsAlreadyAdded);
					}

				//
				// Calculate the space left remaining in this new PDU...
				//
				size = SmsPDU().UserData().MaxPackedUDUnitsInBodyRemaining();
				LOGGSMU2("CSmsMessage::EncodeBufferL - remaining size in PDU is %d",size);
				}
			
			//
			// We have space in this PDU and no elements to place, so obtain
			// the next peice of text that can fit and append it.
			//
			segmenter->SegmentNextL(ptr, size, aUnconvertedChars, aDowngradedChars, encodingToUse);
			SmsPDU().UserData().AppendBodyL(ptr);

			TUint charsInSegment= isUnicode ? ptr.Length()/2 : ptr.Length();
			LOGGSMU2("CSmsMessage::EncodeBufferL: segmenting added %d chars", charsInSegment);

			//
			// At this point the working PDU is either full (e.g. we filled the
			// remaining space with a chuck of text, or the is some space as we
			// stored the last part of the text.
			//
			// If there are any elements not yet stored, add them to the
			// formatting array...
			//
			charsAdded2CurrentPDU+=charsInSegment;
			CorrectFormattingL(charsAdded2CurrentPDU, correctedFormatingIEArray, charsAlreadyAdded);
			charsAlreadyAdded+=charsAdded2CurrentPDU;
			LOGGSMU2("CSmsMessage::EncodeBufferL(): charsAlreadyAdded=%d", charsAlreadyAdded);
			
			//
			// Now store this PDU and reset the working PDU...
			//
			aFreeUDUnitsInLastPDU = SmsPDU().UserData().MaxPackedUDUnitsInBodyRemaining();
			AddCurrentPDUToPDUArrayL(aDoEncode);
			ResetWorkingPDUL();
			informationToSend=EFalse;
			charsAdded2CurrentPDU=0;
			
			//
			// Find the encoding method for the next segment...
			//
			//encodingToUse = ESmsEncodingNone;
			//TRAP_IGNORE(encodingToUse = segmenter->FindBestAlternativeEncodingL(iAdditionalInfo->iAlternative7bitEncoding,
			//												                    maxBodyLength));
			SmsPDU().SetNationalLanguageEncodingL(encodingToUse);

			//
			// Add any elements that can be placed now given we have a new
			// empty PDU... 
			//
			LOGGSMU3("CSmsMessage::EncodeBufferL: IE count  %d corrected  count %d",
			         iInformationElementArray->Count(), correctedFormatingIEArray.Count() );
			if ((TUint)iInformationElementArray->Count() > currEMSIEno  ||
				correctedFormatingIEArray.Count() > 0)
				{
				AddEMSInformationElementsToMultiSegmentMessageL(*segmenter, charsAdded2CurrentPDU,
																aDoEncode, encodingToUse,
																correctedFormatingIEArray,
																currEMSIEno, charsAlreadyAdded);
				}
			LOGGSMU1("CSmsMessage::EncodeBufferL end Moreloop");
			}
	CleanupStack::PopAndDestroy(buf);

	LOGGSMU1("CSmsMessage::EncodeBufferL - last PDU");
	
	//
	// This is the last PDU. We need to check if there is a partial PDU left over
	// and add it if needed.
	//
	if (informationToSend)
		{
		aFreeUDUnitsInLastPDU = SmsPDU().UserData().MaxPackedUDUnitsInBodyRemaining();
		AddCurrentPDUToPDUArrayL(aDoEncode);
		ResetWorkingPDUL();
		}

	//
	// At this point the segmenting and layouts of PDU is complete, so if we
	// find any more data something is wrong...
	//
	if (segmenter->MoreL()  ||  correctedFormatingIEArray.Count() > 0)
		{
		User::Leave(KErrCorrupt);
		}

	CleanupStack::PopAndDestroy(3, &correctedFormatingIEArray); // segmenter, converter & correctedFormatingIEArray

	//
	// By now the PDUs are segmented and correctly setup for encoding.
	// They have not been encoded yet, as this requires the PDU total to be known.
	// So we go through the array setting the final PDU number/total PDUs and
	// perform the encoding from CSmsPDU to the TGsmPdu object.
	//
	// Note: In the case were aDoEncode is EFalse, iSmsPDUArray will be full
	//       of NULL pointers as it is only the count that we need in that case.
	//
	TInt  numPDUs = iAdditionalInfo->SmsPDUArray().Count();

	LOGGSMU2("CSmsMessage::EncodeBufferL number of PDUs: %d", iAdditionalInfo->SmsPDUArray().Count());
	
	if (aDoEncode)
		{
		CSmsMessageAdditionalAttributes::CSmsStatusReportScheme&  scheme = iAdditionalInfo->GetStatusReportScheme();
		TGsmSms  gsmSms;
		
		for (TInt pdu = 0;  pdu < numPDUs;  pdu++)
			{
			if(scheme.Id() == EControlParametersScheme)
				{
				if(smscPresent)				
					{
					CSmsSMSCCtrlParameterOperations&  ieOp = (CSmsSMSCCtrlParameterOperations&)GetOperationsForIEL(CSmsInformationElement::ESmsIEISMSCControlParameters);
					TUint8 octet(0);

					if (ieOp.GetStatusReport(pdu, octet) == KErrNone)
						{					
						iAdditionalInfo->SmsPDUArray()[pdu]->UpdateSMSCCtrlParameterL(octet);
						}
					}
				else
					{
					User::Leave(KErrNotFound);
					}
				}
			else if(scheme.Id() == ETPSRRScheme)
				{
				TSmsFirstOctet smsReportRequest;
				CSmsTPSRROperations& nonIEOp = (CSmsTPSRROperations&)GetOperationsForNonIEL(ESmsTPSRRParameter);
				smsReportRequest = nonIEOp.GetStatusReport(pdu);
								
				iAdditionalInfo->SmsPDUArray()[pdu]->UpdateTPSRRL(smsReportRequest);
				}

			if(emailPresent)				
				{
				iAdditionalInfo->SmsPDUArray()[pdu]->UpdateEmailHeaderDataL(emailOverallHeaderLength);
				}

			//
			// Set the concatenation Message Reference number and PDU numbers...
			//
			iAdditionalInfo->SmsPDUArray()[pdu]->UpdateConcatenationDataL(aReference, pdu+1, numPDUs);

			//
			// Encode this PDU...
			//
			
			TEncodeParams encodeParams;
			encodeParams.iTimeStamp = &Time();
			
			TTimeIntervalSeconds interval = UTCOffset();
			encodeParams.iTimeIntervalInSeconds = &interval;
						
			iAdditionalInfo->SmsPDUArray()[pdu]->EncodeMessagePDUL(gsmSms, &encodeParams);
	 		aSmsArray.AppendL(gsmSms);
			}

		//
		// Since the working PDU has been reset the data in it does not match
		// the data of the last PDU. Some tests in SMS Stack assume it does
		// and therefore to retain compatibility we decode the last PDU back
		// over the working PDU.
		//
		TGsmuLex8 lex(gsmSms.Pdu());
		SmsPDU().DecodeL(lex);
		}
	} // CSmsMessage::EncodeBufferL


/**
 *  Attempts to encode into the single PDU. This function is the private version
 *  to the public EncodeIntoSinglePDUL() function. It performs the work and also
 *  returns more information regarding the characters encoded.
 *  
 *  The Text and the EMS objects are laid out with respect to start position
 *  locations.
 *  
 *  @leave KErrUnderflow if the Ems objects are not in Start position order.
 *
 *  @param aSmsArray               Returned array of newly created TSms objects
 *                                 containing the encoded message.
 *  @param aUnconvertedChars       Exit param for the number of characters not converted.
 *  @param aDowngradedChars        Exit param for the number of characters downgraded.
 *  @param aFreeUDUnitsInLastPDU   Exit param for the number of characters free
 *                                 in the last PDU.
 */
TBool CSmsMessage::EncodeIntoSinglePDUL(CArrayFix<TGsmSms>& aSmsArray, TInt& aUnconvertedChars,
		                                TInt& aDowngradedChars, TInt& aFreeUDUnitsInLastPDU)
	{
	LOGGSMU1("CSmsMessage::EncodeIntoSinglePDUL()");

	__ASSERT_DEBUG((aSmsArray.Count()==0),Panic(KGsmuPanicSmsArrayNotEmpty));

	aUnconvertedChars     = 0;
	aDowngradedChars      = 0;
	aFreeUDUnitsInLastPDU = 0;

	iAdditionalInfo->SmsPDUArray().ResetAndDestroy();

	if (!TextPresent())
		{
        if(SmsPDU().Type()==CSmsPDU::ESmsCommand)
            {
            // Commands don't contain text, so commands
            // are only encoded in this branch.
            PrepareCommandMessageL();
            }

		TGsmSms sms;
		SmsPDU().EncodeMessagePDUL(sms);
		aSmsArray.AppendL(sms);
		return ETrue;
		}

	ResetWorkingPDUL();

    // find the length of all the control information elements
    TUint ieLength=0;
    for (TUint8 category = 0; category <  TSmsInformationElementCategories::ENumberOfCategories; category++)
        {
        switch (category)
            {
            case  TSmsInformationElementCategories::ECtrlMandatoryIn1stPDUOnly:
            case  TSmsInformationElementCategories::ECtrlSingleInstanceOnly:
            case  TSmsInformationElementCategories::ECtrlMultipleInstancesAllowed:
                {
                for (TUint j = 0; j < iAdditionalInfo->NumberOfControlInformationElements((CSmsMessageAdditionalAttributes::TCategory) category); j++)
                    {
                    ieLength += iAdditionalInfo->GetControlInformationElementL( ( (CSmsMessageAdditionalAttributes::TCategory) category), j).Length();
                    }
                break;
                }
            default:
                break;
            }
        }
    LOGGSMU2("CSmsMessage::EncodeIntoSinglePDUL, ctrl elem len = %d", ieLength);

	CEmsInformationElement* emsIE =NULL;
	for (TInt num=0; num<iInformationElementArray->Count();num++)
		{
		emsIE = (*iInformationElementArray)[num];
        ieLength+=emsIE->Length();
		}

	TInt remainInBody=SmsPDU().UserData().MaxPackedUDUnitsInBodyRemaining(ieLength);

	TInt msgLength=MessageLengthL(); // in octets

	if( msgLength > remainInBody) return EFalse;

    LOGGSMU4("CSmsMessage::EncodeIntoSinglePDUL, ie len = %d, remainInBody = %d, msgLength = %d", ieLength, msgLength, remainInBody);
    //  add all control information elements into working PDU.
    //
    for (TUint8 category = 0; category <  TSmsInformationElementCategories::ENumberOfCategories; category++)
        {
        switch (category)
            {
            case  TSmsInformationElementCategories::ECtrlMandatoryIn1stPDUOnly:
            case  TSmsInformationElementCategories::ECtrlSingleInstanceOnly:
            case  TSmsInformationElementCategories::ECtrlMultipleInstancesAllowed:
                {
                for (TUint j = 0; j < iAdditionalInfo->NumberOfControlInformationElements((CSmsMessageAdditionalAttributes::TCategory) category); j++)
                    {
                    CSmsInformationElement& informationElement = iAdditionalInfo->GetControlInformationElementL( ( (CSmsMessageAdditionalAttributes::TCategory) category), j);

                    CSmsInformationElement* cloneInformationElement = CSmsInformationElement::NewL(informationElement.Identifier(),informationElement.Data());
                    CleanupStack::PushL(cloneInformationElement);
                    TBool willFit = SmsPDU().UserData().ControlInformationElementWillFitL(cloneInformationElement);
                    CleanupStack::PopAndDestroy(cloneInformationElement);

                    if (willFit)
                        {
                        SmsPDU().UserData().UpdateInformationElementArrayL(informationElement.Identifier(),informationElement.Data());
                        }
                    else
                        {
                        ResetWorkingPDUL();
                        return EFalse;
                        }
                    }
                break;
                }
            default:
                break;
            }
        }
		
	EncodingTPSRRFromSchemesIntoSinglePDUL();

	TInt maxBodyLength=SmsPDU().UserData().MaxPackedUDUnitsInBodyRemaining();
	CSmsAlphabetConverter* converter=CSmsAlphabetConverter::NewLC(*iCharacterSetConverter,iFs,SmsPDU().Alphabet(),BinaryData());
	CSmsEMSBufferSegmenter* segmenter=CSmsEMSBufferSegmenter::NewLC(*converter,*iBuffer, maxBodyLength);

	//
	// Find the best alternative encoding for the current segment. This will be
	// set at the beginning of each PDU using the list of encoders.
	//
	TSmsEncoding  encodingToUse = ESmsEncodingNone;

	TRAP_IGNORE(encodingToUse= segmenter->FindBestAlternativeEncodingL(iAdditionalInfo->Alternative7bitEncoding(),
													                   maxBodyLength));
	SmsPDU().SetNationalLanguageEncodingL(encodingToUse);
	
	if(!AddEMSInformationElementsToSingleSegmentMessageL(*segmenter, encodingToUse))
		{
		aSmsArray.Reset();
		CleanupStack::PopAndDestroy(2, converter);
		return EFalse;
		}

	if(segmenter->MoreL())
			{
			TInt size = SmsPDU().UserData().MaxPackedUDUnitsInBodyRemaining();
			// Defensive code to prevent SegmentNextL being called with size of zero
			if (size == 0)
				{
				CleanupStack::PopAndDestroy(2, converter);
				aSmsArray.Reset();
				return EFalse;
				}
			HBufC8* buf=HBufC8::NewMaxLC(maxBodyLength);
			TPtr8 ptr(buf->Des());
			segmenter->SegmentNextL(ptr, size, aUnconvertedChars, aDowngradedChars, encodingToUse);
			SmsPDU().UserData().AppendBodyL(ptr);
			CleanupStack::PopAndDestroy(buf);
			}
	CorrectFormattingInSinglePDUL();

	if (segmenter->MoreL())
		{
		CleanupStack::PopAndDestroy(2, converter);
		aSmsArray.Reset();
		return EFalse;
		}
	CleanupStack::PopAndDestroy(2, converter);

	aFreeUDUnitsInLastPDU = SmsPDU().UserData().MaxPackedUDUnitsInBodyRemaining();

	//
	// Encode the PDU...
	//
	TGsmSms  gsmSms;
		
	TEncodeParams encodeParams;
	encodeParams.iTimeStamp = &Time();
		
	TTimeIntervalSeconds interval = UTCOffset();
	encodeParams.iTimeIntervalInSeconds = &interval;
		
	SmsPDU().EncodeMessagePDUL(gsmSms, &encodeParams);
	aSmsArray.AppendL(gsmSms);

	return ETrue;
	} // CSmsMessage::EncodeIntoSinglePDUL


/**
 *  Attempts to encode into the single PDU
 *  The Text and the EMS objects are layed out with respect to start position locations and encoded into the correct Tsms object.
 *  Ensure this function is called before EncodeMessagePDUsL to properly process single PDU messages
 *  
 *  @return KErrUnderflow if the Ems objects are not in Start position order.
 *  @param aSmsArray returned array of newly created TSms objects containing the encoded message
 *  
 *  @capability None
 */
EXPORT_C TBool CSmsMessage::EncodeIntoSinglePDUL(CArrayFix<TGsmSms>& aSmsArray)
	{
	LOGGSMU1("CSmsMessage::EncodeIntoSinglePDUL()");
	
	TInt  unconvertedChars, downgradedChars, freeUDUnitsInLastPDU;
	
	return EncodeIntoSinglePDUL(aSmsArray, unconvertedChars,
                                downgradedChars, freeUDUnitsInLastPDU);
	} // CSmsMessage::EncodeIntoSinglePDUL


void CSmsMessage::EncodingTPSRRFromSchemesIntoSinglePDUL()
	{
	TInt smscIndex(0);
	TBool smscPresent(EFalse);
	
	CSmsMessageAdditionalAttributes::CSmsStatusReportScheme& scheme = iAdditionalInfo->GetStatusReportScheme();	
	if(scheme.Id() == EControlParametersScheme)
		{
		if(SmsPDU().UserData().InformationElementIndex(CSmsInformationElement::ESmsIEISMSCControlParameters,smscIndex))
			{
			smscPresent=ETrue;
			}
		if(smscPresent)				
			{
			TUint8 octet(0);

			CSmsSMSCCtrlParameterOperations& ieOp = (CSmsSMSCCtrlParameterOperations&)GetOperationsForIEL(CSmsInformationElement::ESmsIEISMSCControlParameters);
			
			if (ieOp.GetStatusReport(0, octet) == KErrNone)
				{
				if(SmsPDU().UserData().InformationElementIndex(CSmsInformationElement::ESmsIEISMSCControlParameters,smscIndex))
					{
					SmsPDU().UserData().InformationElement(smscIndex).Data()[0] = octet;
					if (SmsPDU().Type() == CSmsPDU::ESmsSubmit)
						{
						((CSmsSubmit&)SmsPDU()).SetStatusReportRequest(ETrue);	
						}
					else if (SmsPDU().Type() == CSmsPDU::ESmsDeliver)
						{
						((CSmsDeliver&)SmsPDU()).SetStatusReportIndication(ETrue);
						}
					}
				else
					{
					User::Leave(KErrCorrupt);	
					}	
				}
			}
		else
			{
			User::Leave(KErrNotFound);
			}	
		}
	else if(scheme.Id() == ETPSRRScheme)
		{
		TSmsFirstOctet smsReportRequest;
		CSmsTPSRROperations& nonIEOp = (CSmsTPSRROperations&)GetOperationsForNonIEL(ESmsTPSRRParameter);
		smsReportRequest = nonIEOp.GetStatusReport(0);
		
		if(smsReportRequest>=0)
			{
			if (SmsPDU().Type() == CSmsPDU::ESmsSubmit)
				{
				((CSmsSubmit&)SmsPDU()).SetStatusReportRequest(smsReportRequest);	
				}
			else if (SmsPDU().Type() == CSmsPDU::ESmsDeliver)
				{
				((CSmsDeliver&)SmsPDU()).SetStatusReportIndication(smsReportRequest);
				}	
			}
		}	
	} // CSmsMessage::EncodingTPSRRFromSchemesIntoSinglePDUL


/**
 *  Decode CSmsMessage
 *  
 *  @param aUserData Object containing the EMS objects.
 *  @param aCharsAlreadyAdded The current, running, start position to be applied to the objects taken out
 *  of the UserData.
 */
void CSmsMessage::DecodeBufferL(CArrayPtr<CSmsPDU>& aSmsPDUArray,
								CSmsBufferBase& aBuffer)
	{
	LOGGSMU1("CSmsMessage::DecodeBufferL()");

	iInformationElementArray->ResetAndDestroy();

	TSmsStatusReportScheme schemeId = FindSchemeL(aSmsPDUArray);
	
	TInt emailLen(0);
	for (TInt i=0; i<aSmsPDUArray.Count(); i++)
		{
		CSmsUserData& usrData = (aSmsPDUArray)[i]->UserData();
		InstallControlInformationElementsL(usrData, i);
		InstallEmsInformationElementsL(usrData, aBuffer.Length());
        // Possible refactoring opportunity:
        // This would be a good place to add a method which provides processing for control information
        // elements which contain values specific to the pdu (as opposed to being constant in every PDU.
        InstallEmailHeaderInformationElementL(usrData,emailLen);
        if(schemeId == ETPSRRScheme)
        	{
        	InstallTPSRRInformationL(aSmsPDUArray, i);
        	}
        
		CSmsAlphabetConverter* converter=CSmsAlphabetConverter::NewLC(*iCharacterSetConverter,iFs,aSmsPDUArray[i]->Alphabet(),BinaryData());
		TSmsBufferReassembler reassembler(*converter,aBuffer);
        TSmsEncoding  encodingUsedInSegment = aSmsPDUArray[i]->NationalLanguageEncoding();
		
        reassembler.ReassembleNextL(usrData.Body(),
									encodingUsedInSegment,
									i==(aSmsPDUArray.Count()-1));
        
		CleanupStack::PopAndDestroy(converter);		
		MergeAlternative7bitEncoding(encodingUsedInSegment);
		}

    // Possible refactoring opportunity:
    // This would be a good place to add a method which provides processing for control information
    // elements which contain values specific to the pdu (as opposed to being constant in every PDU.
    if(emailLen>0)
		{
		if(emailLen>255)
			User::Leave(KErrTooBig);
		TBuf8<1> data;
		data.SetLength(1);
		data[0]=static_cast<TUint8>(emailLen);
		TInt emailIndex;
		if(SmsPDU().UserData().InformationElementIndex(CSmsInformationElement::ESmsIEIRFC822EmailHeader,emailIndex))
			SmsPDU().UserData().InformationElement(emailIndex).Data()[0]=static_cast<TInt8>(emailLen);
		else
			SmsPDU().UserData().AddInformationElementL(CSmsInformationElement::ESmsIEIRFC822EmailHeader,data);
		}
	UpdateUserPromptAndODIElementsStartPosition();
	} // CSmsMessage::DecodeBufferL

/**
 *  It decodes only raw text data.
 *  
 *  @param aSmsPDUArray Object containing PDUs.
 *  @param aBuffer where the decoded data will be stored.
 *
 */
void CSmsMessage::DecodeOnlyTextL(CArrayPtr<CSmsPDU>& aSmsPDUArray,
								CSmsBufferBase& aBuffer)
	{
	LOGGSMU1("CSmsMessage::DecodeOnlyTextL()");

	for (TInt i=0; i<aSmsPDUArray.Count(); i++)
		{
        CSmsAlphabetConverter* converter=CSmsAlphabetConverter::NewLC(*iCharacterSetConverter,iFs,aSmsPDUArray[i]->Alphabet(),BinaryData());
        TSmsBufferReassembler reassembler(*converter,aBuffer);
		TSmsEncoding  encodingUsedInSegment = aSmsPDUArray[i]->NationalLanguageEncoding();
        
		reassembler.ReassembleNextL(aSmsPDUArray[i]->UserData().Body(),
									encodingUsedInSegment,
									i==(aSmsPDUArray.Count()-1));
		
		CleanupStack::PopAndDestroy(converter);
		MergeAlternative7bitEncoding(encodingUsedInSegment);
		}
	} // CSmsMessage::DecodeBufferL

/**
 *  It adds the incomplete message info in additional attribute field.
 *  
 *  @param aStartPDU starting PDU index of incomplete message.
 *  @param aEndPDU end PDU index of incomplete message.
 *  @param aLastPartialCompleteMsg boolean value indicating whether 
 				this message is the last incomplete message sent to client.
 */
void CSmsMessage::AddIncompleteMessageInfoL(TInt aStartPDU, TInt aEndPDU, TBool aLastPartialCompleteMsg)
	{
	LOGGSMU1("CSmsMessage::AddIncompleteMessageInfoL()");

	CIncompleteClass0MessageInfo& incompleteClass0MsgInfo = (CIncompleteClass0MessageInfo&) iAdditionalInfo->GetNonIEOperationL(ESmsIncompleteClass0MessageParameter);
	incompleteClass0MsgInfo.SetVersion(CIncompleteClass0MessageInfo::ESmsIncompleteClass0MessageV0);
	incompleteClass0MsgInfo.SetIncompleteMessageInfoL(aStartPDU, aEndPDU, aLastPartialCompleteMsg);
	}

/**
 *  This method copies control information elements from the user data (passed as an
 *  input argument) into either the CSmsMessage's user data (working pdu) OR one of the
 *  collection of control information elements (sorted by category) which is contained
 *  in CSmsMessage::iAdditionalInfo.
 *  
 *  @param aUserData
 *  The user data which is currently being processed, and whose elements may be copied
 *  into the appropriate collection belonging to the CSmsMessage.
 *  @leave
 *  KErrArgument if an information element's type is unknown
 *  
 *  @internalComponent
 */
void CSmsMessage::InstallControlInformationElementsL(CSmsUserData& aUserData, TInt aSegmentSequenceNum)
    {
	// Installs all the information elements within the subsequent PDUs.
	LOGGSMU1("CSmsMessage::InstallControlInformationElements()");

    CSmsMessageAdditionalAttributes::CSmsStatusReportScheme& scheme = iAdditionalInfo->GetStatusReportScheme();
    
	for (TInt z=0; z<aUserData.NumInformationElements(); z++)
		{
		const CSmsInformationElement& ie = aUserData.InformationElement(z);

         TSmsInformationElementCategories::TInformationElementCategory category;

        if (TSmsInformationElementCategories::GetCategoryDefinition(ie.Identifier(), category))
        	{
            switch (category)
                {
                case  TSmsInformationElementCategories::ECtrlMandatoryInEveryPDUMultipleInstancesPerPDU: // e.g. Special SMS Message Indication
                    LOGGSMU2("CSmsMessage::InstallControlInformationElements \
                    ECtrlMandatoryInEveryPDUMultipleInstancesPerPDU id = %d", ie.Identifier());

                    if (ie.Identifier()== CSmsInformationElement::ESmsIEISpecialSMSMessageIndication)
                        {
                        TBool found = EFalse;
                        CArrayFixFlat<TInt>* indices = new(ELeave) CArrayFixFlat<TInt>(4);
                        CleanupStack::PushL(indices);
                        SmsPDU().UserData().InformationElementIndicesL(ie.Identifier(), *indices);

                        TInt count = indices->Count();
		                for (TInt i=0; ((i<count) && (found==EFalse)); i++)
		                    {
		                    TUint index = indices->operator[](i);
		                    CSmsInformationElement& ieAlreadyInWorkingPDU = SmsPDU().UserData().InformationElement(index);

                            if (ieAlreadyInWorkingPDU.Data()[0] == ie.Data()[0])
                                {
                                ieAlreadyInWorkingPDU.Data()[1] = ie.Data()[1];
                                found = ETrue;
                                break;
                                }
		                    }
                        CleanupStack::PopAndDestroy(indices);

                        if (found == EFalse)
                            {
                            SmsPDU().UserData().UpdateInformationElementArrayL(ie.Identifier(),ie.Data());
                            }
                        }
                    else
                        {
                        // Unknown category.
                        LOGGSMU3("CSmsMessage::InstallControlInformationElementsL category = %d, id = %d", category, ie.Identifier());
                        User::Leave(KErrArgument);
                        }
                    break;
                case  TSmsInformationElementCategories::ECtrlMandatoryIn1stPDUOnly:
                case  TSmsInformationElementCategories::ECtrlSingleInstanceOnly:
                    {
                    LOGGSMU2("CSmsMessage::InstallControlInformationElements ECtrlMandatoryIn1stPDUOnly "
                                 "ECtrlSingleInstanceOnly, id = %d", ie.Identifier());

                    TUint index = 0;
                    if (iAdditionalInfo->Find1stInstanceOfControlInformationElement(ie.Identifier(), index))
                        {
                        iAdditionalInfo->DeleteControlInformationElement(category, index); //  might want to pass the
                                                                                                //  id in as an additional check
                                                                                                //  this should be an exception case
                        CSmsInformationElement* newInformationElement = CSmsInformationElement::NewL(ie.Identifier(),ie.Data());

                    	CleanupStack::PushL(newInformationElement);
                    	iAdditionalInfo->AddControlInformationElementL(category, newInformationElement);
                        CleanupStack::Pop(newInformationElement);
                        }
                     else
                        {
                        CSmsInformationElement* cloneInformationElement = CSmsInformationElement::NewL(ie.Identifier(),ie.Data());
                        CleanupStack::PushL(cloneInformationElement);
                        iAdditionalInfo->AddControlInformationElementL(category, cloneInformationElement);
                        CleanupStack::Pop(cloneInformationElement);
                        }
                    break;
                    }
                case  TSmsInformationElementCategories::ECtrlMandatoryInEveryPDUAndWithIdenticalValues:
                    {
                    LOGGSMU2("CSmsMessage::InstallControlInformationElements ECtrlMandatoryInEveryPDUAndWithIdenticalValues "
                                 "ECtrlSingleInstanceOnly, id = %d", ie.Identifier());
                    TInt index = 0;
                    if (SmsPDU().UserData().InformationElementIndex(ie.Identifier(),index))
                        {
                        CSmsInformationElement* newInformationElement = CSmsInformationElement::NewL(ie.Identifier(),ie.Data());

                    	CleanupStack::PushL(newInformationElement);
                        CSmsInformationElement*& indexedPDU = SmsPDU().UserData().InformationElementPtr(index);
                        CSmsInformationElement* oldInformationElement = indexedPDU;
                        SmsPDU().UserData().InformationElementPtr(index) = newInformationElement;
						// In next loop, oldInformationElement will point to the newly created information element. So double free is no case.
						// coverity[double_free]
                        delete oldInformationElement;
                        CleanupStack::Pop(newInformationElement);
                        }
                    else
                        {
                        SmsPDU().UserData().UpdateInformationElementArrayL(ie.Identifier(),ie.Data());
                        }
                    break;
                    }

                case  TSmsInformationElementCategories::ECtrlMultipleInstancesAllowed:
                    {
                    LOGGSMU2("CSmsMessage::InstallControlInformationElements ECtrlMandatoryInEveryPDUAndWithIdenticalValues "
                                 "ECtrlSingleInstanceOnly, id = %d", ie.Identifier() );

                    CSmsInformationElement* cloneInformationElement = CSmsInformationElement::NewL( ie.Identifier(),ie.Data() );
                    CleanupStack::PushL(cloneInformationElement);
                    iAdditionalInfo->AddControlInformationElementL( TSmsInformationElementCategories::ECtrlMultipleInstancesAllowed, cloneInformationElement);
                    CleanupStack::Pop(cloneInformationElement);
                    break;
                    }
                case  TSmsInformationElementCategories::ECtrlMandatoryInEveryPDUButWithValueSpecificToPDU:
                    LOGGSMU2("CSmsMessage::InstallControlInformationElements ECtrlMandatoryInEveryPDUButWithValueSpecificToPDU "
                                 "ECtrlSingleInstanceOnly, id = %d", ie.Identifier() );
                    
                    if (ie.Identifier() == CSmsInformationElement::ESmsIEISMSCControlParameters)
                    	{
                       	if(scheme.Id() == EControlParametersScheme)
                    		{
                    		CSmsMessageAdditionalAttributes::CControlParametersScheme& ctrlParamScheme = (CSmsMessageAdditionalAttributes::CControlParametersScheme&)scheme;
                    		ctrlParamScheme.iDefaultStatusReport = 0x00;
                    		
	                       	const TUint8* octet(0);
	                    	octet = ie.Data().Ptr();
	                    	if(*octet)
                    			{
                    			CSmsMessageAdditionalAttributes::CControlParametersScheme::TSmsSMSCCtrlParameterStatus smscCtrlParameterStatus;
								smscCtrlParameterStatus.iSegmentSequenceNum = aSegmentSequenceNum;
								smscCtrlParameterStatus.iSelectiveStatus = *octet;
								(ctrlParamScheme.iControlParametersStatusReport).AppendL(smscCtrlParameterStatus);
                    			}
                    		ctrlParamScheme.iNumOfPDUs++;
	                    	}
	                    }
                
                    // e.g  Email Header / Concatenation Elements.
                    // Consider whether EMail Header should be ported here.
                    // or left as is.
                    break;
                case  TSmsInformationElementCategories::EEmsInformationElement:
                    LOGGSMU2("CSmsMessage::InstallControlInformationElements ECtrlMandatoryInEveryPDUButWithValueSpecificToPDU "
                                 "ECtrlSingleInstanceOnly, id = %d", ie.Identifier() );
                    // Will be handled in the method InstallEmsInformationElements, nothing to do here
                    break;
                default:
                    LOGGSMU3("CSmsMessage::InstallControlInformationElementsToMultiSegmentMessageL, default switch, category = %d, id= %d", category, ie.Identifier() );
                    break;
                }
            }
    	}
    } // CSmsMessage::InstallControlInformationElementsL


/**
 *  Extracts all the EMS information objects out of the given UserData into the CSmsMessage IEArray.
 *  
 *  @param aUserData Object containing the EMS objects.
 *  @param aCharsAlreadyAdded The current, running, start position to be applied to the objects taken out
 *  of the UserData.
 */
void  CSmsMessage::InstallEmsInformationElementsL(CSmsUserData& aUserData, TInt aCharsAlreadyAdded)
	{
	// Installs all the information elements within the subsequent PDUs.
	LOGGSMU1("CSmsMessage::InstallEmsInformationElements()");

	CSmsInformationElement::TSmsInformationElementIdentifier id;
	CEmsInformationElement* newIE =NULL;

	for (TInt z=0; z<aUserData.NumInformationElements(); z++)
		{
		const CSmsInformationElement& ie = aUserData.InformationElement(z);

		// is the ie an EMS information Element;
		id = ie.Identifier();
		if (CEmsFactory::Supported(id))
			{
			newIE=NULL;
			TRAPD(ret,newIE = CEmsFactory::CreateReceivedEmsIEL(ie,aCharsAlreadyAdded));
			if(newIE != NULL)
				{
				if(ret == KErrNone)
					{
					ret=AddReceivedEmsInformationElement(newIE);
					if(ret==KErrNone)  // Remove the Information Element from the User Data.
						aUserData.RemoveInformationElement(z--);
					else
						// newIE is made NULL in first place and then a new IE is created. so double free is no case
						// coverity[double_free]
						delete newIE;
					}
				else
					delete newIE;
				}
			}
		}
	} // CSmsMessage::InstallEmsInformationElementsL


/**
 *  Extracts all the EMS information objects out of the given Command into the CSmsMessage IEArray.
 *  
 *  @param aCommand Object containing the EMS objects.
 *  @param aCharsAlreadyAdded The current, running, start position to be applied to the objects taken out
 *  of the UserData.
 */
void  CSmsMessage::InstallEmsInformationElementsL(CSmsCommand& aCommand, TInt aCharsAlreadyAdded)
    {
    // Ignore in code coverage - not used in SMS stack and not exported
    // but cannot be removed as impacts public header.
    BULLSEYE_OFF    
    // Installs all the information elements within the subsequent PDUs.
    LOGGSMU1("CSmsMessage::InstallEmsInformationElements()");
    
    CSmsInformationElement::TSmsInformationElementIdentifier id;
    CEmsInformationElement* newIE=NULL;
    
    for (TInt z=0; z<aCommand.NumInformationElements(); z++)
        {
        const CSmsInformationElement& ie = aCommand.InformationElement(z);
        // is the ie an EMS information Element;
        id = ie.Identifier();
        if (CEmsFactory::Supported(id))
            {
            newIE=NULL;
            TRAPD(ret,newIE = CEmsFactory::CreateReceivedEmsIEL(ie,aCharsAlreadyAdded));
            if(newIE != NULL )
                {
                    if(ret == KErrNone)
                    {
                        ret=AddReceivedEmsInformationElement(newIE);
                        if(ret==KErrNone)  // Remove the Information Element from the User Data.
                            aCommand.RemoveInformationElement(z--);
                        else
                            // newIE is made NULL in first place and then a new IE is created. so double free is no case.
                            // coverity[double_free]
                            delete newIE;
                    }
                    else
                        delete newIE;
                }
            }
        }
    BULLSEYE_RESTORE
    }

TSmsStatusReportScheme CSmsMessage::FindSchemeL(const CArrayPtr<CSmsPDU>& aSmsPDUArray)
	{
	TInt smscIndex(0);
	
	if(SmsPDU().UserData().InformationElementIndex(CSmsInformationElement::ESmsIEISMSCControlParameters,smscIndex))
		{
		iAdditionalInfo->SetStatusReportSchemeL(EControlParametersScheme);
		return EControlParametersScheme;
		}
		
	TSmsFirstOctet oldTpsrr;
	TSmsFirstOctet tpsrr;
	CSmsSubmit* smsSubmit;
	CSmsDeliver* smsDeliver;
	
	if (SmsPDU().Type() == CSmsPDU::ESmsSubmit)
		{
		smsSubmit = reinterpret_cast<CSmsSubmit*>((aSmsPDUArray)[0]);
		oldTpsrr = smsSubmit->StatusReportRequest();
		}
	else if (SmsPDU().Type() == CSmsPDU::ESmsDeliver)
		{
		smsDeliver = reinterpret_cast<CSmsDeliver*>((aSmsPDUArray)[0]);
		oldTpsrr = smsDeliver->StatusReportIndication();
		}
		
	for (TInt ii=1; ii<aSmsPDUArray.Count(); ii++)
		{
		if (SmsPDU().Type() == CSmsPDU::ESmsSubmit)
			{
			smsSubmit = reinterpret_cast<CSmsSubmit*>((aSmsPDUArray)[ii]);
			tpsrr = smsSubmit->StatusReportRequest();
			}
		else if (SmsPDU().Type() == CSmsPDU::ESmsDeliver)
			{
			smsDeliver = reinterpret_cast<CSmsDeliver*>((aSmsPDUArray)[ii]);
			tpsrr = smsDeliver->StatusReportIndication();
			}
		if(tpsrr != oldTpsrr)
			{
			iAdditionalInfo->SetStatusReportSchemeL(ETPSRRScheme);
			return ETPSRRScheme;
			}
		}
	
	iAdditionalInfo->SetStatusReportSchemeL(EDefaultScheme);

	return EDefaultScheme;
	} // CSmsMessage::FindSchemeL

	
void CSmsMessage::InstallTPSRRInformationL(const CArrayPtr<CSmsPDU>& aSmsPDUArray, TInt aSegmentSequenceNum)
	{
    CSmsMessageAdditionalAttributes::CSmsStatusReportScheme& scheme = iAdditionalInfo->GetStatusReportScheme();
    CSmsMessageAdditionalAttributes::CTPSRRScheme& tpsrrScheme = (CSmsMessageAdditionalAttributes::CTPSRRScheme&)scheme;
    
    tpsrrScheme.iDefaultStatusReport = TSmsFirstOctet::ESmsStatusReportNotRequested;
    tpsrrScheme.iNumOfPDUs++;
    
    TSmsFirstOctet tpsrr;
    
    if (SmsPDU().Type() == CSmsPDU::ESmsSubmit)
		{
		CSmsSubmit* smsSubmit = reinterpret_cast<CSmsSubmit*>((aSmsPDUArray)[aSegmentSequenceNum]);
		tpsrr = smsSubmit->StatusReportRequest();
		}
	else if (SmsPDU().Type() == CSmsPDU::ESmsDeliver)
		{
		CSmsDeliver* smsDeliver = reinterpret_cast<CSmsDeliver*>((aSmsPDUArray)[aSegmentSequenceNum]);
		tpsrr = smsDeliver->StatusReportIndication();
		}
    
    if(tpsrr)
    	{
    	CSmsMessageAdditionalAttributes::CTPSRRScheme::TSmsTPSRRStatus tpsrrStatus;

    	tpsrrStatus.iSegmentSequenceNum = aSegmentSequenceNum;
		tpsrrStatus.iTPSRRStatus = TSmsFirstOctet::ESmsStatusReportRequested;
		tpsrrScheme.iTPSRRStatusReport.AppendL(tpsrrStatus);	
    	}
	} // CSmsMessage::InstallTPSRRInformationL


/**
 *  Adds EMS IE to a CSmsMessage
 *  
 *  @param aEmsIE EMS object to be added.
 *  
 *  @capability None
 */
EXPORT_C void CSmsMessage::AddEMSInformationElementL(const CEmsInformationElement& aEmsIE)
	{
	LOGGSMU1("CSmsMessage::AddEMSInformationElementL()");

	if(aEmsIE.StartPosition() > (TUint)iBuffer->Length())
		{
		User::Leave(KErrOverflow);
		}

	if(CSmsInformationElement::ESmsEnhancedTextFormatting == aEmsIE.Identifier() && aEmsIE.Length() ==0)
		{
		User::Leave(KErrUnderflow);
		}

	if(CSmsInformationElement::ESmsEnhancedUserPromptIndicator == aEmsIE.Identifier())
		{
		AddEmsUserPromptL(static_cast<const CEmsUserPrompt&>(aEmsIE));
		}
	else if(CSmsInformationElement::ESmsEnhancedODI == aEmsIE.Identifier())
		{
		AddEmsObjectDistributionL(static_cast<const CEmsObjectDistribution&>(aEmsIE));
		}
	else
		{
		TInt count=iInformationElementArray->Count();
		TInt currNo=0;
		const CEmsInformationElement* emsIE=NULL;

		while(currNo < count)
		{
			emsIE=(*iInformationElementArray)[currNo];
			if(emsIE->StartPosition() == aEmsIE.StartPosition() && (CSmsInformationElement::ESmsEnhancedUserPromptIndicator == emsIE->Identifier() || CSmsInformationElement::ESmsEnhancedODI == emsIE->Identifier()))
				User::Leave(KErrArgument);
			else if(emsIE->StartPosition() > aEmsIE.StartPosition())
				break;
			currNo++;
		}
		User::LeaveIfError(iInformationElementArray->Insert(aEmsIE.DuplicateL(),currNo));
		}
	} // CSmsMessage::AddEMSInformationElementL


TBool CSmsMessage::CanBeRemoved(const CEmsInformationElement& aIE, const TUint aIEIndex)
	{
	LOGGSMU1("CSmsMessage::CanBeRemoved()");

	TBool ret=ETrue;
	if(CSmsInformationElement::ESmsEnhancedODI == aIE.Identifier())
		return ret;

	TUint ieStartPosition=aIE.StartPosition();
	TUint count=iInformationElementArray->Count();
	CEmsInformationElement* ie=NULL;

	// is there a UserPrompt/ODI at the same position
	for (TUint i=0; i < count && ret; ++i)
		{
		ie = (*iInformationElementArray)[i];
		// Do not allow removal if start position matches a user prompt and it is not the user prompt itself being removed
		if (ie->StartPosition() == ieStartPosition && ie->Identifier() == CSmsInformationElement::ESmsEnhancedUserPromptIndicator && aIE.Identifier() != CSmsInformationElement::ESmsEnhancedUserPromptIndicator)
		    ret=EFalse;
		if (ie->Identifier() == CSmsInformationElement::ESmsEnhancedODI)
		    {
		    // don't allow removal of IE if position inside object count of ODI
		    if ((static_cast<CEmsObjectDistribution*>(ie)->ObjectCount()+i) >= aIEIndex || static_cast<CEmsObjectDistribution*>(ie)->ObjectCount() == 0)
		        ret=EFalse;
		    }
		}

	LOGGSMU2("CSmsMessage::CanBeRemoved() returns %d", ret);

	return ret;
	} // CSmsMessage::CanBeRemoved


/**
 *  Adds EMS IE to a CSmsMessage
 *  
 *  @param aEmsIE EMS object to be added.
 */
void CSmsMessage::AddEmsUserPromptL(const CEmsUserPrompt& aUserPromptIE)
	{
	LOGGSMU1("CSmsMessage::AddEmsUserPromptL()");

		if(aUserPromptIE.ObjectCount() == 0 )User::Leave(KErrArgument);

		TUint count=iInformationElementArray->Count();
		TUint userPromptStartPosition=aUserPromptIE.StartPosition();

		TSmsId id=aUserPromptIE.Identifier();
		TBool allSameType=ETrue;
		TUint countOnTheSamePos=0;
		TUint firstIndex=0;

		// collect all IEs on the same startPosition as the new User Prompt
		CEmsInformationElement* ie=NULL;

		for (TUint i=0; i < count ; i++)
		{
			ie = (*iInformationElementArray)[i];
			if (ie->StartPosition() == userPromptStartPosition)
			{
				if(countOnTheSamePos==0)
				{
					firstIndex=i;
					id = ie->Identifier();
				}
				else
				{
					if(allSameType && id != ie->Identifier())allSameType=EFalse;
					else id = ie->Identifier();
				}
				++countOnTheSamePos;
			}
		}

		if(countOnTheSamePos != aUserPromptIE.ObjectCount())
			User::Leave(KErrArgument);


	if( countOnTheSamePos == 1)
	{
		switch(id)
		{
		case CEmsInformationElement::ESmsEnhancedPredefinedAnimation:
		case CEmsInformationElement::ESmsEnhancedLargeAnimation :
		case CEmsInformationElement::ESmsEnhancedSmallAnimation	:
		case CEmsInformationElement::ESmsEnhancedUserDefinedSound:
		case CEmsInformationElement::ESmsEnhancedLargePicture:
		case CEmsInformationElement::ESmsEnhancedSmallPicture:
		case CEmsInformationElement::ESmsEnhancedVariablePicture:
		User::LeaveIfError(iInformationElementArray->Insert(aUserPromptIE.DuplicateL(),firstIndex));
			break;
		default:
		User::Leave(KErrCorrupt);
			break;
		}
		return;
	}

	// stitching - more than one object
	if(allSameType  && (id == CEmsInformationElement::ESmsEnhancedUserDefinedSound || id == CEmsInformationElement::ESmsEnhancedLargePicture || id == CEmsInformationElement::ESmsEnhancedSmallPicture || id == CEmsInformationElement::ESmsEnhancedVariablePicture))
		{
		User::LeaveIfError(iInformationElementArray->Insert(aUserPromptIE.DuplicateL(),firstIndex));
		return;
		}

    //  not of the same type or not supported IE types
	User::Leave(KErrCorrupt);
	} // CSmsMessage::AddEmsUserPromptL


/**
 *  Adds EMS IE to a CSmsMessage
 *  
 *  @param aEmsIE EMS object to be added.
 */
void CSmsMessage::AddEmsObjectDistributionL(const CEmsObjectDistribution& aObjectDistributionIE)
    {
	LOGGSMU1("CSmsMessage::AddEmsObjectDistributionL()");

    TUint count=iInformationElementArray->Count();
    TUint objectDistributionStartPosition=aObjectDistributionIE.StartPosition();

    TBool firstIndexFound=EFalse;
    TUint firstIndex=0;
    TUint countOnSameOrSubsequentPos=0;

    // collect any IEs on the same or greater startPosition as the new Object Distribution Indicator
    CEmsInformationElement* ie=NULL;

    for (TUint i=0; i < count; i++)
        {
        ie = (*iInformationElementArray)[i];
        if (ie->StartPosition() >= objectDistributionStartPosition)
            {
            // Only set index if at least one IE found at desired start position
            if (!firstIndexFound)
                {
                firstIndex=i;
                firstIndexFound=ETrue;
                }
            ++countOnSameOrSubsequentPos;
            }
        }
    // check number of IEs present for desired ObjectCount
    if(countOnSameOrSubsequentPos < aObjectDistributionIE.ObjectCount())
        User::Leave(KErrArgument);

    if (firstIndexFound)
        {
        User::LeaveIfError(iInformationElementArray->Insert(aObjectDistributionIE.DuplicateL(),firstIndex));
        return;
        }

    //  no valid IE for ODI to apply to
    User::Leave(KErrCorrupt);
    } // CSmsMessage::AddEmsObjectDistributionL


/**
 *  Removes first EMS IE that matches the criteria from the CSmsMessage
 *  
 *  @param aStartPosition of a EMS object to be removed.
 *  @param aEmsId type of a EMS object to be removed
 *  @return pointer EMS information element
 *  @capability
 *  @capability None
 */
EXPORT_C CEmsInformationElement* CSmsMessage::RemoveEMSInformationElementL(const TUint aStartPosition,const TSmsId aEmsId)
	{
	LOGGSMU1("CSmsMessage::RemoveEMSInformationElementL()");

	CEmsInformationElement* emsIE=NULL;
	CEmsInformationElement* ie=NULL;

	TUint count=iInformationElementArray->Count();

	for (TInt i=--count; i >=0 ; --i)
		{
		ie = (*iInformationElementArray)[i];
		if (ie->StartPosition() == aStartPosition && ie->Identifier() == aEmsId)
			{
				if(CanBeRemoved(*ie, i))
				{
					iInformationElementArray->Remove(i);
					emsIE=ie;
				}
				else
					emsIE=NULL;
				break;
			}

		}
	return emsIE;
	} // CSmsMessage::RemoveEMSInformationElementL


/**
 *  Removes all EMS IEs that matches the criteria from the CSmsMessage
 *  
 *  @param aStartPosition of a EMS object to be removed.
 *  @param aEmsId type of a EMS object to be removed
 *  @return pointer to array of EMS IE
 *  
 *  @capability None
 */
EXPORT_C RPointerArray<CEmsInformationElement>* CSmsMessage::RemoveEMSInformationElementsL(const TUint aStartPosition,const TSmsId aEmsId)
	{
	LOGGSMU1("CSmsMessage::RemoveEMSInformationElementsL()");

	CEmsInformationElement* ie=NULL;
	RPointerArray<CEmsInformationElement>* selectedIEs = NULL;
	TUint count=iInformationElementArray->Count();

	for (TInt i=--count; i >= 0 ; --i)
		{
		ie = (*iInformationElementArray)[i];
		if (ie->StartPosition() == aStartPosition && ie->Identifier() == aEmsId)
			{
				if(CanBeRemoved(*ie, i))
				{
					if(selectedIEs == NULL)selectedIEs=new (ELeave) RPointerArray<CEmsInformationElement>(8);
					iInformationElementArray->Remove(i);
					selectedIEs->Append(ie);
				}
			}

		}
	return selectedIEs;
	} // CSmsMessage::RemoveEMSInformationElementsL

/**
 *  Resets EMS IE from a CSmsMessage
 *  
 *  
 *  @capability None
 */
EXPORT_C void  CSmsMessage::ResetEMSL()
	{
	LOGGSMU1("CSmsMessage::ResetEMSL()");

	iInformationElementArray->ResetAndDestroy();
	} // CSmsMessage::ResetEMSL

/**
 *  Removes EMS IE from a CSmsMessage
 *  
 *  @return CArrayPtrFlat<const CEmsInformationElement>& of internal EMS array
 *  
 *  @capability None
 */
EXPORT_C const RPointerArray<const CEmsInformationElement>& CSmsMessage::GetEMSInformationElementsL()const
	{
	LOGGSMU1("CSmsMessage::GetEMSInformationElementsL()");

	 return (const RPointerArray<const CEmsInformationElement>&)(*iInformationElementArray);
	} // CSmsMessage::GetEMSInformationElementsL


void CSmsMessage::UpdateUserPromptAndODIElementsStartPosition()
	{
	LOGGSMU1("CSmsMessage::UpdateUserPromptAndODIElementsStartPosition()");

		TUint num=iInformationElementArray->Count();
		TInt startPosition=-1;
		CEmsInformationElement* ie = NULL;


		// is the ie an EMS information Element;

		for(TInt i=num-1;i >=0; --i)
		{
			ie = (*iInformationElementArray)[i];
			if(ie->Identifier() == CSmsInformationElement::ESmsEnhancedUserPromptIndicator || ie->Identifier() == CSmsInformationElement::ESmsEnhancedODI)
			{
				if(startPosition >=0)
					ie->SetStartPosition(startPosition);
				else
					iInformationElementArray->Remove(i);
			}
			else
				startPosition=ie->StartPosition();

		}
	} // CSmsMessage::UpdateUserPromptAndODIElementsStartPosition


TInt CSmsMessage::AddReceivedEmsInformationElement(CEmsInformationElement* aEmsIE)
	{
	LOGGSMU1("CSmsMessage::AddReceivedEmsInformationElement()");

	TInt ret=KErrNone;
	if(CSmsInformationElement::ESmsEnhancedUserPromptIndicator == aEmsIE->Identifier() || CSmsInformationElement::ESmsEnhancedODI == aEmsIE->Identifier())
		ret=iInformationElementArray->Append(aEmsIE);
	else
		{
			TInt count=iInformationElementArray->Count();
			TInt currNo=0;
			const CEmsInformationElement* emsIE=NULL;
			while(currNo < count)
			{
				emsIE=(*iInformationElementArray)[currNo];
				if(emsIE->StartPosition() > aEmsIE->StartPosition() && (CSmsInformationElement::ESmsEnhancedUserPromptIndicator != emsIE->Identifier() || CSmsInformationElement::ESmsEnhancedODI != emsIE->Identifier()))
					break;
				currNo++;
			}
			ret=iInformationElementArray->Insert(aEmsIE,currNo);
		}
	return ret;
	} // CSmsMessage::AddReceivedEmsInformationElement


/**
 *  Updates the slot information for the given message as described in the supplied buffer
 *  
 *  @param aDesc buffer, layout: byte 0 store id, byte 1..n PDU slot indexes
 *  @leave KErrArgument
 *  
 *  @capability None
 */
EXPORT_C void CSmsMessage::UpdateSlotsL(TDesC8& aDesc)
    {
	LOGGSMU1("CSmsMessage::UpdateSlotsL()");

    TGsmSmsSlotEntry newSlot;

    switch (aDesc[0])
        {
        case CSmsMessage::ESmsSIMStorage:
            {
            newSlot.iStore = KETelIccSmsStore;
            break;
            }
        case CSmsMessage::ESmsPhoneStorage:
            {
            newSlot.iStore = KETelMeSmsStore;
            break;
            }
        case CSmsMessage::ESmsCombinedStorage:
            {
            newSlot.iStore = KETelCombinedSmsStore;
            break;
            }
        default:
            User::Leave(KErrArgument);
        }

    for (TInt index = 1; index < aDesc.Size(); index++)
        {
        newSlot.iIndex = aDesc[index]; // point to PDU indexes
        iSlotArray.AppendL(newSlot);
        }
    } // CSmsMessage::UpdateSlotsL

/**
 *  Copy EMS elements to dest CSmsMessage
 *  Uses CSmsMessage extended EMS API
 *  Creates new CEmsInformationElement instance(s) and passes ownership to the dest CSmsMessage
 *  
 *  @return aToMessage  - Destination CSmsMessage
 *  
 *  @capability None
 */
EXPORT_C void CSmsMessage::CopyEmsElementsL(CSmsMessage& aToMessage) const
	{
	LOGGSMU1("CSmsMessage::CopyEmsElementsL()");

	// CSmsMessage extended EMS API method creates array of references to EMS elements in
	// the source message
	// Loop through the array copying each individual method using the CSmsMessage
	// and CEmsInformationElement API's
	CEmsInformationElement* dupl=NULL;
	aToMessage.ResetEMSL();
	for(TInt i = iInformationElementArray->Count()-1 ; i >=0; --i)
		{
		dupl = (*iInformationElementArray)[i]->DuplicateL();
		CleanupStack::PushL(dupl);
		aToMessage.AddEMSInformationElementL(*dupl);
		CleanupStack::PopAndDestroy(dupl);
		}
	} // CSmsMessage::CopyEmsElementsL

/**
 *  @capability None
 */
EXPORT_C void CSmsMessage::AddSlotL(const TGsmSmsSlotEntry& aSlot)
	{
	LOGGSMU1("CSmsMessage::AddSlotL()");

		TInt count = iSlotArray.Count();
		TInt i(0);
		TBool found(EFalse);
		while(!found && i<count)
		{
			if(aSlot.iIndex == iSlotArray[i].iIndex)found=ETrue;
			else ++i;
		}
		LOGGSMU3("CSmsMessage::AddSlotL current no in: %d, adds index %d", count,aSlot.iIndex );
		LOGGSMU3("found %d at position %d",found,i);
		iSlotArray.AppendL(aSlot);
	} // CSmsMessage::AddSlotL

/**
 *  @capability None
 */
EXPORT_C TBool CSmsMessage::MatchSlots(const CArrayFixFlat<TGsmSmsSlotEntry>& aSlotArray)
	{
	LOGGSMU1("CSmsMessage::MatchSlots()");

		TBool match = EFalse;
		TInt count = aSlotArray.Count();
		if(iSlotArray.Count() == count)
		{
			TInt i = 0;
			match = ETrue;
			while(match && i < count)
				{
				const TGsmSmsSlotEntry& newSlot=aSlotArray[i];
				const TGsmSmsSlotEntry& slot =iSlotArray[i];
				if ( ( slot.iIndex != newSlot.iIndex ) || ( slot.iStore != newSlot.iStore ) )
						match = EFalse;
				i++;
				}

		}
		return match;
	} // CSmsMessage::MatchSlots

/**
 *  c'tor
 *  @capability None
 */
EXPORT_C TGsmSmsSlotEntry::TGsmSmsSlotEntry()
	{
	} // TGsmSmsSlotEntry::TGsmSmsSlotEntry

/**
 *  Internalize
 *  
 *  @param aStream For internalizing the data
 */
void TGsmSmsSlotEntry::InternalizeL(RReadStream& aStream)
	{
	aStream >> iStore;
	iIndex=aStream.ReadInt32L();
	} // TGsmSmsSlotEntry::InternalizeL

/**
 *  Externalize
 *  
 *  @param aStream For Externalizing the data
 */
void TGsmSmsSlotEntry::ExternalizeL(RWriteStream& aStream) const
	{
	aStream << iStore;
	aStream.WriteInt32L(iIndex);
	} // TGsmSmsSlotEntry::ExternalizeL

 /**
  *  Extracts all RFC 822 Email header information objects out of the given UserData into the CSmsMessage's iSmsEmailHeaderLength.
  *  
  *  @param aUserData Object containing the EmailHeader information element.
  *  @param aCharsAlreadyAdded The current, running, start position to be applied to the objects taken out
  *  of the UserData.
  */
void  CSmsMessage::InstallEmailHeaderInformationElementL(CSmsUserData& aUserData,TInt& aHeaderLength)
 	{
 	// Installs all the information elements within the subsequent PDUs.
 	LOGGSMU1("CSmsMessage::InstallEmailHeaderInformationElementL()");

 	CSmsInformationElement::TSmsInformationElementIdentifier id;

 	for (TInt z=0; z<aUserData.NumInformationElements(); z++)
 		{
 		const CSmsInformationElement& ie = aUserData.InformationElement(z);
 		id = (CSmsInformationElement::TSmsInformationElementIdentifier)ie.Identifier();
 		if (id == CSmsInformationElement::ESmsIEIRFC822EmailHeader)
 			{
 				const TDesC8& des = ie.Data();
 				aHeaderLength+=des[0];
 			}
 		}
 	} // CSmsMessage::InstallEmailHeaderInformationElementL

/**
 *  Extracts all RFC 822 Email header information objects out of the given UserData into the CSmsMessage's iSmsEmailHeaderLength.
 *  
 *  @param aCommand Object containing the EmailHeader information element.
 *  @param aCharsAlreadyAdded The current, running, start position to be applied to the objects taken out
 *  of the UserData.
 */
void CSmsMessage::InstallEmailHeaderInformationElementL(CSmsCommand& aCommand,TInt& aHeaderLength)
    {
    // Ignore in code coverage - not used in SMS stack and not exported
    // but cannot be removed as impacts public header.
    BULLSEYE_OFF    
    // Installs all the information elements within the subsequent PDUs.
    LOGGSMU1("CSmsMessage::InstallEmailHeaderInformationElementL()");
    
    CSmsInformationElement::TSmsInformationElementIdentifier id;
    
    for (TInt z=0; z<aCommand.NumInformationElements(); z++)
        {
        const CSmsInformationElement& ie = aCommand.InformationElement(z);
        id = (CSmsInformationElement::TSmsInformationElementIdentifier)ie.Identifier();
        if (id == CSmsInformationElement::ESmsIEIRFC822EmailHeader)
            {
            const TDesC8& des = ie.Data();
            aHeaderLength+=des[0];
            }
        }
    BULLSEYE_RESTORE
    }

// E-mail header methods

/**
 *  Adds a header to an email body
 *  
 *  @param aEMailBody The buffer to which the header gets appended
 *  @param aEmailHeader The buffer which holds RFC 822 e-mail header
 *  @publishedAll
 *  @released
 *  
 *  @capability None
 */
EXPORT_C void CSmsMessage::AddEmailHeaderL(const TDesC& aEmailHeader, const TDesC& aEmailBody)
 	{
 	LOGGSMU1("CSmsMessage::AddEmailHeaderL()");

 	if(IsEmailHeader())
 		User::Leave(KErrAlreadyExists);

 	TInt len =aEmailHeader.Length();
 	if(len <= 0)
 		User::Leave(KErrCorrupt);

	if(len >= 255 )
 		User::Leave(KErrTooBig);


	if(SmsPDU().UserData().NumInformationElements())
		{
		TBool is16bit = EFalse;
		TBool concatenationIEPresent= SmsPDU().TextConcatenated( &is16bit );
		if(concatenationIEPresent)
			{
			if(SmsPDU().UserData().NumInformationElements() >=2) // something more than concat IE
				User::Leave(KErrAccessDenied);
			}
		else
			User::Leave(KErrAccessDenied);
		}


	TBuf8<1> data;
	data.SetLength(1);
	data[0]=static_cast<TUint8>(len);
	SmsPDU().UserData().AddInformationElementL(CSmsInformationElement::ESmsIEIRFC822EmailHeader,data);

 	iBuffer->Reset();
 	iBuffer->InsertL(0,aEmailHeader);
 	iBuffer->InsertL(iBuffer->Length(),aEmailBody);
 	} // CSmsMessage::AddEmailHeaderL

 /**
  *  Checks whether the SmsMessage contains an e-mail.
  *  
  *  @return ETrue if CSmsMessage contains e-mail ( e-mail header information element), otherwise EFalse
  *  @publishedAll
  *  @released
  *  
  *  @capability None
  */
EXPORT_C TBool CSmsMessage::IsEmailHeader() const
 	{
 	LOGGSMU1("CSmsMessage::IsEmailHeader()");

	TInt emailIndex;
	return SmsPDU().UserData().InformationElementIndex(CSmsInformationElement::ESmsIEIRFC822EmailHeader,emailIndex);
 	} // CSmsMessage::IsEmailHeader

 /**
  *  Extracts e-mail header and body from the SmsMessage
  *  
  *  @return aEMailHeader The buffer containing the extracted email header
  *  @return aEMailBody The buffer containing the extracted email body
  *  @return ETrue if extraction of e-mail header and body succeeds
  *  @publishedAll
  *  @released
  *  
  *  @capability None
  */
EXPORT_C TBool CSmsMessage::GetEmailHeaderL(HBufC** aEmailHeader,HBufC** aEmailBody)
 	{
 	LOGGSMU1("CSmsMessage::GetEmailHeaderL()");

 	if(IsEmailHeader())
 		{
 			TInt bufLen=iBuffer->Length();
 			TInt emailHeaderLen(0);
			TInt emailIndex(0);
			if(SmsPDU().UserData().InformationElementIndex(CSmsInformationElement::ESmsIEIRFC822EmailHeader,emailIndex))
				emailHeaderLen=SmsPDU().UserData().InformationElement(emailIndex).Data()[0];
			else
				User::Leave(KErrCorrupt);

 			TInt emailBodyLen=bufLen-emailHeaderLen;

 			if(emailBodyLen<0)
 				return EFalse;

 			*aEmailHeader=HBufC::NewL(emailHeaderLen);
 			CleanupStack::PushL(*aEmailHeader);

 			*aEmailBody=HBufC::NewL(emailBodyLen);
 			CleanupStack::PushL(*aEmailBody);

 			TPtr headerPtr((*aEmailHeader)->Des());
 			iBuffer->Extract(headerPtr,0,emailHeaderLen);

 			TPtr bodyPtr((*aEmailBody)->Des());
 			iBuffer->Extract(bodyPtr,emailHeaderLen,emailBodyLen);

 			CleanupStack::Pop(2);
 			return ETrue;
 		}
 	return EFalse;
 	} // CSmsMessage::GetEmailHeaderL


 /**
  *  Extracts the offset between universal time and local time in seconds.
  *  If the value is > 0, local time is ahead of universal time.
  *  If the value is < 0, local time is behind universal time.
  *  
  *  @return TTTimeIntervalSeconds The time zone offset in seconds.
  *  @publishedAll
  *  @capability None
  */
EXPORT_C TTimeIntervalSeconds CSmsMessage::UTCOffset() const
    {
    LOGGSMU1("CSmsMessage::UTCOffset()");

    TUint timeZoneOffset = ((iFlags & ESmsUTCOffsetSecondGranularityMask) >> ESecondBitOffset);

    if (iFlags & ESmsUTCOffsetSignBit)
        {
        timeZoneOffset = -timeZoneOffset;
        }

    return (TTimeIntervalSeconds(timeZoneOffset));
    } // CSmsMessage::UTCOffset


 /**
  *  Sets the offset between universal time and local time in seconds.
  *  If the value is > 0, local time is ahead of universal time.
  *  If the value is < 0, local time is behind universal time.
  *  The CSmsMessage Time Zone Offset has the range +/- 71100 seconds. This is same range as
  *  the Service Centre Time Stamp Time Zone Offset as specified in 23.040 V4.4.0 Sect 9.2.3.11.
  *  
  *  @return True if input value was set successfully (in range), False otherwise
  *  @publishedAll
  *  @capability None
  */
EXPORT_C TBool CSmsMessage::SetUTCOffset(const TTimeIntervalSeconds& aTimeOffset)
    {
    LOGGSMU1("CSmsMessage::SetUTCOffset()");

    TBool rc = ETrue;

    TInt timeOffset = aTimeOffset.Int();

    if ((timeOffset <=  EMaximumSeconds) &&
        (timeOffset >= -EMaximumSeconds))
        {
        // Clear the timezone offset bits
        iFlags &= (~(ESmsUTCOffsetSecondGranularityMask   |
                     ESmsUTCOffsetSignBit));

        if (timeOffset < 0)
            {
            iFlags |= ESmsUTCOffsetSignBit;
            timeOffset = -timeOffset;
            }

        iFlags |= (timeOffset << ESecondBitOffset);
        }
    else
        {
        LOGGSMU2("CSmsMessage::SetUTCOffset offset [out of range] = %d",timeOffset);
        rc = EFalse;
        }

    return rc;
} // CSmsMessage::SetUTCOffset


/**
 *  Returns the message version number.
 *  @capability
 */
EXPORT_C TInt CSmsMessage::Version()
 	{
 	LOGGSMU1("CSmsMessage::Version()");

 	return iVersion;
 	} // CSmsMessage::Version


/**
 *  @internalComponent
 *  Validates and sets the message version number.
 *  
 *  @param aVersion version number to set.
 *  @return KErrNone if aVersion is valid and successfully set, KErrArgument otherwise.
 *  @capability None
 */
EXPORT_C TInt CSmsMessage::SetVersion(TInt aVersion)
	{
 	LOGGSMU2("CSmsMessage::SetVersion()", aVersion);

	if((aVersion>=ESmsMessageV0) && (aVersion<=ESmsMessageV4))
		{
		iVersion=aVersion;
		return KErrNone;
		}

	return KErrArgument;
	} // CSmsMessage::SetVersion


/**
 *  @internalComponent
 *  Internalises all object data except for the CSmsBufferBase and the message version.
 *  
 *  This is used when the buffer is stored elsewhere.
 *  
 *  @param aStream Stream to read from
 *  @capability None
 */
EXPORT_C void CSmsMessage::InternalizeWithoutBufferAndVersionL(RReadStream& aStream)
	{
 	LOGGSMU1("CSmsMessage::InternalizeWithoutBufferAndVersionL()");

	iFlags=aStream.ReadInt32L();
	iStatus=(NMobileSmsStore::TMobileSmsStoreStatus) aStream.ReadInt32L();
	iLogServerId=aStream.ReadInt32L();
	TInt64 time;
	aStream >> time;
	iTime=time;

	CSmsPDU* smspdu=CSmsPDU::NewL(aStream,*iCharacterSetConverter,iFs);
	delete iSmsPDU;
	iSmsPDU=smspdu;
	iIs16BitConcatenation=aStream.ReadInt32L();

	iInformationElementArray->ResetAndDestroy();
	CEmsFactory::InternalizeL(*iInformationElementArray, aStream);

	TInt count=aStream.ReadInt32L();
	iSlotArray.Reset();
	TInt i=0;
	TGsmSmsSlotEntry newSlot;
	for (; i<count; i++)
		{
		newSlot.InternalizeL(aStream);
		AddSlotL(newSlot);
		}
	} // CSmsMessage::InternalizeWithoutBufferAndVersionL


/**
 *  @internalComponent
 *  Externalises all object data except for the CSmsBufferBase and the message version.
 *  
 *  This is used when the buffer is stored elsewhere.
 *  
 *  @param aStream Stream to write to
 *  @capability None
 */
EXPORT_C void CSmsMessage::ExternalizeWithoutBufferAndVersionL(RWriteStream& aStream) const
	{
 	LOGGSMU1("CSmsMessage::ExternalizeWithoutBufferAndVersionL()");

	aStream.WriteInt32L(iFlags);
	aStream.WriteInt32L(iStatus);
	aStream.WriteInt32L(iLogServerId);
	aStream << Time().Int64();

	SmsPDU().ExternalizeL(aStream);
	aStream.WriteInt32L(iIs16BitConcatenation);

	CEmsFactory::ExternalizeL(*iInformationElementArray, aStream);
	TInt count=iSlotArray.Count();
	aStream.WriteInt32L(count);
	TInt i=0;
	for (; i<count; i++)
		{
		iSlotArray[i].ExternalizeL(aStream);
		}
	} // CSmsMessage::ExternalizeWithoutBufferAndVersionL


/**
 *  @internalComponent
 *  Internalises the CSmsBufferBase.
 *  
 *  @param aStream Stream to read from
 *  @capability None
 */
EXPORT_C void CSmsMessage::InternalizeBufferL(RReadStream& aStream)
	{
	aStream >> *iBuffer;
	} // CSmsMessage::InternalizeBufferL


/**
 *  @internalComponent
 *  Externalises the CSmsBufferBase.
 *  
 *  @param aStream Stream to write to
 *  @capability None
 */
EXPORT_C void CSmsMessage::ExternalizeBufferL(RWriteStream& aStream) const
	{
	aStream << *iBuffer;
	} // CSmsMessage::ExternalizeBufferL


/**
 *  @internalComponent
 *  Internalises the message version.
 *  
 *  @param aStream Stream to read from
 *  @capability None
 */
EXPORT_C void CSmsMessage::InternalizeVersionL(RReadStream& aStream)
	{
	TInt32 versionNumber = ESmsMessageV0;
	TRAPD(err,versionNumber=aStream.ReadInt32L());
	if(err==KErrEof)
		{
		versionNumber=ESmsMessageV0;
		}

	iVersion=static_cast<TSmsMessageVersion> (versionNumber);
	} // CSmsMessage::InternalizeVersionL


/**
 *  @internalComponent
 *  Externalises the message version.
 *  
 *  @param aStream Stream to write to
 *  @capability None
 */
EXPORT_C void CSmsMessage::ExternalizeVersionL(RWriteStream& aStream) const
	{
	aStream.WriteInt32L(iVersion);
	} // CSmsMessage::ExternalizeVersionL


/**
 *  @publishedAll
 *  
 *  Returns a reference to a specialisation of the class CSmsIEOperation. Clients can use this sub-class
 *  to access the attributes of a specific type of information element encapsulated inside the CSmsMessage.
 *  The ownership to the CSmsIEOperation class belongs to the CSmsMessage. When the CSmsMessage is deleted,
 *  the CSmsIEOperation will also be deleted and the reference will become stale.
 *  
 *  @param aId
 *  Specifies the sub class of CSmsIEOperation required.
 *  @leave KErrNotSupported
 *  When there is not accessor class available for the specified type of information element.
 *  @capability None
 */
EXPORT_C CSmsIEOperation& CSmsMessage::GetOperationsForIEL(CSmsInformationElement::TSmsInformationElementIdentifier aId) const
    {
    LOGGSMU1("CSmsMessage::GetOperationsForIEL()");

    if (iVersion < CSmsMessage::ESmsMessageV1)
        {
        LOGGSMU2("CSmsMessage::GetOperationsForIEL, Operation not supported, Msg Version %d", iVersion);
        User::Leave(KErrNotSupported);
        }

    return iAdditionalInfo->GetIEOperationL(aId);
    } // CSmsMessage::GetOperationsForIEL

EXPORT_C CSmsNonIEOperation& CSmsMessage::GetOperationsForNonIEL(TSmsNonIEIdentifier aId) const
	{
	LOGGSMU1("CSmsMessage::GetOperationsForNonIEL");

	if (iVersion < CSmsMessage::ESmsMessageV2)
		{
		LOGGSMU2("GetOperationsForNonIEL not supported, Msg Version %d", iVersion);
		User::Leave(KErrNotSupported);
		}

	return iAdditionalInfo->GetNonIEOperationL(aId);
	} // CSmsMessage::GetOperationsForNonIEL


void CSmsMessage::CreateControlIEOperationsClassesL()
    {
 	LOGGSMU1("CSmsMessage::CreateControlIEOperationsClassesL()");

    CSmsIEOperation* iEOperation = NULL;

    iEOperation = CSmsIEOperation::NewL(CSmsInformationElement::ESmsHyperLinkFormat, *this,	*iCharacterSetConverter, iFs);
    iAdditionalInfo->SetIEOperationL(iEOperation);

    iEOperation = CSmsIEOperation::NewL(CSmsInformationElement::ESmsReplyAddressFormat, *this,	*iCharacterSetConverter, iFs);
    iAdditionalInfo->SetIEOperationL(iEOperation);

    iEOperation = CSmsIEOperation::NewL(CSmsInformationElement::ESmsEnhanceVoiceMailInformation, *this,	*iCharacterSetConverter, iFs);
    iAdditionalInfo->SetIEOperationL(iEOperation);

    iEOperation = CSmsIEOperation::NewL(CSmsInformationElement::ESmsIEISpecialSMSMessageIndication, *this,	*iCharacterSetConverter, iFs);
    iAdditionalInfo->SetIEOperationL(iEOperation);
    
    iEOperation = CSmsIEOperation::NewL( CSmsInformationElement::ESmsIEISMSCControlParameters, *this,	*iCharacterSetConverter, iFs);
    iAdditionalInfo->SetIEOperationL(iEOperation);
    } // CSmsMessage::CreateControlIEOperationsClassesL

    
void CSmsMessage::CreateControlNonIEOperationsClassesL()
    {
    CSmsNonIEOperation* nonIEOperation = NULL;

    nonIEOperation = CSmsNonIEOperation::NewL(ESmsTPSRRParameter, *this);
    iAdditionalInfo->SetNonIEOperationL(nonIEOperation);

	nonIEOperation = CSmsNonIEOperation::NewL(ESmsIncompleteClass0MessageParameter, *this);
	iAdditionalInfo->SetNonIEOperationL(nonIEOperation);
	} // CSmsMessage::CreateControlNonIEOperationsClassesL


/**
 *  Gets the scheme of the status report.
 *  
 *  @return Staus Report Scheme 
 */
EXPORT_C TSmsStatusReportScheme CSmsMessage::Scheme() const
	{
	return iAdditionalInfo->GetStatusReportScheme().Id();
	}

/**
 *  @publishedAll
 *  
 *  Used by the SMS Stack to indicate that when the message was decoded, it was determined
 *  to be on the SIM. If the message's DCS byte is configured for automatic delete or the
 *  PID is set to Type 0, the message may subsequently have been deleted from the SIM during
 *  decoding in which case its storage status is unstored.
 *  
 *  @param aOnSim
 *  Used to indicate that the message was on the SIM when it was decoded.
 *  @capability None
 */
EXPORT_C void  CSmsMessage::SetDecodedOnSIM(TBool aOnSim)
    {
 	LOGGSMU2("CSmsMessage::SetDecodedOnSIM(): %d", aOnSim);

    if (aOnSim)
        {
        iFlags = iFlags | EDecodedOnSimBit;
        }
    else
        {
        iFlags = iFlags & ~EDecodedOnSimBit;
        }
    } // CSmsMessage::SetDecodedOnSIM


/**
 *  @publishedAll
 *  
 *  Used by the SMS Stack to indicate that when the message was decoded, it was determined
 *  to be on the SIM. If the message's DCS byte is configured for automatic delete or the
 *  PID is set to Type 0, the message may subsequently have been deleted from the SIM during
 *  decoding in which case its storage status is unstored.
 *  
 *  @return
 *  True if the message was on the SIM during decoding,
 *  False otherwise.
 *  @capability None
 */
EXPORT_C TBool CSmsMessage::DecodedOnSim()
    {
    LOGGSMU1("CSmsMessage::DecodedOnSim()");

    if (iFlags & EDecodedOnSimBit)
        {
        return ETrue;
        }
    else
        {
        return EFalse;
        }
    } // CSmsMessage::DecodedOnSim


/**
 *  @publishedAll
 *  
 *  Used by the SMS Stack to indicate that when the message was decoded, it was determined
 *  that it should be forwarded to the client. The message will not be forwarded to the
 *  client when the PID byte is set to type 0 (except by default for class 0 and class 2 messages).
 *  
 *  @param forward
 *  Used to indicate that the message needs to be forwarded to clients.
 *  @capability None
 */
EXPORT_C void CSmsMessage::SetForwardToClient(TBool aForward)
    {
 	LOGGSMU2("CSmsMessage::SetForwardToClient(): %d", aForward);

    if (aForward)
        {
        iFlags = iFlags | EForwardToClientBit;
        }
    else
        {
        iFlags = iFlags & ~EForwardToClientBit;
        }
    } // CSmsMessage::SetForwardToClient


/**
 *  @publishedAll
 *  
 *  Used by the SMS Stack to indicate that when the message was decoded, it was determined
 *  that is should be forwarded to the client. The message will not be forwarded to the client
 *  when the PID byte is set to type 0 (except by default for class 0 and class 2 messages).
 *  
 *  @return
 *  True if the message is to be forwarded to clients
 *  False otherwise.
 *  @capability None
 */
EXPORT_C TBool CSmsMessage::ForwardToClient()
    {
    LOGGSMU1("CSmsMessage::ForwardToClient()");

    if (iFlags & EForwardToClientBit)
        {
        return ETrue;
        }
    else
        {
        return EFalse;
        }
    } // CSmsMessage::ForwardToClient
