// Copyright (c) 1997-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 "gsmubuf.h"
#include "gsmuset.h"
#include "Gsmuelem.h"
#include "Gsmumsg.h"
#include "gsmusar.h"
#include "gsmupdu.h"
#include "WAPDGRM.H"
#include "ws_main.h"
#include "smsstackutils.h"

//
// For incoming short message
//
CWapDatagram* CWapDatagram::NewL(const CSmsMessage& aSms)
    {
    LOGWAPPROT2("CWapDatagram::NewL(): aSms=0x%08x", (TInt) &aSms);

    CWapDatagram* datagram = new (ELeave)CWapDatagram();

    CleanupStack::PushL(datagram);
    datagram->ConstructL(aSms);
    CleanupStack::Pop();

    LOGWAPPROT2("CWapDatagram::NewL(): iFromPort: %d", datagram->iFromPort);
    LOGWAPPROT2("CWapDatagram::NewL(): iToPort: %d", datagram->iToPort);
    LOGWAPPROT2("CWapDatagram::NewL(): iReference: %d", datagram->iReference);
    LOGWAPPROT2("CWapDatagram::NewL(): iTotalSegments: %d", datagram->iTotalSegments);
    LOGWAPPROT2("CWapDatagram::NewL(): iSegmentNumber: %d", datagram->iSegmentNumber);
    LOGWAPPROT2("CWapDatagram::NewL(): iIsComplete: %d", datagram->iIsComplete);
    LOGWAPPROT2("CWapDatagram::NewL(): iReference: %d", datagram->iReference);
    LOGWAPPROT2("CWapDatagram::NewL(): iIsTextHeader: %d", datagram->iIsTextHeader);
    LOGWAPPROT2("CWapDatagram::NewL(): iLogServerId: %d", datagram->iLogServerId);
    LOGWAPPROT2("CWapDatagram::NewL(): i16BitPorts: %d", datagram->i16BitPorts);

    // assert destination port
	if (datagram->i16BitPorts)
		{
		if (datagram->iToPort>=0  &&  datagram->iToPort<=65535)
			{
			LOGWAPPROT1("iToPort OK");
			}
		else
			{
			LOGWAPPROT1("iToPort FAILED");
			}
		}
    else
		{
        if (datagram->iToPort>=0  &&  datagram->iToPort<=255)
			{
		    LOGWAPPROT1("iToPort OK");
			}
		else
			{
			LOGWAPPROT1("iToPort FAILED");
			}
		}

	if (datagram->i16BitPorts)
		{
		if (datagram->iFromPort>=0  &&  datagram->iFromPort<=65535)
			{
			LOGWAPPROT1("iFromPort OK");
			}
		else
			{
			LOGWAPPROT1("iFromPort FAILED");
			}
		}
    else
		{
        if (datagram->iFromPort>=0  &&  datagram->iFromPort<=255)
			{
		    LOGWAPPROT1("iFromPort OK");
			}
		else
			{
			LOGWAPPROT1("iFromPort FAILED");
			}
		}

    return datagram;
    } // CWapDatagram::NewL


//
// For outgoing short messages
//
CWapDatagram* CWapDatagram::NewL(const TDesC8& aSendBuffer)
    {
    LOGWAPPROT2("CWapDatagram::NewL(): aSendBuffer=0x%08x", (TInt) &aSendBuffer);

    CWapDatagram* datagram = new (ELeave)CWapDatagram();

    CleanupStack::PushL(datagram);
    datagram->Construct(aSendBuffer);
    CleanupStack::Pop();

    return datagram;
    } // CWapDatagram::NewL


CWapDatagram::~CWapDatagram()
    {
    LOGWAPPROT1("CWapDatagram::~CWapDatagram()");

	delete iRecvbuf;
    delete iBuffer;
    delete iSegment;
    } // CWapDatagram::~CWapDatagram


//
// What about service centre address ?
// aSmsMessageArray contains CSmsMessage objects
//
void CWapDatagram::EncodeConcatenatedMessagesL(RFs& aFs, CArrayPtr<CSmsMessage>& aSmsMessageArray)
    {
    LOGWAPPROT2("CWapDatagram::EncodeConcatenatedMessagesL(): %d messages", aSmsMessageArray.Count());

    // Couple of checkings makes sense
    __ASSERT_DEBUG(iToPort >=0 && iToAddress.Length()>=0
            && (iUserDataSettings.Alphabet()==TSmsDataCodingScheme::ESmsAlphabet8Bit
                 || iUserDataSettings.Alphabet()==TSmsDataCodingScheme::ESmsAlphabet7Bit),
            Panic(KPanicUsageError));

    // Determine CSmsMessage encoding by character width
    if (iUserDataSettings.Alphabet() == TSmsDataCodingScheme::ESmsAlphabet8Bit)
        {
        CSmsBufferBase* SmsBuffer = CSmsBuffer::NewL();

        CleanupStack::PushL(SmsBuffer);
        ConvertL(iSendBuffer,*SmsBuffer);
        CleanupStack::Pop(); // SmsBuffer, popped here since it is pushed again in the following NewL call
        CSmsMessage* SmsMessage = CSmsMessage::NewL(aFs, CSmsPDU::ESmsSubmit,SmsBuffer,EFalse);
        CleanupStack::PushL(SmsMessage);
        // one and only object
        aSmsMessageArray.AppendL(SmsMessage);
        CleanupStack::Pop(); // SmsMessage

        SetSmsMessageSettingsL(*SmsMessage,ETrue);
        }
    else
        {
        // contruct TWapTextMessage object
        TWapTextMessage* Segment = new (ELeave) TWapTextMessage(KNullDesC8);
        CleanupStack::PushL(Segment);
        CArrayPtrFlat<HBufC8>* SegmentArray = new (ELeave) CArrayPtrFlat<HBufC8> (8);
        CleanupStack::PushL(SegmentArray);
		// coverity[double_push]
        CleanupResetAndDestroyPushL(*SegmentArray);

        Segment->SetDestinationPort(iToPort,i16BitPorts);
        Segment->SetSourcePort(iFromPort,i16BitPorts);
        Segment->SetReferenceNumber(iReference);
        Segment->SetUserData(iSendBuffer);
        Segment->SetOtherHeader(iOtherHeader);
        Segment->EncodeSegmentsL(*SegmentArray);

        // contruct array of CSmsMessages
        TInt Count = SegmentArray->Count();
        for (TInt i=0; i<Count; i++)
            {
            CSmsBufferBase* SmsBuffer = CSmsBuffer::NewL();

            CleanupStack::PushL(SmsBuffer);
            ConvertL(*(SegmentArray->At(i)),*SmsBuffer);
            CleanupStack::Pop(); // SmsBuffer, popped here since it is pushed again in the following NewL call
            CSmsMessage* SmsMessage=CSmsMessage::NewL(aFs, CSmsPDU::ESmsSubmit,SmsBuffer,EFalse);
            CleanupStack::PushL(SmsMessage);
            aSmsMessageArray.AppendL(SmsMessage);
            CleanupStack::Pop(); // SmsMessage

            SetSmsMessageSettingsL(*SmsMessage,EFalse);
            }
        CleanupStack::PopAndDestroy(3, Segment);  // SegmentArray elements (Reset and Destroy), SegmentArray, Segment

        }
    } // CWapDatagram::EncodeConcatenatedMessagesL

void CWapDatagram::DecodeConcatenatedMessagesL(CArrayPtr<TSegmentData>& aSmsMessageArray)
    {
    LOGWAPPROT1("CWapDatagram::DecodeConcatenatedMessagesL()");

    // The TSegmentData elements are in the random order in the array
    TInt Count = aSmsMessageArray.Count();
    TInt i=0;

    if (Count > 0)
        {
        TInt DataLength = 0;  // total length of data
        TInt TempLength = 0;

        // Every TSegmentData should be equal length except last segment
        // Use that 'constant' segment length as indexes
        // Count the actual data length
        for(i=0; i<Count; i++)
            {
            TempLength = aSmsMessageArray.At(i)->iData.Length();
            DataLength += TempLength;
            }

        TSegmentData* Segment = NULL;

        if (iBuffer)
            {
            delete iBuffer;
            iBuffer = NULL;
            }
        iBuffer = HBufC8::NewL(DataLength );

        TPtr8 BufferPtr(iBuffer->Des());
        BufferPtr.SetLength(DataLength );

        TInt segmentStartPosition=0;

        for(i=0; i<Count; i++)
            {
              if (segmentStartPosition >= DataLength)  
                  {
                  // Once the start position is out of range,
                  // there is no point in continuing reconstructing the
                  // wapdatagram.
                  break;
				 }			
			
            Segment = aSmsMessageArray.At(i);
			TPtr8 CopyBufferPtr(&(BufferPtr[segmentStartPosition]),0,160);
			CopyBufferPtr.Copy(Segment->iData);
 			segmentStartPosition+=Segment->iData.Length();
            }
        }
    iIsComplete = ETrue;
    } // CWapDatagram::DecodeConcatenatedMessagesL


void CWapDatagram::InternalizeL(RReadStream& aStream)
    {
    aStream >> iUserDataSettings;
    aStream >> iToAddress;
    iToPort = aStream.ReadInt32L();
    aStream >> iFromAddress;
    iFromPort = aStream.ReadInt32L();
    TInt64 time;
    aStream >> time;
    iTime = time;
    iUTCOffset = aStream.ReadInt32L();
    iIsTextHeader = aStream.ReadInt32L();
    iReference = aStream.ReadInt32L();
    iTotalSegments = aStream.ReadInt32L();
    iSegmentNumber = aStream.ReadInt32L();
    iLogServerId = aStream.ReadInt32L();
    iVersionNumber = aStream.ReadInt32L();
    iSpare1 =  aStream.ReadInt32L();
    iSpare2 =  aStream.ReadInt32L();
    iSpare3 =  aStream.ReadInt32L();
	
    // Required for version 1, which is reading and internalizing extra SMS parameters from stream
    if(iVersionNumber == EFirstVersion)
    	{
    	TInt length = aStream.ReadInt32L();
    	if(length>0)
    		{
    		HBufC8* smsBuffer=HBufC8::NewMaxLC(length);
			TPtr8 recvbuftmp = smsBuffer->Des();
			aStream >> recvbuftmp;
			if(!iRecvbuf)
				{
				iRecvbuf = CBufFlat::NewL(KSmsBufferExpansion);
				}
			iRecvbuf->ResizeL(recvbuftmp.Size());
			iRecvbuf->Write(0, recvbuftmp);
			CleanupStack::PopAndDestroy(smsBuffer);
			}
    	}
    
    } // CWapDatagram::InternalizeL


//
//	Reads iBuffer from stream (for 8Bit messages)
//
void CWapDatagram::InternalizeBufferL(RReadStream& aStream)
	{
	TInt length;
	length=aStream.ReadInt32L();
	iBuffer=HBufC8::NewMaxL(length);
	TPtr8 tmp = iBuffer->Des();
	aStream >> tmp;
	iIsComplete = ETrue;
	}


void CWapDatagram::ExternalizeL(RWriteStream& aStream) const
    {
    aStream << iUserDataSettings;
    aStream << iToAddress;
    aStream.WriteInt32L(iToPort);

    aStream << iFromAddress;
    aStream.WriteInt32L(iFromPort);
    aStream << iTime.Int64();
    aStream.WriteInt32L(iUTCOffset.Int());

    aStream.WriteInt32L(iIsTextHeader);
    aStream.WriteInt32L(iReference);
    aStream.WriteInt32L(iTotalSegments);
    aStream.WriteInt32L(iSegmentNumber);

    aStream.WriteInt32L(iLogServerId);

    aStream.WriteInt32L(iVersionNumber);
    aStream.WriteInt32L(iSpare1);
    aStream.WriteInt32L(iSpare2);
    aStream.WriteInt32L(iSpare3);
	
	// Externalizing the SMS params if exists
	if(iRecvbuf)
		{
		TPtr8 recvbuftmp = iRecvbuf->Ptr(0);
		aStream.WriteInt32L(recvbuftmp.Length());
		aStream << recvbuftmp;
		}
	else
		{
		aStream.WriteInt32L(0);
		}
    }


//
// writes iBuffer to writeStream for 8-bit incomming messages
//
void CWapDatagram::ExternalizeBufferL(RWriteStream& aStream) const
	{
	TInt length= WapDatagramLength();
	aStream.WriteInt32L(length);
	TPtr8 tmp= iBuffer->Des();
	aStream << tmp;

	} // CWapDatagram::ExternalizeBufferL


//
// Outgoing
//
void CWapDatagram::Construct(const TDesC8& aSendBuffer)
    {
    LOGWAPPROT1("CWapDatagram::Construct()");
	
	// Set version number to 1, as we have had to make 
	// changes to CWapDatagram for CR0929
	iVersionNumber = EFirstVersion;
	
    iSendBuffer.Set(aSendBuffer);
    } // CWapDatagram::Construct


//
// Incoming
// code actualy supports 7-bit characters and port numbers
// encoded in information elements (IE) although it is not
// a requirement
//
void CWapDatagram::ConstructL(const CSmsMessage& aSms)
    {
    LOGWAPPROT1("CWapDatagram::ConstructL()");

	// Set version number to 1, as we have had to make 
	// changes to CWapDatagram for CR0929
	iVersionNumber = EFirstVersion;

	iRecvbuf = CBufFlat::NewL(KSmsBufferExpansion);

	RBufWriteStream writestream(*iRecvbuf);
	writestream.Open(*iRecvbuf);
	CleanupClosePushL(writestream);
	
	// Externalizing everything within CSMSMessage except buffer
	aSms.ExternalizeWithoutBufferL(writestream);
	
	CleanupStack::PopAndDestroy();  //  writestream

	iRecvbuf->Compress();

    const CSmsBufferBase& SmsBufferBase = aSms.Buffer();

    GetDatagramSettings(aSms);

    // Get the data from CSmsMessage and convert
    // SmsBuffer store Unicode short message from CSmsMessage
    ConvertL(SmsBufferBase,&iBuffer);
	GetDatagramSettingsL();

    iIsComplete = ETrue;
    if (iUserDataSettings.Alphabet() == TSmsDataCodingScheme::ESmsAlphabet7Bit)
        {
        // this is a good candidate to include a text header.
        if (iIsTextHeader)
            {
            // there is still a minor chance that message is complete:
            // one fragment long datagram
            if (iTotalSegments == 1)
                {
                // Get the segment data
                TPtr8 PtrNarrow = iBuffer->Des();
                PtrNarrow.Zero();
                iSegment->UserData(PtrNarrow);
                }
            else
                {
                iIsComplete = EFalse;
                // delete incomplete message from iBuffer;
                delete iBuffer;
                iBuffer = 0;
                }
            }
        else
            // This means that IEs with 7-bits are used
            // Happily we support it
            iIsComplete = ETrue;

        }
    } // CWapDatagram::ConstructL


//
// Set WAP datagram private members from a text based concatenated message
// Copies contents of iBuffer to iSmsBuffer
// Allocates iSegment object
//
void CWapDatagram::GetDatagramSettingsL()
    {
    LOGWAPPROT1("CWapDatagram::GetDatagramSettingsL()");

	/* The WAP stack always receives 8 bit WAP datagrams from the SMS Stack as a single CSmsMessage.
	This is because 8 bit WAP messages which cannot be encoded into a single PDU are sent in a segmented SMS
	message which is reassembled inside the SMS Stack; the SMS Stack always passes the WAP stack
	a single CSmsMessage containing a single WAP message regardless of the message length.
	The WAP stack can receive 7 Bit WAP datagrams either as a single CSmsMessage or as a number of CSmsMessages
	which need to be reassembled into a single WAP message by the WAP stack.  The latter method is provided to maintain backward
	compatibility with early versions of the WAP stack which were implemented before the concatenation function was
	available in the SMS stack.*/

	/* 160 is the maximum number of character that can be fitted in a single PDU using 7 bit encoding */
	if((iUserDataSettings.Alphabet()== TSmsDataCodingScheme::ESmsAlphabet8Bit) ||
	(iUserDataSettings.Alphabet()== TSmsDataCodingScheme::ESmsAlphabet7Bit && iBuffer->Length()>160 ))
		{
		iSegment = new (ELeave)TWapTextMessage(*iBuffer);
		}
	else if (iBuffer->Length()<=160)
        {
        iSmsBuffer = *iBuffer;
		iSegment = new (ELeave)TWapTextMessage(iSmsBuffer);
        iIsTextHeader = iSegment->Parse();

        if (iIsTextHeader)
            {
            iFromPort = iSegment->SourcePort(&i16BitPorts);
            iToPort = iSegment->DestinationPort(&i16BitPorts);
            iReference = iSegment->ReferenceNumber();
            iTotalSegments = iSegment->TotalSegments();
            iSegmentNumber = iSegment->SegmentNumber();
            }
        }
	else
		{
        delete iSegment;
        iSegment = 0;
		}
     } // CWapDatagram::GetDatagramSettingsL


//
// Set WAP datagram private members from information element structures
//
void CWapDatagram::GetDatagramSettings(const CSmsMessage& aSms)
    {
    LOGWAPPROT1("CWapDatagram::GetDatagramSettings()");

    const CSmsPDU& Pdu = aSms.SmsPDU();

    __ASSERT_DEBUG(Pdu.Type()==CSmsPDU::ESmsDeliver || Pdu.Type()==CSmsPDU::ESmsSubmit, // this line is testing purposes only
			Panic(KPanicUsageError));

    aSms.UserDataSettings(iUserDataSettings);

    Pdu.ApplicationPortAddressing(iToPort,iFromPort,&i16BitPorts);
    if (iFromPort == (-1))
        iFromPort = iToPort;

    // From WAP datagram point of view following numbers are applicable
    iReference = 0;
    iTotalSegments = 1;
    iSegmentNumber = 1;

    iFromAddress = aSms.ToFromAddress();
    iToAddress = KNullDesC;
    iTime = aSms.Time();

    TBool result = SetUTCOffset(aSms.UTCOffset());
    __ASSERT_DEBUG(result, Panic(KPanicSmsMsgTimeZoneOutOfRange));
    } // CWapDatagram::GetDatagramSettings


//
// Set Alphabet information of iUserDataSettings before calling the method
// Converts from 7/8-bit to UNICODE
//
void CWapDatagram::ConvertL(const TDesC8& aNarrowChars,CSmsBufferBase& aSmsBuffer) const
    {
    LOGWAPPROT1("CWapDatagram::ConvertL()");

	// Convert the data in segments of specified max size
	const TInt KMaxSegmentSize=CSmsBufferBase::EMaxBufLength;

	// Create converter and reassembler
	RFs fs;
	CCnvCharacterSetConverter* charConv=CCnvCharacterSetConverter::NewLC();
	CSmsAlphabetConverter* converter=CSmsAlphabetConverter::NewLC(*charConv,fs,iUserDataSettings.Alphabet(),ETrue);
	TSmsBufferReassembler reassembler(*converter,aSmsBuffer);

	// Rassemble
	TInt elementsRemaining=aNarrowChars.Length();
	while (elementsRemaining)
		{
		TInt segmentLength=Min(KMaxSegmentSize,elementsRemaining);
		TPtrC8 ptr(aNarrowChars.Ptr()+aNarrowChars.Length()-elementsRemaining,segmentLength);
		reassembler.ReassembleNextL(ptr, ESmsEncodingNone,
									elementsRemaining==segmentLength);
		elementsRemaining-=segmentLength;
		}

	CleanupStack::PopAndDestroy(2);	// charConv,
    } // CWapDatagram::ConvertL


//
// Converts from UNICODE to 7/8-bit
//
void CWapDatagram::ConvertL(const CSmsBufferBase& aSmsBuffer,HBufC8** aNarrowChars) const
    {
    LOGWAPPROT1("CWapDatagram::ConvertL()");

	// Convert the data in segments of specified max size
	const TInt KMaxSegmentSize=CSmsBufferBase::EMaxBufLength;

	// Delete the existing buffer
	if (*aNarrowChars)
		{
		delete *aNarrowChars;
		*aNarrowChars=NULL;
		}

	// Create converter and segmenter
	RFs fs;
	CCnvCharacterSetConverter* charConv=CCnvCharacterSetConverter::NewLC();
	CSmsAlphabetConverter* converter=CSmsAlphabetConverter::NewLC(*charConv,fs,iUserDataSettings.Alphabet(),ETrue);
	CSmsBufferSegmenter* segmenter=CSmsBufferSegmenter::NewLC(*converter,aSmsBuffer,KMaxSegmentSize);

	// Create a new buffer based on converted length
	TInt convertedLength=segmenter->TotalConvertedLengthL(ESmsEncodingNone);
	*aNarrowChars=HBufC8::NewMaxL(((convertedLength+KMaxSegmentSize-1)/KMaxSegmentSize)*KMaxSegmentSize);
	TPtr8 narrowPtr=(*aNarrowChars)->Des();

	// Now do the conversion
	TInt elementsConverted=0;
	TInt unconvertedChars=0;
	TInt downgradedChars=0;
	TBool complete=EFalse;
	while (elementsConverted<convertedLength)
		{
		__ASSERT_DEBUG(elementsConverted<convertedLength,Panic(KPanicTooLongData));
		TPtr8 ptr((TUint8*)narrowPtr.Ptr()+elementsConverted,0,KMaxSegmentSize);
		complete=segmenter->SegmentNextL(ptr, unconvertedChars, downgradedChars, ESmsEncodingNone);
		elementsConverted+=ptr.Length();
		}
    if((!complete || (convertedLength!=elementsConverted)) &&  convertedLength)
        User::Leave( KErrCorrupt );
	narrowPtr.SetLength(convertedLength);

	CleanupStack::PopAndDestroy(3);	// charConv,converter,segmenter
    } // CWapDatagram::ConvertL


void CWapDatagram::SetSmsMessageSettingsL(CSmsMessage& aSmsMessage, TBool aSetPorts)
    {
    LOGWAPPROT1("CWapDatagram::SetSmsMessageSettingsL()");

    CSmsPDU& Pdu = aSmsMessage.SmsPDU();

    aSmsMessage.SetToFromAddressL(iToAddress);
    aSmsMessage.SetUserDataSettingsL(iUserDataSettings);
    aSmsMessage.SetTime(iTime);

    TBool result = aSmsMessage.SetUTCOffset(iUTCOffset);
    __ASSERT_DEBUG(result, Panic(KPanicWapDgrmTimeZoneOutOfRange));
    
    if (aSetPorts)
        {
        if (iFromPort == (-1))
            iFromPort = iToPort;

        // Determine whether long or short form is used
        if (iFromPort > 255 || iToPort>255)
            i16BitPorts = ETrue;

	    Pdu.SetApplicationPortAddressingL(ETrue,iToPort,iFromPort,i16BitPorts);
		// WAP implementation guidelines recommend DCS of 0x15, however
		// some gateways / SCs will only accept DCS 0xf5
		Pdu.SetBits7To4(TSmsDataCodingScheme::ESmsDCSTextUncompressed7BitOr8Bit);
		Pdu.SetAlphabet(TSmsDataCodingScheme::ESmsAlphabet8Bit);
		Pdu.SetClass(ETrue,TSmsDataCodingScheme::ESmsClass1);
        }
    } // CWapDatagram::SetSmsMessageSettingsL


CWapDatagram::CWapDatagram():
    iUserDataSettings(),
    iToAddress(KNullDesC),
    iFromAddress(KNullDesC),
    i16BitPorts(EFalse),
    iFromPort(-1),
    iToPort(-1),
    iTime(),
    iUTCOffset(0),
    iIsComplete(EFalse),
    iReference(0),
    iTotalSegments(0),
    iSegmentNumber(0),
    iIsTextHeader(EFalse),
    iSegment(NULL),
    iSmsBuffer(KNullDesC8),
    iBuffer(NULL),
    iSendBuffer(KNullDesC8),
    iOtherHeader(KNullDesC8),
    iLogServerId(0),
    iVersionNumber(EBaseVersion),// Set version number to EBaseVersion by default.
    iSpare1(0),
    iSpare2(0),
    iSpare3(0),
    iRecvbuf(NULL)
    {
    iTime.UniversalTime();

    TBool result = SetUTCOffset(User::UTCOffset());
   __ASSERT_DEBUG(result, Panic(KPanicUserSuppliedTimeZoneOutOfRange));

    iUserDataSettings.SetAlphabet(TSmsDataCodingScheme::ESmsAlphabet8Bit);
    __DECLARE_NAME(_S("CWapDatagram"));
    } // CWapDatagram::CWapDatagram


//
// Return the location of the link
//
TInt CWapDatagram::LinkOffset()
	{
	LOGWAPPROT1("CWapDatagram::LinkOffset()");


	return _FOFF(CWapDatagram,iLink);
	} // CWapDatagram::LinkOffset


TBool CWapDatagram::SetUTCOffset(const TTimeIntervalSeconds& aUTCOffset)
    {
    LOGWAPPROT1("CWapDatagram::SetUTCOffset()");

    TBool rc = ETrue;
    TInt utcOffset = aUTCOffset.Int();

    if ((utcOffset <=  CSmsMessage::EMaximumSeconds) &&
        (utcOffset >= -CSmsMessage::EMaximumSeconds))
       {
       iUTCOffset = utcOffset;
       }
    else
       {
       LOGWAPPROT2("CWapDatagram:SetUTCOffset offset [out of range] = %d",utcOffset);
       rc = EFalse;
       }

    return rc;
    } // CWapDatagram::SetUTCOffset
    


CBufFlat* CWapDatagram::SmsExternalisedStream() const
	{
	return iRecvbuf;
	}
