--- /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;
+ }
+ }