--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/webengine/osswebengine/cache/src/HttpCacheUtil.cpp Mon Mar 30 12:54:55 2009 +0300
@@ -0,0 +1,1680 @@
+/*
+* Copyright (c) 2005 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "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: Implementation of HttpCacheUtil
+*
+*/
+
+
+// INCLUDE FILES
+#include "HttpCacheUtil.h"
+#include <http/rhttpheaders.h>
+#include <http/RHTTPTransaction.h>
+#include <http.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stringpool.h>
+#include <flogger.h>
+#include "TInternetdate.h"
+#include "HttpFilterCommonStringsExt.h"
+#include "HttpCacheEntry.h"
+
+// EXTERNAL DATA STRUCTURES
+
+// EXTERNAL FUNCTION PROTOTYPES
+
+// CONSTANTS
+_LIT8( KHttpNewLine, "\r\n" );
+// if you change it here, change in HeadersToBufferL too
+_LIT8( KHttpFieldSeparator, ": " );
+_LIT8( KHttpValueSep, "," );
+const TInt KMaxHeaderStrLen = 1024;
+_LIT8( KHttpContentType, "Content-Type" );
+
+#ifdef __CACHELOG__
+_LIT( KDateString,"%D%M%Y%/0%1%/1%2%/2%3%/3 %-B%:0%J%:1%T%:2%S%.%*C4%:3%+B");
+_LIT( KHttpCacheGeneralFileName, "cachehandler.txt" );
+_LIT( KHttpCacheHashFileName, "hash.txt" );
+const TInt KCurrentLogLevel = 0;
+#endif // __CACHELOG__
+
+// MACROS
+
+// LOCAL CONSTANTS AND MACROS
+
+// MODULE DATA STRUCTURES
+
+// LOCAL FUNCTION PROTOTYPES
+
+// FORWARD DECLARATIONS
+
+// ============================= LOCAL FUNCTIONS ===============================
+
+// ============================ MEMBER FUNCTIONS ===============================
+
+// ============================ MEMBER FUNCTIONS ===============================
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::SetEntryOnTransL
+//
+// -----------------------------------------------------------------------------
+//
+void HttpCacheUtil::SetEntryOnTransL(
+ CHttpCacheEntry& aEntry,
+ RHTTPTransaction& aTrans,
+ RStringF& aCacheEntryStr )
+ {
+ RHTTPTransactionPropertySet propSet = aTrans.PropertySet();
+
+ // set transaction property with the event handler callback functions' pointer
+ THTTPHdrVal tokenVal = (TInt)(CHttpCacheEntry*)&aEntry;
+ aTrans.PropertySet().RemoveProperty( aCacheEntryStr );
+ aTrans.PropertySet().SetPropertyL( aCacheEntryStr, tokenVal );
+ }
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::EntryOnTransL
+//
+// -----------------------------------------------------------------------------
+//
+CHttpCacheEntry* HttpCacheUtil::EntryOnTransL(
+ RHTTPTransaction& aTrans,
+ RStringF& aCacheEntryStr )
+ {
+ //
+ CHttpCacheEntry* entry = NULL;
+ //
+ THTTPHdrVal entryPtr;
+ RHTTPTransactionPropertySet propSet = aTrans.PropertySet();
+ // this is a transaction, already forwarded to download manager
+ if( propSet.Property( aCacheEntryStr, entryPtr ) )
+ {
+ entry = REINTERPRET_CAST( CHttpCacheEntry*, entryPtr.Int() );
+ }
+ return entry;
+ }
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::HeadersToBufferLC
+//
+// -----------------------------------------------------------------------------
+//
+HBufC8* HttpCacheUtil::HeadersToBufferLC(
+ RHTTPHeaders& aHttpHeaders,
+ RStringPool& aStrP )
+ {
+ // Get an iterator for the collection of response headers
+ HBufC8* headerString = HBufC8::NewL( KMaxHeaderStrLen );
+ TPtr8 headerPtr( headerString->Des() );
+ TInt newLength( 0 );
+ THTTPHdrFieldIter it = aHttpHeaders.Fields();
+
+ while( it.AtEnd() == EFalse )
+ {
+ TPtrC8 rawData;
+ // Get name of next header field
+ RStringTokenF fieldName = it();
+ RStringF fieldNameStr = aStrP.StringF( fieldName );
+ aHttpHeaders.GetRawField( fieldNameStr, rawData );
+
+ newLength+=( fieldNameStr.DesC().Length() + KHttpFieldSeparator().Length() + rawData.Length() +
+ KHttpNewLine().Length() );
+
+ if( headerPtr.MaxLength() < newLength )
+ {
+ // realloc
+ HBufC8* temp = HBufC8::New( newLength + KMaxHeaderStrLen );
+ if( !temp )
+ {
+ delete headerString;
+ User::LeaveNoMemory();
+ }
+ temp->Des().Copy( headerPtr );
+ delete headerString;
+ headerString = temp;
+ headerPtr.Set( headerString->Des() );
+ }
+ // append
+ headerPtr.Append( fieldNameStr.DesC() );
+ headerPtr.Append( KHttpFieldSeparator );
+ headerPtr.Append( rawData );
+ headerPtr.Append( KHttpNewLine );
+ ++it;
+ }
+ CleanupStack::PushL( headerString );
+ return headerString;
+ }
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::BufferToHeadersL
+//
+// -----------------------------------------------------------------------------
+//
+void HttpCacheUtil::BufferToHeadersL(
+ const TDesC8& aHeaderStr,
+ RHTTPHeaders& aHttpHeaders,
+ RStringPool& aStrP )
+ {
+ // take header str line by line
+ TInt sepPos( 0 );
+ TInt lineEnd( 0 );
+ TPtrC8 headerStr( aHeaderStr );
+
+ while( lineEnd != KErrNotFound )
+ {
+ lineEnd = headerStr.Find( KHttpNewLine );
+ sepPos = headerStr.Find( KHttpFieldSeparator );
+
+ if( lineEnd != KErrNotFound && sepPos != KErrNotFound &&
+ lineEnd > sepPos )
+ {
+ // ETag: "9be043c1-175-793-41419a44"
+ TPtrC8 line( headerStr.Left( lineEnd ) );
+ // ETag
+ TPtrC8 key( line.Left( sepPos ) );
+ // "9be043c1-175-793-41419a44"
+ TInt sepEnd( sepPos + KHttpFieldSeparator().Length() );
+ TPtrC8 value( line.Mid( sepEnd ) );
+ // add it to the http header
+ RStringF nameStr = aStrP.OpenFStringL( key );
+ CleanupClosePushL( nameStr );
+ // remove if exists
+ THTTPHdrVal headerValue;
+ if( aHttpHeaders.GetField( nameStr, 0, headerValue ) == KErrNone )
+ {
+ //
+ aHttpHeaders.RemoveField( nameStr );
+ }
+ TRAPD( err, aHttpHeaders.SetRawFieldL( nameStr, value, KHttpValueSep ) );
+ // leave OOM only
+ if( err == KErrNoMemory )
+ {
+ User::Leave( err );
+ }
+ CleanupStack::PopAndDestroy(); // namestr
+
+ // move string to the end of the line
+ headerStr.Set( headerStr.Mid( lineEnd + KHttpNewLine().Length() ) );
+ }
+ }
+ }
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::BodyToBufferL
+//
+// -----------------------------------------------------------------------------
+//
+HBufC8* HttpCacheUtil::BodyToBufferL(
+ MHTTPDataSupplier& aBodySupplier )
+ {
+ // get it from the transaction
+ TPtrC8 ptr;
+ aBodySupplier.GetNextDataPart( ptr );
+ //
+ return ptr.AllocL();
+ }
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::MergeHeadersLC
+//
+// -----------------------------------------------------------------------------
+//
+HBufC8* HttpCacheUtil::MergeHeadersLC(
+ const TDesC8& aCachedHeaderStr,
+ RHTTPHeaders& aResponseHeaders,
+ RStringPool& aStrP )
+ {
+ // FYI: it uses the response header to merge
+ // take header str line by line
+ TInt sepPos( 0 );
+ TInt lineEnd( 0 );
+ TPtrC8 headerStr( aCachedHeaderStr );
+
+ while( lineEnd != KErrNotFound )
+ {
+ lineEnd = headerStr.Find( KHttpNewLine );
+ sepPos = headerStr.Find( KHttpFieldSeparator );
+
+ if( lineEnd != KErrNotFound && sepPos != KErrNotFound &&
+ lineEnd > sepPos )
+ {
+ // ETag: "9be043c1-175-793-41419a44"
+ TPtrC8 line( headerStr.Left( lineEnd ) );
+ // ETag
+ TPtrC8 key( line.Left( sepPos ) );
+ // "9be043c1-175-793-41419a44"
+ TInt sepEnd( sepPos + KHttpFieldSeparator().Length() );
+ TPtrC8 value( line.Mid( sepEnd ) );
+ // add it to the http header
+ RStringF nameStr = aStrP.OpenFStringL( key );
+ CleanupClosePushL( nameStr );
+ // avoid corrupted content type from the stack in case of 304
+ if(key == KHttpContentType)
+ {
+ aResponseHeaders.RemoveField(nameStr);
+ }
+ THTTPHdrVal tempVal;
+ // add this field unless it already exists
+ if( aResponseHeaders.GetField( nameStr, 0, tempVal ) == KErrNotFound )
+ {
+ TRAPD( err, aResponseHeaders.SetRawFieldL( nameStr, value, KHttpValueSep ) );
+ // leave OOM only
+ if( err == KErrNoMemory )
+ {
+ User::Leave( err );
+ }
+ }
+ CleanupStack::PopAndDestroy(); // namestr
+ // move string to the end of the line
+ headerStr.Set( headerStr.Mid( lineEnd + KHttpNewLine().Length() ) );
+ }
+ }
+ // we've got the merged headers, let's covert it to buffer
+ return HeadersToBufferLC( aResponseHeaders, aStrP );
+ }
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::AddHeaderLC
+//
+// -----------------------------------------------------------------------------
+//
+HBufC8* HttpCacheUtil::AddHeaderLC(
+ const TDesC8& aName,
+ const TDesC8& aValue,
+ const TDesC8& aHeaders )
+ {
+ HBufC8* newHeaders = NULL;
+ // first check if the header exists so
+ // then all we need to do is to update the value
+ TInt pos( aHeaders.FindF( aName ) );
+ //
+ newHeaders = HBufC8::NewLC( aHeaders.Length() + aName.Length() + KHttpFieldSeparator().Length() +
+ aValue.Length() + KHttpNewLine().Length() );
+ //
+ if( pos != KErrNotFound )
+ {
+ // replace old header with this new one
+ TPtr8 newHeadersPtr( newHeaders->Des() );
+ // copy headers
+ newHeadersPtr.Copy( aHeaders );
+ // check what we need to replace
+ TPtrC8 leftover( aHeaders.Mid( pos ) );
+ // headers are terminated by \r\n
+ TInt endPos( leftover.Find( KHttpNewLine ) );
+ if( endPos != KErrNotFound )
+ {
+ // value pos
+ TInt valuePos( pos + aName.Length() + KHttpFieldSeparator().Length() );
+ newHeadersPtr.Replace( valuePos, pos + endPos - valuePos, aValue );
+ }
+ }
+ else
+ {
+ // add new header
+ // new header = old header\r\n aName: aValue\r\n
+ TPtr8 newHeadersPtr( newHeaders->Des() );
+ // old headers
+ newHeadersPtr.Copy( aHeaders );
+ // Cache Control
+ newHeadersPtr.Append( aName );
+ // :
+ newHeadersPtr.Append( KHttpFieldSeparator );
+ // max age=180
+ newHeadersPtr.Append( aValue );
+ // \r\n
+ newHeadersPtr.Append( KHttpNewLine );
+ }
+ return newHeaders;
+ }
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::HeaderFieldFromBufferLC
+//
+// -----------------------------------------------------------------------------
+//
+HBufC8* HttpCacheUtil::HeaderFieldFromBufferLC(
+ const TDesC8& aHeaders,
+ const TDesC8& aKey )
+ {
+ HBufC8* value = NULL;
+ //
+ TInt pos( aHeaders.Find( aKey ) );
+ if( pos != KErrNotFound )
+ {
+ // line: Cache-Control: max-age=180
+ TPtrC8 linePtr = aHeaders.Mid( pos );
+ // line end \r\n
+ TInt lineEnd( linePtr.Find( KHttpNewLine ) );
+ // separator :
+ TInt sepPos( linePtr.Find( KHttpFieldSeparator ) );
+ //
+ if( sepPos != KErrNotFound )
+ {
+ // fix line end
+ lineEnd = lineEnd == KErrNotFound ? linePtr.Length(): lineEnd;
+ // max-age=180
+ TInt valueLen( lineEnd - ( sepPos + KHttpFieldSeparator().Length() ) );
+ value = HBufC8::NewLC( valueLen );
+ value->Des().Copy( linePtr.Mid( sepPos + KHttpFieldSeparator().Length(), valueLen ) );
+ }
+ }
+ return value;
+ }
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::MethodFromStr
+//
+// -----------------------------------------------------------------------------
+//
+
+TCacheLoadMethod HttpCacheUtil::MethodFromStr(
+ RStringF aMethodStr,
+ RStringPool aStrP )
+ {
+ if( aMethodStr == aStrP.StringF( HTTP::EGET, RHTTPSession::GetTable() ) )
+ {
+ return EMethodGet;
+ }
+ if( aMethodStr == aStrP.StringF(HTTP::ECONNECT, RHTTPSession::GetTable() ) )
+ {
+ return EMethodConnect;
+ }
+ if( aMethodStr == aStrP.StringF(HTTP::EDELETE, RHTTPSession::GetTable() ) )
+ {
+ return EMethodDelete;
+ }
+ if( aMethodStr == aStrP.StringF(HTTP::EHEAD, RHTTPSession::GetTable() ) )
+ {
+ return EMethodHead;
+ }
+ if( aMethodStr == aStrP.StringF(HTTP::EOPTIONS, RHTTPSession::GetTable() ) )
+ {
+ return EMethodOptions;
+ }
+ if( aMethodStr == aStrP.StringF(HTTP::EPUT, RHTTPSession::GetTable() ) )
+ {
+ return EMethodPut;
+ }
+ if( aMethodStr == aStrP.StringF(HTTP::EPOST, RHTTPSession::GetTable() ) )
+ {
+ return EMethodPost;
+ }
+ if( aMethodStr == aStrP.StringF(HTTP::ETRACE, RHTTPSession::GetTable() ) )
+ {
+ return EMethodTrace;
+ }
+ return EMethodGet;
+ }
+
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::ProtectedEntry
+//
+// Returns true if the entry is protected
+//
+// -----------------------------------------------------------------------------
+//
+TBool HttpCacheUtil::ProtectedEntry(
+ RHTTPHeaders& aHttpHeaders,
+ RStringPool& aStrP )
+ {
+ TBool protectedEntry( EFalse );
+ //
+ THTTPHdrVal contType;
+ RStringF fieldName = aStrP.StringF( HTTP::EContentType, RHTTPSession::GetTable() );
+ //
+ if( aHttpHeaders.GetField( fieldName, 0, contType ) == KErrNone )
+ {
+ if( contType.Type() == THTTPHdrVal::KStrFVal )
+ {
+ RStringF fieldValStr = aStrP.StringF( contType.StrF() );
+ // css and javascrip are protected entries
+ protectedEntry = fieldValStr.DesC().Find( _L8("text/css") ) != KErrNotFound ||
+ fieldValStr.DesC().Find( _L8("javascript") ) != KErrNotFound;
+ }
+ }
+ return protectedEntry;
+ }
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::CacheTimeIsFresh
+//
+// Returns true if the entry is fresh.
+//
+// -----------------------------------------------------------------------------
+//
+TBool HttpCacheUtil::CacheTimeIsFresh(
+ const RHTTPHeaders& aRequestHeaders,
+ const RHTTPHeaders& aCachedHeaders,
+ RStringPool aStrP )
+ {
+ TInt status( KErrNone );
+ TBool isFresh( EFalse );
+ TInt64 freshness;
+ TInt64 age;
+ TInt64 maxAge;
+ TInt64 minFresh;
+ TInt64 maxStale;
+ TTime cachedRequestTime;
+ TTime cachedResponseTime;
+
+ TTime date;
+ TInt err;
+ THTTPHdrVal dateValue;
+ RStringF fieldName;
+
+ // Get the date from the headers
+ fieldName = aStrP.StringF( HTTP::EDate, RHTTPSession::GetTable() );
+ err = aCachedHeaders.GetField( fieldName, 0, dateValue );
+ if( err == KErrNotFound || dateValue.Type() != THTTPHdrVal::KDateVal )
+ {
+ date = 0;
+ }
+ else
+ {
+ date = TTime( dateValue.DateTime() );
+ }
+ cachedRequestTime = date;
+ cachedResponseTime = date;
+
+ // Get Max Age header. If maxAge = 0, then must revalidate.
+ status = GetCacheControls( aCachedHeaders, &maxAge, NULL, NULL, NULL, NULL, NULL, aStrP );
+ if( status == KErrNone && maxAge == 0 )
+ {
+ return EFalse; //Must revalidate
+ }
+
+ // Get the freshness and age of the cachedHeaders
+ freshness = Freshness( aCachedHeaders, cachedResponseTime, aStrP );
+ age = Age( aCachedHeaders, cachedRequestTime, cachedResponseTime, aStrP );
+#ifdef __CACHELOG__
+ TBuf<50> dateString;
+ TTime fr( freshness );
+
+ fr.FormatL( dateString, KDateString );
+ HttpCacheUtil::WriteLog( 0, _L( "fresness" ) );
+ HttpCacheUtil::WriteLog( 0, dateString );
+
+ TTime ca( age );
+ ca.FormatL( dateString, KDateString );
+ HttpCacheUtil::WriteLog( 0, _L( "age" ) );
+ HttpCacheUtil::WriteLog( 0, dateString );
+#endif // __CACHELOG__
+
+ // Get useful cache-control directives from the requestHeaders
+ status = GetCacheControls( aRequestHeaders, &maxAge, &minFresh, &maxStale, NULL, NULL, NULL, aStrP );
+ if( status == KErrNone )
+ {
+ // Above values for maxAge, minFresh and maxStale are in seconds
+ // Convert to micro seconds before using it.
+
+ // If maxStale is present it means that it is willing to
+ // accept a response maxStale seconds after it expires. To
+ // allow this freshness is extended by maxStale seconds.
+ if( maxStale != -1 )
+ {
+ if( maxStale == MAKE_TINT64( KMaxTInt, KMaxTInt ) )
+ {
+ freshness = MAKE_TINT64( KMaxTInt, KMaxTInt );
+ }
+ else
+ {
+ freshness += maxStale * 1000 * 1000;
+ }
+ }
+ // If minFresh is present reject it if it would expire
+ // within minFresh seconds.
+ if( minFresh != -1 )
+ {
+ if( ( age + minFresh * 1000 * 1000 ) > freshness )
+ {
+ return EFalse;
+ }
+ }
+ // If the age > request's maxAge, reject it
+ if( maxAge != -1 )
+ {
+ if( age > maxAge * 1000 * 1000 )
+ {
+ return EFalse;
+ }
+ }
+ // If age is less than freshness its fresh
+ // If age == 0 and freshness == 0 - revalidation is needed
+ if( freshness && age <= freshness )
+ {
+ isFresh = ETrue;
+ }
+ }
+ return isFresh;
+ }
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::AddValidationHeaders
+//
+// Adds appropriate headers to make the given "request" conditional.
+//
+// -----------------------------------------------------------------------------
+//
+void HttpCacheUtil::AddValidationHeaders(
+ const RHTTPHeaders aCachedHeaders,
+ RHTTPHeaders& aRequestHeaders,
+ RStringPool& aStrP )
+ {
+ THTTPHdrVal lastMod;
+ THTTPHdrVal eTag;
+ RStringF fieldName;
+ TInt err;
+
+ // Get the entry's last-modified header
+ fieldName = aStrP.StringF( HTTP::ELastModified, RHTTPSession::GetTable() );
+ err = aCachedHeaders.GetField( fieldName, 0, lastMod );
+
+ // If last-Modified is present add an If-Modified-Since header
+ if( err != KErrNotFound )
+ {
+ fieldName = aStrP.StringF( HTTP::EIfModifiedSince, RHTTPSession::GetTable() );
+ TRAP_IGNORE( aRequestHeaders.SetFieldL( fieldName, lastMod ) );
+ }
+
+ // Get the entry's ETag header
+ fieldName = aStrP.StringF( HTTP::EETag, RHTTPSession::GetTable() );
+ err = aCachedHeaders.GetField( fieldName, 0, eTag );
+
+ // If etag is present add an etag header
+ if( err != KErrNotFound )
+ {
+ fieldName = aStrP.StringF( HTTP::EIfNoneMatch, RHTTPSession::GetTable() );
+ TRAP_IGNORE( aRequestHeaders.SetFieldL( fieldName, eTag ) );
+ }
+ }
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::OperatorCacheContent
+// This function checks if aUrl is part of the operator domain. for examp: if aUrl->http://www.opdomain.com/pub/index.html
+// and operator domain is http://www.opdomain.com/pub then it returns ETrue.
+// -----------------------------------------------------------------------------
+//
+TBool HttpCacheUtil::OperatorCacheContent(
+ const TDesC8& aOpDomain,
+ const TDesC8& aUrl )
+ {
+ TUriParser8 opDomain;
+ TUriParser8 url;
+ TBool found( EFalse );
+
+ // check if both URIs are correct.
+ if( opDomain.Parse( aOpDomain ) == KErrNone && url.Parse( aUrl ) == KErrNone )
+ {
+ // host value must be the same. for examp: www.operator.co is different from www.operator.com
+ // note: check if compare is case sensitive!
+ // note2: if http:// is missing from operator domain then TUriParser8 takes the host part as path.
+ if( opDomain.Compare( url, EUriHost ) == 0 )
+ {
+ // further checking on the path value. domain path must be a substring of the given url.
+ // for examp: op path: /news
+ // url path: /news/local_news.html
+ const TDesC8& opPath = opDomain.Extract( EUriPath );
+ if( opPath.Length() )
+ {
+ const TDesC8& urlPath = url.Extract( EUriPath );
+ // we don't take the content if the url path is not defined (while domain path is) or the
+ // domain path is not a substring of url path. actually url path must start with domain path
+ // op path: /news url path: /media/news/local_news.html is not valid.
+ found = ( urlPath.Length() && urlPath.FindF( opPath ) == 0 );
+ }
+ else
+ {
+ // no opPath means that we take every content on this host.
+ found = ETrue;
+ }
+ }
+ }
+ return found;
+ }
+
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::VSSCacheContent
+//
+// -----------------------------------------------------------------------------
+//
+TBool HttpCacheUtil::VSSCacheContent( const TDesC8& aUrl, const HBufC8* aWhiteList )
+ {
+ TBool found( EFalse );
+ if( aWhiteList )
+ {
+ TUint8* bufPtr = (TUint8* ) aWhiteList->Ptr();
+ TUint8* startBufPtr = bufPtr;
+ TUint len = aWhiteList->Length();
+ TInt i, startUri, uriLen;
+ TPtrC8 uri ( aUrl );
+ for(i=0; i < len; i++)
+ {
+ startUri = i;
+ for( ;( ( *bufPtr != ';' ) && ( i<len ) ) ; i++, bufPtr++ ) ;
+ uriLen = i - startUri;
+ if( i == len )
+ {
+ uriLen += 2; //For getting total length
+ }
+ TPtrC8 uriDomain( startBufPtr, uriLen);
+ if ( OperatorCacheContent( uriDomain, uri ) )
+ {
+ found = ETrue;
+ break;
+ }
+ startBufPtr = ++bufPtr;
+ i++;
+ } //end for()
+ } //end for()
+ return found;
+ }
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::PragmaNoCache
+//
+// -----------------------------------------------------------------------------
+//
+TBool HttpCacheUtil::PragmaNoCache(
+ RHTTPTransaction& aTrans )
+ {
+ //
+ RHTTPHeaders requestHeaders = aTrans.Request().GetHeaderCollection();
+ RStringPool strP = aTrans.Session().StringPool();
+ RStringF fieldName = strP.StringF( HTTP::ENoCache, RHTTPSession::GetTable() );
+
+ // check no-cache
+ THTTPHdrVal headerVal;
+ TInt noCacheField( requestHeaders.GetField( fieldName, 0, headerVal ) );
+ // check no-store
+ fieldName = strP.StringF( HTTP::ENoStore, RHTTPSession::GetTable() );
+ TInt noStoreField( requestHeaders.GetField( fieldName, 0, headerVal ) );
+ //
+ return( noCacheField == KErrNone || noStoreField == KErrNone );
+ }
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::GetHeaderFileName
+//
+// -----------------------------------------------------------------------------
+//
+void HttpCacheUtil::GetHeaderFileName(
+ const TDesC& aBodyFileName,
+ TDes& aHeaderFileName )
+ {
+ // body file name = foo
+ // header file name = foo.h
+ aHeaderFileName.Copy( aBodyFileName );
+ // take the filename and append the new extension
+ aHeaderFileName.Append( KHttpCacheHeaderExt() );
+ }
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::AdjustExpirationTime
+//
+// -----------------------------------------------------------------------------
+//
+void HttpCacheUtil::AdjustExpirationTimeL(
+ RHTTPHeaders& aResponseHeaders,
+ RStringPool& aStrP )
+ {
+ const TStringTable& stringTable = RHTTPSession::GetTable();
+ THTTPHdrVal hdrVal;
+
+ if( aResponseHeaders.GetField( aStrP.StringF( HTTP::EExpires, stringTable ), 0, hdrVal ) == KErrNone )
+ {
+ HttpCacheUtil::WriteLog( 0, _L( "adjust expiration time from" ) );
+
+ TTime expDate( hdrVal.DateTime() );
+#ifdef __CACHELOG__
+ TBuf<50> dateString;
+ TTime expTime( hdrVal.DateTime() );
+
+ expTime.FormatL( dateString, KDateString );
+ HttpCacheUtil::WriteLog( 0, dateString );
+#endif // __CACHELOG__
+ // double it
+ TTimeIntervalMinutes minutes;
+ TTimeIntervalHours hours;
+ TTime now;
+ now.UniversalTime();
+
+ if( expDate.MinutesFrom( now, minutes ) == KErrNone )
+ {
+#ifdef __CACHELOG__
+ //
+ now.FormatL( dateString, KDateString );
+ HttpCacheUtil::WriteLog( 0, _L( "current time" ) );
+ HttpCacheUtil::WriteLog( 0, dateString );
+ //
+ now.FormatL( dateString, KDateString );
+ HttpCacheUtil::WriteLog( 0, _L( "expires in (minutes)" ), minutes.Int() );
+#endif // __CACHELOG__
+ //
+ expDate+=minutes;
+ }
+ // minutes owerflow? take hours instead
+ else if( expDate.HoursFrom( now, hours ) == KErrNone )
+ {
+ expDate+=hours;
+ }
+ else
+ {
+ // last resort
+ TTimeIntervalDays days( expDate.DaysFrom( now ) );
+ expDate+=days;
+ }
+ // set new date on the response header
+ aResponseHeaders.RemoveField( aStrP.StringF( HTTP::EExpires, stringTable ) );
+ // add it
+ hdrVal.SetDateTime( expDate.DateTime() );
+#ifdef __CACHELOG__
+ HttpCacheUtil::WriteLog( 0, _L( "to" ) );
+ //
+ TTime newExptime( hdrVal.DateTime() );
+ newExptime.FormatL( dateString, KDateString );
+ HttpCacheUtil::WriteLog( 0, dateString );
+
+ TTimeIntervalMinutes min;
+
+ newExptime.MinutesFrom( now, min );
+
+ HttpCacheUtil::WriteLog( 0, _L( "now it expires in (minutes)" ), min.Int() );
+#endif // __CACHELOG__
+ aResponseHeaders.SetFieldL( aStrP.StringF( HTTP::EExpires, stringTable ), hdrVal );
+ }
+ }
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::CacheNeedsUpdateL
+//
+// -----------------------------------------------------------------------------
+//
+TBool HttpCacheUtil::CacheNeedsUpdateL(
+ RHTTPHeaders& aResponseHeaders,
+ const TDesC8& aCachedHeadersStr,
+ RStringPool& aStrP )
+ {
+ HBufC8* valueStr;
+ TTime cachedDate( 0 );
+ TTime responseDate( 0 );
+ TTime cachedLastModified( 0 );
+ TTime responseLastModified( 0 );
+ RStringF cachedETag;
+ RStringF responseETag;
+ RStringF fieldName;
+ TInternetDate value;
+ THTTPHdrVal hdrValue;
+ TBool eTag( EFalse );
+ TBool eLastModified( EFalse );
+ TBool eDate( EFalse );
+ TInt status;
+ TBool update( ETrue );
+
+ // date
+ fieldName = aStrP.StringF( HTTP::EDate, RHTTPSession::GetTable() );
+ // new
+ status = aResponseHeaders.GetField( fieldName, 0, hdrValue );
+ if( status == KErrNone )
+ {
+ responseDate = TTime( hdrValue.DateTime() );
+ // orig
+ valueStr = HttpCacheUtil::HeaderFieldFromBufferLC( aCachedHeadersStr, fieldName.DesC() );
+ if( valueStr )
+ {
+ eDate = ETrue;
+ value.SetDateL( valueStr->Des() );
+ cachedDate = TTime( value.DateTime() );
+ CleanupStack::PopAndDestroy(); // valueStr
+ }
+ }
+
+
+ // last modified
+ fieldName = aStrP.StringF( HTTP::ELastModified, RHTTPSession::GetTable() );
+ // new
+ status = aResponseHeaders.GetField( fieldName, 0, hdrValue );
+ if( status == KErrNone )
+ {
+ responseLastModified = TTime( hdrValue.DateTime() );
+ // orig
+ valueStr = HttpCacheUtil::HeaderFieldFromBufferLC( aCachedHeadersStr, fieldName.DesC() );
+ if( valueStr )
+ {
+ eLastModified = ETrue;
+ value.SetDateL( valueStr->Des() );
+ cachedLastModified = TTime( value.DateTime() );
+ CleanupStack::PopAndDestroy(); // valueStr
+ }
+ }
+
+ // Etag
+ fieldName = aStrP.StringF( HTTP::EETag, RHTTPSession::GetTable() );
+ // new
+ status = aResponseHeaders.GetField( fieldName, 0, hdrValue );
+ if( status == KErrNone )
+ {
+ responseETag = hdrValue.StrF();
+ // orig
+ valueStr = HttpCacheUtil::HeaderFieldFromBufferLC( aCachedHeadersStr, fieldName.DesC() );
+ if( valueStr )
+ {
+ TRAP( status, cachedETag = aStrP.OpenFStringL( valueStr->Des() ) );
+ CleanupStack::PopAndDestroy(); // valueStr
+ eTag = ( status == KErrNone );
+ //
+ if( eTag )
+ {
+ CleanupClosePushL( cachedETag );
+ }
+ }
+ }
+
+ // If the entry's date is greater than the responses's date,
+ // ignore the response.
+ if( eDate && cachedDate > responseDate )
+ {
+ update = EFalse;
+ }
+ // If the entry's ETag does not equal the responses's ETag,
+ // replace the cached entry with the response.
+ else if( eTag && cachedETag != responseETag )
+ {
+ update = ETrue;
+ }
+ // If the entry's lastMod is greater than the responses's lastMod,
+ // ignore the response.
+ else if( eLastModified && cachedLastModified > responseLastModified )
+ {
+ update = EFalse;
+ }
+ // If the entry's lastMod is equal to the responses's lastMod,
+ // update the cached headers with the new response headers.
+ else if( eLastModified && cachedLastModified == responseLastModified )
+ {
+ update = ETrue;
+ }
+ // If the entry's lastMod is less than the responses's lastMod,
+ // replace the cached entry with the response.
+ else if( eLastModified && cachedLastModified < responseLastModified )
+ {
+ update = ETrue;
+ }
+ else
+ {
+ // If we get here the headers need to be replaced
+ update = ETrue;
+ }
+ //
+ if( eTag )
+ {
+ CleanupStack::PopAndDestroy(); // cachedETag
+ }
+ return update;
+ }
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::IsCacheable
+//
+// -----------------------------------------------------------------------------
+//
+TBool HttpCacheUtil::IsCacheable(
+ RHTTPTransaction& aTrans,
+ TUint aMaxSize,
+ TBool& aProtectedEntry )
+ {
+ TBool isCacheable( ETrue );
+ TBool hasExplicitCache( EFalse );
+ TInt64 maxAge;
+ TBool noCache;
+ TBool noStore;
+ TTime now;
+ TTime expires;
+ RStringF fieldName;
+ THTTPHdrVal expiresVal;
+ TInt err;
+ RStringPool strP = aTrans.Session().StringPool();
+ RHTTPHeaders respHeaders = aTrans.Response().GetHeaderCollection();
+
+ // not protected by default
+ aProtectedEntry = EFalse;
+ // 1. do not cache sesnitive content (DRM)
+ // 2. do not cache content bigger than the cache
+ // 3. check if the content is protected
+ // 4. check normal cache directives
+
+ // init the field name
+ THTTPHdrVal contType;
+ fieldName = strP.StringF( HTTP::EContentType, RHTTPSession::GetTable() );
+
+ // check if this is a noncacheable content type
+ if( respHeaders.GetField( fieldName, 0, contType ) == KErrNone &&
+ contType.StrF() == strP.StringF( HttpFilterCommonStringsExt::EApplicationVndOmaDrm,
+ HttpFilterCommonStringsExt::GetTable() ) )
+ {
+ HttpCacheUtil::WriteLog( 0, _L( "sensitive content. do not cache" ) );
+ // drm == nocache
+ isCacheable = EFalse;
+ }
+ else
+ {
+ // check if the content is bigger than the cache
+ THTTPHdrVal contLen;
+ fieldName = strP.StringF( HTTP::EContentLength, RHTTPSession::GetTable() );
+
+ if( respHeaders.GetField( fieldName, 0, contLen ) != KErrNotFound &&
+ ( contLen.Type() == THTTPHdrVal::KTIntVal && contLen.Int() > aMaxSize ) )
+ {
+ HttpCacheUtil::WriteLog( 0, _L( "oversized content. do not cache" ) );
+ // oversized content
+ return EFalse;
+ }
+ // check if this is a proteced entry
+ aProtectedEntry = ProtectedEntry( respHeaders, strP );
+ // check various cache controls
+ if( GetCacheControls( respHeaders, &maxAge, NULL, NULL, NULL,
+ &noCache, &noStore, strP ) == KErrNone )
+ {
+ // There are several header "conditions" that make a resource
+ // non-cachable. Reject if they are present.
+ //
+ // If no-cache or no-store directives exist -> don't cache.
+ if( noCache || noStore )
+ {
+ HttpCacheUtil::WriteLog( 0, _L( "no cache/no store header. do not cache" ) );
+ // no protection on this entry
+ aProtectedEntry = EFalse;
+ return EFalse;
+ }
+ // Get the current time
+ now.UniversalTime();
+
+ // Get the expires from the respHeaders
+ fieldName = strP.StringF( HTTP::EExpires, RHTTPSession::GetTable() );
+ err = respHeaders.GetField( fieldName, 0, expiresVal );
+ if( err == KErrNone )
+ {
+ expires = TTime( expiresVal.DateTime() );
+ }
+ else
+ {
+ expires = now;
+ }
+
+ // if past-expire date do not cache. According to RFC2616, section 13.2.4/14.9.3,
+ // if maxage is present, then ignore expires
+ if (!maxAge && now > expires)
+ {
+ return EFalse;
+ }
+
+ if( err == KErrNone || maxAge > 0 )
+ {
+ hasExplicitCache = ETrue;
+ }
+ // Reject if the http status code doesn't equal 200, 304, 301, 410.
+ // Note: We accept status codes, 307, 302 if some cache
+ // control directives exist.
+ switch( aTrans.Response().StatusCode() )
+ {
+ case HTTPStatus::EOk:
+ case HTTPStatus::ENotModified:
+ case HTTPStatus::EMovedPermanently:
+ case HTTPStatus::EGone:
+ {
+ break;
+ }
+ case HTTPStatus::EFound:
+ case HTTPStatus::ETemporaryRedirect:
+ {
+ if( !hasExplicitCache )
+ {
+ isCacheable = EFalse;
+ }
+ break;
+ }
+ default:
+ {
+ isCacheable = EFalse;
+ break;
+ }
+ }
+
+ // Check method specific conditions.
+ switch( HttpCacheUtil::MethodFromStr( aTrans.Request().Method(), strP ) )
+ {
+ case EMethodGet:
+ {
+ // Reject if the url contains a "query" part unless there
+ // are caching directives.
+ TBool isQuery;
+ isQuery = aTrans.Request().URI().IsPresent( EUriQuery );
+
+ if( isQuery && !hasExplicitCache )
+ {
+ isCacheable = EFalse;
+ }
+ break;
+ }
+ case EMethodPost:
+ {
+ // Reject unless there are caching directives
+ if( !hasExplicitCache )
+ {
+ isCacheable = EFalse;
+ }
+ break;
+ }
+ case EMethodHead:
+ {
+ // Don't cache Head responses...we don't need to, and the
+ // cache implementation can't currently handle it anyway.
+ // So just fall through to the default case.
+ // Reject all other methods
+ }
+ default:
+ {
+ isCacheable = EFalse;
+ break;
+ }
+ }
+ }
+ }
+ return isCacheable;
+ }
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::WriteLog
+//
+// -----------------------------------------------------------------------------
+//
+void HttpCacheUtil::WriteLog(
+ TInt aLogLevel,
+ TPtrC aBuf,
+ TInt aAny )
+ {
+#ifdef __CACHELOG__
+ TBool log( aLogLevel <= KCurrentLogLevel );
+ TPtrC fileName( KHttpCacheGeneralFileName );
+
+ if( aLogLevel == 1 )
+ {
+ // hash
+ fileName.Set( KHttpCacheHashFileName );
+ log = ETrue;
+ }
+ if( log )
+ {
+ if( aAny != 0xffff )
+ {
+ RFileLogger::WriteFormat(_L("Browser"), fileName, EFileLoggingModeAppend,
+ _L("%S %d"), &aBuf, aAny );
+ }
+ else
+ {
+ RFileLogger::WriteFormat(_L("Browser"), fileName, EFileLoggingModeAppend,
+ _L("%S"), &aBuf );
+ }
+ }
+#else // __CACHELOG__
+ (void)aLogLevel;
+ (void)aBuf;
+ (void)aAny;
+#endif // __CACHELOG__
+ }
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::WriteUrlToLog
+//
+// Get the freshness of the "entry".
+//
+// -----------------------------------------------------------------------------
+//
+void HttpCacheUtil::WriteUrlToLog(
+ TInt aLogLevel,
+ const TDesC8& aUrl,
+ TInt aAny )
+ {
+#ifdef __CACHELOG__
+ HBufC* tmp = HBufC::New( aUrl.Length() );
+ if( tmp )
+ {
+ TPtr tmpPtr( tmp->Des() );
+ tmpPtr.Copy( aUrl );
+ HttpCacheUtil::WriteLog( aLogLevel, tmpPtr, aAny );
+ delete tmp;
+ }
+#else // __CACHELOG__
+ (void)aLogLevel;
+ (void)aUrl;
+ (void)aAny;
+#endif // __CACHELOG__
+ }
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::WriteUrlToLog
+//
+// Get the freshness of the "entry".
+//
+// -----------------------------------------------------------------------------
+//
+void HttpCacheUtil::WriteUrlToLog(
+ TInt aLogLevel,
+ const TDesC& aTxt,
+ const TDesC8& aUrl,
+ TInt aAny )
+ {
+#ifdef __CACHELOG__
+ TBool log( aLogLevel <= KCurrentLogLevel );
+ TPtrC fileName( KHttpCacheGeneralFileName );
+
+ if( aLogLevel == 1 )
+ {
+ // hash
+ fileName.Set( KHttpCacheHashFileName );
+ log = ETrue;
+ }
+ if( log )
+ { //
+ HBufC* tmp = HBufC::New( aUrl.Length() );
+ if( tmp )
+ {
+ TPtr tmpPtr( tmp->Des() );
+ tmpPtr.Copy( aUrl );
+ if( aAny != 0xffff )
+ {
+ RFileLogger::WriteFormat( _L("Browser"), fileName, EFileLoggingModeAppend,
+ _L("%S %S %d"), &aTxt, &tmpPtr, aAny );
+ }
+ else
+ {
+ RFileLogger::WriteFormat(_L("Browser"), fileName, EFileLoggingModeAppend,
+ _L("%S %S"), &aTxt, &tmpPtr );
+ }
+ delete tmp;
+ }
+ }
+#else // __CACHELOG__
+ (void)aLogLevel;
+ (void)aTxt;
+ (void)aUrl;
+ (void)aAny;
+#endif // __CACHELOG__
+ }
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::GenerateNameLC
+// Given a URL, generates fully qualified Symbian path for storing HTTP response.
+// The format is <cache base dir> + <subdirectory> + <file name>.
+// Caller must free the returned HBufC* when done.
+// -----------------------------------------------------------------------------
+HBufC* HttpCacheUtil::GenerateNameLC(
+ const TDesC8& aUrl, const TDesC& aBaseDir)
+ {
+
+ TUint32 crc (0);
+
+ //use the entire URL for CRC calculation: maximizes source entropy/avoids collisions
+ Mem::Crc32(crc, aUrl.Ptr(), aUrl.Size());
+ TUint32 nibble (crc & (KCacheSubdirCount-1)); // extract least significant 4 bits (nibble) for subdirectory
+
+ HBufC* fileName = HBufC::NewLC( KMaxPath ); // e.g E\078AFEFE
+ _LIT(KFormat,"%S%x%c%08x"); // Note the %08x : a 32-bit value can represented as 0xFFFFFFFF
+ fileName->Des().Format(KFormat, &aBaseDir, nibble, KPathDelimiter, crc);
+ return fileName;
+
+ }
+
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::Freshness
+//
+// Get the freshness of the "entry".
+//
+// -----------------------------------------------------------------------------
+//
+TInt64 HttpCacheUtil::Freshness(
+ const RHTTPHeaders& aHeaders,
+ TTime aResponseTime,
+ RStringPool aStrP )
+ {
+ TInt status( KErrNone );
+ TInt64 maxAge;
+ TTime expires;
+ TTime lastModified( 0 );
+ TTime date;
+ RStringF fieldName;
+ THTTPHdrVal hdrValue;
+ TInt err;
+ TInt64 freshness( 0 );
+
+ // Get the date from the headers
+ fieldName = aStrP.StringF( HTTP::EDate, RHTTPSession::GetTable() );
+ err = aHeaders.GetField( fieldName, 0, hdrValue );
+ if( err == KErrNotFound || hdrValue.Type() != THTTPHdrVal::KDateVal )
+ {
+ date = aResponseTime;
+ }
+ else
+ {
+ date = TTime( hdrValue.DateTime() );
+ }
+
+ // Get useful cache-control directives
+ status = GetCacheControls( aHeaders, &maxAge, NULL, NULL, NULL, NULL, NULL, aStrP );
+ if( status == KErrNone )
+ {
+ // max-age is in delta-seconds. Convert it to micro seconds.
+ // All our calculations are in micro-seconds
+ // If maxAge is present, use it
+ if( maxAge != -1 )
+ {
+ freshness = maxAge * 1000 * 1000;
+
+ return freshness;
+ }
+
+ // Get the expires from the headers
+ fieldName = aStrP.StringF( HTTP::EExpires, RHTTPSession::GetTable() );
+ err = aHeaders.GetField( fieldName, 0, hdrValue );
+ if( err == KErrNotFound || hdrValue.Type() != THTTPHdrVal::KDateVal )
+ {
+ expires = 0;
+ }
+ else
+ {
+ expires = TTime( hdrValue.DateTime() );
+ }
+
+ // Otherwise, if the expires is present use it
+ if( err != KErrNotFound )
+ {
+ freshness = expires.Int64() - date.Int64();
+ return freshness;
+ }
+
+ // Get the last modified from the headers
+ fieldName = aStrP.StringF( HTTP::ELastModified, RHTTPSession::GetTable() );
+ err = aHeaders.GetField( fieldName, 0, hdrValue );
+ if( err == KErrNotFound || hdrValue.Type() != THTTPHdrVal::KDateVal )
+ {
+ hdrValue = 0;
+ }
+ else
+ {
+ lastModified = TTime( hdrValue.DateTime() );
+ }
+
+ // Otherwise, if last-modified is present use it
+ if( err != KErrNotFound )
+ {
+ if( aResponseTime > lastModified )
+ {
+ freshness = aResponseTime.Int64() - lastModified.Int64();
+ freshness += ((TInt64)( freshness * 0.10 ) );
+ }
+ return freshness;
+ }
+ // If it get here then we have no way to determine if it is
+ // fresh. So we set freshness to zero, which will most likely
+ // cause the resource to be validated.
+ freshness = 0;
+ }
+ return freshness;
+ }
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::Age
+//
+// Get the age of the "entry".
+//
+// -----------------------------------------------------------------------------
+//
+TInt64 HttpCacheUtil::Age(
+ const RHTTPHeaders& aHeaders,
+ TTime aRequestTime,
+ TTime aResponseTime,
+ RStringPool aStrP )
+ {
+ TTime now;
+ TDateTime date;
+ TTime time;
+
+ // Int64 timeinsec;
+ TInt64 correctedRecvAge;
+ TInt64 apparentAge;
+ TInt64 responseDelay;
+ TInt64 correctedInitialAge;
+ TInt64 residentTime;
+ TInt64 currentAge;
+ TInt64 age;
+
+
+ RStringF fieldName;
+ THTTPHdrVal dateValue;
+ TInt err;
+
+ // Get the current time. All internet dates are GMT
+ now.UniversalTime();
+#ifdef __CACHELOG__
+ TBuf<50> dateString;
+ //
+ now.FormatL( dateString, KDateString );
+ HttpCacheUtil::WriteLog( 0, _L( "current time" ) );
+ HttpCacheUtil::WriteLog( 0, dateString );
+#endif // __CACHELOG__
+
+ // The aRequestTime is same as that of headers time.
+
+ time = aRequestTime;
+
+ // Do a sanity check
+ if( aRequestTime > now )
+ {
+ aRequestTime = now;
+ }
+ if( aResponseTime > now )
+ {
+ aResponseTime = now;
+ }
+ if( aRequestTime > aResponseTime )
+ {
+ aRequestTime = aResponseTime;
+ }
+ if( time > aResponseTime )
+ {
+ time = aResponseTime;
+ }
+
+ // Get the age from the headers. If age is missing it equals
+ // 0, which is a safe value to use below.
+ fieldName = aStrP.StringF( HTTP::EAge, RHTTPSession::GetTable() );
+ err = aHeaders.GetField( fieldName, 0, dateValue );
+ if( err == KErrNotFound )
+ {
+ age = 0;
+ }
+ else
+ {
+ // Age should be in delta seconds
+ if( dateValue.Type() == THTTPHdrVal::KTIntVal )
+ {
+ age = dateValue;
+ }
+ else
+ {
+ age = 0;
+ }
+ }
+
+ // Get the "current" age
+ apparentAge = Max( TInt64( 0 ), aResponseTime.Int64() - time.Int64() );
+ // The TTime::Int64() gives the time in micro seconds. And the age field is in
+ // delta seconds.
+ correctedRecvAge = Max( apparentAge, age * 1000 * 1000 );
+ responseDelay = aResponseTime.Int64() - aRequestTime.Int64();
+ correctedInitialAge = correctedRecvAge + responseDelay;
+ residentTime = now.Int64() - aResponseTime.Int64();
+
+ currentAge = correctedInitialAge + residentTime;
+
+ //the current age in micro seconds.
+ return currentAge;
+ }
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::GetCacheControls
+//
+// Description: Returns various cache controls.
+//
+// -----------------------------------------------------------------------------
+//
+TInt HttpCacheUtil::GetCacheControls(
+ const RHTTPHeaders& aHeaders,
+ TInt64* aMaxAge,
+ TInt64* aMinFresh,
+ TInt64* aMaxStale,
+ TBool* aMustRevalidate,
+ TBool* aNoCache,
+ TBool* aNoStore,
+ RStringPool aStrP )
+ {
+ TInt status( KErrNone );
+ TInt i;
+ TInt cacheCount( 0 );
+ RStringF directive;
+ TInt64 value;
+ char* extraValues = NULL;
+ RStringF fieldName;
+
+
+ if( aMaxAge )
+ {
+ *aMaxAge = -1;
+ }
+ if( aMinFresh )
+ {
+ *aMinFresh = -1;
+ }
+ if( aMaxStale )
+ {
+ *aMaxStale = -1;
+ }
+ if( aMustRevalidate )
+ {
+ *aMustRevalidate = EFalse;
+ }
+ if( aNoCache )
+ {
+ *aNoCache = EFalse;
+ }
+ if( aNoStore )
+ {
+ *aNoStore = EFalse;
+ }
+
+ // init the field name
+ fieldName = aStrP.StringF( HTTP::ECacheControl, RHTTPSession::GetTable() );
+ TRAP( status, cacheCount = aHeaders.FieldPartsL( fieldName ) );
+ if( status == KErrNone )
+ {
+ for( i = 0; i < cacheCount; i++ )
+ {
+ status = GetCacheControlDirective( aHeaders, i, directive, &value, &extraValues, aStrP );
+ if( status == KErrNone )
+ {
+ if( directive == aStrP.StringF( HTTP::EMaxAge, RHTTPSession::GetTable() ) )
+ {
+ if( aMaxAge )
+ {
+ *aMaxAge = value;
+ }
+ }
+ else if( directive == aStrP.StringF( HTTP::EMinFresh, RHTTPSession::GetTable() ) )
+ {
+ if( aMinFresh )
+ {
+ *aMinFresh = value;
+ }
+ }
+ else if( directive == aStrP.StringF( HTTP::EMaxStale, RHTTPSession::GetTable() ) )
+ {
+ if( aMaxStale )
+ {
+ *aMaxStale = value;
+ }
+ }
+ else if( directive == aStrP.StringF( HTTP::EMustRevalidate, RHTTPSession::GetTable() ) )
+ {
+ if( aMustRevalidate )
+ {
+ *aMustRevalidate = ETrue;
+ }
+ }
+ else if( directive == aStrP.StringF( HTTP::ENoCache, RHTTPSession::GetTable() ) )
+ {
+ if( aNoCache )
+ {
+ *aNoCache = ETrue;
+ }
+ }
+ else if( directive == aStrP.StringF( HTTP::ENoStore, RHTTPSession::GetTable() ) )
+ {
+ if( aNoStore )
+ {
+ *aNoStore = ETrue;
+ }
+ }
+ directive.Close();
+ // It ignores extraValues, just free it
+ User::Free( extraValues );
+ extraValues = NULL;
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ return status;
+ }
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::GetCacheControlDirective
+//
+// -----------------------------------------------------------------------------
+//
+TInt HttpCacheUtil::GetCacheControlDirective(
+ const RHTTPHeaders& aHeaders,
+ TInt aIndex,
+ RStringF& aDirective,
+ TInt64* aDirectiveValue,
+ char** aExtraValue,
+ RStringPool aStrP )
+ {
+ RStringF fieldName;
+ THTTPHdrVal hdrVal;
+ TInt err;
+
+ // Get the cache-control from the headers
+ fieldName = aStrP.StringF( HTTP::ECacheControl, RHTTPSession::GetTable() );
+ aHeaders.GetField( fieldName, aIndex, hdrVal );
+
+ if( hdrVal.Type() == THTTPHdrVal::KStrVal || hdrVal.Type() == THTTPHdrVal::KStrFVal )
+ {
+ RStringF cacheDir = hdrVal.StrF();
+
+ TInt endPos;
+ _LIT8(KFind, "=");
+
+ endPos = cacheDir.DesC().Find( KFind );
+ if( endPos != -1 )
+ {
+ TRAP( err, aDirective = aStrP.OpenFStringL( cacheDir.DesC().Left( endPos ) ) );
+ if( err != KErrNone )
+ {
+ return err;
+ }
+ TPtrC8 value = cacheDir.DesC().Right( cacheDir.DesC().Length() - endPos - 1 );
+ err = ExtractCacheControlDirectiveValue( aStrP, aDirective, value, aDirectiveValue, aExtraValue );
+ if( err != KErrNone )
+ {
+ aDirective.Close();
+ return err;
+ }
+ }
+ else
+ {
+ aDirective = cacheDir.Copy();
+ // Directives which MUST have values;
+ if( ( aDirective == aStrP.StringF( HTTP::EMaxAge, RHTTPSession::GetTable() ) ) ||
+ ( aDirective == aStrP.StringF( HTTP::EMinFresh, RHTTPSession::GetTable() ) ) ||
+ ( aDirective == aStrP.StringF( HTTP::ESMaxAge, RHTTPSession::GetTable() ) ) )
+ {
+ aDirective.Close();
+ return KErrGeneral;
+ }
+ }
+ }
+ return KErrNone;
+ }
+
+// -----------------------------------------------------------------------------
+// HttpCacheUtil::ExtractCacheControlDirectiveValue
+//
+// -----------------------------------------------------------------------------
+//
+TInt HttpCacheUtil::ExtractCacheControlDirectiveValue(
+ RStringPool aStrP,
+ RStringF& aDirective,
+ TDesC8& aValue,
+ TInt64* aDirectiveValue,
+ char** aExtraValue )
+ {
+ TInt status( KErrNone );
+ TInt temp;
+ char* errorIfNull;
+
+ *aDirectiveValue = -1;
+ *aExtraValue = NULL;
+ char* valuestr = (char*)User::Alloc( aValue.Length() + 1 );
+ if( !valuestr )
+ {
+ return KErrNoMemory;
+ }
+ memcpy( valuestr, aValue.Ptr(), aValue.Length() );
+ valuestr[ aValue.Length() ] = '\0';
+
+
+ if( ( aDirective == aStrP.StringF( HTTP::EMaxAge, RHTTPSession::GetTable() ) ) ||
+ ( aDirective == aStrP.StringF( HTTP::EMinFresh, RHTTPSession::GetTable() ) ) ||
+ ( aDirective == aStrP.StringF( HTTP::ESMaxAge, RHTTPSession::GetTable() ) ) )
+ {
+ // Cases with directiveValues
+ temp = strtoul( valuestr, &errorIfNull, 10 );
+ if( !errorIfNull )
+ {
+ status = KErrGeneral;
+ }
+ else
+ {
+ *aDirectiveValue = temp;
+ }
+ User::Free( valuestr );
+ return status;
+ }
+
+ if( ( aDirective == aStrP.StringF( HTTP::EMaxStale, RHTTPSession::GetTable() ) ) )
+ {
+ // Cases with optional directiveValues
+ temp = strtoul( valuestr, &errorIfNull, 10 );
+ if( errorIfNull )
+ {
+ *aDirectiveValue = temp;
+ }
+ User::Free( valuestr );
+ return status;
+ }
+
+ if( ( aDirective == aStrP.StringF( HTTP::ENoCache, RHTTPSession::GetTable() ) ) ||
+ ( aDirective == aStrP.StringF( HTTP::EPrivate, RHTTPSession::GetTable() ) ) )
+ {
+ *aExtraValue = valuestr;
+ return status;
+ }
+ User::Free( valuestr );
+ return status;
+ }
+
+// End of File