applayerpluginsandutils/httpprotocolplugins/httpheadercodec/chttpgeneralheaderreader.cpp
changeset 0 b16258d2340f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/applayerpluginsandutils/httpprotocolplugins/httpheadercodec/chttpgeneralheaderreader.cpp	Tue Feb 02 01:09:52 2010 +0200
@@ -0,0 +1,531 @@
+// Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of "Eclipse Public License v1.0"
+// which accompanies this distribution, and is available
+// at the URL "http://www.eclipse.org/legal/epl-v10.html".
+//
+// Initial Contributors:
+// Nokia Corporation - initial contribution.
+//
+// Contributors:
+//
+// Description:
+//
+
+#include "chttpgeneralheaderreader.h"
+
+#include <http/rhttpsession.h>
+#include <httpstringconstants.h>
+#include <httperr.h>
+#include <inetprottextutils.h>
+
+_LIT8(KCommaSpaceSep,", ");
+_LIT8(KCommaNewline, ",\n");
+_LIT8(KEqualQuote, "=\"");
+
+CHttpGeneralHeaderReader* CHttpGeneralHeaderReader::NewL(RStringPool aStringPool)
+/** 
+	Factory constructor.
+	@internalComponent
+	@param		aStringPool	The current string pool.
+	@return		A pointer to a fully initialised object.
+	@leave		KErrNoMemory	Not enough memory to create object.
+*/
+	{
+	return new(ELeave) CHttpGeneralHeaderReader(aStringPool);
+	}
+
+CHttpGeneralHeaderReader::~CHttpGeneralHeaderReader()
+/**
+	Destructor
+	@internalComponent
+*/	{
+	}
+
+CHttpGeneralHeaderReader::CHttpGeneralHeaderReader(RStringPool aStringPool)
+: CHttpHeaderReader(aStringPool)
+/**
+	Constructor
+	@internalComponent
+	@param		aStringPool	The current string pool.
+*/
+	{
+	}
+
+void CHttpGeneralHeaderReader::DecodeCacheControlL(RHeaderField& aHeader) const
+/**
+	Decodes a Cache-Control header. RFC 2616 section 14.9 - 
+
+		Cache-Control			=	"Cache-Control" ":" 1#cache-directive
+		cache-directive			=	cache-request-directive 
+								|	cache-response-directive
+		cache-request-directive	=	"no-cache" 
+								|	"no-store" 
+								|	"max-age" "=" delta-seconds
+								|	"max-stale" [ "=" delta-seconds ] 
+								|	"min-fresh" "=" delta-seconds 
+								|	"no-transform" 
+								|	"only-if-cached" 
+								|	cache-extension
+
+		cache-response-directive=	"public" 
+								|	"private" [ "=" <"> 1#field-name <"> ] 
+								|	"no-cache" [ "=" <"> 1#field-name <"> ]
+								|	"no-store" 
+								|	"no-transform"
+								|	"must-revalidate"
+								|	"proxy-revalidate"
+								|	"max-age" "=" delta-seconds
+								|	"s-maxage" "=" delta-seconds
+								|	cache-extension
+
+		cache-extension			=	token [ "=" ( token | quoted-string ) ]
+
+	The cache-control header value is a comma separated list of values with at
+	least one value.
+	@internalComponent
+	@param		aHeader	The cache-control header field to decode.
+	@leave		CHttpReader::DecodeGenericL
+	@todo		Is there any point in this? Why not call DecodeGenericL() straight away
+*/
+	{
+	TPtrC8 rawData;
+	aHeader.RawDataL(rawData);
+	TInt remaining = rawData.Length();
+	TPtrC8 token;
+	TInt tokensFound =0;
+	RStringF maxAge = iStrPool.StringF(HTTP::EMaxAge, iStringTable);
+	RStringF maxStale = iStrPool.StringF(HTTP::EMaxStale, iStringTable);
+	RStringF minFresh = iStrPool.StringF(HTTP::EMinFresh, iStringTable);
+	RStringF smaxAge = iStrPool.StringF(HTTP::ESMaxAge, iStringTable);
+	while(remaining > 0)
+	    {
+		remaining -= InetProtTextUtils::ExtractNextTokenFromList(rawData, token, KCommaNewline());
+	
+
+		if(token.Locate('=') == KErrNotFound)
+		   {
+		  SetNewFStringPartL(aHeader, tokensFound, token);    
+		   }
+		else
+		   {
+		   CHeaderFieldPart*  part = SetNewFStringPartL(aHeader, tokensFound, KNullDesC8());
+		   TPtrC8 paramName;
+		   InetProtTextUtils::ExtractNextTokenFromList(token, paramName, '=');
+		   if ( (paramName.Compare(maxAge.DesC()) == 0) ||
+		   (paramName.Compare(maxStale.DesC()) == 0) ||
+		   (paramName.Compare(minFresh.DesC()) == 0)||
+		   (paramName.Compare(smaxAge.DesC()) == 0) )
+		       { 
+		       SetNewIntegerParamL(*part, paramName, token); 
+		       }
+		   else
+		       {
+		       SetNewFStringParamL(*part, paramName, token); 
+		       } 
+	       	   }
+	 ++tokensFound; 
+	    }
+	}
+		
+
+void CHttpGeneralHeaderReader::DecodeConnectionL(RHeaderField& aHeader) const
+/**
+	Decodes the Connection header. RFC2616 section 14.10 - 
+
+		Connection = "Connection" ":" 1#(connection-token)
+		connection-token  = token
+	The connection header value is a comma separated list of values with at least
+	one value.
+	@internalComponent
+	@param		aHeader	The connection header field to decode.
+	@leave		CHttpReader::DecodeGenericL
+	@todo		Is there any point in this? Why not call DecodeGenericL() straight away
+*/
+	{
+	DecodeGenericL(aHeader, KCommaNewline);
+	}
+
+void CHttpGeneralHeaderReader::DecodeTransferEncodingL(RHeaderField& aHeader) const
+/**
+	Decodes the transfer-encoding header. RFC2616 section 14.41 -
+
+		Transfer-Encoding       =	"Transfer-Encoding" ":" 1#transfer-coding
+		transfer-coding         =	"chunked" | transfer-extension
+		transfer-extension      =	token *( ";" parameter )
+		parameter               =	attribute "=" value
+		attribute               =	token
+		value                   =	token | quoted-string
+
+	The transfer-encoding header is a comma separated list of values with at 
+	least one value.
+	@internalComponent
+	@param		aHeader	The transfer-encoding header field to decode.
+	@leave		RHeaderField::RawDataL
+	@leave		CHttpReader::SetNewFStringPartL
+	@leave		CHttpReader::SetNewFStringParamL
+	@leave		KErrHttpDecodeTransferEncoding	There was a transfer-extension
+												with no token before the 
+												parameter.
+*/
+	{
+	TPtrC8 rawData;
+	aHeader.RawDataL(rawData);
+	TInt remaining = rawData.Length();
+	TInt xfrCodingsFound = 0;
+
+	// Iterate over the token list
+	while( remaining > 0 )
+		{
+		// locate a token and set the new header part at the appropriate position.
+		// Note - the token delimiter may be a '\n' or a ','
+		TPtrC8 xfrCoding;
+		remaining -= InetProtTextUtils::ExtractNextTokenFromList(rawData, xfrCoding, KCommaNewline);
+
+		// Search for ';' separator to determine if there is a parameter within 
+		// this transfer-coding. Don't bother check for 'chunked' per se, just 
+		// treat it as equivalent to a transfer-extension which means we would
+		// erroneously store parameters passed with 'chunked' (which the RFC doesn't allow)
+		TInt pos = xfrCoding.Locate(';');
+		if( pos == 0 )
+			{
+			// No transfer extension before the parameter pair!
+			User::Leave(KErrHttpDecodeTransferEncoding);
+			}
+		if( pos == KErrNotFound )
+			{
+			// A transfer encoding with no parameter. Trim surrounding whitespace.
+			InetProtTextUtils::RemoveWhiteSpace(xfrCoding, InetProtTextUtils::ERemoveBoth);
+
+			// Create and set the new header part at position zero
+			SetNewFStringPartL(aHeader, xfrCodingsFound, xfrCoding);
+			}
+		else
+			{
+			// Found the ';' separator - presumably a parameter/value pair.
+			TPtrC8 xfrExtn;
+			xfrExtn.Set(xfrCoding.Left(pos));
+			InetProtTextUtils::RemoveWhiteSpace(xfrExtn, InetProtTextUtils::ERemoveBoth);
+
+			// Set the transfer extension as a new header part
+			CHeaderFieldPart* part = SetNewFStringPartL(aHeader, xfrCodingsFound, xfrExtn);
+
+			// Get the param. Advance over the separator to the attrib-value pair.
+			xfrCoding.Set(xfrCoding.Mid(pos + 1));
+
+			// Search for '=' separator. If not found, then the param/value pair
+			// is badly formed - ignore for the sake of robustness.
+			pos = xfrCoding.Locate(TChar('='));
+			if( pos > 0 ) // shortest parameter name, 1 char
+				{
+				TPtrC8 parameter;
+				parameter.Set(xfrCoding.Left(pos));
+				InetProtTextUtils::RemoveWhiteSpace(parameter, InetProtTextUtils::ERemoveBoth);
+
+				TPtrC8 paramValue;
+				paramValue.Set(xfrCoding.Mid(pos + 1));
+				InetProtTextUtils::RemoveWhiteSpace(paramValue, InetProtTextUtils::ERemoveBoth);
+
+				// set the parameter of the header part
+				SetNewFStringParamL(*part, parameter, paramValue);
+				}
+			}
+		++xfrCodingsFound;
+		}
+	}
+
+void CHttpGeneralHeaderReader::DecodeContentTypeL(RHeaderField& aHeader) const
+/**
+	Decodes the content-type header. RFC2616 section 14.17
+
+		Content-Type			=	"Content-Type" ":" media-type
+		media-type				=	type "/" subtype *( ";" parameter )
+		type					=	token
+		subtype					=	token
+		parameter				=	attribute '=' value
+		attribute               =	token
+		value                   =	token | quoted-string
+	
+	The content-type header has a single media-type value. The atttribute 
+	is normally the 'charset'. E.g. Content-Type: text/html; charset=ISO-8859-4
+	@internalComponent
+	@param		aHeader	The content-type header field to decode.
+	@leave		RHeaderField::RawDataL
+	@leave		CHttpReader::SetNewFStringPartL
+	@leave		CHttpReader::SetNewFStringParamL
+	@leave		KErrHttpDecodeContentType	There was a transfer-extension with 
+											no token before the parameter.
+*/
+	{
+	TPtrC8 rawData;
+	aHeader.RawDataL(rawData);
+	TInt remaining = rawData.Length();
+
+	// Search for '\n' separator. In the case when a duplicate header has been received,
+	// only use the first instance of the valid data.
+	TInt newLinePos = rawData.Locate('\n');
+	if (newLinePos != KErrNotFound)
+		{
+		rawData.Set(rawData.Left(newLinePos));
+		}
+
+	// Search for ';' separator
+	TInt pos = rawData.Locate(TChar(';'));
+	if( pos == 0 )
+		{
+		// No media type!
+		User::Leave(KErrHttpDecodeContentType);
+		}
+	if( pos == KErrNotFound )
+		{
+		// Media type only - no parameter. Trim surrounding whitespace.
+		InetProtTextUtils::RemoveWhiteSpace(rawData, InetProtTextUtils::ERemoveBoth);
+
+		// create and set the new header part at position zero
+		SetNewFStringPartL(aHeader, 0, rawData);
+		}
+	else
+		{
+		// found the separator; there is presumably a parameter/value pair
+		TPtrC8 mediaType;
+		mediaType.Set(rawData.Left(pos));
+		InetProtTextUtils::RemoveWhiteSpace(mediaType, InetProtTextUtils::ERemoveBoth);
+
+		// set the media type as a new header part
+		CHeaderFieldPart* part = SetNewFStringPartL(aHeader, 0, mediaType); // part 0, i.e. the first (and only) part
+
+		// get the param
+		remaining -= pos;
+		if (remaining <= 0)
+			User::Leave(KErrHttpDecodeContentType);
+		rawData.Set(rawData.Mid(pos + 1)); // move rawData onto the string after the ; char
+
+		TPtrC8 token;
+		while (remaining > 0)
+			{
+			remaining -= InetProtTextUtils::ExtractNextTokenFromList(rawData, token, ';');
+			if (!token.Length())
+				{
+				// no more tokens.
+				break;
+				}
+			SetParamNameAndValueL(*part, token, ETrue);
+			}
+		}
+	}
+
+void CHttpGeneralHeaderReader::DecodeContentDispositionL(RHeaderField& aHeader) const
+/**
+	Decodes the content-dispostion header. RFC2183 section 2
+
+		disposition				:=	"Content-Disposition" ":" 
+									disposition-type
+									*(";" disposition-parm)
+									
+		disposition-type		:=	"inline"
+									/ "attachment"
+									/ extension-token 
+									; values are not case-sensitive
+		
+		disposition-param		:=	filename-parm
+                       				/ creation-date-parm
+                       				/ modification-date-parm
+                       				/ read-date-parm
+                       				/ size-parm
+                       				/ parameter
+		
+		filename-parm 			:= 	"filename" "=" value
+
+     	creation-date-parm 		:= 	"creation-date" "=" quoted-date-time
+
+     	modification-date-parm 	:= 	"modification-date" "=" quoted-date-time
+
+     	read-date-parm 			:= 	"read-date" "=" quoted-date-time
+
+     	size-parm 				:= 	"size" "=" 1*DIGIT
+
+     	quoted-date-time 		:= 	quoted-string
+                      				; contents MUST be an RFC 822 `date-time'
+                      				; numeric timezones (+HHMM or -HHMM) MUST be used
+		
+	
+	@internalComponent
+	@param		aHeader	The content-disposition header field to decode.
+	@leave		RHeaderField::RawDataL
+	@leave		CHttpReader::SetNewFStringPartL
+	@leave		CHttpReader::SetNewFStringParamL
+	@leave		KErrHttpDecodeContentDisposition	There was a transfer-extension with 
+											no token before the parameter.
+*/
+	{
+	TPtrC8 rawData;
+	aHeader.RawDataL(rawData);
+	TInt remaining = rawData.Length();
+
+	// Search for '\n' separator. In the case when a duplicate header has been received,
+	// only use the first instance of the valid data.
+	TInt newLinePos = rawData.Locate('\n');
+	if (newLinePos != KErrNotFound)
+		{
+		rawData.Set(rawData.Left(newLinePos));
+		}
+
+	// Search for ';' separator
+	TInt pos = rawData.Locate(TChar(';'));
+	if( pos == 0 )
+		{
+		// No disposition type!
+		User::Leave(KErrHttpDecodeContentDisposition);
+		}
+	if( pos == KErrNotFound )
+		{
+		// Disposition type only - no parameter. Trim surrounding whitespace.
+		InetProtTextUtils::RemoveWhiteSpace(rawData, InetProtTextUtils::ERemoveBoth);
+
+		// create and set the new header part at position zero
+		SetNewFStringPartL(aHeader, 0, rawData);
+		}
+	else
+		{
+		// found the separator; there is presumably a parameter/value pair
+		TPtrC8 mediaType;
+		mediaType.Set(rawData.Left(pos));
+		InetProtTextUtils::RemoveWhiteSpace(mediaType, InetProtTextUtils::ERemoveBoth);
+
+		// set the disposition type as a new header part
+		CHeaderFieldPart* part = SetNewFStringPartL(aHeader, 0, mediaType); // part 0, i.e. the first (and only) part
+
+		// get the param
+		remaining -= pos;
+		if (remaining <= 0)
+			{
+			User::Leave(KErrHttpDecodeContentDisposition); 
+			}
+		rawData.Set(rawData.Mid(pos + 1)); // move rawData onto the string after the ; char
+
+		while (remaining > 0)
+			{
+				if ( rawData.Find(KEqualQuote) != KErrNotFound )
+				{			     			     
+				TPtrC8 rawDataBuf;
+				rawDataBuf.Set(rawData);
+				
+				TPtrC8 param;		
+				InetProtTextUtils::ExtractNextTokenFromList(rawDataBuf, param, '=');
+				
+				TPtrC8 paramVal;					  
+				if(rawDataBuf.Locate('"') == 0)
+				{				   					 
+					// Go past the first '"' after the '='
+					rawDataBuf.Set(rawDataBuf.Mid(rawDataBuf.Locate('"')+1));
+					if ( rawDataBuf.Length() > 0)  
+					{
+						InetProtTextUtils::ExtractNextTokenFromList(rawDataBuf, paramVal, '"');
+					
+					rawData.Set(rawDataBuf);
+					if (rawData.Locate(';') != KErrNotFound)
+					{
+					// Go past the ';' character.
+					rawData.Set(rawData.Mid(1));
+					}
+					
+					SetNewFStringParamL(*part, param, paramVal);
+					}
+					else
+					{
+					// No value for the param, so set an empty param.
+					SetNewFStringParamL(*part, param, KNullDesC8());	
+					}
+				}
+				else
+				{
+					InetProtTextUtils::ExtractNextTokenFromList(rawDataBuf, paramVal, ';');
+					rawData.Set(rawDataBuf);
+					SetNewFStringParamL(*part, param, paramVal);			    
+				}
+				}
+				else
+				{
+					TPtrC8 token;
+					remaining -= InetProtTextUtils::ExtractNextTokenFromList(rawData, token,';' );				
+					
+					if (!token.Length())
+					{
+					// no more tokens.
+					break;
+					}
+					SetParamNameAndValueL(*part, token, ETrue);
+				}
+			}			
+		}
+	}
+	
+/*
+ *	Methods from CHeaderReader
+ */
+
+void CHttpGeneralHeaderReader::DecodeHeaderL(RHeaderField& aHeader)
+/** 
+	Decodes the raw header field value.
+	@internalComponent
+	@param		aHeader		The header field to decode.
+	@leave		CHttpGeneralHeaderReader::DecodeCacheControlL
+	@leave		CHttpGeneralHeaderReader::DecodeConnectionL
+	@leave		CHttpGeneralHeaderReader::DecodeContentTypeL
+	@leave		CHttpReader::DecodeDateL
+	@leave		CHttpReader::DecodeGenericL
+	@leave		CHttpReader::DecodeGenericNumberL
+	@leave		KErrNotSupported	The reader was asked to decode a header that
+									is does not support.
+*/
+	{
+	RStringF fieldStr = iStrPool.StringF(aHeader.Name());
+	switch( fieldStr.Index(iStringTable) )
+		{
+	case HTTP::ECacheControl:
+		{
+		DecodeCacheControlL(aHeader);
+		} break;
+	case HTTP::EConnection:
+		{
+		DecodeConnectionL(aHeader);
+		} break;
+	case HTTP::EDate:
+	case HTTP::EExpires:
+		{
+		DecodeDateL(aHeader);
+		} break;
+	case HTTP::EPragma:
+	case HTTP::EContentLanguage:
+	case HTTP::EContentLocation: 	// Only expect one value
+	case HTTP::EContentMD5: 		// Only expect one value
+	case HTTP::EUpgrade:
+		{
+		DecodeGenericL(aHeader, KCommaNewline());
+		} break;
+	case HTTP::ETransferEncoding:
+		{
+		DecodeTransferEncodingL(aHeader);
+		} break;
+	case HTTP::EContentEncoding:
+		{
+		DecodeTokenListHeaderL(aHeader, KCommaSpaceSep());
+		} break;
+	case HTTP::EContentLength:
+		{
+		DecodeGenericNumberL(aHeader);
+		} break;
+	case HTTP::EContentType:
+		{
+		DecodeContentTypeL(aHeader);
+		} break;
+	case HTTP::EContentDisposition:
+		{
+		DecodeContentDispositionL(aHeader);	
+		} break;
+	default:
+		User::Leave(KErrNotSupported);
+		break;
+		}
+	}