webengine/osswebengine/cache/src/HttpCacheUtil.cpp
changeset 0 dd21522fd290
child 8 7c90e6132015
equal deleted inserted replaced
-1:000000000000 0:dd21522fd290
       
     1 /*
       
     2 * Copyright (c) 2005 Nokia Corporation and/or its subsidiary(-ies).
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of the License "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:   Implementation of HttpCacheUtil
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 // INCLUDE FILES
       
    20 #include "HttpCacheUtil.h"
       
    21 #include <http/rhttpheaders.h>
       
    22 #include <http/RHTTPTransaction.h>
       
    23 #include <http.h>
       
    24 #include <stdlib.h>
       
    25 #include <string.h>
       
    26 #include <stringpool.h>
       
    27 #include <flogger.h>
       
    28 #include "TInternetdate.h"
       
    29 #include "HttpFilterCommonStringsExt.h"
       
    30 #include "HttpCacheEntry.h"
       
    31 
       
    32 // EXTERNAL DATA STRUCTURES
       
    33 
       
    34 // EXTERNAL FUNCTION PROTOTYPES
       
    35 
       
    36 // CONSTANTS
       
    37 _LIT8( KHttpNewLine, "\r\n" );
       
    38 // if you change it here, change in HeadersToBufferL too
       
    39 _LIT8( KHttpFieldSeparator, ": " );
       
    40 _LIT8( KHttpValueSep, "," );
       
    41 const TInt KMaxHeaderStrLen = 1024;
       
    42 _LIT8( KHttpContentType, "Content-Type" );
       
    43 
       
    44 #ifdef __CACHELOG__
       
    45 _LIT( KDateString,"%D%M%Y%/0%1%/1%2%/2%3%/3 %-B%:0%J%:1%T%:2%S%.%*C4%:3%+B");
       
    46 _LIT( KHttpCacheGeneralFileName, "cachehandler.txt" );
       
    47 _LIT( KHttpCacheHashFileName, "hash.txt" );
       
    48 const TInt KCurrentLogLevel = 0;
       
    49 #endif // __CACHELOG__
       
    50 
       
    51 // MACROS
       
    52 
       
    53 // LOCAL CONSTANTS AND MACROS
       
    54 
       
    55 // MODULE DATA STRUCTURES
       
    56 
       
    57 // LOCAL FUNCTION PROTOTYPES
       
    58 
       
    59 // FORWARD DECLARATIONS
       
    60 
       
    61 // ============================= LOCAL FUNCTIONS ===============================
       
    62 
       
    63 // ============================ MEMBER FUNCTIONS ===============================
       
    64 
       
    65 // ============================ MEMBER FUNCTIONS ===============================
       
    66 
       
    67 // -----------------------------------------------------------------------------
       
    68 // HttpCacheUtil::SetEntryOnTransL
       
    69 //
       
    70 // -----------------------------------------------------------------------------
       
    71 //
       
    72 void HttpCacheUtil::SetEntryOnTransL(
       
    73     CHttpCacheEntry& aEntry,
       
    74     RHTTPTransaction& aTrans,
       
    75     RStringF& aCacheEntryStr )
       
    76     {
       
    77     RHTTPTransactionPropertySet propSet = aTrans.PropertySet();
       
    78 
       
    79     // set transaction property with the event handler callback functions' pointer
       
    80     THTTPHdrVal tokenVal = (TInt)(CHttpCacheEntry*)&aEntry;
       
    81     aTrans.PropertySet().RemoveProperty( aCacheEntryStr );
       
    82     aTrans.PropertySet().SetPropertyL( aCacheEntryStr, tokenVal );
       
    83     }
       
    84 
       
    85 // -----------------------------------------------------------------------------
       
    86 // HttpCacheUtil::EntryOnTransL
       
    87 //
       
    88 // -----------------------------------------------------------------------------
       
    89 //
       
    90 CHttpCacheEntry* HttpCacheUtil::EntryOnTransL(
       
    91     RHTTPTransaction& aTrans,
       
    92     RStringF& aCacheEntryStr )
       
    93     {
       
    94     //
       
    95     CHttpCacheEntry* entry = NULL;
       
    96     //
       
    97     THTTPHdrVal entryPtr;
       
    98     RHTTPTransactionPropertySet propSet = aTrans.PropertySet();
       
    99     // this is a transaction, already forwarded to download manager
       
   100     if( propSet.Property( aCacheEntryStr, entryPtr ) )
       
   101         {
       
   102         entry = REINTERPRET_CAST( CHttpCacheEntry*, entryPtr.Int() );
       
   103         }
       
   104     return entry;
       
   105     }
       
   106 
       
   107 // -----------------------------------------------------------------------------
       
   108 // HttpCacheUtil::HeadersToBufferLC
       
   109 //
       
   110 // -----------------------------------------------------------------------------
       
   111 //
       
   112 HBufC8* HttpCacheUtil::HeadersToBufferLC(
       
   113     RHTTPHeaders& aHttpHeaders,
       
   114     RStringPool& aStrP )
       
   115     {
       
   116     // Get an iterator for the collection of response headers
       
   117     HBufC8* headerString = HBufC8::NewL( KMaxHeaderStrLen );
       
   118     TPtr8 headerPtr( headerString->Des() );
       
   119     TInt newLength( 0 );
       
   120     THTTPHdrFieldIter it = aHttpHeaders.Fields();
       
   121 
       
   122     while( it.AtEnd() == EFalse )
       
   123         {
       
   124         TPtrC8 rawData;
       
   125         // Get name of next header field
       
   126         RStringTokenF fieldName = it();
       
   127         RStringF fieldNameStr = aStrP.StringF( fieldName );
       
   128         aHttpHeaders.GetRawField( fieldNameStr, rawData );
       
   129 
       
   130         newLength+=( fieldNameStr.DesC().Length() + KHttpFieldSeparator().Length() + rawData.Length() +
       
   131             KHttpNewLine().Length() );
       
   132 
       
   133         if( headerPtr.MaxLength() < newLength )
       
   134             {
       
   135             // realloc
       
   136             HBufC8* temp = HBufC8::New( newLength + KMaxHeaderStrLen );
       
   137             if( !temp )
       
   138                 {
       
   139                 delete headerString;
       
   140                 User::LeaveNoMemory();
       
   141                 }
       
   142             temp->Des().Copy( headerPtr );
       
   143             delete headerString;
       
   144             headerString = temp;
       
   145             headerPtr.Set( headerString->Des() );
       
   146             }
       
   147         // append
       
   148         headerPtr.Append( fieldNameStr.DesC() );
       
   149         headerPtr.Append( KHttpFieldSeparator );
       
   150         headerPtr.Append( rawData );
       
   151         headerPtr.Append( KHttpNewLine );
       
   152         ++it;
       
   153         }
       
   154     CleanupStack::PushL( headerString );
       
   155     return headerString;
       
   156     }
       
   157 
       
   158 // -----------------------------------------------------------------------------
       
   159 // HttpCacheUtil::BufferToHeadersL
       
   160 //
       
   161 // -----------------------------------------------------------------------------
       
   162 //
       
   163 void HttpCacheUtil::BufferToHeadersL(
       
   164     const TDesC8& aHeaderStr,
       
   165     RHTTPHeaders& aHttpHeaders,
       
   166     RStringPool& aStrP )
       
   167     {
       
   168     // take header str line by line
       
   169     TInt sepPos( 0 );
       
   170     TInt lineEnd( 0 );
       
   171     TPtrC8 headerStr( aHeaderStr );
       
   172 
       
   173     while( lineEnd != KErrNotFound )
       
   174         {
       
   175         lineEnd = headerStr.Find( KHttpNewLine );
       
   176         sepPos = headerStr.Find( KHttpFieldSeparator );
       
   177 
       
   178         if( lineEnd != KErrNotFound && sepPos != KErrNotFound &&
       
   179             lineEnd > sepPos )
       
   180             {
       
   181             // ETag: "9be043c1-175-793-41419a44"
       
   182             TPtrC8 line( headerStr.Left( lineEnd ) );
       
   183             // ETag
       
   184             TPtrC8 key( line.Left( sepPos ) );
       
   185             // "9be043c1-175-793-41419a44"
       
   186             TInt sepEnd( sepPos + KHttpFieldSeparator().Length() );
       
   187             TPtrC8 value( line.Mid( sepEnd ) );
       
   188             // add it to the http header
       
   189             RStringF nameStr = aStrP.OpenFStringL( key );
       
   190             CleanupClosePushL( nameStr );
       
   191             // remove if exists
       
   192             THTTPHdrVal headerValue;
       
   193             if( aHttpHeaders.GetField( nameStr, 0, headerValue ) == KErrNone )
       
   194                 {
       
   195                 //
       
   196                 aHttpHeaders.RemoveField( nameStr );
       
   197                 }
       
   198             TRAPD( err, aHttpHeaders.SetRawFieldL( nameStr, value, KHttpValueSep ) );
       
   199             // leave OOM only
       
   200             if( err == KErrNoMemory )
       
   201                 {
       
   202                 User::Leave( err );
       
   203                 }
       
   204             CleanupStack::PopAndDestroy(); // namestr
       
   205 
       
   206             // move string to the end of the line
       
   207             headerStr.Set( headerStr.Mid( lineEnd + KHttpNewLine().Length() ) );
       
   208             }
       
   209         }
       
   210     }
       
   211 
       
   212 // -----------------------------------------------------------------------------
       
   213 // HttpCacheUtil::BodyToBufferL
       
   214 //
       
   215 // -----------------------------------------------------------------------------
       
   216 //
       
   217 HBufC8* HttpCacheUtil::BodyToBufferL(
       
   218     MHTTPDataSupplier& aBodySupplier )
       
   219     {
       
   220     // get it from the transaction
       
   221     TPtrC8 ptr;
       
   222     aBodySupplier.GetNextDataPart( ptr );
       
   223     //
       
   224     return ptr.AllocL();
       
   225     }
       
   226 
       
   227 // -----------------------------------------------------------------------------
       
   228 // HttpCacheUtil::MergeHeadersLC
       
   229 //
       
   230 // -----------------------------------------------------------------------------
       
   231 //
       
   232 HBufC8* HttpCacheUtil::MergeHeadersLC(
       
   233     const TDesC8& aCachedHeaderStr,
       
   234     RHTTPHeaders& aResponseHeaders,
       
   235     RStringPool& aStrP )
       
   236     {
       
   237     // FYI: it uses the response header to merge
       
   238     // take header str line by line
       
   239     TInt sepPos( 0 );
       
   240     TInt lineEnd( 0 );
       
   241     TPtrC8 headerStr( aCachedHeaderStr );
       
   242 
       
   243     while( lineEnd != KErrNotFound )
       
   244         {
       
   245         lineEnd = headerStr.Find( KHttpNewLine );
       
   246         sepPos = headerStr.Find( KHttpFieldSeparator );
       
   247 
       
   248         if( lineEnd != KErrNotFound && sepPos != KErrNotFound &&
       
   249             lineEnd > sepPos )
       
   250             {
       
   251             // ETag: "9be043c1-175-793-41419a44"
       
   252             TPtrC8 line( headerStr.Left( lineEnd ) );
       
   253             // ETag
       
   254             TPtrC8 key( line.Left( sepPos ) );
       
   255             // "9be043c1-175-793-41419a44"
       
   256             TInt sepEnd( sepPos + KHttpFieldSeparator().Length() );
       
   257             TPtrC8 value( line.Mid( sepEnd ) );
       
   258             // add it to the http header
       
   259             RStringF nameStr = aStrP.OpenFStringL( key );
       
   260             CleanupClosePushL( nameStr );
       
   261             // avoid corrupted content type from the stack in case of 304
       
   262             if(key == KHttpContentType)
       
   263                 {
       
   264                 aResponseHeaders.RemoveField(nameStr);
       
   265                 }
       
   266             THTTPHdrVal tempVal;
       
   267             // add this field unless it already exists
       
   268             if( aResponseHeaders.GetField( nameStr, 0, tempVal ) == KErrNotFound )
       
   269                 {
       
   270                 TRAPD( err, aResponseHeaders.SetRawFieldL( nameStr, value, KHttpValueSep ) );
       
   271                 // leave OOM only
       
   272                 if( err == KErrNoMemory )
       
   273                     {
       
   274                     User::Leave( err );
       
   275                     }
       
   276                 }
       
   277             CleanupStack::PopAndDestroy(); // namestr
       
   278             // move string to the end of the line
       
   279             headerStr.Set( headerStr.Mid( lineEnd + KHttpNewLine().Length() ) );
       
   280             }
       
   281         }
       
   282     // we've got the merged headers, let's covert it to buffer
       
   283     return HeadersToBufferLC( aResponseHeaders, aStrP );
       
   284     }
       
   285 
       
   286 // -----------------------------------------------------------------------------
       
   287 // HttpCacheUtil::AddHeaderLC
       
   288 //
       
   289 // -----------------------------------------------------------------------------
       
   290 //
       
   291 HBufC8* HttpCacheUtil::AddHeaderLC(
       
   292     const TDesC8& aName,
       
   293     const TDesC8& aValue,
       
   294     const TDesC8& aHeaders )
       
   295     {
       
   296     HBufC8* newHeaders = NULL;
       
   297     // first check if the header exists so
       
   298     // then all we need to do is to update the value
       
   299     TInt pos( aHeaders.FindF( aName ) );
       
   300     //
       
   301     newHeaders = HBufC8::NewLC( aHeaders.Length() + aName.Length() + KHttpFieldSeparator().Length() +
       
   302         aValue.Length() + KHttpNewLine().Length() );
       
   303     //
       
   304     if( pos != KErrNotFound )
       
   305         {
       
   306         // replace old header with this new one
       
   307         TPtr8 newHeadersPtr( newHeaders->Des() );
       
   308         // copy headers
       
   309         newHeadersPtr.Copy( aHeaders );
       
   310         // check what we need to replace
       
   311         TPtrC8 leftover( aHeaders.Mid( pos ) );
       
   312         // headers are terminated by \r\n
       
   313         TInt endPos( leftover.Find( KHttpNewLine ) );
       
   314         if( endPos != KErrNotFound )
       
   315             {
       
   316             // value pos
       
   317             TInt valuePos( pos + aName.Length() + KHttpFieldSeparator().Length() );
       
   318             newHeadersPtr.Replace( valuePos, pos + endPos - valuePos, aValue );
       
   319             }
       
   320         }
       
   321     else
       
   322         {
       
   323         // add new header
       
   324         // new header = old header\r\n aName: aValue\r\n
       
   325         TPtr8 newHeadersPtr( newHeaders->Des() );
       
   326         // old headers
       
   327         newHeadersPtr.Copy( aHeaders );
       
   328         // Cache Control
       
   329         newHeadersPtr.Append( aName );
       
   330         // :
       
   331         newHeadersPtr.Append( KHttpFieldSeparator );
       
   332         // max age=180
       
   333         newHeadersPtr.Append( aValue );
       
   334         // \r\n
       
   335         newHeadersPtr.Append( KHttpNewLine );
       
   336         }
       
   337     return newHeaders;
       
   338     }
       
   339 
       
   340 // -----------------------------------------------------------------------------
       
   341 // HttpCacheUtil::HeaderFieldFromBufferLC
       
   342 //
       
   343 // -----------------------------------------------------------------------------
       
   344 //
       
   345 HBufC8* HttpCacheUtil::HeaderFieldFromBufferLC(
       
   346     const TDesC8& aHeaders,
       
   347     const TDesC8& aKey )
       
   348     {
       
   349     HBufC8* value = NULL;
       
   350     //
       
   351     TInt pos( aHeaders.Find( aKey ) );
       
   352     if( pos != KErrNotFound )
       
   353         {
       
   354         // line: Cache-Control: max-age=180
       
   355         TPtrC8 linePtr = aHeaders.Mid( pos );
       
   356         // line end \r\n
       
   357         TInt lineEnd( linePtr.Find( KHttpNewLine ) );
       
   358         // separator :
       
   359         TInt sepPos( linePtr.Find( KHttpFieldSeparator ) );
       
   360         //
       
   361         if( sepPos != KErrNotFound )
       
   362             {
       
   363             // fix line end
       
   364             lineEnd = lineEnd == KErrNotFound ? linePtr.Length(): lineEnd;
       
   365             // max-age=180
       
   366             TInt valueLen( lineEnd - ( sepPos + KHttpFieldSeparator().Length() ) );
       
   367             value = HBufC8::NewLC( valueLen );
       
   368             value->Des().Copy( linePtr.Mid( sepPos + KHttpFieldSeparator().Length(), valueLen ) );
       
   369             }
       
   370         }
       
   371     return value;
       
   372     }
       
   373 
       
   374 // -----------------------------------------------------------------------------
       
   375 // HttpCacheUtil::MethodFromStr
       
   376 //
       
   377 // -----------------------------------------------------------------------------
       
   378 //
       
   379 
       
   380 TCacheLoadMethod HttpCacheUtil::MethodFromStr(
       
   381     RStringF aMethodStr,
       
   382     RStringPool aStrP )
       
   383     {
       
   384     if( aMethodStr == aStrP.StringF( HTTP::EGET, RHTTPSession::GetTable() ) )
       
   385         {
       
   386         return EMethodGet;
       
   387         }
       
   388     if( aMethodStr == aStrP.StringF(HTTP::ECONNECT, RHTTPSession::GetTable() ) )
       
   389         {
       
   390         return EMethodConnect;
       
   391         }
       
   392     if( aMethodStr == aStrP.StringF(HTTP::EDELETE, RHTTPSession::GetTable() ) )
       
   393         {
       
   394         return EMethodDelete;
       
   395         }
       
   396     if( aMethodStr == aStrP.StringF(HTTP::EHEAD, RHTTPSession::GetTable() ) )
       
   397         {
       
   398         return EMethodHead;
       
   399         }
       
   400     if( aMethodStr == aStrP.StringF(HTTP::EOPTIONS, RHTTPSession::GetTable() ) )
       
   401         {
       
   402         return EMethodOptions;
       
   403         }
       
   404     if( aMethodStr == aStrP.StringF(HTTP::EPUT, RHTTPSession::GetTable() ) )
       
   405         {
       
   406         return EMethodPut;
       
   407         }
       
   408     if( aMethodStr == aStrP.StringF(HTTP::EPOST, RHTTPSession::GetTable() ) )
       
   409         {
       
   410         return EMethodPost;
       
   411         }
       
   412     if( aMethodStr == aStrP.StringF(HTTP::ETRACE, RHTTPSession::GetTable() ) )
       
   413         {
       
   414         return EMethodTrace;
       
   415         }
       
   416     return EMethodGet;
       
   417     }
       
   418 
       
   419 
       
   420 // -----------------------------------------------------------------------------
       
   421 // HttpCacheUtil::ProtectedEntry
       
   422 //
       
   423 // Returns true if the entry is protected
       
   424 //
       
   425 // -----------------------------------------------------------------------------
       
   426 //
       
   427 TBool HttpCacheUtil::ProtectedEntry(
       
   428     RHTTPHeaders& aHttpHeaders,
       
   429     RStringPool& aStrP )
       
   430     {
       
   431     TBool protectedEntry( EFalse );
       
   432     //
       
   433     THTTPHdrVal contType;
       
   434     RStringF fieldName = aStrP.StringF( HTTP::EContentType, RHTTPSession::GetTable() );
       
   435     //
       
   436     if( aHttpHeaders.GetField( fieldName, 0, contType ) == KErrNone )
       
   437         {
       
   438         if( contType.Type() == THTTPHdrVal::KStrFVal )
       
   439             {
       
   440             RStringF fieldValStr = aStrP.StringF( contType.StrF() );
       
   441             // css and javascrip are protected entries
       
   442             protectedEntry = fieldValStr.DesC().Find( _L8("text/css") ) != KErrNotFound ||
       
   443                 fieldValStr.DesC().Find( _L8("javascript") ) != KErrNotFound;
       
   444             }
       
   445         }
       
   446     return protectedEntry;
       
   447     }
       
   448 
       
   449 // -----------------------------------------------------------------------------
       
   450 // HttpCacheUtil::CacheTimeIsFresh
       
   451 //
       
   452 // Returns true if the entry is fresh.
       
   453 //
       
   454 // -----------------------------------------------------------------------------
       
   455 //
       
   456 TBool HttpCacheUtil::CacheTimeIsFresh(
       
   457     const RHTTPHeaders& aRequestHeaders,
       
   458     const RHTTPHeaders& aCachedHeaders,
       
   459     RStringPool aStrP )
       
   460     {
       
   461     TInt status( KErrNone );
       
   462     TBool isFresh( EFalse );
       
   463     TInt64 freshness;
       
   464     TInt64 age;
       
   465     TInt64 maxAge;
       
   466     TInt64 minFresh;
       
   467     TInt64 maxStale;
       
   468     TTime cachedRequestTime;
       
   469     TTime cachedResponseTime;
       
   470 
       
   471     TTime date;
       
   472     TInt err;
       
   473     THTTPHdrVal dateValue;
       
   474     RStringF fieldName;
       
   475 
       
   476     // Get the date from the headers
       
   477     fieldName = aStrP.StringF( HTTP::EDate, RHTTPSession::GetTable() );
       
   478     err = aCachedHeaders.GetField( fieldName, 0, dateValue );
       
   479     if( err == KErrNotFound || dateValue.Type() != THTTPHdrVal::KDateVal )
       
   480         {
       
   481         date = 0;
       
   482         }
       
   483     else
       
   484         {
       
   485         date = TTime( dateValue.DateTime() );
       
   486         }
       
   487     cachedRequestTime = date;
       
   488     cachedResponseTime = date;
       
   489 
       
   490     // Get Max Age header. If maxAge = 0, then must revalidate.
       
   491     status = GetCacheControls( aCachedHeaders, &maxAge, NULL, NULL, NULL, NULL, NULL, aStrP );
       
   492     if( status == KErrNone &&  maxAge == 0 )
       
   493         {
       
   494         return EFalse; //Must revalidate
       
   495         }
       
   496 
       
   497     // Get the freshness and age of the cachedHeaders
       
   498     freshness = Freshness( aCachedHeaders, cachedResponseTime, aStrP );
       
   499     age = Age( aCachedHeaders, cachedRequestTime, cachedResponseTime, aStrP );
       
   500 #ifdef __CACHELOG__
       
   501         TBuf<50> dateString;
       
   502         TTime fr( freshness );
       
   503 
       
   504         fr.FormatL( dateString, KDateString );
       
   505         HttpCacheUtil::WriteLog( 0, _L( "fresness" ) );
       
   506         HttpCacheUtil::WriteLog( 0, dateString );
       
   507 
       
   508         TTime ca( age );
       
   509         ca.FormatL( dateString, KDateString );
       
   510         HttpCacheUtil::WriteLog( 0, _L( "age" ) );
       
   511         HttpCacheUtil::WriteLog( 0, dateString );
       
   512 #endif // __CACHELOG__
       
   513 
       
   514     // Get useful cache-control directives from the requestHeaders
       
   515     status = GetCacheControls( aRequestHeaders, &maxAge, &minFresh, &maxStale, NULL, NULL, NULL, aStrP );
       
   516     if( status == KErrNone )
       
   517         {
       
   518         // Above values for maxAge, minFresh and maxStale are in seconds
       
   519         // Convert to micro seconds before using it.
       
   520 
       
   521         // If maxStale is present it means that it is willing to
       
   522         // accept a response maxStale seconds after it expires.  To
       
   523         // allow this freshness is extended by maxStale seconds.
       
   524         if( maxStale != -1 )
       
   525             {
       
   526             if( maxStale == MAKE_TINT64( KMaxTInt, KMaxTInt ) )
       
   527                 {
       
   528                 freshness = MAKE_TINT64( KMaxTInt, KMaxTInt );
       
   529                 }
       
   530             else
       
   531                 {
       
   532                 freshness += maxStale * 1000 * 1000;
       
   533                 }
       
   534             }
       
   535         // If minFresh is present reject it if it would expire
       
   536         // within minFresh seconds.
       
   537         if( minFresh != -1 )
       
   538             {
       
   539             if( ( age + minFresh * 1000 * 1000 ) > freshness )
       
   540                 {
       
   541                 return EFalse;
       
   542                 }
       
   543             }
       
   544         // If the age > request's maxAge, reject it
       
   545         if( maxAge != -1 )
       
   546             {
       
   547             if( age > maxAge * 1000 * 1000 )
       
   548                 {
       
   549                 return EFalse;
       
   550                 }
       
   551             }
       
   552         // If age is less than freshness its fresh
       
   553     // If age == 0 and freshness == 0 - revalidation is needed
       
   554         if( freshness && age <= freshness )
       
   555     {
       
   556     isFresh = ETrue;
       
   557     }
       
   558     }
       
   559     return isFresh;
       
   560     }
       
   561 
       
   562 // -----------------------------------------------------------------------------
       
   563 // HttpCacheUtil::AddValidationHeaders
       
   564 //
       
   565 // Adds appropriate headers to make the given "request" conditional.
       
   566 //
       
   567 // -----------------------------------------------------------------------------
       
   568 //
       
   569 void HttpCacheUtil::AddValidationHeaders(
       
   570     const RHTTPHeaders aCachedHeaders,
       
   571     RHTTPHeaders& aRequestHeaders,
       
   572     RStringPool& aStrP )
       
   573     {
       
   574     THTTPHdrVal lastMod;
       
   575     THTTPHdrVal eTag;
       
   576     RStringF fieldName;
       
   577     TInt err;
       
   578 
       
   579     // Get the entry's last-modified header
       
   580     fieldName = aStrP.StringF( HTTP::ELastModified, RHTTPSession::GetTable() );
       
   581     err = aCachedHeaders.GetField( fieldName, 0, lastMod );
       
   582 
       
   583     // If last-Modified is present add an If-Modified-Since header
       
   584     if( err != KErrNotFound )
       
   585         {
       
   586         fieldName = aStrP.StringF( HTTP::EIfModifiedSince, RHTTPSession::GetTable() );
       
   587         TRAP_IGNORE( aRequestHeaders.SetFieldL( fieldName, lastMod ) );
       
   588         }
       
   589 
       
   590     // Get the entry's ETag header
       
   591     fieldName = aStrP.StringF( HTTP::EETag, RHTTPSession::GetTable() );
       
   592     err = aCachedHeaders.GetField( fieldName, 0, eTag );
       
   593 
       
   594     // If etag is present add an etag header
       
   595     if( err != KErrNotFound )
       
   596         {
       
   597         fieldName = aStrP.StringF( HTTP::EIfNoneMatch, RHTTPSession::GetTable() );
       
   598         TRAP_IGNORE( aRequestHeaders.SetFieldL( fieldName, eTag ) );
       
   599         }
       
   600     }
       
   601 
       
   602 // -----------------------------------------------------------------------------
       
   603 // HttpCacheUtil::OperatorCacheContent
       
   604 // This function checks if aUrl is part of the operator domain. for examp: if aUrl->http://www.opdomain.com/pub/index.html
       
   605 // and operator domain is http://www.opdomain.com/pub then it returns ETrue.
       
   606 // -----------------------------------------------------------------------------
       
   607 //
       
   608 TBool HttpCacheUtil::OperatorCacheContent(
       
   609     const TDesC8& aOpDomain,
       
   610     const TDesC8& aUrl )
       
   611     {
       
   612     TUriParser8 opDomain;
       
   613     TUriParser8 url;
       
   614     TBool found( EFalse );
       
   615 
       
   616     // check if both URIs are correct.
       
   617     if( opDomain.Parse( aOpDomain ) == KErrNone && url.Parse( aUrl ) == KErrNone )
       
   618         {
       
   619         // host value must be the same. for examp: www.operator.co is different from www.operator.com
       
   620         // note: check if compare is case sensitive!
       
   621         // note2: if http:// is missing from operator domain then TUriParser8 takes the host part as path.
       
   622         if( opDomain.Compare( url, EUriHost ) == 0 )
       
   623             {
       
   624             // further checking on the path value. domain path must be a substring of the given url.
       
   625             // for examp: op path: /news
       
   626             // url path: /news/local_news.html
       
   627             const TDesC8& opPath = opDomain.Extract( EUriPath );
       
   628             if( opPath.Length() )
       
   629                 {
       
   630                 const TDesC8& urlPath = url.Extract( EUriPath );
       
   631                 // we don't take the content if the url path is not defined (while domain path is) or the
       
   632                 // domain path is not a substring of url path. actually url path must start with domain path
       
   633                 // op path: /news url path: /media/news/local_news.html is not valid.
       
   634                 found = ( urlPath.Length() && urlPath.FindF( opPath ) == 0 );
       
   635                 }
       
   636             else
       
   637                 {
       
   638                 // no opPath means that we take every content on this host.
       
   639                 found = ETrue;
       
   640                 }
       
   641             }
       
   642         }
       
   643     return found;
       
   644     }
       
   645 
       
   646 
       
   647 // -----------------------------------------------------------------------------
       
   648 // HttpCacheUtil::VSSCacheContent
       
   649 //
       
   650 // -----------------------------------------------------------------------------
       
   651 //
       
   652 TBool HttpCacheUtil::VSSCacheContent( const TDesC8& aUrl, const HBufC8* aWhiteList )
       
   653   {
       
   654     TBool found( EFalse );
       
   655   if( aWhiteList )
       
   656     {
       
   657     TUint8* bufPtr = (TUint8* ) aWhiteList->Ptr();
       
   658     TUint8* startBufPtr = bufPtr;
       
   659     TUint len = aWhiteList->Length();
       
   660     TInt i, startUri, uriLen;
       
   661     TPtrC8 uri ( aUrl );
       
   662     for(i=0; i < len; i++)
       
   663       {
       
   664       startUri = i;
       
   665       for( ;( ( *bufPtr != ';' ) && ( i<len ) ) ; i++, bufPtr++ ) ;
       
   666       uriLen = i - startUri;
       
   667       if( i == len ) 
       
   668           {
       
   669           uriLen += 2; //For getting total length 
       
   670           }
       
   671       TPtrC8 uriDomain( startBufPtr, uriLen);
       
   672       if ( OperatorCacheContent( uriDomain, uri ) )
       
   673         {
       
   674         found = ETrue;
       
   675         break;
       
   676         }
       
   677       startBufPtr = ++bufPtr;
       
   678       i++;
       
   679       } //end for()
       
   680      } //end for()
       
   681      return found;
       
   682     }
       
   683 
       
   684 // -----------------------------------------------------------------------------
       
   685 // HttpCacheUtil::PragmaNoCache
       
   686 //
       
   687 // -----------------------------------------------------------------------------
       
   688 //
       
   689 TBool HttpCacheUtil::PragmaNoCache(
       
   690     RHTTPTransaction& aTrans )
       
   691     {
       
   692     //
       
   693     RHTTPHeaders requestHeaders = aTrans.Request().GetHeaderCollection();
       
   694     RStringPool strP = aTrans.Session().StringPool();
       
   695     RStringF fieldName = strP.StringF( HTTP::ENoCache, RHTTPSession::GetTable() );
       
   696 
       
   697     // check no-cache
       
   698     THTTPHdrVal headerVal;
       
   699     TInt noCacheField( requestHeaders.GetField( fieldName, 0, headerVal ) );
       
   700     // check no-store
       
   701     fieldName = strP.StringF( HTTP::ENoStore, RHTTPSession::GetTable() );
       
   702     TInt noStoreField( requestHeaders.GetField( fieldName, 0, headerVal ) );
       
   703     //
       
   704     return( noCacheField == KErrNone || noStoreField == KErrNone );
       
   705     }
       
   706 
       
   707 // -----------------------------------------------------------------------------
       
   708 // HttpCacheUtil::GetHeaderFileName
       
   709 //
       
   710 // -----------------------------------------------------------------------------
       
   711 //
       
   712 void HttpCacheUtil::GetHeaderFileName(
       
   713     const TDesC& aBodyFileName,
       
   714     TDes& aHeaderFileName )
       
   715     {
       
   716     // body file name = foo
       
   717     // header file name = foo.h
       
   718     aHeaderFileName.Copy( aBodyFileName );
       
   719     // take the filename and append the new extension
       
   720     aHeaderFileName.Append( KHttpCacheHeaderExt() );
       
   721     }
       
   722 
       
   723 // -----------------------------------------------------------------------------
       
   724 // HttpCacheUtil::AdjustExpirationTime
       
   725 //
       
   726 // -----------------------------------------------------------------------------
       
   727 //
       
   728 void HttpCacheUtil::AdjustExpirationTimeL(
       
   729     RHTTPHeaders& aResponseHeaders,
       
   730     RStringPool& aStrP )
       
   731     {
       
   732     const TStringTable& stringTable = RHTTPSession::GetTable();
       
   733     THTTPHdrVal hdrVal;
       
   734 
       
   735     if( aResponseHeaders.GetField( aStrP.StringF( HTTP::EExpires, stringTable ), 0, hdrVal ) == KErrNone )
       
   736         {
       
   737         HttpCacheUtil::WriteLog( 0, _L( "adjust expiration time from" ) );
       
   738 
       
   739         TTime expDate( hdrVal.DateTime() );
       
   740 #ifdef __CACHELOG__
       
   741         TBuf<50> dateString;
       
   742         TTime expTime( hdrVal.DateTime() );
       
   743 
       
   744         expTime.FormatL( dateString, KDateString );
       
   745         HttpCacheUtil::WriteLog( 0, dateString );
       
   746 #endif // __CACHELOG__
       
   747         // double it
       
   748         TTimeIntervalMinutes minutes;
       
   749         TTimeIntervalHours hours;
       
   750         TTime now;
       
   751         now.UniversalTime();
       
   752 
       
   753         if( expDate.MinutesFrom( now, minutes ) == KErrNone )
       
   754             {
       
   755 #ifdef __CACHELOG__
       
   756         //
       
   757         now.FormatL( dateString, KDateString );
       
   758         HttpCacheUtil::WriteLog( 0, _L( "current time" ) );
       
   759         HttpCacheUtil::WriteLog( 0, dateString );
       
   760         //
       
   761         now.FormatL( dateString, KDateString );
       
   762         HttpCacheUtil::WriteLog( 0, _L( "expires in (minutes)" ), minutes.Int() );
       
   763 #endif // __CACHELOG__
       
   764             //
       
   765             expDate+=minutes;
       
   766             }
       
   767         // minutes owerflow? take hours instead
       
   768         else if( expDate.HoursFrom( now, hours ) == KErrNone )
       
   769             {
       
   770             expDate+=hours;
       
   771             }
       
   772         else
       
   773             {
       
   774             // last resort
       
   775             TTimeIntervalDays days( expDate.DaysFrom( now ) );
       
   776             expDate+=days;
       
   777             }
       
   778         // set new date on the response header
       
   779         aResponseHeaders.RemoveField( aStrP.StringF( HTTP::EExpires, stringTable ) );
       
   780         // add it
       
   781         hdrVal.SetDateTime( expDate.DateTime() );
       
   782 #ifdef __CACHELOG__
       
   783         HttpCacheUtil::WriteLog( 0, _L( "to" ) );
       
   784         //
       
   785         TTime newExptime( hdrVal.DateTime() );
       
   786         newExptime.FormatL( dateString, KDateString );
       
   787         HttpCacheUtil::WriteLog( 0, dateString );
       
   788 
       
   789         TTimeIntervalMinutes min;
       
   790 
       
   791         newExptime.MinutesFrom( now, min );
       
   792 
       
   793         HttpCacheUtil::WriteLog( 0, _L( "now it expires in (minutes)" ), min.Int() );
       
   794 #endif // __CACHELOG__
       
   795         aResponseHeaders.SetFieldL( aStrP.StringF( HTTP::EExpires, stringTable ), hdrVal );
       
   796         }
       
   797     }
       
   798 
       
   799 // -----------------------------------------------------------------------------
       
   800 // HttpCacheUtil::CacheNeedsUpdateL
       
   801 //
       
   802 // -----------------------------------------------------------------------------
       
   803 //
       
   804 TBool HttpCacheUtil::CacheNeedsUpdateL(
       
   805     RHTTPHeaders& aResponseHeaders,
       
   806     const TDesC8& aCachedHeadersStr,
       
   807     RStringPool& aStrP )
       
   808     {
       
   809     HBufC8* valueStr;
       
   810     TTime cachedDate( 0 );
       
   811     TTime responseDate( 0 );
       
   812     TTime cachedLastModified( 0 );
       
   813     TTime responseLastModified( 0 );
       
   814     RStringF cachedETag;
       
   815     RStringF responseETag;
       
   816     RStringF fieldName;
       
   817     TInternetDate value;
       
   818     THTTPHdrVal hdrValue;
       
   819     TBool eTag( EFalse );
       
   820     TBool eLastModified( EFalse );
       
   821     TBool eDate( EFalse );
       
   822     TInt status;
       
   823     TBool update( ETrue );
       
   824 
       
   825      // date
       
   826     fieldName = aStrP.StringF( HTTP::EDate, RHTTPSession::GetTable() );
       
   827     // new
       
   828     status = aResponseHeaders.GetField( fieldName, 0, hdrValue );
       
   829     if( status == KErrNone )
       
   830         {
       
   831         responseDate = TTime( hdrValue.DateTime() );
       
   832         // orig
       
   833         valueStr = HttpCacheUtil::HeaderFieldFromBufferLC( aCachedHeadersStr, fieldName.DesC() );
       
   834         if( valueStr )
       
   835             {
       
   836             eDate = ETrue;
       
   837             value.SetDateL( valueStr->Des() );
       
   838             cachedDate = TTime( value.DateTime() );
       
   839             CleanupStack::PopAndDestroy(); // valueStr
       
   840             }
       
   841         }
       
   842 
       
   843 
       
   844     // last modified
       
   845     fieldName = aStrP.StringF( HTTP::ELastModified, RHTTPSession::GetTable() );
       
   846     // new
       
   847     status = aResponseHeaders.GetField( fieldName, 0, hdrValue );
       
   848     if( status == KErrNone )
       
   849         {
       
   850         responseLastModified = TTime( hdrValue.DateTime() );
       
   851         // orig
       
   852         valueStr = HttpCacheUtil::HeaderFieldFromBufferLC( aCachedHeadersStr, fieldName.DesC() );
       
   853         if( valueStr )
       
   854             {
       
   855             eLastModified = ETrue;
       
   856             value.SetDateL( valueStr->Des() );
       
   857             cachedLastModified  = TTime( value.DateTime() );
       
   858             CleanupStack::PopAndDestroy(); // valueStr
       
   859             }
       
   860         }
       
   861 
       
   862     // Etag
       
   863     fieldName = aStrP.StringF( HTTP::EETag, RHTTPSession::GetTable() );
       
   864     // new
       
   865     status = aResponseHeaders.GetField( fieldName, 0, hdrValue );
       
   866     if( status == KErrNone )
       
   867         {
       
   868         responseETag = hdrValue.StrF();
       
   869         // orig
       
   870         valueStr = HttpCacheUtil::HeaderFieldFromBufferLC( aCachedHeadersStr, fieldName.DesC() );
       
   871         if( valueStr )
       
   872             {
       
   873             TRAP( status, cachedETag = aStrP.OpenFStringL( valueStr->Des() ) );
       
   874             CleanupStack::PopAndDestroy(); // valueStr
       
   875             eTag = ( status == KErrNone );
       
   876             //
       
   877             if( eTag )
       
   878                 {
       
   879                 CleanupClosePushL( cachedETag );
       
   880                 }
       
   881             }
       
   882         }
       
   883 
       
   884     // If the entry's date is greater than the responses's date,
       
   885     // ignore the response.
       
   886     if( eDate && cachedDate > responseDate )
       
   887         {
       
   888         update  = EFalse;
       
   889         }
       
   890     // If the entry's ETag does not equal the responses's ETag,
       
   891     // replace the cached entry with the response.
       
   892     else if( eTag && cachedETag != responseETag )
       
   893         {
       
   894         update  = ETrue;
       
   895         }
       
   896     // If the entry's lastMod is greater than the responses's lastMod,
       
   897     // ignore the response.
       
   898     else if( eLastModified && cachedLastModified > responseLastModified )
       
   899         {
       
   900         update  = EFalse;
       
   901         }
       
   902     // If the entry's lastMod is equal to the responses's lastMod,
       
   903     // update the cached headers with the new response headers.
       
   904     else if( eLastModified && cachedLastModified == responseLastModified )
       
   905         {
       
   906         update  = ETrue;
       
   907         }
       
   908     // If the entry's lastMod is less than the responses's lastMod,
       
   909     // replace the cached entry with the response.
       
   910     else if( eLastModified && cachedLastModified < responseLastModified )
       
   911         {
       
   912         update  = ETrue;
       
   913         }
       
   914     else
       
   915         {
       
   916         // If we get here the headers need to be replaced
       
   917         update  = ETrue;
       
   918         }
       
   919     //
       
   920     if( eTag )
       
   921         {
       
   922         CleanupStack::PopAndDestroy(); // cachedETag
       
   923         }
       
   924     return update;
       
   925     }
       
   926 
       
   927 // -----------------------------------------------------------------------------
       
   928 // HttpCacheUtil::IsCacheable
       
   929 //
       
   930 // -----------------------------------------------------------------------------
       
   931 //
       
   932 TBool HttpCacheUtil::IsCacheable(
       
   933     RHTTPTransaction& aTrans,
       
   934     TUint aMaxSize,
       
   935     TBool& aProtectedEntry )
       
   936     {
       
   937     TBool isCacheable( ETrue );
       
   938     TBool hasExplicitCache( EFalse );
       
   939     TInt64 maxAge;
       
   940     TBool noCache;
       
   941     TBool noStore;
       
   942     TTime now;
       
   943     TTime expires;
       
   944     RStringF fieldName;
       
   945     THTTPHdrVal expiresVal;
       
   946     TInt err;
       
   947     RStringPool strP = aTrans.Session().StringPool();
       
   948     RHTTPHeaders respHeaders = aTrans.Response().GetHeaderCollection();
       
   949 
       
   950     // not protected by default
       
   951     aProtectedEntry = EFalse;
       
   952     // 1. do not cache sesnitive content (DRM)
       
   953     // 2. do not cache content bigger than the cache
       
   954     // 3. check if the content is protected
       
   955     // 4. check normal cache directives
       
   956 
       
   957     // init the field name
       
   958     THTTPHdrVal contType;
       
   959     fieldName = strP.StringF( HTTP::EContentType, RHTTPSession::GetTable() );
       
   960 
       
   961     // check if this is a noncacheable content type
       
   962     if( respHeaders.GetField( fieldName, 0, contType ) == KErrNone &&
       
   963         contType.StrF() == strP.StringF( HttpFilterCommonStringsExt::EApplicationVndOmaDrm,
       
   964         HttpFilterCommonStringsExt::GetTable() ) )
       
   965         {
       
   966         HttpCacheUtil::WriteLog( 0, _L( "sensitive content. do not cache" ) );
       
   967         // drm == nocache
       
   968         isCacheable = EFalse;
       
   969         }
       
   970     else
       
   971         {
       
   972         // check if the content is bigger than the cache
       
   973         THTTPHdrVal contLen;
       
   974         fieldName = strP.StringF( HTTP::EContentLength, RHTTPSession::GetTable() );
       
   975 
       
   976         if( respHeaders.GetField( fieldName, 0, contLen ) != KErrNotFound &&
       
   977             ( contLen.Type() == THTTPHdrVal::KTIntVal && contLen.Int() > aMaxSize ) )
       
   978             {
       
   979             HttpCacheUtil::WriteLog( 0, _L( "oversized content. do not cache" ) );
       
   980             // oversized content
       
   981             return EFalse;
       
   982             }
       
   983         // check if this is a proteced entry
       
   984         aProtectedEntry = ProtectedEntry( respHeaders, strP );
       
   985         // check various cache controls
       
   986         if( GetCacheControls( respHeaders, &maxAge, NULL, NULL, NULL,
       
   987             &noCache, &noStore, strP ) == KErrNone )
       
   988             {
       
   989             // There are several header "conditions" that make a resource
       
   990             // non-cachable.  Reject if they are present.
       
   991             //
       
   992             // If no-cache or no-store directives exist -> don't cache.
       
   993             if( noCache || noStore )
       
   994                 {
       
   995                 HttpCacheUtil::WriteLog( 0, _L( "no cache/no store header. do not cache" ) );
       
   996                 // no protection on this entry
       
   997                 aProtectedEntry = EFalse;
       
   998                 return EFalse;
       
   999                 }
       
  1000             // Get the current time
       
  1001             now.UniversalTime();
       
  1002 
       
  1003             // Get the expires from the respHeaders
       
  1004             fieldName = strP.StringF( HTTP::EExpires, RHTTPSession::GetTable() );
       
  1005             err = respHeaders.GetField( fieldName, 0, expiresVal );
       
  1006             if( err == KErrNone )
       
  1007                 {
       
  1008                 expires = TTime( expiresVal.DateTime() );
       
  1009                 }
       
  1010             else
       
  1011                 {
       
  1012                 expires = now;
       
  1013                 }
       
  1014             
       
  1015             // if past-expire date do not cache. According to RFC2616, section 13.2.4/14.9.3,
       
  1016             // if maxage is present, then ignore expires
       
  1017             if (!maxAge && now > expires)
       
  1018                 {
       
  1019                 return EFalse;
       
  1020                 }
       
  1021             
       
  1022             if( err == KErrNone || maxAge > 0 )
       
  1023                 {
       
  1024                 hasExplicitCache = ETrue;
       
  1025                 }
       
  1026             // Reject if the http status code doesn't equal 200, 304, 301, 410.
       
  1027             // Note: We accept status codes, 307, 302 if some cache
       
  1028             //       control directives exist.
       
  1029             switch( aTrans.Response().StatusCode() )
       
  1030                 {
       
  1031                 case HTTPStatus::EOk:
       
  1032                 case HTTPStatus::ENotModified:
       
  1033                 case HTTPStatus::EMovedPermanently:
       
  1034                 case HTTPStatus::EGone:
       
  1035                     {
       
  1036                     break;
       
  1037                     }
       
  1038                 case HTTPStatus::EFound:
       
  1039                 case HTTPStatus::ETemporaryRedirect:
       
  1040                     {
       
  1041                     if( !hasExplicitCache )
       
  1042                         {
       
  1043                         isCacheable = EFalse;
       
  1044                         }
       
  1045                     break;
       
  1046                     }
       
  1047                 default:
       
  1048                     {
       
  1049                     isCacheable = EFalse;
       
  1050                     break;
       
  1051                     }
       
  1052                 }
       
  1053 
       
  1054             // Check method specific conditions.
       
  1055             switch( HttpCacheUtil::MethodFromStr( aTrans.Request().Method(), strP ) )
       
  1056                 {
       
  1057                 case EMethodGet:
       
  1058                     {
       
  1059                     // Reject if the url contains a "query" part unless there
       
  1060                     // are caching directives.
       
  1061                     TBool isQuery;
       
  1062                     isQuery = aTrans.Request().URI().IsPresent( EUriQuery );
       
  1063 
       
  1064                     if( isQuery && !hasExplicitCache )
       
  1065                         {
       
  1066                         isCacheable = EFalse;
       
  1067                         }
       
  1068                     break;
       
  1069                     }
       
  1070                 case EMethodPost:
       
  1071                     {
       
  1072                     // Reject unless there are caching directives
       
  1073                     if( !hasExplicitCache )
       
  1074                         {
       
  1075                         isCacheable = EFalse;
       
  1076                         }
       
  1077                     break;
       
  1078                     }
       
  1079                 case EMethodHead:
       
  1080                     {
       
  1081                     // Don't cache Head responses...we don't need to, and the
       
  1082                     // cache implementation can't currently handle it anyway.
       
  1083                     // So just fall through to the default case.
       
  1084                     // Reject all other methods
       
  1085                     }
       
  1086                 default:
       
  1087                     {
       
  1088                     isCacheable = EFalse;
       
  1089                     break;
       
  1090                     }
       
  1091                 }
       
  1092             }
       
  1093         }
       
  1094     return isCacheable;
       
  1095     }
       
  1096 
       
  1097 // -----------------------------------------------------------------------------
       
  1098 // HttpCacheUtil::WriteLog
       
  1099 //
       
  1100 // -----------------------------------------------------------------------------
       
  1101 //
       
  1102 void HttpCacheUtil::WriteLog(
       
  1103     TInt aLogLevel,
       
  1104     TPtrC aBuf,
       
  1105     TInt aAny )
       
  1106     {
       
  1107 #ifdef __CACHELOG__
       
  1108     TBool log( aLogLevel <= KCurrentLogLevel );
       
  1109     TPtrC fileName( KHttpCacheGeneralFileName );
       
  1110 
       
  1111     if( aLogLevel == 1 )
       
  1112         {
       
  1113         // hash
       
  1114         fileName.Set( KHttpCacheHashFileName );
       
  1115         log = ETrue;
       
  1116         }
       
  1117     if( log )
       
  1118         {
       
  1119         if( aAny != 0xffff )
       
  1120             {
       
  1121             RFileLogger::WriteFormat(_L("Browser"), fileName, EFileLoggingModeAppend,
       
  1122                     _L("%S %d"), &aBuf, aAny );
       
  1123             }
       
  1124         else
       
  1125             {
       
  1126             RFileLogger::WriteFormat(_L("Browser"), fileName, EFileLoggingModeAppend,
       
  1127                     _L("%S"), &aBuf );
       
  1128             }
       
  1129         }
       
  1130 #else // __CACHELOG__
       
  1131     (void)aLogLevel;
       
  1132     (void)aBuf;
       
  1133     (void)aAny;
       
  1134 #endif // __CACHELOG__
       
  1135     }
       
  1136 
       
  1137 // -----------------------------------------------------------------------------
       
  1138 // HttpCacheUtil::WriteUrlToLog
       
  1139 //
       
  1140 // Get the freshness of the "entry".
       
  1141 //
       
  1142 // -----------------------------------------------------------------------------
       
  1143 //
       
  1144 void HttpCacheUtil::WriteUrlToLog(
       
  1145     TInt aLogLevel,
       
  1146     const TDesC8& aUrl,
       
  1147     TInt aAny )
       
  1148     {
       
  1149 #ifdef __CACHELOG__
       
  1150     HBufC* tmp = HBufC::New( aUrl.Length() );
       
  1151     if( tmp )
       
  1152         {
       
  1153         TPtr tmpPtr( tmp->Des() );
       
  1154         tmpPtr.Copy( aUrl );
       
  1155         HttpCacheUtil::WriteLog( aLogLevel, tmpPtr, aAny );
       
  1156         delete tmp;
       
  1157         }
       
  1158 #else // __CACHELOG__
       
  1159     (void)aLogLevel;
       
  1160     (void)aUrl;
       
  1161     (void)aAny;
       
  1162 #endif // __CACHELOG__
       
  1163     }
       
  1164 
       
  1165 // -----------------------------------------------------------------------------
       
  1166 // HttpCacheUtil::WriteUrlToLog
       
  1167 //
       
  1168 // Get the freshness of the "entry".
       
  1169 //
       
  1170 // -----------------------------------------------------------------------------
       
  1171 //
       
  1172 void HttpCacheUtil::WriteUrlToLog(
       
  1173     TInt aLogLevel,
       
  1174     const TDesC& aTxt,
       
  1175     const TDesC8& aUrl,
       
  1176     TInt aAny )
       
  1177     {
       
  1178 #ifdef __CACHELOG__
       
  1179     TBool log( aLogLevel <= KCurrentLogLevel );
       
  1180     TPtrC fileName( KHttpCacheGeneralFileName );
       
  1181 
       
  1182     if( aLogLevel == 1 )
       
  1183         {
       
  1184         // hash
       
  1185         fileName.Set( KHttpCacheHashFileName );
       
  1186         log = ETrue;
       
  1187         }
       
  1188     if( log )
       
  1189         {    //
       
  1190         HBufC* tmp = HBufC::New( aUrl.Length() );
       
  1191         if( tmp )
       
  1192             {
       
  1193             TPtr tmpPtr( tmp->Des() );
       
  1194             tmpPtr.Copy( aUrl );
       
  1195             if( aAny != 0xffff )
       
  1196                 {
       
  1197                 RFileLogger::WriteFormat( _L("Browser"), fileName, EFileLoggingModeAppend,
       
  1198                         _L("%S %S %d"), &aTxt, &tmpPtr, aAny );
       
  1199                 }
       
  1200             else
       
  1201                 {
       
  1202                 RFileLogger::WriteFormat(_L("Browser"), fileName, EFileLoggingModeAppend,
       
  1203                         _L("%S %S"), &aTxt, &tmpPtr );
       
  1204                 }
       
  1205             delete tmp;
       
  1206             }
       
  1207         }
       
  1208 #else // __CACHELOG__
       
  1209     (void)aLogLevel;
       
  1210     (void)aTxt;
       
  1211     (void)aUrl;
       
  1212     (void)aAny;
       
  1213 #endif // __CACHELOG__
       
  1214     }
       
  1215 
       
  1216 // -----------------------------------------------------------------------------
       
  1217 // HttpCacheUtil::GenerateNameLC
       
  1218 // Given a URL, generates fully qualified Symbian path for storing HTTP response. 
       
  1219 // The format is <cache base dir> + <subdirectory> + <file name>. 
       
  1220 // Caller must free the returned HBufC* when done. 
       
  1221 // -----------------------------------------------------------------------------
       
  1222 HBufC* HttpCacheUtil::GenerateNameLC(
       
  1223         const TDesC8& aUrl, const TDesC& aBaseDir)
       
  1224         {
       
  1225 
       
  1226     TUint32 crc (0);
       
  1227 
       
  1228     //use the entire URL for CRC calculation: maximizes source entropy/avoids collisions
       
  1229     Mem::Crc32(crc, aUrl.Ptr(), aUrl.Size()); 
       
  1230     TUint32 nibble (crc & (KCacheSubdirCount-1)); // extract least significant 4 bits (nibble) for subdirectory
       
  1231 
       
  1232     HBufC* fileName = HBufC::NewLC( KMaxPath ); // e.g E\078AFEFE
       
  1233     _LIT(KFormat,"%S%x%c%08x"); // Note the %08x : a 32-bit value can represented as 0xFFFFFFFF 
       
  1234     fileName->Des().Format(KFormat, &aBaseDir, nibble, KPathDelimiter, crc);
       
  1235     return fileName;
       
  1236 
       
  1237     }
       
  1238 
       
  1239 
       
  1240 // -----------------------------------------------------------------------------
       
  1241 // HttpCacheUtil::Freshness
       
  1242 //
       
  1243 // Get the freshness of the "entry".
       
  1244 //
       
  1245 // -----------------------------------------------------------------------------
       
  1246 //
       
  1247 TInt64 HttpCacheUtil::Freshness(
       
  1248     const RHTTPHeaders& aHeaders,
       
  1249     TTime aResponseTime,
       
  1250     RStringPool aStrP )
       
  1251     {
       
  1252     TInt status( KErrNone );
       
  1253     TInt64 maxAge;
       
  1254     TTime expires;
       
  1255     TTime lastModified( 0 );
       
  1256     TTime date;
       
  1257     RStringF fieldName;
       
  1258     THTTPHdrVal hdrValue;
       
  1259     TInt err;
       
  1260     TInt64 freshness( 0 );
       
  1261 
       
  1262     // Get the date from the headers
       
  1263     fieldName = aStrP.StringF( HTTP::EDate, RHTTPSession::GetTable() );
       
  1264     err = aHeaders.GetField( fieldName, 0, hdrValue );
       
  1265     if( err == KErrNotFound || hdrValue.Type() != THTTPHdrVal::KDateVal )
       
  1266         {
       
  1267         date = aResponseTime;
       
  1268         }
       
  1269     else
       
  1270         {
       
  1271         date = TTime( hdrValue.DateTime() );
       
  1272         }
       
  1273 
       
  1274     // Get useful cache-control directives
       
  1275     status = GetCacheControls( aHeaders, &maxAge, NULL, NULL, NULL, NULL, NULL, aStrP );
       
  1276     if( status == KErrNone )
       
  1277         {
       
  1278         // max-age is in delta-seconds. Convert it to micro seconds.
       
  1279         // All our calculations are in micro-seconds
       
  1280         // If maxAge is present, use it
       
  1281         if( maxAge != -1 )
       
  1282             {
       
  1283             freshness = maxAge * 1000 * 1000;
       
  1284 
       
  1285             return freshness;
       
  1286             }
       
  1287 
       
  1288         // Get the expires from the headers
       
  1289         fieldName = aStrP.StringF( HTTP::EExpires, RHTTPSession::GetTable() );
       
  1290         err = aHeaders.GetField( fieldName, 0, hdrValue );
       
  1291         if( err == KErrNotFound || hdrValue.Type() != THTTPHdrVal::KDateVal )
       
  1292             {
       
  1293             expires = 0;
       
  1294             }
       
  1295         else
       
  1296             {
       
  1297             expires = TTime( hdrValue.DateTime() );
       
  1298             }
       
  1299 
       
  1300         // Otherwise, if the expires is present use it
       
  1301         if( err != KErrNotFound )
       
  1302             {
       
  1303             freshness = expires.Int64() - date.Int64();
       
  1304             return freshness;
       
  1305             }
       
  1306 
       
  1307         // Get the last modified from the headers
       
  1308         fieldName = aStrP.StringF( HTTP::ELastModified, RHTTPSession::GetTable() );
       
  1309         err = aHeaders.GetField( fieldName, 0, hdrValue );
       
  1310         if( err == KErrNotFound || hdrValue.Type() != THTTPHdrVal::KDateVal )
       
  1311             {
       
  1312             hdrValue = 0;
       
  1313             }
       
  1314         else
       
  1315             {
       
  1316             lastModified = TTime( hdrValue.DateTime() );
       
  1317             }
       
  1318 
       
  1319         // Otherwise, if last-modified is present use it
       
  1320         if( err != KErrNotFound )
       
  1321             {
       
  1322             if( aResponseTime > lastModified )
       
  1323                 {
       
  1324                 freshness = aResponseTime.Int64() - lastModified.Int64();
       
  1325                 freshness += ((TInt64)( freshness * 0.10 ) );
       
  1326                 }
       
  1327             return freshness;
       
  1328             }
       
  1329         // If it get here then we have no way to determine if it is
       
  1330         // fresh.  So we set freshness to zero, which will most likely
       
  1331         // cause the resource to be validated.
       
  1332         freshness = 0;
       
  1333         }
       
  1334     return freshness;
       
  1335     }
       
  1336 
       
  1337 // -----------------------------------------------------------------------------
       
  1338 // HttpCacheUtil::Age
       
  1339 //
       
  1340 // Get the age of the "entry".
       
  1341 //
       
  1342 // -----------------------------------------------------------------------------
       
  1343 //
       
  1344 TInt64 HttpCacheUtil::Age(
       
  1345     const RHTTPHeaders& aHeaders,
       
  1346     TTime aRequestTime,
       
  1347     TTime aResponseTime,
       
  1348     RStringPool aStrP )
       
  1349     {
       
  1350     TTime now;
       
  1351     TDateTime date;
       
  1352     TTime time;
       
  1353 
       
  1354     // Int64 timeinsec;
       
  1355     TInt64 correctedRecvAge;
       
  1356     TInt64 apparentAge;
       
  1357     TInt64 responseDelay;
       
  1358     TInt64 correctedInitialAge;
       
  1359     TInt64 residentTime;
       
  1360     TInt64 currentAge;
       
  1361     TInt64 age;
       
  1362 
       
  1363 
       
  1364     RStringF fieldName;
       
  1365     THTTPHdrVal dateValue;
       
  1366     TInt err;
       
  1367 
       
  1368     // Get the current time. All internet dates are GMT
       
  1369     now.UniversalTime();
       
  1370 #ifdef __CACHELOG__
       
  1371     TBuf<50> dateString;
       
  1372     //
       
  1373     now.FormatL( dateString, KDateString );
       
  1374     HttpCacheUtil::WriteLog( 0, _L( "current time" ) );
       
  1375     HttpCacheUtil::WriteLog( 0, dateString );
       
  1376 #endif // __CACHELOG__
       
  1377 
       
  1378     // The aRequestTime is same as that of headers time.
       
  1379 
       
  1380     time = aRequestTime;
       
  1381 
       
  1382     // Do a sanity check
       
  1383     if( aRequestTime > now )
       
  1384         {
       
  1385         aRequestTime = now;
       
  1386         }
       
  1387     if( aResponseTime > now )
       
  1388         {
       
  1389         aResponseTime = now;
       
  1390         }
       
  1391     if( aRequestTime > aResponseTime )
       
  1392         {
       
  1393         aRequestTime = aResponseTime;
       
  1394         }
       
  1395     if( time > aResponseTime )
       
  1396         {
       
  1397         time = aResponseTime;
       
  1398         }
       
  1399 
       
  1400     // Get the age from the headers.  If age is missing it equals
       
  1401     // 0, which is a safe value to use below.
       
  1402     fieldName = aStrP.StringF( HTTP::EAge, RHTTPSession::GetTable() );
       
  1403     err = aHeaders.GetField( fieldName, 0, dateValue );
       
  1404     if( err == KErrNotFound )
       
  1405         {
       
  1406         age = 0;
       
  1407         }
       
  1408     else
       
  1409         {
       
  1410         // Age should be in delta seconds
       
  1411         if( dateValue.Type() == THTTPHdrVal::KTIntVal )
       
  1412             {
       
  1413             age = dateValue;
       
  1414             }
       
  1415         else
       
  1416             {
       
  1417             age = 0;
       
  1418             }
       
  1419         }
       
  1420 
       
  1421     // Get the "current" age
       
  1422     apparentAge = Max( TInt64( 0 ), aResponseTime.Int64() - time.Int64() );
       
  1423     // The TTime::Int64() gives the time in micro seconds. And the age field is in
       
  1424     // delta seconds.
       
  1425     correctedRecvAge = Max( apparentAge, age * 1000 * 1000 );
       
  1426     responseDelay = aResponseTime.Int64() - aRequestTime.Int64();
       
  1427     correctedInitialAge = correctedRecvAge + responseDelay;
       
  1428     residentTime = now.Int64() - aResponseTime.Int64();
       
  1429 
       
  1430     currentAge = correctedInitialAge + residentTime;
       
  1431 
       
  1432     //the current age in micro seconds.
       
  1433     return currentAge;
       
  1434     }
       
  1435 
       
  1436 // -----------------------------------------------------------------------------
       
  1437 // HttpCacheUtil::GetCacheControls
       
  1438 //
       
  1439 // Description: Returns various cache controls.
       
  1440 //
       
  1441 // -----------------------------------------------------------------------------
       
  1442 //
       
  1443 TInt HttpCacheUtil::GetCacheControls(
       
  1444     const RHTTPHeaders& aHeaders,
       
  1445     TInt64* aMaxAge,
       
  1446     TInt64* aMinFresh,
       
  1447     TInt64* aMaxStale,
       
  1448     TBool* aMustRevalidate,
       
  1449     TBool* aNoCache,
       
  1450     TBool* aNoStore,
       
  1451     RStringPool aStrP )
       
  1452     {
       
  1453     TInt status( KErrNone );
       
  1454     TInt i;
       
  1455     TInt cacheCount( 0 );
       
  1456     RStringF directive;
       
  1457     TInt64 value;
       
  1458     char* extraValues = NULL;
       
  1459     RStringF fieldName;
       
  1460 
       
  1461 
       
  1462     if( aMaxAge )
       
  1463         {
       
  1464         *aMaxAge = -1;
       
  1465         }
       
  1466     if( aMinFresh )
       
  1467         {
       
  1468         *aMinFresh = -1;
       
  1469         }
       
  1470     if( aMaxStale )
       
  1471         {
       
  1472         *aMaxStale = -1;
       
  1473         }
       
  1474     if( aMustRevalidate )
       
  1475         {
       
  1476         *aMustRevalidate = EFalse;
       
  1477         }
       
  1478     if( aNoCache )
       
  1479         {
       
  1480         *aNoCache = EFalse;
       
  1481         }
       
  1482     if( aNoStore )
       
  1483         {
       
  1484         *aNoStore = EFalse;
       
  1485         }
       
  1486 
       
  1487     // init the field name
       
  1488     fieldName = aStrP.StringF( HTTP::ECacheControl, RHTTPSession::GetTable() );
       
  1489     TRAP( status, cacheCount = aHeaders.FieldPartsL( fieldName ) );
       
  1490     if( status == KErrNone )
       
  1491         {
       
  1492         for( i = 0; i < cacheCount; i++ )
       
  1493             {
       
  1494             status = GetCacheControlDirective( aHeaders, i, directive, &value, &extraValues, aStrP );
       
  1495             if( status == KErrNone )
       
  1496                 {
       
  1497                 if( directive == aStrP.StringF( HTTP::EMaxAge, RHTTPSession::GetTable() ) )
       
  1498                     {
       
  1499                     if( aMaxAge )
       
  1500                         {
       
  1501                         *aMaxAge = value;
       
  1502                         }
       
  1503                     }
       
  1504                 else if( directive == aStrP.StringF( HTTP::EMinFresh, RHTTPSession::GetTable() ) )
       
  1505                     {
       
  1506                     if( aMinFresh )
       
  1507                         {
       
  1508                         *aMinFresh = value;
       
  1509                         }
       
  1510                     }
       
  1511                 else if( directive == aStrP.StringF( HTTP::EMaxStale, RHTTPSession::GetTable() ) )
       
  1512                     {
       
  1513                     if( aMaxStale )
       
  1514                         {
       
  1515                         *aMaxStale = value;
       
  1516                         }
       
  1517                     }
       
  1518                 else if( directive == aStrP.StringF( HTTP::EMustRevalidate, RHTTPSession::GetTable() ) )
       
  1519                     {
       
  1520                     if( aMustRevalidate )
       
  1521                         {
       
  1522                         *aMustRevalidate = ETrue;
       
  1523                         }
       
  1524                     }
       
  1525                 else if( directive == aStrP.StringF( HTTP::ENoCache, RHTTPSession::GetTable() ) )
       
  1526                     {
       
  1527                     if( aNoCache )
       
  1528                         {
       
  1529                         *aNoCache = ETrue;
       
  1530                         }
       
  1531                     }
       
  1532                 else if( directive == aStrP.StringF( HTTP::ENoStore, RHTTPSession::GetTable() ) )
       
  1533                     {
       
  1534                     if( aNoStore )
       
  1535                         {
       
  1536                         *aNoStore = ETrue;
       
  1537                         }
       
  1538                     }
       
  1539                 directive.Close();
       
  1540                 // It ignores extraValues, just free it
       
  1541                 User::Free( extraValues );
       
  1542                 extraValues = NULL;
       
  1543                 }
       
  1544             else
       
  1545                 {
       
  1546                 break;
       
  1547                 }
       
  1548             }
       
  1549         }
       
  1550     return status;
       
  1551     }
       
  1552 
       
  1553 // -----------------------------------------------------------------------------
       
  1554 // HttpCacheUtil::GetCacheControlDirective
       
  1555 //
       
  1556 // -----------------------------------------------------------------------------
       
  1557 //
       
  1558 TInt HttpCacheUtil::GetCacheControlDirective(
       
  1559     const RHTTPHeaders& aHeaders,
       
  1560     TInt aIndex,
       
  1561     RStringF& aDirective,
       
  1562     TInt64* aDirectiveValue,
       
  1563     char** aExtraValue,
       
  1564     RStringPool aStrP )
       
  1565     {
       
  1566     RStringF fieldName;
       
  1567     THTTPHdrVal hdrVal;
       
  1568     TInt err;
       
  1569 
       
  1570     // Get the cache-control from the headers
       
  1571     fieldName = aStrP.StringF( HTTP::ECacheControl, RHTTPSession::GetTable() );
       
  1572     aHeaders.GetField( fieldName, aIndex, hdrVal );
       
  1573 
       
  1574     if( hdrVal.Type() == THTTPHdrVal::KStrVal || hdrVal.Type() == THTTPHdrVal::KStrFVal )
       
  1575         {
       
  1576         RStringF cacheDir = hdrVal.StrF();
       
  1577 
       
  1578         TInt endPos;
       
  1579         _LIT8(KFind, "=");
       
  1580 
       
  1581         endPos = cacheDir.DesC().Find( KFind );
       
  1582         if( endPos != -1 )
       
  1583             {
       
  1584             TRAP( err, aDirective = aStrP.OpenFStringL( cacheDir.DesC().Left( endPos ) ) );
       
  1585             if( err != KErrNone )
       
  1586                 {
       
  1587                 return err;
       
  1588                 }
       
  1589             TPtrC8 value = cacheDir.DesC().Right( cacheDir.DesC().Length() - endPos - 1 );
       
  1590             err = ExtractCacheControlDirectiveValue( aStrP, aDirective, value, aDirectiveValue, aExtraValue );
       
  1591             if( err != KErrNone )
       
  1592                 {
       
  1593                 aDirective.Close();
       
  1594                 return err;
       
  1595                 }
       
  1596             }
       
  1597         else
       
  1598             {
       
  1599             aDirective = cacheDir.Copy();
       
  1600             // Directives which MUST have values;
       
  1601             if( ( aDirective == aStrP.StringF( HTTP::EMaxAge, RHTTPSession::GetTable() ) ) ||
       
  1602                 ( aDirective == aStrP.StringF( HTTP::EMinFresh, RHTTPSession::GetTable() ) ) ||
       
  1603                 ( aDirective == aStrP.StringF( HTTP::ESMaxAge, RHTTPSession::GetTable() ) ) )
       
  1604                 {
       
  1605                 aDirective.Close();
       
  1606                 return KErrGeneral;
       
  1607                 }
       
  1608             }
       
  1609         }
       
  1610     return KErrNone;
       
  1611     }
       
  1612 
       
  1613 // -----------------------------------------------------------------------------
       
  1614 // HttpCacheUtil::ExtractCacheControlDirectiveValue
       
  1615 //
       
  1616 // -----------------------------------------------------------------------------
       
  1617 //
       
  1618 TInt HttpCacheUtil::ExtractCacheControlDirectiveValue(
       
  1619     RStringPool aStrP,
       
  1620     RStringF& aDirective,
       
  1621     TDesC8& aValue,
       
  1622     TInt64* aDirectiveValue,
       
  1623     char** aExtraValue )
       
  1624     {
       
  1625     TInt status( KErrNone );
       
  1626     TInt temp;
       
  1627     char* errorIfNull;
       
  1628 
       
  1629     *aDirectiveValue = -1;
       
  1630     *aExtraValue = NULL;
       
  1631     char* valuestr = (char*)User::Alloc( aValue.Length() + 1 );
       
  1632     if( !valuestr )
       
  1633         {
       
  1634         return KErrNoMemory;
       
  1635         }
       
  1636     memcpy( valuestr, aValue.Ptr(), aValue.Length() );
       
  1637     valuestr[ aValue.Length() ] = '\0';
       
  1638 
       
  1639 
       
  1640     if( ( aDirective == aStrP.StringF( HTTP::EMaxAge, RHTTPSession::GetTable() ) ) ||
       
  1641         ( aDirective == aStrP.StringF( HTTP::EMinFresh, RHTTPSession::GetTable() ) ) ||
       
  1642         ( aDirective == aStrP.StringF( HTTP::ESMaxAge, RHTTPSession::GetTable() ) ) )
       
  1643         {
       
  1644         // Cases with directiveValues
       
  1645         temp = strtoul( valuestr, &errorIfNull, 10 );
       
  1646         if( !errorIfNull )
       
  1647             {
       
  1648             status = KErrGeneral;
       
  1649             }
       
  1650         else
       
  1651             {
       
  1652             *aDirectiveValue = temp;
       
  1653             }
       
  1654         User::Free( valuestr );
       
  1655         return status;
       
  1656         }
       
  1657 
       
  1658     if( ( aDirective == aStrP.StringF( HTTP::EMaxStale, RHTTPSession::GetTable() ) ) )
       
  1659         {
       
  1660         // Cases with optional directiveValues
       
  1661         temp = strtoul( valuestr, &errorIfNull, 10 );
       
  1662         if( errorIfNull )
       
  1663             {
       
  1664             *aDirectiveValue = temp;
       
  1665             }
       
  1666         User::Free( valuestr );
       
  1667         return status;
       
  1668         }
       
  1669 
       
  1670     if( ( aDirective == aStrP.StringF( HTTP::ENoCache, RHTTPSession::GetTable() ) ) ||
       
  1671         ( aDirective == aStrP.StringF( HTTP::EPrivate, RHTTPSession::GetTable() ) ) )
       
  1672         {
       
  1673         *aExtraValue = valuestr;
       
  1674         return status;
       
  1675         }
       
  1676     User::Free( valuestr );
       
  1677     return status;
       
  1678     }
       
  1679 
       
  1680 //  End of File