// 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:
//

#include "Gsmumain.h"
#include "gsmuieoperations.h"
#include "Gsmumsg.h"
#include "gsmumsgadditionalattributes.h"
#include "Gsmuelem.h"
#include "smsstacklog.h"


/**
 *  This is used by the CSmsMessage to create a CSmsIEOperation.
 *  Each instance is owned by the CSmsMessage which created it.
 *  Therefore only the CSmsMessage (GSMU component) is allowed to
 *  create and delete CSmsIEOperations.
 *  
 *  @param aId
 *  The ID for the CSmsInformationElement for whom an operation class needs to be created.
 *  @param aMessage
 *  The CSmsMessage that this operations class belongs to.
 *  @param aCharacterSetConverter
 *  A reference to a character set converter, needed by some derived operation classes.
 *  @param aFs
 *  A reference to the file server, needed by some derived operations classes.
 *  
 *  @internalComponent
 */
CSmsIEOperation*  CSmsIEOperation::NewL(CSmsInformationElement::TSmsInformationElementIdentifier aId, CSmsMessage& aMessage, CCnvCharacterSetConverter& aCharacterSetConverter, RFs& aFs)
	{
	LOGGSMU1("CSmsIEOperation::NewL()");

	CSmsIEOperation* iEOperation = NULL;

	switch (aId)
	    {
	    case CSmsInformationElement::ESmsHyperLinkFormat:
	        iEOperation = new (ELeave)  CSmsHyperLinkOperations(aId,aMessage);
	        break;
	    case CSmsInformationElement::ESmsReplyAddressFormat:
	        iEOperation = new (ELeave) CSmsReplyAddressOperations(aId, aMessage, aCharacterSetConverter, aFs);
	        break;
	    case CSmsInformationElement::ESmsEnhanceVoiceMailInformation:
	        iEOperation = new (ELeave) CSmsEnhancedVoiceMailOperations(aId, aMessage, aCharacterSetConverter, aFs);
	        break;
	    case CSmsInformationElement::ESmsIEISpecialSMSMessageIndication:
	        iEOperation = new (ELeave)  CSmsSpecialSMSMessageOperations(aId,aMessage);
	        break;
	    case CSmsInformationElement::ESmsIEISMSCControlParameters:
	        iEOperation = new (ELeave)  CSmsSMSCCtrlParameterOperations(aId,aMessage); 
	        break;
	    default:
	        User::Leave(KErrArgument);
	        break;
	    }

	iEOperation->ConstructL();

	return iEOperation;
	} // CSmsIEOperation::NewL


/**
 *  @publishedAll
 *  
 *  Identifies the information element which is supported by this operations class.
 *  
 *  @return The information element ID supported by this operations class.
 *  @capability None
 */
EXPORT_C CSmsInformationElement::TSmsInformationElementIdentifier CSmsIEOperation::Id() const
	{
	LOGGSMU1("CSmsIEOperation::Id()");

	return iId;
	} // CSmsInformationElement::TSmsInformationElementIdentifier


/**
 *  @internalComponent
 *  
 *  Identifies whether the message type supports is supported
 *  
 *  @return True if the message type is supported. False otherwise.
 */
TBool CSmsIEOperation::MessageTypeSupported() const
	{
	LOGGSMU1("CSmsIEOperation::MessageTypeSupported()");

	CSmsPDU::TSmsPDUType type = iMessage.Type();

    return ((type == CSmsPDU::ESmsDeliver) ||
            (type == CSmsPDU::ESmsSubmit));
	} // CSmsIEOperation::MessageTypeSupported


CSmsIEOperation::CSmsIEOperation(CSmsInformationElement::TSmsInformationElementIdentifier aId, CSmsMessage& aMessage) : iId(aId), iMessage(aMessage)
	{
	//NOP
	} // CSmsIEOperation::CSmsIEOperation


/**
 *  @internalComponent
 *  
 *  Prevent clients from using the assignment operator by including it in the class definition
 *  but making it protected and not exporting it.
 *  
 *  @capability None
 */
void CSmsIEOperation::operator=(const CSmsIEOperation&)
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF    
    LOGGSMU1("CSmsIEOperation::operator=");
    Panic(KGsmuPanicMethodBodyNotImplemented1);
    BULLSEYE_RESTORE
    }

/**
 *  @internalComponent
 *  
 *  Prevent clients from using the equality operator by including it in the class definition
 *  but making it protected and not exporting it.
 *  
 *  @capability None
 */
TBool CSmsIEOperation::operator==(const CSmsIEOperation&)
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF    
    LOGGSMU1("CSmsIEOperation::operator==");
    Panic(KGsmuPanicMethodBodyNotImplemented1);
    return EFalse;
    BULLSEYE_RESTORE
    }

/**
 *  @internalComponent
 *  
 *  Prevent clients from using the assignment operator by including it in the class definition
 *  but making it protected and not exporting it.
 *  
 *  @capability None
 */
void CSmsCtrlOperation::operator=(const CSmsCtrlOperation&)
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF    
    LOGGSMU1("CSmsCtrlOperation::operator=");
    Panic(KGsmuPanicMethodBodyNotImplemented1);
    BULLSEYE_RESTORE
    }

/**
 *  @internalComponent
 *  
 *  Prevent clients from using the equality operator by including it in the class definition
 *  but making it protected and not exporting it.
 *  
 *  @capability None
 */
TBool CSmsCtrlOperation::operator==(const CSmsCtrlOperation&)
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF    
    LOGGSMU1("CSmsCtrlOperation::operator==");
    Panic(KGsmuPanicMethodBodyNotImplemented1);
    return EFalse;
    BULLSEYE_RESTORE
    }

CSmsCtrlOperation::CSmsCtrlOperation(CSmsInformationElement::TSmsInformationElementIdentifier aId, CSmsMessage& aMessage) : CSmsIEOperation(aId, aMessage)
	{
	//NOP
	} // CSmsCtrlOperation::CSmsCtrlOperation


CSmsHyperLinkOperations::CSmsHyperLinkOperations(CSmsInformationElement::TSmsInformationElementIdentifier aId, CSmsMessage& aMessage) : CSmsCtrlOperation(aId, aMessage)
	{
	//NOP
	} // CSmsHyperLinkOperations::CSmsHyperLinkOperations


void CSmsIEOperation::ConstructL()
	{
	LOGGSMU1("CSmsIEOperation::ConstructL()");
	} // CSmsIEOperation::ConstructL


/**
 *  @internalComponent
 *  
 *  Prevent clients from using the assignment operator by including it in the class definition
 *  but making it protected and not exporting it.
 *  
 *  @capability None
 */
void CSmsHyperLinkOperations::operator=(const CSmsHyperLinkOperations&)
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF    
    LOGGSMU1("CSmsHyperLinkOperations::operator=");
    Panic(KGsmuPanicMethodBodyNotImplemented1);
    BULLSEYE_RESTORE
    }

/**
 *  @internalComponent
 *  
 *  Prevent clients from using the equality operator by including it in the class definition
 *  but making it protected and not exporting it.
 *  
 *  @capability None
 */
TBool CSmsHyperLinkOperations::operator==(const CSmsHyperLinkOperations&)
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF    
    LOGGSMU1("CSmsHyperLinkOperations::operator==");
    Panic(KGsmuPanicMethodBodyNotImplemented1);
    return EFalse;
    BULLSEYE_RESTORE
    }

/**
 *  @internalComponent
 *  
 *  Identifies whether the message type or version supports this operation
 *  
 *  @leave
 *  If the message type or version does not support this operation.
 */
void CSmsHyperLinkOperations::ValidateOperationL() const
	{
	LOGGSMU1("CSmsHyperLinkOperations::ValidateOperationL()");

	if (iMessage.Version() < CSmsMessage::ESmsMessageV1)
	    {
	    LOGGSMU2("CSmsHyperLinkOperations::AddHyperLinkL, Operation not supported, Msg Version %d", iMessage.Version());
	    User::Leave(KErrNotSupported);
	    }

	if (!MessageTypeSupported())
	    {
	    LOGGSMU2("CSmsHyperLinkOperations::AddHyperLinkL, Operation not supported by this PDU type, type = %d", iMessage.Type());
	    User::Leave(KErrNotSupported);
	    }
	} // CSmsHyperLinkOperations::ValidateOperationL


/**
 *  @publishedAll
 *  
 *  This method adds a HyperLink to the CSmsMessage.
 *  
 *  @param aPosition
 *  The absolute character position within the message text.
 *  @param aTitle
 *  An integer representation of the number of characters in the hyperlink title.
 *  @param URL Length
 *  An integer representation of the number of characters in the URL.
 *  @leave KErrNotSupported
 *  If the message version or type does not support hyperlinks.
 *  @capability None
 */
EXPORT_C void CSmsHyperLinkOperations::AddHyperLinkL(TUint aPosition, TUint8 aTitleLength,  TUint8  aURLLength) const
	{
	LOGGSMU1("CSmsHyperLinkOperations::AddHyperLinkL()");

	ValidateOperationL();

	CSmsMessageAdditionalAttributes& additionalAttributes = *((CSmsMessageAdditionalAttributes*) iMessage.AdditionalInfo());

	TBuf8 <4> buffer;
	buffer.SetLength(4);
	buffer[0] = (TUint8) (aPosition >> 8); // lowest octet contains the
	buffer[1] = (TUint8)  aPosition;       // most significant bits
	buffer[2] = aTitleLength;
	buffer[3] = aURLLength;

	CSmsInformationElement* iE = CSmsInformationElement::NewL(CSmsInformationElement::ESmsHyperLinkFormat, buffer);
	CleanupStack::PushL(iE);
	additionalAttributes.AddControlInformationElementL(iE);
	CleanupStack::Pop(iE);
	} // CSmsHyperLinkOperations::AddHyperLinkL


/**
 *  @publishedAll
 *  
 *  Returns the number of Hyper Link IEs contained in this message.
 *  @leave KErrNotSupported
 *  If this functionality is not supported by this version or type of message.
 *  @return
 *  The number of hyper links contained in this message.
 *  @capability None
 */
EXPORT_C TUint CSmsHyperLinkOperations::NumberOfHyperLinksL() const
	{
	LOGGSMU1("CSmsHyperLinkOperations::NumberOfHyperLinks");

	ValidateOperationL();

	TUint numberOfHyperLinks = 0;

	TSmsInformationElementCategories::TInformationElementCategory category;

	if (TSmsInformationElementCategories::GetCategoryDefinition(CSmsInformationElement::ESmsHyperLinkFormat, category) == EFalse)
			{
		User::Leave(KErrArgument);
		}

	CSmsMessageAdditionalAttributes* additionalAttributes = (CSmsMessageAdditionalAttributes*) iMessage.AdditionalInfo();
	TUint numberOfElements = additionalAttributes->NumberOfControlInformationElements(category);

	for (TUint i = 0; i < numberOfElements; i++)
	    {
	    CSmsInformationElement::TSmsInformationElementIdentifier id = CSmsInformationElement::ESmsIEMaximum;
	    
	    // If function leaves, allow the error to propagate upwards
	    id = additionalAttributes->GetControlInformationElementL(category, i).Identifier();

	    if (id == CSmsInformationElement::ESmsHyperLinkFormat)
	        {
	        numberOfHyperLinks++;
	        }
	    }

	return numberOfHyperLinks;
	} // CSmsHyperLinkOperations::NumberOfHyperLinksL


/**
 *  @publishedAll
 *  
 *  Copies the attributes of the specified hyperlink.
 *  aIndex has the range [0..[n-1]] where n is the
 *  value returned by NumberOfHyperLinks()
 *  
 *  @param aIndex
 *  Identifies the hyperlink to extract. Has the range [0..[n-1]]
 *  where n is the value returned by NumberOfHyperLinks().
 *  @param &aPosition
 *  The absolute character position within the message text.
 *  @param &aTitleLength
 *  An integer representation of the number of characters in the hyperlink title.
 *  @param &aURLLength
 *  An integer representation of the number of characters in the URL.
 *  @leave KErrNotSupported
 *  If the message version or type does not support hyperlinks.
 *  @leave KErrArgument
 *  If aIndex is out of range.
 *  @capability None
 */
EXPORT_C void  CSmsHyperLinkOperations::CopyHyperLinkAtIndexL(TUint aIndex, TUint& aPosition, TUint8& aTitleLength,  TUint8&  aURLLength) const
	{
	LOGGSMU2("CSmsHyperLinkOperations::CopyHyperLinkAtIndexL(): aIndex=%d", aIndex);

	ValidateOperationL();

	TUint numberOfHyperLinks = 0;

	TSmsInformationElementCategories::TInformationElementCategory category;
	if (TSmsInformationElementCategories::GetCategoryDefinition(CSmsInformationElement::ESmsHyperLinkFormat, category) == EFalse)
		{
		User::Leave(KErrArgument);
		}

	CSmsMessageAdditionalAttributes* additionalAttributes = (CSmsMessageAdditionalAttributes*) iMessage.AdditionalInfo();
	TUint numberOfElements = additionalAttributes->NumberOfControlInformationElements(category);

	if (aIndex >= numberOfElements)
	    {
	    User::Leave(KErrArgument);
	    }

	for (TUint i = 0; i < numberOfElements; i++)
	    {
	    CSmsInformationElement::TSmsInformationElementIdentifier id = CSmsInformationElement::ESmsIEMaximum;
	    
	    // If function leaves, allow the error to propagate upwards
	    id = additionalAttributes->GetControlInformationElementL(category, i).Identifier();

	    if ((id == CSmsInformationElement::ESmsHyperLinkFormat) &&
	        (numberOfHyperLinks++ == aIndex))
	        {
	        aPosition     = (additionalAttributes->GetControlInformationElementL(category, i).Data()[0] << 8);
	        aPosition    +=  additionalAttributes->GetControlInformationElementL(category, i).Data()[1];

	        aTitleLength = additionalAttributes->GetControlInformationElementL(category, i).Data()[2];
	        aURLLength   = additionalAttributes->GetControlInformationElementL(category, i).Data()[3];
	        break;
	        }
	    }
	} // CSmsHyperLinkOperations::CopyHyperLinkAtIndexL


/**
 *  @publishedAll
 *  
 *  Removes all hyper Link IEs contained in this message.
 *  
 *  @leave KErrNotSupported
 *  If the message version or type does not support hyperlinks.
 *  @capability None
 */
EXPORT_C void  CSmsHyperLinkOperations::RemoveAllHyperLinksL() const
	{
	LOGGSMU1("CSmsHyperLinkOperations::RemoveAllHyperLinks");

	ValidateOperationL();

	TSmsInformationElementCategories::TInformationElementCategory category;

	if (TSmsInformationElementCategories::GetCategoryDefinition(CSmsInformationElement::ESmsHyperLinkFormat, category) == EFalse)
		{
		User::Leave(KErrArgument);
		}

	CSmsMessageAdditionalAttributes* additionalAttributes = (CSmsMessageAdditionalAttributes*) iMessage.AdditionalInfo();
	TUint i = additionalAttributes->NumberOfControlInformationElements(category);

	while (i-- != 0)
	    {
	    CSmsInformationElement::TSmsInformationElementIdentifier id = CSmsInformationElement::ESmsIEMaximum;
	    
	    // If function leaves, allow the error to propagate upwards
	    id = additionalAttributes->GetControlInformationElementL(category, i).Identifier();

	    if (id == CSmsInformationElement::ESmsHyperLinkFormat)
	        {
	        additionalAttributes->DeleteControlInformationElement(category, i);
	        }
	    }
	} // CSmsHyperLinkOperations::RemoveAllHyperLinksL


/**
 *  @publishedAll
 *  
 *  Removes the indexed hyperlink from this message.
 *  
 *  @param aIndex
 *  aIndex has the range [0..[n-1]] where n is the
 *  value returned by NumberOfHyperLinks()
 *  @leave KErrNotSupported
 *  If the message version or type does not support hyperlinks.
 *  @leave KErrArgument
 *  If the index is out of range.
 *  @capability None
 */
EXPORT_C void  CSmsHyperLinkOperations::RemoveHyperLinkL(TUint aIndex) const
	{
	LOGGSMU1("CSmsHyperLinkOperations::RemoveHyperLink");

	ValidateOperationL();

	TUint numberOfHyperLinks = 0;

	TSmsInformationElementCategories::TInformationElementCategory category;

	if (TSmsInformationElementCategories::GetCategoryDefinition(CSmsInformationElement::ESmsHyperLinkFormat, category) == EFalse)
		{
		User::Leave(KErrArgument);
		}

	CSmsMessageAdditionalAttributes* additionalAttributes = (CSmsMessageAdditionalAttributes*) iMessage.AdditionalInfo();
	TUint numberOfElements = additionalAttributes->NumberOfControlInformationElements(category);

	if (aIndex >= numberOfElements)
	    {
	    User::Leave(KErrArgument);
	    }

	for (TUint i = 0; i < numberOfElements; i++)
	    {
	    CSmsInformationElement::TSmsInformationElementIdentifier id = CSmsInformationElement::ESmsIEMaximum;

	    // If function leaves, allow the error to propagate upwards
	    id = additionalAttributes->GetControlInformationElementL(category, i).Identifier();

	    if ((id == CSmsInformationElement::ESmsHyperLinkFormat) &&
	        (numberOfHyperLinks++ == aIndex))
	        {
	        additionalAttributes->DeleteControlInformationElement(category, i);
	        break;
	        }
	    }
	} // CSmsHyperLinkOperations::RemoveHyperLinkL


/**
 *  @internalComponent
 *  
 *  Prevent clients from using the assignment operator by including it in the class definition
 *  but making it protected and not exporting it.
 *  
 *  @capability None
 */
void CSmsReplyAddressOperations::operator=(const CSmsReplyAddressOperations&)
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF    
    LOGGSMU1("CSmsReplyAddressOperations::operator=");
    Panic(KGsmuPanicMethodBodyNotImplemented1);
    BULLSEYE_RESTORE
    }

/**
 *  @internalComponent
 *  
 *  Prevent clients from using the equality operator by including it in the class definition
 *  but making it protected and not exporting it.
 *  
 *  @capability None
 */
TBool CSmsReplyAddressOperations::operator==(const CSmsReplyAddressOperations&)
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF    
    LOGGSMU1("CSmsReplyAddressOperations::operator==");
    Panic(KGsmuPanicMethodBodyNotImplemented1);
    return EFalse;
    BULLSEYE_RESTORE
    }

/**
 *  @internalComponent
 *  
 *  Identifies whether the message type or version supports this operation
 *  
 *  @leave
 *  If the message type or version does not support this operation.
 */
void CSmsReplyAddressOperations::ValidateOperationL() const
	{
	LOGGSMU1("CSmsReplyAddressOperations::ValidateOperationL()");

	if (iMessage.Version() < CSmsMessage::ESmsMessageV1)
	    {
	    LOGGSMU2("CSmsReplyAddressOperations::AddReplyAddressL, Operation not supported, Msg Version %d", iMessage.Version());
	    User::Leave(KErrNotSupported);
	    }

	if (!MessageTypeSupported())
	    {
	    LOGGSMU2("CSmsReplyAddressOperations::AddReplyAddressL, Operation not supported by this PDU type, type = %d", iMessage.Type());
	    User::Leave(KErrNotSupported);
	    }
	} // CSmsReplyAddressOperations::ValidateOperationL


/**
 *  @publishedAll
 *  
 *  Adds a reply address information element.
 *  
 *  @param aAddress
 *  The international number to be used in the reply address.
 *  @leave KErrNotSupported
 *  If the message version or type does not support a reply addresses.
 *  @leave KErrAlreadyExists
 *  If the message already contains a reply address.
 *  @capability None
 */
EXPORT_C void  CSmsReplyAddressOperations::AddReplyAddressL(const TDesC& aAddress) const
	{
	LOGGSMU1("CSmsReplyAddressOperations::AddReplyAddressL()");

	ValidateOperationL();

	if (ContainsReplyAddressIEL())
	    {
	    LOGGSMU1("CSmsReplyAddressOperations::AddReplyAddressL, Already Exists");
	    User::Leave(KErrAlreadyExists);
	    }

	CSmsMessageAdditionalAttributes& additionalAttributes = *((CSmsMessageAdditionalAttributes*) iMessage.AdditionalInfo());

	CSmsAddress* address = CSmsAddress::NewL(iCharacterSetConverter,iFs); // needed for encoding the address into the IE.
	CleanupStack::PushL(address);
	address->SetAddressL(aAddress);
	TBuf8<CSmsAddress::KSmsAddressMaxAddressLength> addressBuffer;
	addressBuffer.SetLength(CSmsAddress::KSmsAddressMaxAddressLength);
	TUint8* startPtr = &addressBuffer[0];
	TUint8* endPtr = address->EncodeL(startPtr);
	TInt length = 0;
	(endPtr > startPtr) ? (length = endPtr - startPtr) : (length = startPtr - endPtr);
	addressBuffer.SetLength(length);
	CleanupStack::PopAndDestroy(address);

	CSmsInformationElement* iE = CSmsInformationElement::NewL(CSmsInformationElement::ESmsReplyAddressFormat, addressBuffer);
	CleanupStack::PushL(iE);
	additionalAttributes.AddControlInformationElementL(iE);
	CleanupStack::Pop(iE);
	} // CSmsReplyAddressOperations::AddReplyAddressL


/**
 *  @publishedAll
 *  
 *  Adds a reply address information element.
 *  
 *  @param aAddress
 *  Sets To and From addresses in ETSI format.
 *  @leave KErrNotSupported
 *  If the message version or type does not support a reply addresses.
 *  @leave KErrAlreadyExists
 *  If the message already contains a reply address.
 *  @capability None
 */
EXPORT_C void  CSmsReplyAddressOperations::AddParsedReplyAddressL(const TGsmSmsTelNumber& aParsedAddress) const
	{
	LOGGSMU1("CSmsReplyAddressOperations::AddParsedReplyAddressL");

	ValidateOperationL();

	if (ContainsReplyAddressIEL())
	    {
	    LOGGSMU1("CSmsReplyAddressOperations::AddParsedReplyAddressL, Already Exists");
	    User::Leave(KErrAlreadyExists);
	    }

	CSmsMessageAdditionalAttributes& additionalAttributes = *((CSmsMessageAdditionalAttributes*) iMessage.AdditionalInfo());

	CSmsAddress* address = CSmsAddress::NewL(iCharacterSetConverter,iFs);
	CleanupStack::PushL(address);
	address->SetParsedAddressL(aParsedAddress);
	TBuf8<CSmsAddress::KSmsAddressMaxAddressLength> addressBuffer;
	addressBuffer.SetLength(CSmsAddress::KSmsAddressMaxAddressLength);
	TUint8* startPtr = &addressBuffer[0];
	TUint8* endPtr = address->EncodeL(startPtr);
	TInt length = 0;
	(endPtr > startPtr) ? (length = endPtr - startPtr) : (length = startPtr - endPtr);
	addressBuffer.SetLength(length);
	CleanupStack::PopAndDestroy(address);

	CSmsInformationElement* iE = CSmsInformationElement::NewL(CSmsInformationElement::ESmsReplyAddressFormat, addressBuffer);
	CleanupStack::PushL(iE);
	additionalAttributes.AddControlInformationElementL(iE);
	CleanupStack::Pop(iE);
	} // CSmsReplyAddressOperations::AddParsedReplyAddressL


/**
 *  @publishedAll
 *  
 *  Identifies whether a reply address has already been added to this
 *  CSmsMessage using the CSmsReplyAddressOperations interface.
 *  
 *  @return
 *  True if the reply address has already been added using this interface.
 *  @leave KErrNotSupported
 *  If the message version or type does not support this query.
 *  @capability None
 */
EXPORT_C TBool CSmsReplyAddressOperations::ContainsReplyAddressIEL() const
	{
	LOGGSMU1("CSmsReplyAddressOperations::ContainsReplyAddressIEL()");

	TBool rc = EFalse;

	ValidateOperationL();

	TSmsInformationElementCategories::TInformationElementCategory category;

	if (TSmsInformationElementCategories::GetCategoryDefinition(CSmsInformationElement::ESmsReplyAddressFormat, category) == EFalse)
		{
		User::Leave(KErrArgument);
		}

	CSmsMessageAdditionalAttributes* additionalAttributes = (CSmsMessageAdditionalAttributes*) iMessage.AdditionalInfo();
	TUint numberOfElements = additionalAttributes->NumberOfControlInformationElements(category);

	for (TUint i = 0; i < numberOfElements; i++)
	    {
	    CSmsInformationElement::TSmsInformationElementIdentifier id = CSmsInformationElement::ESmsIEMaximum;
	    
	    // If function leaves, allow the error to propagate upwards
	    id = additionalAttributes->GetControlInformationElementL(category, i).Identifier();

	    if (id == CSmsInformationElement::ESmsReplyAddressFormat)
	        {
	        rc = ETrue;
	        break;
	        }
	    }

	return rc;
	} // CSmsReplyAddressOperations::ContainsReplyAddressIEL


/**
 *  @publishedAll
 *  
 *  Returns the reply address as an international number
 *  
 *  @return
 *  The reply address as an international number.
 *  @leave KErrNotSupported
 *  If the message version or type does not support this query.
 *  @leave KErrNotFound
 *  If the message does not contain a reply address information element.
 *  @leave KErrCorrupt
 *  If the reply address is corrupt
 *  @capability None
 */
EXPORT_C HBufC* CSmsReplyAddressOperations::GetReplyAddressL() const
	{
	LOGGSMU1("CSmsReplyAddressOperations::GetReplyAddressL");

	ValidateOperationL();

	TBool found = EFalse;

	CSmsAddress* address = CSmsAddress::NewL(iCharacterSetConverter,iFs);
	CleanupStack::PushL(address);

	TSmsInformationElementCategories::TInformationElementCategory category;

	if (TSmsInformationElementCategories::GetCategoryDefinition(CSmsInformationElement::ESmsReplyAddressFormat, category) == EFalse)
		{
		User::Leave(KErrArgument);
		}

	CSmsMessageAdditionalAttributes* additionalAttributes = (CSmsMessageAdditionalAttributes*) iMessage.AdditionalInfo();
	TUint numberOfElements = additionalAttributes->NumberOfControlInformationElements(category);

	for (TUint i = 0; i < numberOfElements; i++)
	    {
	    CSmsInformationElement::TSmsInformationElementIdentifier id = CSmsInformationElement::ESmsIEMaximum;
	    
	    // If function leaves, allow the error to propagate upwards
	    id = additionalAttributes->GetControlInformationElementL(category, i).Identifier();

	    if (id == CSmsInformationElement::ESmsReplyAddressFormat)
	        {
	        TGsmuLex8 encodedAddress(additionalAttributes->GetControlInformationElementL(category, i).Data());
	        TRAPD(err,address->DecodeL(encodedAddress));

	        if (err == KErrNone)
	            {
	            found = ETrue;
	            break;
	            }
	        else
	            {
	            User::Leave(KErrCorrupt);
	            }
	        }
	    }

	HBufC* buf = NULL;
	if (found)
	    {
	    buf = HBufC::NewL(TGsmSmsTelNumberMaxLen); // 20 characters preceeded by '+'
	    *buf = address->Address();
	    }
	else
	    {
	    User::Leave(KErrNotFound);
	    }

	CleanupStack::PopAndDestroy(address);

	return buf;
	} // CSmsReplyAddressOperations::GetReplyAddressL


/**
 *  @publishedAll
 *  
 *  Returns the reply address in ETSI format.
 *  
 *  @return
 *  True, if the CSmsMessage contains a reply address.
 *  False, otherwise
 *  @leave KErrNotSupported
 *  If the message version or type does not support this query.
 *  @leave KErrCorrupt
 *  If the reply address is corrupt
 *  @capability None
 */
EXPORT_C TInt  CSmsReplyAddressOperations::GetParsedReplyAddressL(TGsmSmsTelNumber& aParsedAddress) const
	{
	LOGGSMU1("CSmsReplyAddressOperations::GetParsedReplyAddressL");

	ValidateOperationL();

	TBool rc = EFalse;

	CSmsAddress* address = CSmsAddress::NewL(iCharacterSetConverter,iFs);
	CleanupStack::PushL(address);

	TSmsInformationElementCategories::TInformationElementCategory category;

	if (TSmsInformationElementCategories::GetCategoryDefinition(CSmsInformationElement::ESmsReplyAddressFormat, category) == EFalse)
		{
		User::Leave(KErrArgument);
		}

	CSmsMessageAdditionalAttributes* additionalAttributes = (CSmsMessageAdditionalAttributes*) iMessage.AdditionalInfo();
	TUint numberOfElements = additionalAttributes->NumberOfControlInformationElements(category);

	for (TUint i = 0; i < numberOfElements; i++)
	    {
	    CSmsInformationElement::TSmsInformationElementIdentifier id = CSmsInformationElement::ESmsIEMaximum;
	    
	    // If function leaves, allow the error to propagate upwards
	    id = additionalAttributes->GetControlInformationElementL(category, i).Identifier();

	    // CSmsAddress is used here for encoding the reply address into an IE.
	    // It also used to encode the source / destination address in the CSmsMessage.
	    // CSmsAddress::DecodeL expects internally that alphanumeric addresses will be size
	    // CSmsAddress::KSmsAddressMaxAddressValueLength=10.
	    // Need to add padding bytes to bring the address field up to the maximum size.
	    if (id == CSmsInformationElement::ESmsReplyAddressFormat)
	        {
	        TBuf8<CSmsAddress::KSmsAddressMaxAddressLength> data;
	        data = additionalAttributes->GetControlInformationElementL(category, i).Data();
	        TUint8 actualDataLength = data.Length();
	        data.SetLength(CSmsAddress::KSmsAddressMaxAddressLength);
	        for (TUint8 j = actualDataLength; j < CSmsAddress::KSmsAddressMaxAddressLength; j++)
	            {
	            data[j] = 0;
	            }

	        TGsmuLex8 encodedAddress(data);
	        TRAPD(err,address->DecodeL(encodedAddress));
	        if (err==KErrNone)
	            {
	            address->ParsedAddress(aParsedAddress);
	            rc = ETrue;
	            break;
	            }
	        else
	            {
	            User::Leave(KErrCorrupt);
	            }
	        }
	    }

	CleanupStack::PopAndDestroy(address);

	return rc;
	} // CSmsReplyAddressOperations::GetParsedReplyAddressL


/**
 *  @publishedAll
 *  
 *  Removes the reply address information element.
 *  
 *  @leave KErrNotSupported
 *  If the message version or type does not support this query.
 *  @capability None
 */
EXPORT_C void  CSmsReplyAddressOperations::RemoveReplyAddressL() const
	{
	LOGGSMU1("CSmsReplyAddressOperations::RemoveReplyAddress");

	ValidateOperationL();

	TSmsInformationElementCategories::TInformationElementCategory category;

	if (TSmsInformationElementCategories::GetCategoryDefinition(CSmsInformationElement::ESmsReplyAddressFormat, category) == EFalse)
		{
		User::Leave(KErrArgument);
		}

	CSmsMessageAdditionalAttributes* additionalAttributes = (CSmsMessageAdditionalAttributes*) iMessage.AdditionalInfo();
	TUint numberOfElements = additionalAttributes->NumberOfControlInformationElements(category);

	for (TInt i = 0; i < numberOfElements; i++)
	    {
	    CSmsInformationElement::TSmsInformationElementIdentifier id = CSmsInformationElement::ESmsIEMaximum;
	    
	    // If function leaves, allow the error to propagate upwards
	    id = additionalAttributes->GetControlInformationElementL(category, i).Identifier();

	    if (id == CSmsInformationElement::ESmsReplyAddressFormat)
	        {
	        additionalAttributes->DeleteControlInformationElement(category, i);
	        break;
	        }
	    }
	} // CSmsReplyAddressOperations::RemoveReplyAddressL


CSmsReplyAddressOperations::CSmsReplyAddressOperations(CSmsInformationElement::TSmsInformationElementIdentifier aId,  CSmsMessage& aMessage,
                                                       CCnvCharacterSetConverter& aCharacterSetConverter, RFs& aFs) : CSmsCtrlOperation(aId, aMessage),
                                                       iCharacterSetConverter(aCharacterSetConverter), iFs(aFs)
	{
	//NOP
	} // CSmsReplyAddressOperations::CSmsReplyAddressOperations


/**
 *  @internalComponent
 *  
 *  Identifies whether the message type or version supports this operation
 *  
 *  @leave
 *  If the message type or version does not support this operation.
 */
void CSmsSpecialSMSMessageOperations::ValidateOperationL() const
	{
	LOGGSMU1("CSmsSpecialSMSMessageOperations::ValidateOperationL()");

	if (iMessage.Version() < CSmsMessage::ESmsMessageV1)
	    {
	    LOGGSMU2("CSmsSpecialSMSMessageOperations::AddSpecialMessageIndicationL, Operation not supported, version %d", iMessage.Version());
	    User::Leave(KErrNotSupported);
	    }

	if (!MessageTypeSupported())
	    {
	    LOGGSMU2("CSmsSpecialSMSMessageOperations::AddSpecialMessageIndicationL, Operation not supported by this PDU type, type = %d", iMessage.Type());
	    User::Leave(KErrNotSupported);
	    }
	} // CSmsSpecialSMSMessageOperations::ValidateOperationL


/**
 *  @internalComponent
 *  
 *  Prevent clients from using the assignment operator by including it in the class definition
 *  but making it protected and not exporting it.
 *  
 *  @capability None
 */
void CSmsSpecialSMSMessageOperations::operator=(const CSmsSpecialSMSMessageOperations&)
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF    
    LOGGSMU1("CSmsSpecialSMSMessageOperations::operator=");
    Panic(KGsmuPanicMethodBodyNotImplemented1);
    BULLSEYE_RESTORE
    }

/**
 *  @internalComponent
 *  
 *  Prevent clients from using the equality operator by including it in the class definition
 *  but making it protected and not exporting it.
 *  
 *  @capability None
 */
TBool CSmsSpecialSMSMessageOperations::operator==(const CSmsSpecialSMSMessageOperations&)
	{
	LOGGSMU1("CSmsSpecialSMSMessageOperations::operator==");
	Panic(KGsmuPanicMethodBodyNotImplemented1);
	return EFalse;
	} // CSmsSpecialSMSMessageOperations::operator


/**
 *  @publishedAll
 *  
 *  Either adds a new or updates an existing special message indication information element.
 *  
 *  @param aStore
 *  Indicates whether or not the message shall be stored.
 *  @param aMessageIndicationType
 *  Indicates the basic message type
 *  @param aExtendedType
 *  Indicates the extended message type
 *  @param aProfile
 *  Indicates the profile ID of the Multiple Subscriber Profile
 *  @param aMessageCount
 *  Indicates the number of messages of the type specified in Octet 1 that are waiting
 *  @leave KErrNotSupported
 *  If the message version or type does not support this query.
 *  @capability None
 */
EXPORT_C void CSmsSpecialSMSMessageOperations::AddSpecialMessageIndicationL(TBool aStore,
                                                                            TSmsMessageIndicationType aMessageIndicationType,
                                                                            TExtendedSmsIndicationType aExtendedType,
                                                                            TSmsMessageProfileType aProfile,
                                                                            TUint8 aMessageCount) const
	{
	LOGGSMU1("CSmsSpecialSMSMessageOperations::AddSpecialMessageIndicationL");

	ValidateOperationL();

	CSmsUserData& userData = iMessage.SmsPDU().UserData();

	TBuf8<2> data;
	data.SetLength(2);

	data[0] = ( ((TUint8) aMessageIndicationType) +
	            ((TUint8) (aExtendedType << 2))   +
	            ((TUint8) (aProfile      << 5))   +
	            ((TUint8) (aStore        << 7)) );

	data[1] = aMessageCount;

	CArrayFixFlat<TInt>* indices = new(ELeave) CArrayFixFlat<TInt>(4);
	CleanupStack::PushL(indices);
	userData.InformationElementIndicesL(CSmsInformationElement::ESmsIEISpecialSMSMessageIndication, *indices);

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

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

	if (found == EFalse)
	    {
	    userData.AddInformationElementL(CSmsInformationElement::ESmsIEISpecialSMSMessageIndication,data);
	    }
	} // CSmsSpecialSMSMessageOperations::AddSpecialMessageIndicationL


/**
 *  @publishedAll
 *  
 *  Gets a count of the number of special message indication information elements which
 *  are stored inside the user data.
 *  
 *  @return The number of special message indication information elements which
 *  are stored inside the user data.
 *  @leave KErrNotSupported
 *  If the message version or type does not support this query.
 *  @capability None
 */
EXPORT_C TUint CSmsSpecialSMSMessageOperations::GetCountOfSpecialMessageIndicationsL() const
	{
	LOGGSMU1("CSmsSpecialSMSMessageOperations::GetCountOfSpecialMessageIndicationsL()");

	ValidateOperationL();

	CSmsUserData& userData = iMessage.SmsPDU().UserData();

	CArrayFixFlat<TInt>* indices = new (ELeave) CArrayFixFlat<TInt>(8);
	CleanupStack::PushL(indices);
	userData.InformationElementIndicesL(CSmsInformationElement::ESmsIEISpecialSMSMessageIndication,*indices);
	TUint  count = indices->Count();
	CleanupStack::PopAndDestroy(indices);

	return count;
	} // CSmsSpecialSMSMessageOperations::GetCountOfSpecialMessageIndicationsL


/**
 *  @publishedAll
 *  
 *  Gets the attributes of the Special Message Indication specified by aIndex.
 *  
 *  @param aIndex
 *  aIndex is a value less than GetCountOfSpecialMessageIndications().
 *  @param aStore
 *  Indicates whether or not the message shall be stored.
 *  @param aMessageIndicationType
 *  Indicates the basic message type
 *  @param aExtendedType
 *  Indicates the extended message type
 *  @param aProfile
 *  Indicates the profile ID of the Multiple Subscriber Profile
 *  @param aMessageCount
 *  Indicates the number of messages of the type specified in Octet 1 that are waiting
 *  @leave KErrNotSupported
 *  If the message version or type does not support this query.
 *  @leave KErrArgument
 *  If aIndex is >= GetCountOfSpecialMessageIndications()
 *  @capability None
 */
EXPORT_C void CSmsSpecialSMSMessageOperations::GetMessageIndicationIEL(TUint aIndex,
                                                                       TBool& aStore,
                                                                       TSmsMessageIndicationType& aMessageIndicationType,
                                                                       TExtendedSmsIndicationType& aExtendedType,
                                                                       TSmsMessageProfileType&  aProfile,
                                                                       TUint8& aMessageCount) const
	{
	LOGGSMU1("CSmsSpecialSMSMessageOperations::GetMessageIndicationIEL()");

	ValidateOperationL();

	CSmsUserData& userData = iMessage.SmsPDU().UserData();

	CArrayFixFlat<TInt>* indices = new (ELeave) CArrayFixFlat<TInt>(8);
	CleanupStack::PushL(indices);
	userData.InformationElementIndicesL(CSmsInformationElement::ESmsIEISpecialSMSMessageIndication,*indices);

	if (aIndex < indices->Count())
	    {
	    CSmsInformationElement& informationElement = userData.InformationElement((*indices)[aIndex]);

	    aMessageIndicationType =  (TSmsMessageIndicationType)   (informationElement.Data()[0]       & 0x03);
	    aExtendedType          =  (TExtendedSmsIndicationType) ((informationElement.Data()[0] >> 2) & 0x07);
	    aProfile               =  (TSmsMessageProfileType)     ((informationElement.Data()[0] >> 5) & 0x03);
	    aStore                 =  (TBool)                      ((informationElement.Data()[0] >> 7) & 0x01);
	    aMessageCount          =  (TUint8)                      (informationElement.Data()[1]);
	    }
	else
	    {
	    User::Leave(KErrArgument);
	    }

	CleanupStack::PopAndDestroy(indices);
	} // CSmsSpecialSMSMessageOperations::GetMessageIndicationIEL


/**
 *  @publishedAll
 *  
 *  Removes the Special Message Indication specified by the input attributes, provided it exists.
 *  
 *  @param aMessageIndicationType
 *  Indicates the basic message type
 *  @param aExtendedType
 *  Indicates the extended message type
 *  @leave KErrNotSupported
 *  If the message version or type does not support this query.
 *  @capability None
 */
EXPORT_C void CSmsSpecialSMSMessageOperations::RemoveSpecialMessageIndicationL(TSmsMessageIndicationType aMessageIndicationType, TExtendedSmsIndicationType aExtendedType) const
	{
	LOGGSMU1("CSmsSpecialSMSMessageOperations::RemoveAllSpecialMessageIndications");

	ValidateOperationL();

	CSmsUserData& userData = iMessage.SmsPDU().UserData();

	CArrayFixFlat<TInt>* indices = new (ELeave) CArrayFixFlat<TInt>(8);
	CleanupStack::PushL(indices);
	userData.InformationElementIndicesL(CSmsInformationElement::ESmsIEISpecialSMSMessageIndication,*indices);

	TUint i = indices->Count();

	while (i-- != 0)
	    {
	    CSmsInformationElement& informationElement = userData.InformationElement((*indices)[i]);

	    TSmsMessageIndicationType          type = ((TSmsMessageIndicationType)   (informationElement.Data()[0]       & 0x03));
	    TExtendedSmsIndicationType extendedType =  (TExtendedSmsIndicationType) ((informationElement.Data()[0] >> 2) & 0x07);

	    if ( (aMessageIndicationType == type) &&
	         (aExtendedType == extendedType) )
	        {
	        userData.RemoveInformationElement((*indices)[i]);
	        break;
	        }
	    }

	CleanupStack::PopAndDestroy(indices);
	} // CSmsSpecialSMSMessageOperations::RemoveSpecialMessageIndicationL


/**
 *  @publishedAll
 *  
 *  Removes all Special Message Indications Information Elements contained in the message.
 *  
 *  @leave KErrNotSupported
 *  If the message version or type does not support this query.
 *  @capability None
 */
EXPORT_C void CSmsSpecialSMSMessageOperations::RemoveAllSpecialMessageIndicationsL() const
	{
	LOGGSMU1("CSmsSpecialSMSMessageOperations::RemoveAllSpecialMessageIndicationsL");

	ValidateOperationL();

	CSmsUserData& userData = iMessage.SmsPDU().UserData();

	CArrayFixFlat<TInt>* indices = new (ELeave) CArrayFixFlat<TInt>(8);
	CleanupStack::PushL(indices);
	userData.InformationElementIndicesL(CSmsInformationElement::ESmsIEISpecialSMSMessageIndication,*indices);

	TUint i = indices->Count();

	while (i-- != 0)
	   {
	   userData.RemoveInformationElement((*indices)[i]);
	   }

	CleanupStack::PopAndDestroy(indices);
	} // CSmsSpecialSMSMessageOperations::RemoveAllSpecialMessageIndicationsL


CSmsSpecialSMSMessageOperations::CSmsSpecialSMSMessageOperations(CSmsInformationElement::TSmsInformationElementIdentifier aId, CSmsMessage& aMessage) : CSmsCtrlOperation(aId, aMessage)
	{
	//NOP
	} // CSmsSpecialSMSMessageOperations::CSmsSpecialSMSMessageOperations


/**
 *  @internalComponent
 *  
 *  Prevent clients from using the assignment operator by including it in the class definition
 *  but making it protected and not exporting it.
 *  
 *  @capability None
 */
void CSmsEnhancedVoiceMailOperations::operator=(const CSmsEnhancedVoiceMailOperations&)
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF    
    LOGGSMU1("CSmsEnhancedVoiceMailOperations::operator=");
    Panic(KGsmuPanicMethodBodyNotImplemented1);
    BULLSEYE_RESTORE
    }

/**
 *  @internalComponent
 *  
 *  Prevent clients from using the equality operator by including it in the class definition
 *  but making it protected and not exporting it.
 *  
 *  @capability None
 */
TBool CSmsEnhancedVoiceMailOperations::operator==(const CSmsEnhancedVoiceMailOperations&)
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF    
    LOGGSMU1("CSmsEnhancedVoiceMailOperations::operator==");
    Panic(KGsmuPanicMethodBodyNotImplemented1);
    return EFalse;
    BULLSEYE_RESTORE
    }

/**
 *  @internalComponent
 *  
 *  Identifies whether the message type or version supports this operation
 *  
 *  @leave
 *  If the message type or version does not support this operation.
 */
void CSmsEnhancedVoiceMailOperations::ValidateOperationL() const
	{
	LOGGSMU1("CSmsEnhancedVoiceMailOperations::ValidateOperationL()");

	if (iMessage.Version() < CSmsMessage::ESmsMessageV1)
	    {
	    LOGGSMU2("CSmsEnhancedVoiceMailInformation::AddEnhancedVoiceMailIEL, Operation not supported, Msg Version %d", iMessage.Version());
	    User::Leave(KErrNotSupported);
	    }

	if (!MessageTypeSupported())
	    {
	    LOGGSMU2("CSmsSpecialSMSMessageOperations::AddEnhancedVoiceMailIEL, Operation not supported by this PDU type, type = %d", iMessage.Type());
	    User::Leave(KErrNotSupported);
	    }
	} // CSmsEnhancedVoiceMailOperations::ValidateOperationL


/**
 *  @publishedAll
 *  
 *  Adds an Enhanced Voice Mail Information Element to the CSmsMessage.
 *  The information element can be either an Enhanced Voice Mail Notification or
 *  an Enhanced Voice Mail Delete Confirmation. Enhanced Voice Mail Notifications
 *  and Enhanced Voice Mail Delete Confirmations are abstracted in classes derived
 *  from CEnhancedVoiceMailBoxInformation.
 *  @param aEVMI
 *  A pointer to the Enhanced Voice Mail Information Element.
 *  @leave KErrNotSupported
 *  If the message version or type does not support this query.
 *  @leave KErrAlreadyExists
 *  If an Enhanced Voice Mail Information Element has already been installed.
 *  @leave KErrArgument
 *  If the input parameter contains invalid parameters.
 *  @capability None
 */
EXPORT_C void CSmsEnhancedVoiceMailOperations::AddEnhancedVoiceMailIEL(const CEnhancedVoiceMailBoxInformation& aEVMI) const
	{
	LOGGSMU1("CSmsEnhancedVoiceMailInformation::AddEnhancedVoiceMailIEL");

	ValidateOperationL();

	if (ContainsEnhancedVoiceMailIEL())
	    {
	    LOGGSMU1("CSmsEnhancedVoiceMailInformation::AddEnhancedVoiceMailIEL, Already Exists");
	    User::Leave(KErrAlreadyExists);
	    }

	CSmsMessageAdditionalAttributes& additionalAttributes = *((CSmsMessageAdditionalAttributes*) iMessage.AdditionalInfo());

	// The Enhanced Voice Mail Information Element must fit into a single PDU.
	// The maximum size for its data field is therefore
	// the maximum size of the user data minus user data header length, the ie identifier byte and
	// ie length byte.
	TBuf8<CEnhancedVoiceMailBoxInformation::KSmsMaxEnhancedVoiceMailSize> voiceMailInfo;
	voiceMailInfo.SetLength(CEnhancedVoiceMailNotification::KSmsMaxEnhancedVoiceMailSize);
	TUint8* startPtr = &voiceMailInfo[0];
	TUint8* endPtr = aEVMI.EncodeL(startPtr, iCharacterSetConverter, iFs);
	TInt length = (TInt) (endPtr - startPtr);
	voiceMailInfo.SetLength(length);

	CSmsInformationElement* iE = CSmsInformationElement::NewL(CSmsInformationElement::CSmsInformationElement::ESmsEnhanceVoiceMailInformation, voiceMailInfo);
	CleanupStack::PushL(iE);

	additionalAttributes.AddControlInformationElementL(iE);
	CleanupStack::Pop();
	} // CSmsEnhancedVoiceMailOperations::AddEnhancedVoiceMailIEL


/**
 *  @publishedAll
 *  
 *  Removes the Enhanced Voice Mail Information Element from the CSmsMessage,
 *  provided it exists.
 *  @leave KErrNotSupported
 *  If the message version or type does not support this query.
 *  @leave KErrNotFound
 *  If the CSmsMessage does not contain an Enhanced Voice Mail Information Element
 *  @capability None
 */
EXPORT_C CEnhancedVoiceMailBoxInformation* CSmsEnhancedVoiceMailOperations::RemoveEnhancedVoiceMailIEL() const
	{
	LOGGSMU1("CSmsEnhancedVoiceMailInformation::RemoveEnhancedVoiceMailIE");

	ValidateOperationL();

	CEnhancedVoiceMailBoxInformation* voiceMailBoxInfo = NULL;
	TBool deleteInformationElement = ETrue;
	
	// If function leaves, allow the error to propagate upwards
	voiceMailBoxInfo = GetEnhancedVoiceMailIEL(deleteInformationElement);

	return voiceMailBoxInfo;
	} // CSmsEnhancedVoiceMailOperations::RemoveEnhancedVoiceMailIEL


/**
 *  @publishedAll
 *  
 *  Copies the Enhanced Voice Mail Information Element contained in the CSmsMessage,
 *  provided one exists.
 *  @leave KErrNotSupported
 *  If the message version or type does not support this query.
 *  @leave KErrNotFound
 *  If the CSmsMessage does not contain an Enhanced Voice Mail Information Element to copy.
 *  @capability None
 */
EXPORT_C CEnhancedVoiceMailBoxInformation* CSmsEnhancedVoiceMailOperations::CopyEnhancedVoiceMailIEL() const
	{
	LOGGSMU1("CSmsEnhancedVoiceMailInformation::CopyEnhancedVoiceMailIEL");

	ValidateOperationL();

	CEnhancedVoiceMailBoxInformation* voiceMailBoxInfo = NULL;
	TBool deleteInformationElement = EFalse;
	
	// If function leaves, allow the error to propagate upwards
	voiceMailBoxInfo = GetEnhancedVoiceMailIEL(deleteInformationElement);

	return voiceMailBoxInfo;
	} // CSmsEnhancedVoiceMailOperations::CopyEnhancedVoiceMailIEL


/**
 *  @internalComponent
 *  
 *  Returns a copy of the Enhanced Voice Mail Information Element contained in the CSmsMessage,
 *  provided one exists. Optionally deletes the information element.
 *  @param aRemove
 *  Indicates that the information element should be deleted once its contents have been copied.
 *  @leave KErrNotFound
 *  If the CSmsMessage does not contain an Enhanced Voice Mail Information Element to copy.
 */
CEnhancedVoiceMailBoxInformation* CSmsEnhancedVoiceMailOperations::GetEnhancedVoiceMailIEL(TBool aRemove) const
	{
	LOGGSMU1("CSmsEnhancedVoiceMailOperations::GetEnhancedVoiceMailIEL()");

	TBool found = EFalse;

	CEnhancedVoiceMailBoxInformation* voiceMailBoxInfo = NULL;

	TSmsInformationElementCategories::TInformationElementCategory category;

	if (TSmsInformationElementCategories::GetCategoryDefinition(CSmsInformationElement::ESmsEnhanceVoiceMailInformation, category) == EFalse)
		{
		User::Leave(KErrArgument);
		}

	CSmsMessageAdditionalAttributes* additionalAttributes = (CSmsMessageAdditionalAttributes*) iMessage.AdditionalInfo();
	TUint numberOfElements = additionalAttributes->NumberOfControlInformationElements(category);

	for (TUint i = 0; i < numberOfElements; i++)
	    {
	    CSmsInformationElement::TSmsInformationElementIdentifier id = CSmsInformationElement::ESmsIEMaximum;
	    id = additionalAttributes->GetControlInformationElementL(category, i).Identifier();

	    if (id == CSmsInformationElement::CSmsInformationElement::ESmsEnhanceVoiceMailInformation)
	        {
	        if (additionalAttributes->GetControlInformationElementL(category, i).Data()[0] & 0x01)
	            {
	            voiceMailBoxInfo = CEnhancedVoiceMailDeleteConfirmations::NewL();
	            }
	        else
	            {
	            voiceMailBoxInfo = CEnhancedVoiceMailNotification::NewL();
	            }

	        CleanupStack::PushL(voiceMailBoxInfo);
	        TGsmuLex8 encodedVoiceMailBox(additionalAttributes->GetControlInformationElementL(category, i).Data());
	        voiceMailBoxInfo->DecodeL(encodedVoiceMailBox, iCharacterSetConverter, iFs);
	        CleanupStack::Pop(voiceMailBoxInfo);

	        if (aRemove)
	            {
	            additionalAttributes->DeleteControlInformationElement(category, i);
	            }

	        found = ETrue;

	        break;
	        }
	    }

	if (found == EFalse)
	    {
	    User::Leave(KErrNotFound);
	    }

	return voiceMailBoxInfo;
	} // CSmsEnhancedVoiceMailOperations::GetEnhancedVoiceMailIEL


/**
 *  @publishedAll
 *  
 *  Determines whether the CSmsMessage contains an Enhanced Voice Mail Information Element.
 *  @leave KErrNotSupported
 *  If the message version or type does not support this query.
 *  @return
 *  True if the CSmsMessage contains an Enhanced Voice Mail Information Element.
 *  @capability None
 */
EXPORT_C TBool CSmsEnhancedVoiceMailOperations::ContainsEnhancedVoiceMailIEL() const
	{
	LOGGSMU1("CSmsEnhancedVoiceMailInformation::ContainsEnhancedVoiceMailIEL");

	ValidateOperationL();

	TBool rc = EFalse;

	TSmsInformationElementCategories::TInformationElementCategory category;

	if (TSmsInformationElementCategories::GetCategoryDefinition(CSmsInformationElement::ESmsEnhanceVoiceMailInformation, category) == EFalse)
		{
		User::Leave(KErrArgument);
		}

	CSmsMessageAdditionalAttributes* additionalAttributes = (CSmsMessageAdditionalAttributes*) iMessage.AdditionalInfo();
	TUint numberOfElements = additionalAttributes->NumberOfControlInformationElements(category);

	for (TUint i = 0; i < numberOfElements; i++)
	    {
	    CSmsInformationElement::TSmsInformationElementIdentifier id = CSmsInformationElement::ESmsIEMaximum;
	    
	    // If function leaves, allow the error to propagate upwards
	    id = additionalAttributes->GetControlInformationElementL(category, i).Identifier();

	    if (id == CSmsInformationElement::ESmsEnhanceVoiceMailInformation)
	        {
	        rc = ETrue;
	        break;
	        }
	    }

	return rc;
	} // CSmsEnhancedVoiceMailOperations::ContainsEnhancedVoiceMailIEL


/**
 *  Status Reporting.
 *  
 *  @note This should be the last thing that should be configured in CSmsMessage.
 *  If anything is changed after this then the number of PDUs might change which
 *  will affect status reporting.
 */
CSmsEnhancedVoiceMailOperations::CSmsEnhancedVoiceMailOperations(CSmsInformationElement::TSmsInformationElementIdentifier aId,  CSmsMessage& aMessage,
                                                                 CCnvCharacterSetConverter& aCharacterSetConverter, RFs& aFs) : CSmsCtrlOperation(aId, aMessage),
                                                                 iCharacterSetConverter(aCharacterSetConverter), iFs(aFs)
	{
	// NOP
	} // CSmsEnhancedVoiceMailOperations::CSmsEnhancedVoiceMailOperations


CSmsSMSCCtrlParameterOperations::CSmsSMSCCtrlParameterOperations(CSmsInformationElement::TSmsInformationElementIdentifier aId, CSmsMessage& aMessage)
: CSmsCtrlOperation(aId, aMessage)
	{
	
	} // CSmsSMSCCtrlParameterOperations::CSmsSMSCCtrlParameterOperations
	

/**
 *  @internalComponent  
 *  
 *  Prevent clients from using the assignment operator by including it in the class definition
 *  but making it protected and not exporting it.
 *  
 *  @capability None
 */
void CSmsSMSCCtrlParameterOperations::operator=(const CSmsSMSCCtrlParameterOperations&)
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF    
    LOGGSMU1("CSmsSMSCCtrlParameterOperations::operator=");
    Panic(KGsmuPanicMethodBodyNotImplemented1);
    BULLSEYE_RESTORE
    }

/**
 *  @internalComponent  
 *  
 *  Prevent clients from using the equality operator by including it in the class definition
 *  but making it protected and not exporting it.
 *  
 *  @capability None
 */
TBool CSmsSMSCCtrlParameterOperations::operator==(const CSmsSMSCCtrlParameterOperations&)
    {
    // Ignore in code coverage - not intended to be used
    BULLSEYE_OFF    
    LOGGSMU1("CSmsSMSCCtrlParameterOperations::operator==");
    Panic(KGsmuPanicMethodBodyNotImplemented1);
    return EFalse;
    BULLSEYE_RESTORE
    }

/**
 *  @internalComponent
 *  
 *  Identifies whether the message type or version supports this operation
 *  
 *  @leave
 *  If the message type or version does not support this operation.
 */
void CSmsSMSCCtrlParameterOperations::ValidateOperationL() const
	{
	if (iMessage.Version() < CSmsMessage::ESmsMessageV2)
	    {
	    LOGGSMU2("CSmsSMSCCtrlParameterOperations Operation not supported, Msg Version %d", iMessage.Version());
	    User::Leave(KErrNotSupported); 
	    }

	if (!MessageTypeSupported())
	    {
	    LOGGSMU2("CSmsSMSCCtrlParameterOperations Operation not supported by this PDU type, type = %d", iMessage.Type());
	    User::Leave(KErrNotSupported);
	    }
	} // CSmsSMSCCtrlParameterOperations::ValidateOperationL()


/**
 *  @internalComponent
 *  
 *  Checks that the octet passed to it has the valid bits set in it. If the bits which 
 *  are not supported yet are set then reset it. 
 *  This method is called by SetStatusReportL.
 *  
 *  @param aSelectiveStatus
 *  The octet which needs to be set as the Selective Status for the report.
 *  @return
 *  ETrue if the selective status is valid.
 *  @leave KErrNotSupported
 *  If the last 4 bits are set as they are not supported.
 */
TBool CSmsSMSCCtrlParameterOperations::ValidateControlParametersL(TUint8& aSelectiveStatus) const
	{
	if (aSelectiveStatus & ESmsStatusReportForFutureUse1)
		{
		User::Leave(KErrNotSupported);
		}
		
	if (aSelectiveStatus & ESmsStatusReportForFutureUse2)
		{
		User::Leave(KErrNotSupported);	
		}
		
	if (aSelectiveStatus & ESmsStatusReportCancelRestSRR)
		{
		User::Leave(KErrNotSupported);	
		}
		
	if (aSelectiveStatus & ESmsStatusReportIncludeOriginalUDH)
		{
		User::Leave(KErrNotSupported);
		}
	
	return ETrue;	
	} // CSmsSMSCCtrlParameterOperations::ValidateControlParametersL


/**
 *  @publishedAll
 *  
 *  Sets the status report for a PDU. First the scheme is obtained by calling the 
 *  GetStatusReportScheme and checked if it is the valid scheme. Then aSelectiveStatus is 
 *  checked to see if it is the default value. If it is then nothing is done. But if 
 *  aSelectiveStatus is non-default then it is appended to the array 
 *  iControlParametersStatusReport along with the aSegmentSequenceNum 
 *  
 *  @param aSegmentSequenceNum
 *  The segment sequence number of the PDU which needs to be updated.
 *  @param aSelectiveStatus
 *  The octet which needs to be set as the Selective Status for the report.
 *  @return KErrNone if the operation is successful.
 *  KErrNotFound if the Segment Sequence Number is out of range or the scheme is inavlid.
 *  KErrNotSupported if aSelectiveStatus is not valid.
 *  @leave
 *  If ValidateOperationL or AppendL leaves.
 *  @capability None
 */
EXPORT_C TInt CSmsSMSCCtrlParameterOperations::SetStatusReportL(TUint aSegmentSequenceNum, TUint8 aSelectiveStatus)
	{
	ValidateOperationL();
	TBool validateStatus = ValidateControlParametersL(aSelectiveStatus);
	
	if(validateStatus)
		{
		CSmsMessageAdditionalAttributes& additionalAttributes = *((CSmsMessageAdditionalAttributes*) iMessage.AdditionalInfo());
		CSmsMessageAdditionalAttributes::CSmsStatusReportScheme& scheme = additionalAttributes.GetStatusReportScheme();
		if (scheme.Id() == EControlParametersScheme)
			{
			CSmsMessageAdditionalAttributes::CControlParametersScheme& ctrlParamsScheme = (CSmsMessageAdditionalAttributes::CControlParametersScheme&)scheme;
			if(aSegmentSequenceNum>=ctrlParamsScheme.iNumOfPDUs)
				{
				return KErrNotFound;	//	aSegmentSequenceNum out of range.
				}
			if (aSelectiveStatus == ctrlParamsScheme.iDefaultStatusReport)
				{
				return KErrNone;	// It's the same as default so need for any updation.
				}
			else
				{
				TInt count = (ctrlParamsScheme.iControlParametersStatusReport).Count();
				TBool found(EFalse);
				for (TInt ii=0; !found && ii<count; ii++)
					{
					if ((ctrlParamsScheme.iControlParametersStatusReport[ii]).iSegmentSequenceNum == aSegmentSequenceNum)
						{
						(ctrlParamsScheme.iControlParametersStatusReport[ii]).iSelectiveStatus = aSelectiveStatus;
						found = ETrue;
						break;
						}
					}
					
				if (!found)
					{
					CSmsMessageAdditionalAttributes::CControlParametersScheme::TSmsSMSCCtrlParameterStatus smscCtrlParameterStatus;
					smscCtrlParameterStatus.iSegmentSequenceNum = aSegmentSequenceNum;
					smscCtrlParameterStatus.iSelectiveStatus = aSelectiveStatus;
					(ctrlParamsScheme.iControlParametersStatusReport).AppendL(smscCtrlParameterStatus);
					}
			
				return KErrNone;	
				}
			}
		else
			{
			return KErrNotFound;	//	Scheme not valid.
			}
		}
	else
		{
		return KErrNotSupported;	//	Invalid aSlectiveStatus.
		}
	} // CSmsSMSCCtrlParameterOperations::SetStatusReportL


/**
 *  @publishedAll
 *  
 *  Gets the selective status for a PDU if the scheme is set to the Control
 *  Parameters Scheme.
 *  
 *  @param aSegmentSequenceNum
 *  The segment sequence number of the PDU whose status report is required.
 *  @param aSelectiveStatus
 *  Returns the selective status octet for that PDU.
 *  
 *  @return KErrNotFound if the segment sequence number or the scheme is invalid.
 *  
 *  @capability None
 */
EXPORT_C TInt CSmsSMSCCtrlParameterOperations::GetStatusReport(TUint aSegmentSequenceNum,
															   TUint8& aSelectiveStatus) const
	{
	CSmsMessageAdditionalAttributes* additionalAttributes = (CSmsMessageAdditionalAttributes*) iMessage.AdditionalInfo();
	CSmsMessageAdditionalAttributes::CSmsStatusReportScheme& scheme = additionalAttributes->GetStatusReportScheme();
	if (scheme.Id() == EControlParametersScheme)
		{
		CSmsMessageAdditionalAttributes::CControlParametersScheme& ctrlParamsScheme = (CSmsMessageAdditionalAttributes::CControlParametersScheme&)scheme;
		
		if(aSegmentSequenceNum>=ctrlParamsScheme.iNumOfPDUs)
			{
			return KErrNotFound;	//	aSegmentSequenceNum out of range.
			}

		TInt count = (ctrlParamsScheme.iControlParametersStatusReport).Count();
		
		for (TInt ii=0; ii<count; ii++)
			{
			if (ctrlParamsScheme.iControlParametersStatusReport[ii].iSegmentSequenceNum == aSegmentSequenceNum)
				{
				aSelectiveStatus = ctrlParamsScheme.iControlParametersStatusReport[ii].iSelectiveStatus;
				return KErrNone;
				}
			}
			
		aSelectiveStatus = ctrlParamsScheme.iDefaultStatusReport;
		return KErrNone;
		}

	return KErrNotFound;
	} // CSmsSMSCCtrlParameterOperations::GetStatusReport


/**
 *  @publishedAll
 *  
 *  Sets the default value of the status report to aDefaultSelectiveStatus.
 *  
 *  @param aDefaultSelectiveStatus
 *  The selective status to be used as default.
 *  @leave KErrNotFound
 *  If the scheme is invalid.
 *  @capability None
 */
EXPORT_C void CSmsSMSCCtrlParameterOperations::SetDefaultL(TUint8 aDefaultSelectiveStatus)
	{
	CSmsMessageAdditionalAttributes& additionalAttributes = *((CSmsMessageAdditionalAttributes*) iMessage.AdditionalInfo());
	CSmsMessageAdditionalAttributes::CSmsStatusReportScheme& scheme = additionalAttributes.GetStatusReportScheme();
	if (scheme.Id() == EControlParametersScheme)
		{
		CSmsMessageAdditionalAttributes::CControlParametersScheme& ctrlParamsScheme = (CSmsMessageAdditionalAttributes::CControlParametersScheme&)scheme;
		if(aDefaultSelectiveStatus & ESmsSMSCControlParametersMask)
			{
			ctrlParamsScheme.iDefaultStatusReport = aDefaultSelectiveStatus;
			}
		else
			{
			ctrlParamsScheme.iDefaultStatusReport = 0x00;	
			}
		}
	else
		{
		User::Leave(KErrNotFound);
		}
	} // CSmsSMSCCtrlParameterOperations::SetDefaultL


/**
 *  @publishedAll
 *  
 *  This method is called to set the scheme to Control Parameters Scheme.
 *  First iStatusReportScheme, which is obtained by calling GetStatusReportScheme,
 *  is deleted and set to NULL. Then a new scheme is created and a default value is set.
 *  This should be the last method to be called in a message sending process as all the 
 *  operations in this interface depend on the number of PDUs being set.
 *  
 *  @leave
 *  If ValidateOperationL or NumMessagePDUsL leaves.
 *  @capability None
 */
EXPORT_C void CSmsSMSCCtrlParameterOperations::SetSchemeL()
	{
	ValidateOperationL();
	
	CSmsMessageAdditionalAttributes& additionalAttributes = *((CSmsMessageAdditionalAttributes*) iMessage.AdditionalInfo());
	additionalAttributes.SetStatusReportSchemeL(EControlParametersScheme);
	
	TBuf8 <1> buffer;
	buffer.SetLength(1);
	buffer[0] = 0;
	
	CSmsUserData& userData = iMessage.SmsPDU().UserData();

	userData.AddInformationElementL(CSmsInformationElement::ESmsIEISMSCControlParameters,buffer);
	
	CSmsMessageAdditionalAttributes::CSmsStatusReportScheme& scheme = additionalAttributes.GetStatusReportScheme();
	CSmsMessageAdditionalAttributes::CControlParametersScheme& ctrlParamsScheme = (CSmsMessageAdditionalAttributes::CControlParametersScheme&)scheme;
	ctrlParamsScheme.iNumOfPDUs = iMessage.NumMessagePDUsL();

	SetDefaultL(0);
	} // CSmsSMSCCtrlParameterOperations::SetSchemeL


/**
 *  @publishedAll
 *  
 *  Gets the current scheme being used.
 *  
 *  @return
 *  EControlParametrsScheme is returned when Control Parameters Scheme is being used.
 *  @capability None
 */
EXPORT_C TSmsStatusReportScheme CSmsSMSCCtrlParameterOperations::GetScheme() const
	{
	CSmsMessageAdditionalAttributes* additionalAttributes = (CSmsMessageAdditionalAttributes*) iMessage.AdditionalInfo();
		
	return (additionalAttributes->GetStatusReportScheme()).Id();
	} // CSmsSMSCCtrlParameterOperations::GetScheme


/**
 *  @publishedAll
 *  
 *  This method re-sets the scheme to the Default Scheme.
 *  
 *  @capability None
 */
EXPORT_C void CSmsSMSCCtrlParameterOperations::ResetSchemeL()
	{
	CSmsMessageAdditionalAttributes& additionalAttributes = *((CSmsMessageAdditionalAttributes*) iMessage.AdditionalInfo());
	
	CSmsUserData& userData = iMessage.SmsPDU().UserData();

	CArrayFixFlat<TInt>* indices = new (ELeave) CArrayFixFlat<TInt>(8);
	CleanupStack::PushL(indices);
	userData.InformationElementIndicesL(CSmsInformationElement::ESmsIEISMSCControlParameters,*indices);

	TUint i = indices->Count();

	while (i-- != 0)
	    {
	    userData.RemoveInformationElement((*indices)[i]);
	    }
    
    CleanupStack::PopAndDestroy(indices);
    additionalAttributes.SetStatusReportSchemeL(EDefaultScheme);
	} // CSmsSMSCCtrlParameterOperations::ResetSchemeL

