messagingfw/wappushfw/pushutils/src/chttpresponse.cpp
changeset 0 8e480a14352b
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/messagingfw/wappushfw/pushutils/src/chttpresponse.cpp	Mon Jan 18 20:36:02 2010 +0200
@@ -0,0 +1,1312 @@
+// Copyright (c) 1998-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:
+// Purpose:  This file provides the definition of the CHTTPResponse class.
+// The HTTP Response class encodes HTTP response headers only. It
+// contains the methods used to transcode from WSP->HTTP fields
+// Local includes
+// 
+//
+
+
+
+// System includes
+//
+#include <wspdecoder.h>
+
+// User includes
+#include "chttpresponse.h"
+
+const TUint8 KWapQuote = 0x7F;
+const TUint8 KTop3BitSet = 0x70;
+const TUint8 KCarryBitMask = 0x80;
+
+// Default MIME type is text/plain if we can't identify any other
+//
+static const TText8* const defaultType = _S8("application/octet-stream");
+
+// Implementation of CHTTPResponse class
+//
+
+
+// Factory method to construct this class.
+//
+// Rtn: a new CHTTPResponse object, by ptr. Ownership is transferred to the
+//      caller.
+//
+
+CHTTPResponse* CHTTPResponse::NewL()
+	{
+	CHTTPResponse* me = new(ELeave)CHTTPResponse();
+	CleanupStack::PushL(me);
+	me->ConstructL();
+	CleanupStack::Pop();
+	return me;
+	}
+
+
+// Destructor for this class. Removes this object and releases memory held
+// by it
+//
+
+CHTTPResponse::~CHTTPResponse()
+    {
+    Reset();
+	//__CLOSE_LOG;
+    }
+
+
+// Clean out the fields buffer
+//
+
+void CHTTPResponse::Reset()
+    {
+	//__LOG_ENTER(_L("CHTTPResponse::Reset"));
+    delete iResponse;
+    iResponse = NULL;
+	//__LOG_RETURN;
+    }
+
+
+// Set the fields buffer with the response received from the WAP Stack
+//
+// In:
+//  aResponse - an 8-bit descriptor field containing the origin server's
+//				WSP-encoded response header. Ownership is transferred to
+//				this class.
+//
+
+void CHTTPResponse::AddResponse(HBufC8* aResponse)
+    {
+	//__LOG_ENTER(_L("CHTTPResponse::AddResponse"));
+    delete iResponse;
+    iResponse = aResponse;
+#ifdef _DEBUG
+	DumpToLog(*aResponse);
+#endif
+	//__LOG_RETURN;
+    }
+
+
+// Accessor to the HTTP response fields buffer
+//
+// Rtn: a reference to the response. Ownership is _NOT_ transferred
+//
+// NOTE THIS SHOULD RETURN CONST - BUT CAN'T BE CHANGED SINCE IT WOULD
+// BREAK BC.
+
+HBufC8& CHTTPResponse::Response() const
+    {
+	//__LOG_ENTER(_L("CHTTPResponse::Response"));
+	//__LOG_RETURN;
+    return *iResponse;
+    }
+
+
+// Accessor to the HTTP status code (e.g. 400, 300, 200, 500)
+//
+// Rtn: the status code - series number only.
+//
+
+THttpStatusCode CHTTPResponse::StatusCode() const
+    {
+	//__LOG_ENTER(_L("CHTTPResponse::StatusCode"));
+	//__LOG_RETURN;
+    return iStatusCode;
+    }
+
+
+// Accessor to the HTTP detailed status code (e.g. 404, 304, 200, 501)
+//
+// Rtn: the status code - series and specific code value
+//
+
+THttpStatusCode CHTTPResponse::DetailedStatusCode() const
+    {
+	//__LOG_ENTER(_L("CHTTPResponse::DetailedStatusCode"));
+	//__LOG_RETURN;
+    return iDetailedStatusCode;
+    }
+
+
+// Accessor to set the HTTP response status.
+//
+// In:
+//  aCode - the WSP-encoded status code
+//
+
+void CHTTPResponse::SetStatusCode(TInt aCode)
+    {
+//	__LOG_ENTER(_L("CHTTPResponse::SetStatusCode"));
+//	__LOG1(_L("CHTTPResponse::SetStatusCode : 'aCode' = %d"), aCode);
+    
+	// Set iDetailedStatusCode to a default
+    iDetailedStatusCode = EHttpUnknown;
+
+    // Magic no.s come from the WAP WSP specification, Appendix A, Table 36
+    switch (aCode)
+        {
+    case 0x10:
+        iStatusCode = EHttpContinue;
+        break;
+    case 0x11:
+        iDetailedStatusCode = EHttpSwitchingProtocols;
+        iStatusCode = EHttpContinue;
+        break;
+
+    case 0x20:
+        iStatusCode = EHttpOK;
+        break;
+    case 0x21:
+        iDetailedStatusCode = EHttpCreated;
+        iStatusCode = EHttpOK;
+        break;
+    case 0x22:
+        iDetailedStatusCode = EHttpAccepted;
+        iStatusCode = EHttpOK;
+        break;
+    case 0x23:
+        iDetailedStatusCode = EHttpNonAuthorativeInformation;
+        iStatusCode = EHttpOK;
+        break;
+    case 0x24:
+        iDetailedStatusCode = EHttpNoContent;
+        iStatusCode = EHttpOK;
+        break;
+    case 0x25:
+        iDetailedStatusCode = EHttpResetContent;
+        iStatusCode = EHttpOK;
+        break;
+    case 0x26:
+        iDetailedStatusCode = EHttpPartialContent;
+        iStatusCode = EHttpOK;
+        break;
+
+    case 0x30:
+        iStatusCode = EHttpMultipleChoices;
+        break;
+    case 0x31:
+        iDetailedStatusCode = EHttpMovedPermanently;
+        iStatusCode = EHttpMultipleChoices;
+        break;
+    case 0x32:
+        iDetailedStatusCode = EHttpMovedTemporarily;
+        iStatusCode = EHttpMultipleChoices;
+        break;
+    case 0x33:
+        iDetailedStatusCode = EHttpSeeOther;
+        iStatusCode = EHttpMultipleChoices;
+        break;
+    case 0x34:
+        iDetailedStatusCode = EHttpNotModified;
+        iStatusCode = EHttpMultipleChoices;
+        break;
+    case 0x35:
+        iDetailedStatusCode = EHttpUseProxy;
+        iStatusCode = EHttpMultipleChoices;
+        break;
+
+    case 0x40:
+        iStatusCode = EHttpBadRequest;
+        break;
+    case 0x41:
+        iDetailedStatusCode = EHttpUnauthorized;
+        iStatusCode = EHttpBadRequest;
+        break;
+    case 0x42:
+        iDetailedStatusCode = EHttpPaymentRequired;
+        iStatusCode = EHttpBadRequest;
+        break;
+    case 0x43:
+        iDetailedStatusCode = EHttpForbidden;
+        iStatusCode = EHttpBadRequest;
+        break;
+    case 0x44:
+        iDetailedStatusCode = EHttpNotFound;
+        iStatusCode = EHttpBadRequest;
+        break;
+    case 0x45:
+        iDetailedStatusCode = EHttpMethodNotAllowed;
+        iStatusCode = EHttpBadRequest;
+        break;
+    case 0x46:
+        iDetailedStatusCode = EHttpNotAcceptable;
+        iStatusCode = EHttpBadRequest;
+        break;
+    case 0x47:
+        iDetailedStatusCode = EHttpProxyAuthenticationRequired;
+        iStatusCode = EHttpBadRequest;
+        break;
+    case 0x48:
+        iDetailedStatusCode = EHttpRequestTimeout;
+        iStatusCode = EHttpBadRequest;
+        break;
+    case 0x49:
+        iDetailedStatusCode = EHttpConflict;
+        iStatusCode = EHttpBadRequest;
+        break;
+    case 0x4a:
+        iDetailedStatusCode = EHttpGone;
+        iStatusCode = EHttpBadRequest;
+        break;
+    case 0x4b:
+        iDetailedStatusCode = EHttpLengthRequired;
+        iStatusCode = EHttpBadRequest;
+        break;
+    case 0x4c:
+        iDetailedStatusCode = EHttpPreconditionFailed;
+        iStatusCode = EHttpBadRequest;
+        break;
+    case 0x4d:
+        iDetailedStatusCode = EHttpRequestEntityTooLarge;
+        iStatusCode = EHttpBadRequest;
+        break;
+    case 0x4e:
+        iDetailedStatusCode = EHttpRequestURITooLong;
+        iStatusCode = EHttpBadRequest;
+        break;
+    case 0x4f:
+        iDetailedStatusCode = EHttpUnsupportedMediaType;
+        iStatusCode = EHttpBadRequest;
+        break;
+
+    case 0x60:
+        iStatusCode = EHttpInternalServerError;
+        break;
+    case 0x61:
+        iDetailedStatusCode = EHttpNotImplemented;
+        iStatusCode = EHttpInternalServerError;
+        break;
+    case 0x62:
+        iDetailedStatusCode = EHttpBadGateway;
+        iStatusCode = EHttpInternalServerError;
+        break;
+    case 0x63:
+        iDetailedStatusCode = EHttpServiceUnavailable;
+        iStatusCode = EHttpInternalServerError;
+        break;
+    case 0x64:
+        iDetailedStatusCode = EHttpGatewayTimeout;
+        iStatusCode = EHttpInternalServerError;
+        break;
+    case 0x65:
+        iDetailedStatusCode = EHttpHTTPVersionNotSupported;
+        iStatusCode = EHttpInternalServerError;
+        break;
+
+    default:
+        iStatusCode = EHttpUnknown;
+        break;
+        }
+    if (iDetailedStatusCode == EHttpUnknown)
+        iDetailedStatusCode = iStatusCode;
+//	__LOG1(_L("CHTTPResponse::SetStatusCode : status code = %d"), iStatusCode);
+//	__LOG1(_L("CHTTPResponse::SetStatusCode : detailed status code = %d"), iDetailedStatusCode);
+//	__LOG_RETURN;
+    }
+
+
+// Method to find a named field, that returns null terminated 
+// WSP text strings. Note that there is no checking that it is a text string
+// which follows.
+//
+// In:
+//  aField		- the field type
+//  aStartIndex	- the index to search from (defaults to the buffer start)
+//
+// Out:
+//  aDesc - a pointer-type descriptor into the response buffer at the
+//			position where the field was located. The caller must NOT
+//			modify the descriptor contents
+//
+// Rtn: TBool - set to ETrue if the field was found, EFalse otherwise
+//
+
+TBool CHTTPResponse::FindField(THttpHeaderField aField,
+							   TPtrC8& aDesc,
+							   TInt aStartIndex) const
+    {
+//	__LOG_ENTER(_L("CHTTPResponse::FindField (string)"));
+//	__LOG1(_L("CHTTPResponse::FindField : searching for field type = %d"), aField);
+    TInt index = LocateField(aField, aStartIndex);
+    if (index >0)
+        {
+        TInt count = 0;
+        for (count = index; (count < iResponse->Length()) &&
+			(iResponse->Des()[count] != 0); count++) {};
+        if (count <= iResponse->Length())
+            {
+            aDesc.Set(&(iResponse->Des()[index]), count-index);
+//			__LOG(_L("CHTTPResponse::FindField : found string:"));
+#ifdef _DEBUG
+			DumpToLog(aDesc);
+#endif
+//			__LOG_RETURN;
+            return ETrue;
+            }
+        }
+//	__LOG(_L("CHTTPResponse::FindField : nothing found"));
+//	__LOG_RETURN;
+    return EFalse;
+    }
+
+
+// Method to find a named field, that returns 8-bit octet data (binary
+// or strings - not stipulated which).
+//
+// In:
+//  aField		- the field type
+//  aStartIndex	- the index to search from (defaults to the buffer start)
+//
+// Out:
+//  aDesc - a pointer-type descriptor into the response buffer at the
+//			position where the field was located. The caller must NOT
+//			modify the descriptor contents
+//
+// Rtn: TBool - set to ETrue if the field was found, EFalse otherwise
+//
+
+TBool CHTTPResponse::FindBinaryDescField(THttpHeaderField aField,
+										 TPtrC8& aDesc,
+										 TInt aStartIndex) const
+    {
+    TInt index = LocateField(aField, aStartIndex);
+    if (index >= 0)
+        {
+		TInt offset = 0;
+		TInt fieldLength = iResponse->Des()[index];	// assume the length is in
+													// a short integer
+		if(fieldLength == 31)
+			{
+			// Nope : Code 31 indicates that the following bytes make a
+			// UIntVar,  which indicates the number of data octets after it. 
+			// The UIntVar itself could be composed of upto 5 bytes
+			// Copy a full 5 bytes from the header
+			// Note that actually fewer might have been used; 
+			// the UIntVar to Int converter function returns the exact number 
+			// that were used.
+			TInt consumed = ParseUIntVar(iResponse->Des().Mid(index + 1), fieldLength);
+
+			__ASSERT_DEBUG( consumed >= KErrNone, User::Invariant() );
+			
+			offset += consumed;			
+			}
+        else if (fieldLength > 127)
+			{
+ 			// Oops be sneaky and reuse this single byte
+ 			// Because this is a special code
+ 			fieldLength = 1;		
+ 			offset = -1;
+ 			}
+
+		if(fieldLength)
+            {
+            aDesc.Set(&(iResponse->Des()[index + offset + 1]), fieldLength);
+#ifdef _DEBUG
+			DumpToLog(aDesc);
+#endif
+            return ETrue;
+            }
+        }
+    return EFalse;
+    }
+
+
+// Method to find a named field, that returns an EPOC date/time structure.
+//
+// In:
+//  aField		- the field type
+//  aStartIndex	- the index to search from (defaults to the buffer start)
+//
+// Out:
+//  aTime - a structure containing the time (and date) found in the header
+//
+// Rtn: TBool - set to ETrue if the field was found, EFalse otherwise
+//
+
+TBool CHTTPResponse::FindField(THttpHeaderField aField,
+							   TTime& aTime,
+							   TInt aStartIndex) const
+    {
+	//__LOG_ENTER(_L("CHTTPResponse::FindField (time)"));
+	TBool result = EFalse;
+    TInt index = LocateField(aField, aStartIndex);
+    if (index > 0)
+        {
+ 		TPtr8 respChars  = iResponse->Des();
+   		ExtractFieldDateValue(respChars,index,aTime);
+		result = ETrue;
+        }
+	//__LOG_RETURN;
+    return result;
+    }
+
+
+// Method to find a named field within the Cache Control header
+//
+// In:
+//  aField		- the field type
+//
+// Out:
+//
+// Rtn: TBool - set to ETrue if the field was found, EFalse otherwise
+//
+
+TBool CHTTPResponse::FindCacheControlFieldValue(TCacheControlFieldValue aField) const
+	{
+	TPtrC8 cacheControl;
+	return FindCacheControlFieldValue(aField,cacheControl) != KErrNotFound;
+	}
+
+
+// Method to find a named field within the Cache Control header, 
+// that returns an EPOC date/time structure.
+//
+// In:
+//  aField		- the field type
+//
+// Out:
+//  aTime - a structure containing the time (and date) found in the header
+//			field
+//
+// Rtn: TBool - set to ETrue if the field was found, EFalse otherwise
+//
+
+TBool CHTTPResponse::ExtractCacheControlTime(TCacheControlFieldValue aField,
+											 TTime& aTime) const
+	{
+	//__LOG_ENTER(_L("CHTTPResponse::ExtractCacheControlTime"));
+	__ASSERT_DEBUG(aField == ECacheCtrlMaxAge || aField == ECacheCtrlMaxStale
+						|| aField == ECacheCtrlMinFresh, User::Invariant());
+	TBool result = EFalse;
+	TPtrC8 cacheControl;
+	aTime = 0;
+	TInt index = FindCacheControlFieldValue(aField, cacheControl);
+	if(index != KErrNotFound)
+		{
+		// Have the cache control and the field position
+		// Now we need to extract the field's delta-secs value
+		index++;
+		TInt time = 0;
+		TPtrC8 integerSource = cacheControl.Mid(index);
+
+		if(integerSource[0] >= 0x80)			// Short integer value
+			time = integerSource[0] & 0x7F;
+		else								// Otherwise its multi octet
+			ExtractMultiOctetInteger(time, integerSource);
+		
+		TTimeIntervalSeconds timeSeconds(time);
+		aTime += timeSeconds;			// Store the seconds in the time field
+		result = ETrue;
+		}
+	//__LOG_RETURN;
+	return result;
+	}
+
+
+// Method to search for the content type encoded in the response header
+//
+// Out:
+//  aDesc - a pointer-type descriptor into the appropriate element of an
+//			array prefilled with all the content types that have WSP
+//			encodings. e.g. "text/vnd.wap.wml".  The contents of the
+//			descriptor must NOT be modified.
+//
+
+void CHTTPResponse::ContentType(TPtrC8& aDesc) const
+    {
+	// Decode the content-type data as per the WSP bnf...
+	// Note - There is no provision available here to handle content-type parameters
+	// so parameters are ignored here.
+	TInt error = LocateField(EHttpContentType);
+	TInt token = 0;
+	TBool isString = EFalse;
+	if (error != KErrNotFound)
+		{
+		TPtrC8 respChars(*iResponse);
+		TWspPrimitiveDecoder wspDecoder(respChars);
+		TWspPrimitiveDecoder::TWspHeaderType type = wspDecoder.VarType();
+		switch(type)
+			{
+			case TWspPrimitiveDecoder::E7BitVal:
+				{
+				// 128-255 - encoded 7 bit value, this header has no more data
+				TUint8 byteCode = 0;
+				error = wspDecoder.Val7Bit(byteCode); // error code
+				token = static_cast<TInt>(byteCode);
+				} break;
+			case TWspPrimitiveDecoder::EString:
+				{
+				// 32-127 - value is a text string, terminated by a '\0' 
+				// Content type is embedded as a text string
+				error = wspDecoder.String(aDesc); // error code
+				isString = ETrue;
+				} break;
+			case TWspPrimitiveDecoder::ELengthVal:
+				{
+				// 0-31 -  octet is a value length
+				TInt dataLength = 0;
+				error = wspDecoder.LengthVal(dataLength);
+				if( error >= KErrNone )
+					{
+					type = wspDecoder.VarType();
+					if( type == TWspPrimitiveDecoder::E7BitVal || type == TWspPrimitiveDecoder::ELengthVal )
+						{
+						TUint32 contentTypeToken = 0;
+						error = wspDecoder.Integer(contentTypeToken);
+						token = static_cast<TInt>(contentTypeToken);
+						}
+					else if( type == TWspPrimitiveDecoder::EString )
+						{
+						error = wspDecoder.String(aDesc);
+						isString = ETrue;
+						}
+					}
+
+				} break;
+			default:
+				{
+				error = KErrNotFound;
+				} break;
+			}
+		}
+
+	if(error < KErrNone)
+		token = KErrNotFound;
+
+	// Look up the appropriate content type, provided an error hasn't occurred or the string
+	// has not already been set
+	if (token == KErrNotFound || !isString)
+		{
+		// Convert the content type string to the supplied descriptor
+		const TText8* type = ContentType(token);
+		aDesc.Set(TPtrC8(type));
+//		__LOG1(_L("CHTTPResponse::ContentType : contentIndex = %d"), contentIndex);
+		}
+
+	}
+
+
+// Method to search for the realm encoded in the response header, when the
+// response challenges the client for HTTP authentication (code 401)
+//
+// Out:
+//  aDesc - a pointer-type descriptor into the response header buffer
+//			positioned at the realm string within the challenge. The
+//			contents of the descriptor must NOT be modified.
+//
+// Rtn: TBool - set to ETrue if a WWWAuthenticate header was found, EFalse
+//				otherwise
+//
+
+TBool CHTTPResponse::FindRealm(TPtrC8& aRealm) const
+    {
+	//__LOG_ENTER(_L("CHTTPResponse::FindRealm"));
+	// Search for the WWWAuthenticate field
+	TPtrC8 realmPtr(aRealm);
+    TBool retVal = FindField(EHttpWWWAuthenticate, realmPtr, 0);
+    if (retVal)
+        {
+		// realmPtr now points to the WWWAuthentication field value. This contains the Authentication scheme, realm
+		// value and optional parameters. Check authentication is Basic (encoded as 0x80). This is stored in the
+		// second byte of the header value (i.e. index [1]).
+		if (realmPtr[1] == 0x80)
+			{
+			// Set the realm descriptor passed in
+            aRealm.Set(realmPtr.Mid(2));
+#ifdef _DEBUG
+			// In debug builds, convert the 8-bit realm to 16-bit UNICODE in order to log it.
+			HBufC16* aRealm16 = HBufC16::New(aRealm.Length());
+			if(aRealm16!=NULL)
+				{
+				TPtr16 aRealm16_Ptr = aRealm16->Des();
+				aRealm16_Ptr.Copy(aRealm);
+				//__LOG1(_L("CHTTPResponse::FindRealm : found realm string: %S"), &aRealm16_Ptr);
+				delete aRealm16;
+				}
+#endif
+			}
+		else
+			{
+			//__LOG(_L("CHTTPResponse::FindRealm : nothing found"));
+			retVal = EFalse;
+			}
+		}
+	//__LOG_RETURN;
+    return retVal;
+    }
+
+
+// Method to search for the character set encoded in the Content-Type field of
+// the response header
+//
+// Out:
+//  aDesc - a pointer-type descriptor into the appropriate element of an
+//			array prefilled with all the character sets that have WSP
+//			encodings. e.g. "utf-8".  The contents of the descriptor must
+//			NOT be modified.
+//
+// Rtn: TBool - set to ETrue if a character set was found, EFalse if not
+//
+
+TBool CHTTPResponse::CharSet(TPtrC8& aDesc) const
+    {
+//	__LOG_ENTER(_L("CHTTPResponse::CharSet"));
+	// Find the byte index in the header for the content type value
+    TInt index = LocateField(EHttpContentType);
+
+    TUint8 byteCode = 0;
+	TInt paramByteCode = KErrNotFound;
+	TInt valueByteCode1 = KErrNotFound;
+	TInt charsetCode = 0;
+	// Read the byte code, unless KErrNotFound was returned
+	if (index != KErrNotFound)
+		{
+		TPtr8 respChars = iResponse->Des();
+		TInt respLength = iResponse->Length();
+
+		// If the byteCode is in the range 0-30 then a range of bytes is
+		// indicated: the following byte gives the content type and the
+		// remainder are arranged as a series of parameter attribute-value
+		// pairs. This method checks for the presence of a 'charset' parameter.
+		byteCode = respChars[index];
+//		__LOG1(_L("CHTTPResponse::CharSet : found bytecode = %d"), byteCode);
+
+		// Check valid range ... note that a range of zero could not contain a charset
+		// parameter anyway, so exclude it...
+		if ((byteCode > 0) && (byteCode <= 30))
+			{
+			// Check for overrun... if this occurs it should be an error.  Note that
+			// corruption _could_ occur in this response buffer - some gateways, which
+			// don't return error decks (e.g. AnyTime GW) send a response buffer 1 byte
+			// long, containing only the value 0x01 - which is invalid WSP.
+			// Be conservative and safe here - we can't overrun.  Use the value of byte-
+			// -Code (which should be the WSP encoding of how many bytes follow), or the
+			// total length of the response - whichever is smaller.
+			if (index + byteCode < respLength)
+				{
+				// e,g, header to illustrate use of offsets in this code:
+				// 03 94 81 84 : Content-Type: application/vnd.wap.wmlc; charset=iso-8859-1
+				// +0 +1 +2 +3 : 03 = no. bytes in Content-Type header
+				//			   : 94 = 14 | 80 = application/vnd.wap.wmlc
+				//			   : 81 = 01 | 80 = Charset parameter
+				//			   : 84 = 04 | 80 = iso-8859-1
+				paramByteCode = respChars[index + 2];
+
+				if ((paramByteCode & 0x7f) == EHttpCharset)
+					{
+					// We have a charset
+					paramByteCode &= 0x7f;
+					valueByteCode1 = respChars[index + 3];
+
+					if (valueByteCode1 & 0x80)
+						{
+						// A short one-byte value
+						charsetCode = valueByteCode1 & 0x7f;
+						}
+					else
+						{
+						// A multibyte value
+						ExtractMultiOctetInteger(charsetCode, 
+												 respChars.Mid(index + 3));
+						}
+					}
+				}
+			else
+				{
+				index = KErrNotFound;
+				}
+			}
+		}
+
+	// If a parameter-value pair was found, determine whether it encodes a
+	// charset
+	if ( (index != KErrNotFound) && (paramByteCode == EHttpCharset) )
+		{
+		// Look up the value from the charset table.
+		const TText8* chset;
+		chset = CharSet(charsetCode);
+
+		// Convert the charset string to the supplied descriptor
+		if (chset)
+			aDesc.Set(TPtrC8(chset));
+		else
+			index = KErrNotFound; // We've found a charset but we don't recognise it
+		}
+	else	// Either no content-type header (hence no charset) or a content-type
+			// header with a parameter other than charset
+		{
+		index = KErrNotFound;
+		}
+
+//	__LOG1(_L("CHTTPResponse::CharSet : CharSet = %S"), &aDesc);
+//	__LOG_RETURN;
+	return (index !=KErrNotFound);
+    }
+
+
+// Normal constructor - do non-allocating creation of this class
+//
+
+CHTTPResponse::CHTTPResponse()
+	: iStatusCode(EHttpUnknown), iDetailedStatusCode(EHttpUnknown)
+    {
+	// Does nothing here
+    }
+
+
+// Second phase construction - any allocation for this class must take place
+// here. Sets up the resources required by an HTTP Response.
+//
+
+void CHTTPResponse::ConstructL()
+    {
+    // Does nothing
+	//__OPEN_LOG(__LOG_WAP_FILE_NAME); 
+    }
+
+
+// Method to locate a named field in the response header, starting at the
+// specified index position.
+//
+// In:
+//  aField		- the header field type
+//  aStartIndex - the (optional) position in the header to start searching
+//
+// Rtn: TInt - the index position of the required field _value_ (not the
+//             field name), or KErrNotFound otherwise.
+//
+
+TInt CHTTPResponse::LocateField(THttpHeaderField aField,
+								TInt aStartIndex) const
+    {
+	// Content-Type is a special case; it appears to always be at the first
+	// byte of the header, and doesn't have any encoding of the field name -
+	// just straight into the Field Value at byte 0.  This is an assumption
+	// however, since the WSP spec is not explicit - could it possibly be just
+	// the NWSS GW's implementation of WSP that does this?
+	if ( (aStartIndex == 0) && (aField == EHttpContentType) )
+		{
+		return aStartIndex; // the content-type field value position - ie. 0
+		}
+	
+	// Deal with other Field Names, (Possibly including Content-Type if the
+	// start index is offset into the header? Note that this is not likely to
+	// occur though, with the abbreviated encoding.)
+	TInt respLength = iResponse->Length();
+	TPtr8 respChars  = iResponse->Des();
+    for (TInt index = aStartIndex; index < respLength; index++)
+        {
+		// Examine the byte at this position in the header
+        TUint8 byteCode = respChars[index];
+
+		// Expect byteCode to be a Field Name code (unless the search is at
+		// position zero, which has a missing content-type field name). Check
+		// for the search field, remembering to clear the top bit
+        if ( ( (byteCode & 0x7f) == aField) && (index != 0) )
+            {
+			// Got it - return the next position to locate the field value,
+			// checking for potential overrun
+			if (index < respLength - 1)
+				{
+				// Advance 1 to the header field value
+				++index;
+				return index;
+				}
+			else
+				{
+				return KErrNotFound;
+				}
+            }
+        else
+            {
+			// Check that we aren't dealing with the Content-Type field
+			// (expected at position 0), since it doesn't use a field type
+			if (index != 0)
+				{
+				// WSP Spec Section 8.4.1.1 - Field Names
+				//
+				// If the byte is an alphanumeric, then it must be a field name that doesn't have
+				// a WSP encoding.  In this circumstance, we can't handle the field, and must
+				// therefore skip over it
+				if ((byteCode >= 32) && (byteCode <= 127))
+					{
+					// Hit the start of a Header Name string - this will be assumed
+					// continuous until the NUL is found or until the end
+					// of the header is hit (which would be an error)
+					while ( (respChars[index] != 0) && (index < respLength - 1) )
+						++index;
+					}
+
+				// WSP Spec Section 8.4.1.2 - Field Values
+				//
+				// Now examine the field value by advancing one place.  If that advance takes us off
+				// the end of the buffer, then (a) the WSP is invalid, and (b) the field is not found!
+				++index;
+				if (index == respLength)
+					return KErrNotFound;
+				}
+
+			// Read the next byte at this position in the header
+            byteCode = respChars[index];
+
+			// Codes 0-30 represent that number of following data octets, so
+			// they should be skipped
+			if (byteCode == 0)			// 0 data octets follow !???? : (Strange but true)
+				{
+				// __DEBUGGER();
+				}
+            else if (byteCode <= 30)
+				{
+                index += byteCode;
+				}
+            else
+                {
+				// Code 31 indicates that the following bytes make a UIntVar,
+				// which indicates the number of data octets after it. The
+				// UIntVar itself could be composed of upto 5 bytes
+                if (byteCode == 31)
+					{
+					// Copy a full 5 bytes from the header - note that actually
+					// fewer might have been used; the UIntVar to Int
+					// converter function returns the exact number that were
+					// used.
+					TInt value = 0;
+					TInt consumed = ParseUIntVar(respChars.Mid(index + 1), value);
+					
+					if( consumed < KErrNone )
+						return KErrCorrupt;
+
+					// Advance to the last byte of data in this header
+                    index += consumed + value;
+					}
+                else
+					// Codes 32-127 are alphanumerics representing a text
+					// string, up to a NUL termination
+                    if (byteCode <= 127)
+						// Hit the start of a string - this will be assumed
+						// continuous until the NUL is found or until the end
+						// of the header is hit (which would be an error)
+                        while ( (respChars[index] != 0) && (index < respLength - 1) )
+							++index;
+                }
+            }
+        }
+
+	// This return only occurs if the search ran off the end of the header
+    return KErrNotFound;
+    }
+
+
+// Perform a look-up of content type given a WSP encoding value, used as
+// an index.
+//
+// In:
+//  aIndex - the WSP encoding value
+//
+// Rtn: const TText* - the required content type text - NOT to be changed
+//
+
+const TText8* CHTTPResponse::ContentType(TInt aContentTypeCode) const
+    {
+
+    if ((aContentTypeCode >= 0) && (aContentTypeCode < KHttpNumContentTypes))
+        return KHttpContentTypes[aContentTypeCode];
+    else
+        return defaultType;
+    }
+
+
+// Perform a look-up of character set given a WSP encoding value, used as
+// an index.
+//
+// In:
+//  aCharsetCode - the index into the content types table/
+//
+// Rtn: const TText8* - the required 8-bit character set text - NOT to be
+//						changed by the caller
+//
+
+const TText8* CHTTPResponse::CharSet(TInt aCharSetCode) const
+    {
+	// Search for an index for the supplied charset code
+	TInt charSetIdx = KErrNotFound;
+	for (TInt index = 0; ((index < KHttpNumCharacterSets) &&
+									(charSetIdx == KErrNotFound)); index++)
+		{
+		if (KHttpCharacterSetCodes[index] == aCharSetCode)
+			{
+			charSetIdx = index;
+			}
+		}
+
+	// If something was found, return the corresponding charset name
+	if (charSetIdx != KErrNotFound)
+		return KHttpCharacterSetNames[charSetIdx];
+	else
+		return NULL;
+    }
+
+
+// Do a conversion from 32-bit UIntVar encoding into 32-bit integer
+//
+TInt CHTTPResponse::ParseUIntVar(const TDesC8& aBuffer, TInt& aVal) const
+	{
+	// Is there any buffer?
+	const TInt length = aBuffer.Length();
+	if( length == 0 )
+		return KErrCorrupt;
+
+	// initialize return val
+	aVal = 0;
+
+	// maximum length for a uintvar is 5
+	TInt lenLeft = Min(length, 5);
+
+	// get the first octet
+	TInt index = 0;
+	TUint8 byte = aBuffer[index++];
+	TInt numBytes = 1;
+
+	--lenLeft;	
+
+	// Check if any of the top 3 bits, ignoring the very top 'continue' bit, are set.  
+	// Later if we see that this is a 5 byte number - we'll know it is corrupt.  
+	// Encoding uses 7 bits/number 7x5=35 and we only support a maxiumum number 
+	// of 32 bits.
+	TBool topThreeBitsSet = byte & KTop3BitSet; 
+	
+	// copy over data from the byte into our return value (the top bit is a carry bit)
+	aVal = byte & KWapQuote;
+
+	// while the 'continue' bit is set and we have more data
+	while ((byte & KCarryBitMask) && (lenLeft > 0))
+		{
+		// shift our last value up
+		aVal <<= 7;
+		// get the next byte
+		byte = aBuffer[index++];
+		// copy it over to the lowest byte
+		aVal |= byte & KWapQuote;
+		--lenLeft;
+		++numBytes;
+		} 
+
+	// last octet has continue bit set ... NOT allowed Or
+	// this was encoded wrong - can't have a number bigger than 32 bits
+	if ((byte & KCarryBitMask) || (numBytes == 5 && topThreeBitsSet))
+		return KErrCorrupt;
+
+	// number of bytes read
+	return numBytes;
+	}
+
+
+// Extract a WSP encoded MultiOctet Integer encoding into 32-bit integer
+//
+// In:
+//  aSource	- the source Multi-Octet integer
+//
+// Out:
+//  aInt		- the 32-bit resulting integer
+//
+void CHTTPResponse::ExtractMultiOctetInteger(TInt& aInt, const TPtrC8& aSource) const
+ 	// Extract a WSP encoded integer from the source descriptor
+	{
+	//__LOG_ENTER(_L("CHTTPResponse::ExtractMultiOctetInteger"));
+	// Get num bytes encoding the integer - 
+	// we are positioned at that location in the source descriptor
+	TUint8 numBytes = aSource[0];
+	aInt = 0;
+	if (numBytes <= 30)
+ 		{
+ 		__ASSERT_DEBUG(numBytes <= aSource.Length(), User::Invariant());
+ 		// Loop over the source, taking each byte and shifting it in to the count.
+ 		for (TInt count = 1; (count <= numBytes); count++)
+ 		        aInt = (aInt << 8) + aSource[count];
+ 		}
+ 	else if (numBytes & 0x80) //  check top bit is set
+ 		aInt = numBytes & 0x7f;
+ 	// anything between 30 and 127 is not handled...
+	//__LOG_RETURN;
+	}
+
+
+// Method to find a named field within the Cache Control header
+//
+// In:
+//  aSource		- the descriptor containing the date value
+//	aFrom		- The position in the descriptor to start from
+//
+// Out:
+//  aTime - a structure containing the time (and date) found in the descriptor
+//
+// NOTE THIS METHOD WAS EXPORTED FOR TESTING OF THE CACHE. IT SHOULDN'T BE
+// NOW, BUT CAN'T BE CHANGED SINCE IT WOULD AFFECT BC.
+void CHTTPResponse::ExtractFieldDateValue(const TPtrC8& aSource,
+										  TInt aFrom,
+										  TTime& aTime) const
+	{
+	//__LOG_ENTER(_L("CHTTPResponse::ExtractFieldDateValue"));
+	// Get num bytes encoding the date - 
+	// we are positioned at that location in the source descriptor
+    TInt time = 0;
+	TPtrC8 integerSource = aSource.Mid(aFrom);
+	ExtractMultiOctetInteger(time, integerSource);
+	// The WSP Date encoding is the number of seconds since the start of the
+	// UNIX epoch (00:00:00.000, 01-Jan-1970), as a long integer
+	TDateTime unixEpocDT(1970, EJanuary, 0, 0, 0, 0, 0);
+	TTime unixEpoch(unixEpocDT);
+    TTimeIntervalSeconds timeSeconds(time);
+    aTime = unixEpoch + timeSeconds;
+	//__LOG_RETURN;
+	}
+
+
+// Method to find a named field within the Cache Control header
+//
+// In:
+//  aField		- the field type
+//
+// Out:
+//	the found aCacheControl string
+//
+// Rtn: TInt - set to KErrNotFound if the field was not found,
+//		otherwise the position in the cache control descriptor that the field
+//		was found
+//
+TInt CHTTPResponse::FindCacheControlFieldValue(TCacheControlFieldValue aField,
+											   TPtrC8& aCacheControl) const
+// Find a named field within the Cache Control header
+	{
+	//__LOG_ENTER(_L("CHTTPResponse::FindCacheControlFieldValue"));
+	TInt pos = KErrNotFound;
+	TInt index = LocateField(EHttpCacheControl, 0);
+    if (index >0)
+        {
+		// Have the cache control descriptor
+		// Now we need to search for the field
+
+		// The following rules are used to encode cache control values.
+		// Cache-control-value	=	No-cache | No-store | Max-stale |
+		//							Only-if-cached | Private | Public |
+		//							No-transform | Must-revalidate | 
+		//							Proxy-revalidate | Cache-extension | 
+		//							Value-length Cache-directive
+		// Cache-directive	=	No-cache 1*(Field-name) | 
+		//						Max-age Delta-second-value |
+		//						Max-stale Delta-second-value |
+		//						Min-fresh Delta-second-value |
+		//						Private 1*(Field-name) |
+		//						Cache-extension Parameter
+		TUint8 byteCode = iResponse->Des()[index];		// check the first byte for a recognised value
+		if((byteCode >= 32) && (byteCode <= 127))
+			{
+			// Hit the start of a Header Name string - this will be assumed
+			// continuous until the NUL is found or until the end
+			// of the header is hit (which would be an error)
+			// - not supported
+			return pos;				
+			}
+		switch (byteCode)
+			{
+			case ECacheControlNoCache:		// "no-cache"
+			case ECacheCtrlNoStore:			// "no-store"
+			case ECacheCtrlMaxStale:		// "max-stale"
+			case ECacheCtrlOnlyIfCached:	// "only-if-cached"
+			case ECacheCtrlPublic:			// "public"
+			case ECacheCtrlPrivate:			// "private"
+			case ECacheCtrlNoTransform:		// "no-transform"
+			case ECacheCtrlMustRevalidate:	// "must-revalidate"
+			case ECacheCtrlProxyRevalidate:	// "proxy-revalidate"
+				if( aField == byteCode )
+					pos = index;			// Right here (right now).
+				break;
+			case ECacheCtrlCacheExtension:	// "cache-extension":
+				break;						// Not handled
+			default:
+				{
+			// Value-length Cache-directive
+				if(FindBinaryDescField(EHttpCacheControl,aCacheControl))
+					{
+					TInt respLength = aCacheControl.Length();
+					TUint8 byteCode = 0;
+					for (TInt count = 0; count < respLength; count++)
+						{
+						byteCode = aCacheControl[count];
+						if(aField == byteCode)
+							{
+							// Found the field we are looking for
+							pos = count;
+							break;
+							}
+						else if(count < (respLength - 1))	// Check for overrun... if this occurs it should be an error
+							{
+							if (byteCode <= 30)
+								{
+								// Codes 0-30 represent that number of following data
+								// octets, check the cache directive field after the length
+								if(aField == aCacheControl[count + 1])
+									{
+									// Found the one we want
+									pos = count + 1;
+									break;
+									}
+								else if(byteCode)
+									{
+									// so the following data octets should be skipped
+									count += byteCode;
+									}
+								else
+									{
+									__DEBUGGER();
+									count++;	// 0 data octets follow !???? : (Strange but true)
+									}
+								}
+							else if (byteCode == 31)
+								{
+								// Code 31 indicates that the following bytes make a
+								// UIntVar,  which indicates the number of data octets
+								// after it. 
+								// The UIntVar itself could be composed of upto 5 bytes
+								// Copy a full 5 bytes from the header
+								// Note that actually fewer might have been used; 
+								// the UIntVar to Int converter function returns the exact
+								// number that were used.
+								TInt value = 0;
+								TInt consumed = ParseUIntVar(aCacheControl.Mid(count + 1), value);
+								
+								if( consumed < KErrNone )
+									return KErrCorrupt;
+																
+								if(aField == aCacheControl[count + 1 + consumed])
+									{
+									// Found the one we want
+									pos = count + 1 + consumed;
+									break;
+									}
+								else
+									{
+									// so the following data octets should be skipped
+									count += 1 + consumed + value;
+									}
+								}
+							}
+						}
+					}
+				}
+				break;
+			}
+		}
+	//__LOG_RETURN;
+	return pos;
+	}
+
+
+// Panic method
+//
+// In:
+//  aPanicCode - a standard HTTP panic code (see <HttpStd.h>)
+//
+/*void CHTTPResponse::Panic(THttpPanicCode aPanicCode) const
+	{
+	_LIT(KWapCHTTPResponse, "CHTTPResp");
+	User::Panic(KWapCHTTPResponse, aPanicCode);
+	}
+*/
+
+#ifdef _DEBUG
+// Debug method to dump to log the response header's binary content
+//
+void CHTTPResponse::DumpToLog(const TDesC8& aData) const
+    {
+//	__LOG_ENTER(_L("CHTTPResponse::DumpToLog"));
+
+	// Iterate the supplied block of data in blocks of 16 bytes
+	//__LOG(_L("CHTTPResponse::DumpToLog : START"));
+	TInt pos = 0;
+	TBuf<KMaxLogEntrySize> logLine;
+	TBuf<KMaxLogEntrySize> anEntry;
+	while (pos < aData.Length())
+		{
+		anEntry.Format(TRefByValue<const TDesC>_L("%04x : "), pos);
+		logLine.Append(anEntry);
+
+		// Hex output
+		for (TInt offset = 0; offset < 16; offset++)
+			{
+			if (pos + offset < aData.Length())
+				{
+				TInt nextByte = aData[pos + offset];
+				anEntry.Format(TRefByValue<const TDesC>_L("%02x "), nextByte);
+				logLine.Append(anEntry);
+				}
+			else
+				{
+				anEntry.Format(TRefByValue<const TDesC>_L("   "));
+				logLine.Append(anEntry);
+				}
+			}
+			anEntry.Format(TRefByValue<const TDesC>_L(": "));
+			logLine.Append(anEntry);
+
+		// Char output
+		for (TInt offset = 0; offset < 16; offset++)
+			{
+			if (pos + offset < aData.Length())
+				{
+				TInt nextByte = aData[pos + offset];
+				if ((nextByte >= 32) && (nextByte <= 127))
+					{
+					anEntry.Format(TRefByValue<const TDesC>_L("%c"), nextByte);
+					logLine.Append(anEntry);
+					}
+				else
+					{
+					anEntry.Format(TRefByValue<const TDesC>_L("."));
+					logLine.Append(anEntry);
+					}
+				}
+			else
+				{
+				anEntry.Format(TRefByValue<const TDesC>_L(" "));
+				logLine.Append(anEntry);
+				}
+			}
+			//__LOG1(_L("%S"), &logLine);
+			logLine.Zero();
+
+		// Advance to next 16 byte segment
+		pos += 16;
+		}
+	//__LOG(_L("CHTTPResponse::DumpToLog : END"));
+//	__LOG_RETURN;
+    }
+#endif
+
+
+// Spare methods for future BC. Const- and non-const versions to assist
+// the caller in preserving const-ness. IMPORT_C ensures they reserve a
+// slot in the vtbl, which is essential to preseve future BC.
+//
+/*TAny* CHTTPResponse::Extend_CHTTPResponse(TAny* aArgs)
+	{
+	Panic(EHttpReservedForFutureExpansion);
+	return (TAny*)aArgs;
+	}
+TAny* CHTTPResponse::Extend_CHTTPResponse_const(TAny* aArgs) const
+	{
+	Panic(EHttpReservedForFutureExpansion);
+	return (TAny*)aArgs;
+	}
+*/