text: //+---------------------------------------------------------------------------
  //
  //  Microsoft Windows NT Security
  //  Copyright (C) Microsoft Corporation, 1997 - 1999
  //
  //  File:       offurl.cpp
  //
  //  Contents:   Offline URL Caching
  //
  //  History:    19-Jan-00    philh    Created
  //              01-Jan-02    philh    Changed to internally use UNICODE Urls
  //----------------------------------------------------------------------------
  #include <global.hxx>
  
  
  //
  // Offline URL Cache Entry.
  //
  // The earliest wire retrieval is delayed according to the number of
  // offline failures.
  //
  // For a successful wire URL retrieval, the entry is removed and deleted.
  //
  // Assumption: the number of offline entries is small, less than 20.
  //
  typedef struct _OFFLINE_URL_CACHE_ENTRY
                          OFFLINE_URL_CACHE_ENTRY, *POFFLINE_URL_CACHE_ENTRY;
  struct _OFFLINE_URL_CACHE_ENTRY {
      CRYPT_DATA_BLOB             UrlBlob;
      CRYPT_DATA_BLOB             ExtraBlob;
      DWORD                       dwContextOid;
      OFFLINE_URL_TIME_INFO       OfflineUrlTimeInfo;
      POFFLINE_URL_CACHE_ENTRY    pNext;
      POFFLINE_URL_CACHE_ENTRY    pPrev;
  };
  
  CRITICAL_SECTION            OfflineUrlCacheCriticalSection;
  POFFLINE_URL_CACHE_ENTRY    pOfflineUrlCacheHead;
  
  
  //
  // Local Functions (Forward Reference)
  // 
  
  //  Assumption: OfflineUrlCache is locked
  POFFLINE_URL_CACHE_ENTRY
  WINAPI
  CreateAndAddOfflineUrlCacheEntry(
      IN PCRYPT_DATA_BLOB pUrlBlob,
      IN PCRYPT_DATA_BLOB pExtraBlob,
      IN LPCSTR pszContextOid,
      IN DWORD dwRetrievalFlags
      );
  
  //  Assumption: OfflineUrlCache is locked
  VOID
  WINAPI
  RemoveAndFreeOfflineUrlCacheEntry(
      IN OUT POFFLINE_URL_CACHE_ENTRY pEntry
      );
  
  //  Assumption: OfflineUrlCache is locked
  POFFLINE_URL_CACHE_ENTRY
  WINAPI
  FindOfflineUrlCacheEntry(
      IN PCRYPT_DATA_BLOB pUrlBlob,
      IN PCRYPT_DATA_BLOB pExtraBlob,
      IN LPCSTR pszContextOid,
      IN DWORD dwRetrievalFlags
      );
  
  
  
  VOID
  WINAPI
  InitializeOfflineUrlCache()
  {
      Pki_InitializeCriticalSection(&OfflineUrlCacheCriticalSection);
  }
  
  VOID
  WINAPI
  DeleteOfflineUrlCache()
  {
      while (pOfflineUrlCacheHead)
          RemoveAndFreeOfflineUrlCacheEntry(pOfflineUrlCacheHead);
  
      DeleteCriticalSection(&OfflineUrlCacheCriticalSection);
  }
  
  
  //
  // Return status:
  //  +1 - Online
  //   0 - Offline, current time >= earliest online time, hit the wire
  //  -1 - Offline, current time < earliest onlime time
  //
  LONG
  WINAPI
  GetOfflineUrlTimeStatus(
      IN POFFLINE_URL_TIME_INFO pInfo
      )
  {
      FILETIME CurrentTime;
  
      if (0 == pInfo->dwOfflineCnt)
      {
          return 1;
      }
  
      GetSystemTimeAsFileTime(&CurrentTime);
      if (0 <= CompareFileTime(&CurrentTime, &pInfo->EarliestOnlineTime))
      {
          return 0;
      }
      else
      {
          return -1;
      }
  }
  
  
  const DWORD rgdwOfflineUrlDeltaSeconds[] = {
      15,                 // 15 seconds
      15,                 // 15 seconds
      60,                 // 1 minute
      60 * 5,             // 5 minutes
      60 * 10,            // 10 minutes
      60 * 30,            // 30 minutes
  };
  
  #define OFFLINE_URL_DELTA_SECONDS_CNT \
      (sizeof(rgdwOfflineUrlDeltaSeconds) / \
          sizeof(rgdwOfflineUrlDeltaSeconds[0]))
  
  
  VOID
  WINAPI
  SetOfflineUrlTime(
      IN OUT POFFLINE_URL_TIME_INFO pInfo
      )
  {
      DWORD dwOfflineCnt;
      FILETIME CurrentTime;
  
      dwOfflineCnt = ++pInfo->dwOfflineCnt;
  
      if (OFFLINE_URL_DELTA_SECONDS_CNT < dwOfflineCnt)
      {
          dwOfflineCnt = OFFLINE_URL_DELTA_SECONDS_CNT;
      }
  
      GetSystemTimeAsFileTime( &CurrentTime );
      I_CryptIncrementFileTimeBySeconds(
          &CurrentTime,
          rgdwOfflineUrlDeltaSeconds[dwOfflineCnt - 1],
          &pInfo->EarliestOnlineTime
          );
  }
  
  VOID
  WINAPI
  SetOnlineUrlTime(
      IN OUT POFFLINE_URL_TIME_INFO pInfo
      )
  {
      pInfo->dwOfflineCnt = 0;
  }
  
  
  
  
  //
  // Return status:
  //  +1 - Online
  //   0 - Offline, current time >= earliest online time, hit the wire
  //  -1 - Offline, current time < earliest onlime time
  //
  LONG
  WINAPI
  GetOriginUrlStatusW(
      IN CRYPT_ORIGIN_IDENTIFIER OriginIdentifier,
      IN LPCWSTR pwszUrl,
      IN LPCSTR pszContextOid,
      IN DWORD dwRetrievalFlags
      )
  {
      LONG lStatus;
      POFFLINE_URL_CACHE_ENTRY pEntry;
      CRYPT_DATA_BLOB UrlBlob;
      CRYPT_DATA_BLOB ExtraBlob;
  
      UrlBlob.pbData = (BYTE *) pwszUrl;
      UrlBlob.cbData = wcslen(pwszUrl) * sizeof(WCHAR);
      ExtraBlob.pbData = OriginIdentifier;
      ExtraBlob.cbData = MD5DIGESTLEN;
  
      EnterCriticalSection(&OfflineUrlCacheCriticalSection);
  
      pEntry = FindOfflineUrlCacheEntry(
          &UrlBlob,
          &ExtraBlob,
          pszContextOid,
          dwRetrievalFlags
          );
      if (pEntry)
          lStatus = GetOfflineUrlTimeStatus(&pEntry->OfflineUrlTimeInfo);
      else
          lStatus = 1;
  
      LeaveCriticalSection(&OfflineUrlCacheCriticalSection);
  
      return lStatus;
  }
  
  VOID
  WINAPI
  SetOnlineOriginUrlW(
      IN CRYPT_ORIGIN_IDENTIFIER OriginIdentifier,
      IN LPCWSTR pwszUrl,
      IN LPCSTR pszContextOid,
      IN DWORD dwRetrievalFlags
      )
  {
      POFFLINE_URL_CACHE_ENTRY pEntry;
      CRYPT_DATA_BLOB UrlBlob;
      CRYPT_DATA_BLOB ExtraBlob;
  
      UrlBlob.pbData = (BYTE *) pwszUrl;
      UrlBlob.cbData = wcslen(pwszUrl) * sizeof(WCHAR);
      ExtraBlob.pbData = OriginIdentifier;
      ExtraBlob.cbData = MD5DIGESTLEN;
  
      EnterCriticalSection(&OfflineUrlCacheCriticalSection);
  
      pEntry = FindOfflineUrlCacheEntry(
          &UrlBlob,
          &ExtraBlob,
          pszContextOid,
          dwRetrievalFlags
          );
      if (pEntry)
          RemoveAndFreeOfflineUrlCacheEntry(pEntry);
  
      LeaveCriticalSection(&OfflineUrlCacheCriticalSection);
  }
  
  VOID
  WINAPI
  SetOfflineOriginUrlW(
      IN CRYPT_ORIGIN_IDENTIFIER OriginIdentifier,
      IN LPCWSTR pwszUrl,
      IN LPCSTR pszContextOid,
      IN DWORD dwRetrievalFlags
      )
  {
      POFFLINE_URL_CACHE_ENTRY pEntry;
      CRYPT_DATA_BLOB UrlBlob;
      CRYPT_DATA_BLOB ExtraBlob;
  
      UrlBlob.pbData = (BYTE *) pwszUrl;
      UrlBlob.cbData = wcslen(pwszUrl) * sizeof(WCHAR);
      ExtraBlob.pbData = OriginIdentifier;
      ExtraBlob.cbData = MD5DIGESTLEN;
  
      EnterCriticalSection(&OfflineUrlCacheCriticalSection);
  
      pEntry = FindOfflineUrlCacheEntry(
          &UrlBlob,
          &ExtraBlob,
          pszContextOid,
          dwRetrievalFlags
          );
  
      if (NULL == pEntry)
          pEntry = CreateAndAddOfflineUrlCacheEntry( 
              &UrlBlob,
              &ExtraBlob,
              pszContextOid,
              dwRetrievalFlags
              );
  
      if (pEntry)
          SetOfflineUrlTime(&pEntry->OfflineUrlTimeInfo);
  
      LeaveCriticalSection(&OfflineUrlCacheCriticalSection);
  }
  
  
  //
  // Return status:
  //  +1 - Online
  //   0 - Offline, current time >= earliest online time, hit the wire
  //  -1 - Offline, current time < earliest onlime time
  //
  LONG
  WINAPI
  GetUrlStatusW(
      IN LPCWSTR pwszUrl,
      IN LPCSTR pszContextOid,
      IN DWORD dwRetrievalFlags
      )
  {
      LONG lStatus;
      POFFLINE_URL_CACHE_ENTRY pEntry;
      CRYPT_DATA_BLOB UrlBlob;
      CRYPT_DATA_BLOB ExtraBlob;
  
      UrlBlob.pbData = (BYTE *) pwszUrl;
      UrlBlob.cbData = wcslen(pwszUrl) * sizeof(WCHAR);
      ExtraBlob.pbData = NULL;
      ExtraBlob.cbData = 0;
  
      EnterCriticalSection(&OfflineUrlCacheCriticalSection);
  
      pEntry = FindOfflineUrlCacheEntry(
          &UrlBlob,
          &ExtraBlob,
          pszContextOid,
          dwRetrievalFlags
          );
      if (pEntry)
          lStatus = GetOfflineUrlTimeStatus(&pEntry->OfflineUrlTimeInfo);
      else
          lStatus = 1;
  
      LeaveCriticalSection(&OfflineUrlCacheCriticalSection);
  
      return lStatus;
  }
  
  VOID
  WINAPI
  SetOnlineUrlW(
      IN LPCWSTR pwszUrl,
      IN LPCSTR pszContextOid,
      IN DWORD dwRetrievalFlags
      )
  {
      POFFLINE_URL_CACHE_ENTRY pEntry;
      CRYPT_DATA_BLOB UrlBlob;
      CRYPT_DATA_BLOB ExtraBlob;
  
      UrlBlob.pbData = (BYTE *) pwszUrl;
      UrlBlob.cbData = wcslen(pwszUrl) * sizeof(WCHAR);
      ExtraBlob.pbData = NULL;
      ExtraBlob.cbData = 0;
  
      EnterCriticalSection(&OfflineUrlCacheCriticalSection);
  
      pEntry = FindOfflineUrlCacheEntry(
          &UrlBlob,
          &ExtraBlob,
          pszContextOid,
          dwRetrievalFlags
          );
      if (pEntry)
          RemoveAndFreeOfflineUrlCacheEntry(pEntry);
  
      LeaveCriticalSection(&OfflineUrlCacheCriticalSection);
  }
  
  VOID
  WINAPI
  SetOfflineUrlW(
      IN LPCWSTR pwszUrl,
      IN LPCSTR pszContextOid,
      IN DWORD dwRetrievalFlags
      )
  {
      POFFLINE_URL_CACHE_ENTRY pEntry;
      CRYPT_DATA_BLOB UrlBlob;
      CRYPT_DATA_BLOB ExtraBlob;
  
      UrlBlob.pbData = (BYTE *) pwszUrl;
      UrlBlob.cbData = wcslen(pwszUrl) * sizeof(WCHAR);
      ExtraBlob.pbData = NULL;
      ExtraBlob.cbData = 0;
  
      EnterCriticalSection(&OfflineUrlCacheCriticalSection);
  
      pEntry = FindOfflineUrlCacheEntry(
          &UrlBlob,
          &ExtraBlob,
          pszContextOid,
          dwRetrievalFlags
          );
  
      if (NULL == pEntry)
          pEntry = CreateAndAddOfflineUrlCacheEntry( 
              &UrlBlob,
              &ExtraBlob,
              pszContextOid,
              dwRetrievalFlags
              );
  
      if (pEntry)
          SetOfflineUrlTime(&pEntry->OfflineUrlTimeInfo);
  
      LeaveCriticalSection(&OfflineUrlCacheCriticalSection);
  }
  
  inline
  DWORD
  GetOfflineUrlCacheContextOid(
      IN LPCSTR pszContextOid,
      IN DWORD dwRetrievalFlags
      )
  {
      DWORD dwContextOid;
  
      if (0xFFFF >= (DWORD) ((DWORD_PTR) pszContextOid))
          dwContextOid = (DWORD) ((DWORD_PTR) pszContextOid);
      else
          dwContextOid = 0x10000;
  
      if (dwRetrievalFlags & CRYPT_WIRE_ONLY_RETRIEVAL)
          dwContextOid |= 0x20000;
  
      return dwContextOid;
  }
  
  //  Assumption: OfflineUrlCache is locked
  POFFLINE_URL_CACHE_ENTRY
  WINAPI
  CreateAndAddOfflineUrlCacheEntry(
      IN PCRYPT_DATA_BLOB pUrlBlob,
      IN PCRYPT_DATA_BLOB pExtraBlob,
      IN LPCSTR pszContextOid,
      IN DWORD dwRetrievalFlags
      )
  {
      POFFLINE_URL_CACHE_ENTRY pEntry;
      DWORD cbEntry;
      BYTE *pb;
  
      cbEntry = sizeof(OFFLINE_URL_CACHE_ENTRY) +
          pUrlBlob->cbData + pExtraBlob->cbData;
  
      pEntry = (POFFLINE_URL_CACHE_ENTRY) new BYTE [cbEntry];
  
      if (pEntry == NULL)
      {
          SetLastError( (DWORD) E_OUTOFMEMORY );
          return( NULL );
      }
  
      memset( pEntry, 0, sizeof( OFFLINE_URL_CACHE_ENTRY ) );
      pb = (BYTE *) &pEntry[1];
  
      if (pUrlBlob->cbData) {
          pEntry->UrlBlob.pbData = pb;
          pEntry->UrlBlob.cbData = pUrlBlob->cbData;
          memcpy(pb, pUrlBlob->pbData, pUrlBlob->cbData);
          pb += pUrlBlob->cbData;
      }
  
      if (pExtraBlob->cbData) {
          pEntry->ExtraBlob.pbData = pb;
          pEntry->ExtraBlob.cbData = pExtraBlob->cbData;
          memcpy(pb, pExtraBlob->pbData, pExtraBlob->cbData);
      }
  
      pEntry->dwContextOid =
          GetOfflineUrlCacheContextOid(pszContextOid, dwRetrievalFlags);
  
      if (pOfflineUrlCacheHead) {
          assert(NULL == pOfflineUrlCacheHead->pPrev);
          pOfflineUrlCacheHead->pPrev = pEntry;
          pEntry->pNext = pOfflineUrlCacheHead;
      }
      // else
      //  pEntry->pNext = NULL;        // already zeroed above
  
      // pEntry->pPrev = NULL;        // already zeroed above
      pOfflineUrlCacheHead = pEntry;
  
      return pEntry;
  }
  
  
  //  Assumption: OfflineUrlCache is locked
  VOID
  WINAPI
  RemoveAndFreeOfflineUrlCacheEntry(
      IN OUT POFFLINE_URL_CACHE_ENTRY pEntry
      )
  {
      if (pEntry->pNext)
      {
          pEntry->pNext->pPrev = pEntry->pPrev;
      }
  
      if (pEntry->pPrev)
      {
          pEntry->pPrev->pNext = pEntry->pNext;
      }
      else
      {
          assert(pOfflineUrlCacheHead == pEntry);
          pOfflineUrlCacheHead = pEntry->pNext;
      }
  
      delete (LPBYTE) pEntry;
  }
  
  //  Assumption: OfflineUrlCache is locked
  POFFLINE_URL_CACHE_ENTRY
  WINAPI
  FindOfflineUrlCacheEntry(
      IN PCRYPT_DATA_BLOB pUrlBlob,
      IN PCRYPT_DATA_BLOB pExtraBlob,
      IN LPCSTR pszContextOid,
      IN DWORD dwRetrievalFlags
      )
  {
      DWORD cbUrl = pUrlBlob->cbData;
      BYTE *pbUrl = pUrlBlob->pbData;
      DWORD cbExtra = pExtraBlob->cbData;
      BYTE *pbExtra = pExtraBlob->pbData;
      DWORD dwContextOid;
      POFFLINE_URL_CACHE_ENTRY pEntry;
  
      dwContextOid =
          GetOfflineUrlCacheContextOid(pszContextOid, dwRetrievalFlags);
  
      for (pEntry = pOfflineUrlCacheHead; pEntry; pEntry = pEntry->pNext)
      {
          if (pEntry->dwContextOid == dwContextOid
                          &&
              pEntry->UrlBlob.cbData == cbUrl
                          &&
              pEntry->ExtraBlob.cbData == cbExtra
                          &&
              (0 == cbExtra || 0 == memcmp(
                  pEntry->ExtraBlob.pbData, pbExtra, cbExtra))
                          &&
              (0 == cbUrl || 0 == memcmp(
                  pEntry->UrlBlob.pbData, pbUrl, cbUrl))
                          )
          {
              return pEntry;
          }
      }
  
      return ( NULL );
  }
  