smsprotocols/smsstack/wapprot/Src/wapthdr.cpp
changeset 0 3553901f7fa8
child 19 630d2f34d719
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/smsprotocols/smsstack/wapprot/Src/wapthdr.cpp	Tue Feb 02 01:41:59 2010 +0200
@@ -0,0 +1,600 @@
+// 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:
+// This file contains the implementation of the TWapTextMessage.
+// TWapTextMessage encodes and decodes 7-bit WAP text headers
+// 
+//
+
+/**
+ @file
+*/
+
+#include <e32std.h>
+#include "wapthdr.h"
+#include "ws_main.h"
+
+const TInt TWapTextMessage::KHeaderCount = 6;
+
+_LIT8(KSCKHeaderLong, "//SCKL"); // standard NBS header for 16-bit adresses
+_LIT8(KSCKHeaderShort,"//SCK");  // standard NBS header for 8-bit adresses
+
+
+/**
+ *  Indices into different types header for each of the header elements
+ *  (dest port, source port, reference, total segments, current segment)
+ */
+const TWapTextMessage::TIndexInfo TWapTextMessage::KElemIndexes[TWapTextMessage::KHeaderCount] =
+{
+// Long header ("//SCKL") indices
+// destination port (, other header)
+	{ 1,{ 6, 10, 0, 0, 0, 0} },
+// destination port, source port (, other header)
+	{ 2,{ 6, 10, 14, 0, 0, 0} },
+// destination port, source port,
+// reference, total segments, current segment(, other header)
+	{ 5,{ 6, 10, 14, 16, 18, 20}},
+
+// Short header ("//SCK") indices
+// destination port (, other header)
+	{ 1, { 5, 7, 0, 0, 0, 0}},
+// destination port, source port (, other header)
+	{ 2, { 5, 7, 9, 0, 0, 0}},
+// destination port, source port,
+// reference, total segments, current segment(, other header)
+	{ 5,{ 5, 7, 9, 11, 13, 15}},
+};
+
+
+/**
+ *  C'tor
+ *  
+ *  @param aWapMessage WAP message to decode/encode
+ *  @note simply use KNullDesC8, if you are going to encode.
+ */
+TWapTextMessage::TWapTextMessage(const TDesC8& aWapMessage)
+	:iIsWapTextMessage(EFalse)
+	,iWAPMessage(aWapMessage)
+	,iIs16Bit(EFalse)
+	,iDestinationPort(-1)
+	,iSourcePort(-1)
+	,iReference(0)
+	,iTotalSegments(0)
+	,iSegmentNumber(0)
+	,iOtherHeader(0)
+	,iOtherHeaderLength(0)
+	,iData(0)
+	,iDataLength(0)
+	,iRefOtherHeader(KNullDesC8)
+	,iRefData(KNullDesC8)
+	{
+	} // TWapTextMessage::TWapTextMessage
+
+
+/**
+ *  Panic is raised, if the header with other header and terminating
+ *  ' ' is > 159 characters. Then 1 byte is left for payload !
+ *  what will be the array element type ?
+ */
+void TWapTextMessage::EncodeSegmentsL(CArrayPtr<HBufC8>& aSegmentArray)
+	{
+	TBuf8<KMaxSmsChars>  header;        // buffer for a modifiable header
+	TInt        headerLength = 0;
+	TInt        segmentNumberIndex = 0; // index of segment number field in the header
+	TBuf8<2>    hexSegmentNumber;
+	TInt        dataSegmented = 0;
+
+	LOGWAPPROT1("TWapTextMessage::EncodeSegmentsL()");
+
+	iSegmentNumber = 0;
+	do
+		{
+		//
+		// Create the segment and add it to the array...
+		//
+		HBufC8*  segment = HBufC8::NewL(KMaxSmsChars); // 160 characters
+		CleanupStack::PushL(segment);
+		TPtr8  ptr(segment->Des());
+		aSegmentArray.AppendL(segment);
+		CleanupStack::Pop(); // segment
+
+		//
+		// Calculate length of header and copy it...
+		//
+		if (iSegmentNumber==0)
+			{
+			headerLength = CreateHeader(header, segmentNumberIndex);
+			}
+
+		ptr.Copy(header);
+		if (iTotalSegments>255)
+			{
+			User::Leave(KErrOverflow);
+			}
+
+		//
+		// Set segment number...
+		//
+		if (segmentNumberIndex != 0)
+			{
+			hexSegmentNumber.NumFixedWidthUC(iSegmentNumber+1, EHex, 2); // two bytes wide
+			ptr.Insert(segmentNumberIndex, hexSegmentNumber);
+			}
+
+		//
+		// Count any escaped characters we can be sure that the converted data
+		// size fits inside the remaining length (e.g. so that non-7bit characters
+		// when converted by the SMS Stack will still fit).
+		//
+		TInt  segmentSize = iRefData.Length() - dataSegmented;
+
+		if (segmentSize > KMaxSmsChars - headerLength)
+			{
+			segmentSize = KMaxSmsChars - headerLength;
+			}
+
+		while (segmentSize > 1)
+			{
+			TPtrC8  segmentData(iRefData.Mid(dataSegmented, segmentSize));
+			TInt  non7bitCharEscapes = 0;
+			
+			//
+			// Count all non-7bit characters that will be escaped (many non-7bit
+			// characters are not escaped, but converted to "?"). The ones
+			// that are known to be escaped are list below:
+			//
+			//	  12  [Form Feed].
+			//	  91  "["
+			//	  92  "\"
+			//	  93  "]"
+			//	  94  "^"
+			//	 123  "{"
+			//	 124  "|"
+			//	 125  "}"
+			//	 126  "~"
+			//
+			for (TInt  ch = 0;  ch < segmentSize;  ch++)
+				{
+				if (segmentData[ch] == 12  ||
+					(segmentData[ch] >= 91  &&  segmentData[ch] <= 94)  ||
+					(segmentData[ch] >= 123  &&  segmentData[ch] <= 126))
+					{
+					non7bitCharEscapes++;
+					}
+				}
+			
+			//
+			// Can it fit? If so store it, otherwise reduce the size...
+			//
+			if (segmentData.Length() + non7bitCharEscapes <= KMaxSmsChars - headerLength)
+				{
+				ptr.Append(segmentData);
+				break;
+				}
+			
+			segmentSize--;
+			}
+		
+			dataSegmented += segmentSize;
+			iSegmentNumber++;
+		}
+	while (dataSegmented < iRefData.Length());
+
+	__ASSERT_DEBUG(iTotalSegments == aSegmentArray.Count(), Panic(KPanicEncodingError));
+	} // TWapTextMessage::EncodeSegmentsL
+
+
+/**
+ *  Returns true, if the short message starts with
+ *  WAP text message header set by SetWapTextMessage.
+ */
+TBool TWapTextMessage::Parse()
+	{
+	TInt waplength = iWAPMessage.Length();
+	LOGWAPPROT2("TWapTextMessage::Parse [%d bytes]", waplength);
+	if(waplength != 0)
+		{
+		// check whether long or short header
+		TInt iBaseIndex = -1;
+
+		// minimum length is 8 "//SCKxx "
+		if(waplength >= 8)
+			{
+			TPtrC8 HeaderIdPart(iWAPMessage.Left(6));
+			if(!HeaderIdPart.Compare(KSCKHeaderLong))
+				{
+				iBaseIndex = 0;
+				iIs16Bit = ETrue; // Yes, the ports are 16 bit wide
+				}
+			else
+				HeaderIdPart.Set(iWAPMessage.Left(5));
+
+			if(iBaseIndex == (-1) && !HeaderIdPart.Compare(KSCKHeaderShort) )
+				iBaseIndex = 3;
+			// else  no match
+			}
+
+		if(iBaseIndex >= 0)
+			{
+			// check which of the TIndexInfos match
+			for(TInt i=iBaseIndex; i<iBaseIndex+3; i++)
+				{
+				// Get length (index) of text header variant
+				TInt lastIndex = KElemIndexes[i].iIndexes[KElemIndexes[i].iLastIndex];
+				if (iWAPMessage.Length() <= lastIndex)
+					{
+					// no hope about match, because other variants are longer
+					iIsWapTextMessage = EFalse;
+					break;
+					}
+
+				TInt LastChar = iWAPMessage[lastIndex];
+				if (LastChar == '/' || LastChar == ' ' || LastChar == '\n')
+					{
+					// reinitialize, because following maybe absent
+					iTotalSegments = 1;
+					iSegmentNumber = 1;
+					iIsWapTextMessage = ParseWapTextHeader(KElemIndexes[i]);
+					break;
+					}
+
+				}
+
+			if (!iIsWapTextMessage)
+				{
+				LOGWAPPROT1("WARNING! illegal incoming WAP message");
+				}
+			}
+		}
+
+	return iIsWapTextMessage;
+	} // TWapTextMessage::Parse
+
+
+/**
+ *  Parses a string of hex characters representing a number
+ *  
+ *  @param aInValue descriptor containing the number
+ *  @param aBigEndian true if number is big endian
+ *  @param aRadix Radixbase; 16, 10 etc
+ *  @return TInt the parsed number
+ *  @leave Panics with KPanicInvalidParseNumber error code, if not a
+ *  number. Maximum width of the hex value can be 4.
+ *  
+ *  TODO use TLex instead
+ */
+TInt TWapTextMessage::ParseNumber(const TDesC8& aInValue,
+								  TBool   aBigEndian,
+								  TInt    aRadix)
+	{
+	LOGWAPPROT2("TWapTextMessage::ParseNumber [%S]", &aInValue);
+	// least significant byte first
+	TInt Values[4] = {0,0,0,0};
+	TInt Temp = 0;
+	TInt length = aInValue.Length();
+	TInt i = 0;
+	TInt Value = 0;
+
+	__ASSERT_DEBUG(length<5,Panic(KPanicInvalidParseNumber));
+    if( length >= 5 )
+        return KErrNotFound;
+	for(i=0; i<length; i++)
+		{
+		Temp = aInValue[i];
+		if (Temp>='0' && Temp<='9')
+			Temp-='0';
+		else if (Temp>='A' && Temp<='Z')
+			Temp = Temp - 'A'+10;
+		else if (Temp>='a' && Temp<='z')
+			Temp = Temp - 'a'+10;
+		else
+            return KErrNotFound;
+		if (aBigEndian)
+			Values[(length-1)-i]=Temp;
+		else
+			Values[i]=Temp;
+		}
+
+	// build the value
+	Value=Values[0];
+	TInt Base=1;
+	for(i=1; i<length; i++)
+		{
+		Base*=aRadix;
+		Value+=(Base)*Values[i];
+		}
+
+	return Value;
+	} // TWapTextMessage::ParseNumber
+
+
+/**
+ *  parse the WAP text header
+ *  
+ *  use ElemIndexes to retrieve the index
+ *  an starting position of an element in iWapMessage
+ *  Length of element is calculated by
+ *  subtracting current index value from the next one
+ *  except for KOtherHeader and user data, of course
+ */
+TBool TWapTextMessage::ParseWapTextHeader(const TIndexInfo& aIndexArray)
+	{
+	LOGWAPPROT2("TWapTextMessage::ParseWapTextHeader: %S", &iWAPMessage );
+
+	TInt ParsedNumber = 0;
+
+	// parse the header
+	TInt ElemIndexCount=aIndexArray.iLastIndex+1;
+	for(TInt i=0; i<ElemIndexCount; i++)
+		{
+		if (i<ElemIndexCount-1)
+			{
+			// all the elems have a length defined in advance
+			if (iWAPMessage.Length() >= aIndexArray.iIndexes[i+1])
+				{
+				// the header fits into the wap datagram
+				TPtrC8 Elem(iWAPMessage.Mid(aIndexArray.iIndexes[i],
+											aIndexArray.iIndexes[i+1]-
+											aIndexArray.iIndexes[i]));
+
+				ParsedNumber = ParseNumber(Elem,ETrue,16);
+                if( ParsedNumber == KErrNotFound )
+                    return EFalse;
+				switch(i)
+					{
+				  case KIndexDestinationPort:
+					  iDestinationPort = ParsedNumber;
+
+					  break;
+				  case KIndexSourcePort:
+					  iSourcePort = ParsedNumber;
+					  break;
+				  case KIndexReferenceNumber:
+					  iReference = ParsedNumber;
+					  break;
+				  case KIndexTotalSegments:
+					  iTotalSegments = ParsedNumber;
+					  break;
+				  case KIndexSegmentNumber:
+					  iSegmentNumber = ParsedNumber;
+					  break;
+				  default:
+					  LOGWAPPROT2("Hm. unhandled WAP index [%d]", i );
+					  break;
+					}
+				}
+			}
+		else
+			{
+			// elems have not a length defined in advance
+			iOtherHeader = 0;
+			iOtherHeaderLength = 0;
+
+			// Search the terminating character ' '
+			iData = iWAPMessage.Locate(' ');
+			TInt dataTmp = iWAPMessage.Locate('\n');
+
+			if (iData == KErrNotFound)
+				{
+				if (dataTmp == KErrNotFound)
+					return EFalse;
+				else
+					iData = dataTmp;
+				}
+			else if (dataTmp != KErrNotFound)
+				iData = Min(iData, dataTmp);
+
+			// check the existence of other header
+			// at least "// " should be there
+			if (   iWAPMessage.Length() > aIndexArray.iIndexes[i]+2
+				   && iWAPMessage[aIndexArray.iIndexes[i]] == '/'
+				   && iWAPMessage[aIndexArray.iIndexes[i]+1] == '/')
+				{
+				iOtherHeader = aIndexArray.iIndexes[i];
+				iOtherHeaderLength=iData-iOtherHeader;
+				}
+
+			// data: check if any characters after ' '
+			iDataLength = 0;
+			iData++;
+			if (iWAPMessage.Length() > iData)
+				{
+				iDataLength = iWAPMessage.Length() - iData;
+				}
+
+			// That's it
+			} // end of other header and data
+		}// end of for loop
+	return ETrue;
+	} // TWapTextMessage::ParseWapTextHeader
+
+
+/**
+ *  Length of header is returned
+ *  Sets internally the iTotalSegments
+ *  Does not set the segment number into the aFixedHeader
+ *  
+ *  Length of complete header is returned.
+ *  On return the header is not complete, if SAR is needed, because the segment
+ *  number is not set. Thus aFixedHeader.Length() != (return value of this function) is true.
+ *  The segment number can be set by inserting it into aSegmentNumberIndex position
+ *  If SAR is not needed aSegmentNumberIndex = 0 and
+ *  aFixedHeader.Length() == (return value of this function) is true
+ */
+TInt TWapTextMessage::CreateHeader(TDes8& aFixedHeader, TInt& aSegmentNumberIndex)
+	{
+	LOGWAPPROT1("TWapTextMessage::CreateHeader");
+
+	// Index into KElemIndexes indicating which header elements are present
+	TInt elemIndex;
+	// Index into Indexes, for current header element
+	TInt minorIndex=KIndexDestinationPort;
+	TBuf8<4> hexNumber;
+
+	// Segment number length is set, if SAR is needed
+	TInt segmentNumberLength = 0;
+	aSegmentNumberIndex = 0;
+
+	// Determine whether long or short form is used
+	if (iIs16Bit || iSourcePort > 255 || iDestinationPort>255)
+		{
+		elemIndex = 0;
+		aFixedHeader.Copy(KSCKHeaderLong);
+		}
+	else
+		{
+		elemIndex = 3;
+		aFixedHeader.Copy(KSCKHeaderShort);
+		}
+
+	// Set destination port
+	hexNumber.NumFixedWidthUC(iDestinationPort,EHex,
+							  KElemIndexes[elemIndex].iIndexes[minorIndex+1]-KElemIndexes[elemIndex].iIndexes[minorIndex]);
+	aFixedHeader.Append(hexNumber);
+
+	// Don't set the source port,
+	// 1) if it is not set or it same as destination port and
+	// 2) data (and other header) fits in one segment
+	if (!((iSourcePort==-1 || iDestinationPort==iSourcePort)
+		  && CalculateTotalSegments(KElemIndexes[elemIndex].iIndexes[minorIndex+1])==1))
+		{
+		// Source port is present
+		elemIndex++;
+		minorIndex++;
+		if (iSourcePort==-1)
+			iSourcePort = iDestinationPort;
+
+		// Set source port
+		hexNumber.NumFixedWidthUC(iSourcePort,EHex,
+								  KElemIndexes[elemIndex].iIndexes[minorIndex+1]-KElemIndexes[elemIndex].iIndexes[minorIndex]);
+		aFixedHeader.Append(hexNumber);
+
+		// Add the SAR info when source port is set
+		elemIndex++;
+
+		// Set reference
+		minorIndex++;
+		hexNumber.NumFixedWidthUC(iReference,EHex,
+								  KElemIndexes[elemIndex].iIndexes[minorIndex+1]-KElemIndexes[elemIndex].iIndexes[minorIndex]);
+		aFixedHeader.Append(hexNumber);
+
+		// Set fragment count
+		minorIndex++;
+		CalculateTotalSegments(KElemIndexes[elemIndex].iIndexes[KElemIndexes[elemIndex].iLastIndex]);
+		hexNumber.NumFixedWidthUC(iTotalSegments,EHex,
+								  KElemIndexes[elemIndex].iIndexes[minorIndex+1]-KElemIndexes[elemIndex].iIndexes[minorIndex]);
+		aFixedHeader.Append(hexNumber);
+
+		// Return the index for segment number in the header
+		minorIndex++;
+		aSegmentNumberIndex = KElemIndexes[elemIndex].iIndexes[minorIndex];
+		segmentNumberLength = 2;
+		}
+
+	aFixedHeader.Append(iRefOtherHeader);
+	aFixedHeader.Append(_L(" "));
+
+	return aFixedHeader.Length()+segmentNumberLength;
+	} // TWapTextMessage::CreateHeader
+
+
+/**
+ *  Calculates count of segments to send a used data (based on iRefData)
+ *  The values is assigned to iTotalSegments
+ *  The affect of terminating ' ' is taken into inside the method
+ */
+TInt TWapTextMessage::CalculateTotalSegments(TInt aFixedLength)
+	{
+	LOGWAPPROT2("TWapTextMessage::CalculateTotalSegments [aFixedLength=%d]", aFixedLength);
+
+	// '+1': length of terminating ' '
+	TInt  length        = aFixedLength + iRefOtherHeader.Length() + 1;
+	TInt  remain        = KMaxSmsChars - length;
+	TInt  dataSegmented = 0;
+
+	__ASSERT_DEBUG(remain > 0, Panic(KPanicTextHeaderTooLong));
+
+	iTotalSegments = 0;
+	do
+		{
+		iTotalSegments++;
+
+		//
+		// Count any escaped characters we can be sure that the converted data
+		// size fits inside the remaining length (e.g. so that non-7bit characters
+		// when converted by the SMS Stack will still fit).
+		//
+		TInt  segmentSize = iRefData.Length() - dataSegmented;
+
+		if (segmentSize > remain)
+			{
+			segmentSize = remain;
+			}
+
+		while (segmentSize > 1)
+			{
+			TPtrC8  segmentData(iRefData.Mid(dataSegmented, segmentSize));
+			TInt  non7bitCharEscapes = 0;
+			
+			//
+			// Count all non-7bit characters that will be escaped (many non-7bit
+			// characters are not escaped, but converted to "?"). The ones
+			// that are known to be escaped are list below:
+			//
+			//	  12  [Form Feed].
+			//	  91  "["
+			//	  92  "\"
+			//	  93  "]"
+			//	  94  "^"
+			//	 123  "{"
+			//	 124  "|"
+			//	 125  "}"
+			//	 126  "~"
+			//
+			for (TInt  ch = 0;  ch < segmentSize;  ch++)
+				{
+				if (segmentData[ch] == 12  ||
+					(segmentData[ch] >= 91  &&  segmentData[ch] <= 94)  ||
+					(segmentData[ch] >= 123  &&  segmentData[ch] <= 126))
+					{
+					non7bitCharEscapes++;
+					}
+				}
+			
+			//
+			// Can it fit? If so store it, otherwise reduce the size...
+			//
+			if (segmentData.Length() + non7bitCharEscapes <= remain)
+				{
+				break;
+				}
+			
+			segmentSize--;
+			}
+		
+			dataSegmented += segmentSize;
+		}
+	while (dataSegmented < iRefData.Length());
+	
+	//
+	// At least one fragment is needed...
+	//
+	if (iTotalSegments == 0)
+		{
+		iTotalSegments = 1;
+		}
+
+	return iTotalSegments;
+	} // TWapTextMessage::CalculateTotalSegments
+
+// EOF - WAPTHDR.CPP