applayerprotocols/wappushsupport/HTTPResponse/CHTTPResponse.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 02 Feb 2010 01:09:52 +0200
changeset 0 b16258d2340f
permissions -rw-r--r--
Revision: 201003 Kit: 201005

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



// System includes
//
#include <wspdecoder.h>
#include <chttpresponse.h>

// Wap Logging
//
#include <waplog.h>
#include <logdef.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.
//

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

EXPORT_C CHTTPResponse::~CHTTPResponse()
    {
    Reset();
	__CLOSE_LOG;
    }


// Clean out the fields buffer
//

EXPORT_C 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.
//

EXPORT_C 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.

EXPORT_C 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.
//

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

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

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

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

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

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

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

EXPORT_C 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.
//

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

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

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

EXPORT_C 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.
//

EXPORT_C 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.
//

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

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

EXPORT_C 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.
//
EXPORT_C TAny* CHTTPResponse::Extend_CHTTPResponse(TAny* aArgs)
	{
	Panic(EHttpReservedForFutureExpansion);
	return (TAny*)aArgs;
	}
EXPORT_C TAny* CHTTPResponse::Extend_CHTTPResponse_const(TAny* aArgs) const
	{
	Panic(EHttpReservedForFutureExpansion);
	return (TAny*)aArgs;
	}