ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

urlcache.c
Go to the documentation of this file.
00001 /*
00002  * Wininet - Url Cache functions
00003  *
00004  * Copyright 2001,2002 CodeWeavers
00005  * Copyright 2003-2008 Robert Shearman
00006  *
00007  * Eric Kohl
00008  * Aric Stewart
00009  *
00010  * This library is free software; you can redistribute it and/or
00011  * modify it under the terms of the GNU Lesser General Public
00012  * License as published by the Free Software Foundation; either
00013  * version 2.1 of the License, or (at your option) any later version.
00014  *
00015  * This library is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018  * Lesser General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU Lesser General Public
00021  * License along with this library; if not, write to the Free Software
00022  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
00023  */
00024 
00025 #include "config.h"
00026 #include "wine/port.h"
00027 
00028 #define NONAMELESSUNION
00029 #define NONAMELESSSTRUCT
00030 
00031 #if defined(__MINGW32__) || defined (_MSC_VER)
00032 #include <ws2tcpip.h>
00033 #endif
00034 
00035 #include <stdarg.h>
00036 #include <stdio.h>
00037 #include <stdlib.h>
00038 #include <string.h>
00039 #include <sys/types.h>
00040 #ifdef HAVE_SYS_SOCKET_H
00041 # include <sys/socket.h>
00042 #endif
00043 #include <time.h>
00044 
00045 #include "windef.h"
00046 #include "winbase.h"
00047 #include "winuser.h"
00048 #include "wininet.h"
00049 #include "winineti.h"
00050 #include "winerror.h"
00051 #include "winreg.h"
00052 #include "shlwapi.h"
00053 #include "shlobj.h"
00054 #include "shellapi.h"
00055 
00056 #include "internet.h"
00057 
00058 #include "wine/unicode.h"
00059 #include "wine/debug.h"
00060 
00061 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
00062 
00063 #define ENTRY_START_OFFSET  0x4000
00064 #define DIR_LENGTH          8
00065 #define BLOCKSIZE           128
00066 #define HASHTABLE_SIZE      448
00067 #define HASHTABLE_BLOCKSIZE 7
00068 #define HASHTABLE_FREE      3
00069 #define ALLOCATION_TABLE_OFFSET 0x250
00070 #define ALLOCATION_TABLE_SIZE   (0x1000 - ALLOCATION_TABLE_OFFSET)
00071 #define HASHTABLE_NUM_ENTRIES   (HASHTABLE_SIZE / HASHTABLE_BLOCKSIZE)
00072 #define NEWFILE_NUM_BLOCKS  0xd80
00073 #define NEWFILE_SIZE        (NEWFILE_NUM_BLOCKS * BLOCKSIZE + ENTRY_START_OFFSET)
00074 
00075 #define DWORD_SIG(a,b,c,d)  (a | (b << 8) | (c << 16) | (d << 24))
00076 #define URL_SIGNATURE   DWORD_SIG('U','R','L',' ')
00077 #define REDR_SIGNATURE  DWORD_SIG('R','E','D','R')
00078 #define LEAK_SIGNATURE  DWORD_SIG('L','E','A','K')
00079 #define HASH_SIGNATURE  DWORD_SIG('H','A','S','H')
00080 
00081 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
00082 
00083 typedef struct _CACHEFILE_ENTRY
00084 {
00085 /*  union
00086     {*/
00087         DWORD dwSignature; /* e.g. "URL " */
00088 /*      CHAR szSignature[4];
00089     };*/
00090     DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
00091 } CACHEFILE_ENTRY;
00092 
00093 typedef struct _URL_CACHEFILE_ENTRY
00094 {
00095     CACHEFILE_ENTRY CacheFileEntry;
00096     FILETIME LastModifiedTime;
00097     FILETIME LastAccessTime;
00098     WORD wExpiredDate; /* expire date in dos format */
00099     WORD wExpiredTime; /* expire time in dos format */
00100     DWORD dwUnknown1; /* usually zero */
00101     ULARGE_INTEGER size; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
00102     DWORD dwUnknown2; /* usually zero */
00103     DWORD dwExemptDelta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
00104     DWORD dwUnknown3; /* usually 0x60 */
00105     DWORD dwOffsetUrl; /* offset of start of url from start of entry */
00106     BYTE CacheDir; /* index of cache directory this url is stored in */
00107     BYTE Unknown4; /* usually zero */
00108     WORD wUnknown5; /* usually 0x1010 */
00109     DWORD dwOffsetLocalName; /* offset of start of local filename from start of entry */
00110     DWORD CacheEntryType; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
00111     DWORD dwOffsetHeaderInfo; /* offset of start of header info from start of entry */
00112     DWORD dwHeaderInfoSize;
00113     DWORD dwOffsetFileExtension; /* offset of start of file extension from start of entry */
00114     WORD wLastSyncDate; /* last sync date in dos format */
00115     WORD wLastSyncTime; /* last sync time in dos format */
00116     DWORD dwHitRate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
00117     DWORD dwUseCount; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
00118     WORD wUnknownDate; /* usually same as wLastSyncDate */
00119     WORD wUnknownTime; /* usually same as wLastSyncTime */
00120     DWORD dwUnknown7; /* usually zero */
00121     DWORD dwUnknown8; /* usually zero */
00122     /* packing to dword align start of next field */
00123     /* CHAR szSourceUrlName[]; (url) */
00124     /* packing to dword align start of next field */
00125     /* CHAR szLocalFileName[]; (local file name excluding path) */
00126     /* packing to dword align start of next field */
00127     /* CHAR szHeaderInfo[]; (header info) */
00128 } URL_CACHEFILE_ENTRY;
00129 
00130 struct _HASH_ENTRY
00131 {
00132     DWORD dwHashKey;
00133     DWORD dwOffsetEntry;
00134 };
00135 
00136 typedef struct _HASH_CACHEFILE_ENTRY
00137 {
00138     CACHEFILE_ENTRY CacheFileEntry;
00139     DWORD dwAddressNext;
00140     DWORD dwHashTableNumber;
00141     struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
00142 } HASH_CACHEFILE_ENTRY;
00143 
00144 typedef struct _DIRECTORY_DATA
00145 {
00146     DWORD dwNumFiles;
00147     char filename[DIR_LENGTH];
00148 } DIRECTORY_DATA;
00149 
00150 typedef struct _URLCACHE_HEADER
00151 {
00152     char szSignature[28];
00153     DWORD dwFileSize;
00154     DWORD dwOffsetFirstHashTable;
00155     DWORD dwIndexCapacityInBlocks;
00156     DWORD dwBlocksInUse;
00157     DWORD dwUnknown1;
00158     ULARGE_INTEGER CacheLimit;
00159     ULARGE_INTEGER CacheUsage;
00160     ULARGE_INTEGER ExemptUsage;
00161     DWORD DirectoryCount; /* number of directory_data's */
00162     DIRECTORY_DATA directory_data[1]; /* first directory entry */
00163 } URLCACHE_HEADER, *LPURLCACHE_HEADER;
00164 typedef const URLCACHE_HEADER *LPCURLCACHE_HEADER;
00165 
00166 typedef struct _STREAM_HANDLE
00167 {
00168     HANDLE hFile;
00169     CHAR lpszUrl[1];
00170 } STREAM_HANDLE;
00171 
00172 typedef struct _URLCACHECONTAINER
00173 {
00174     struct list entry; /* part of a list */
00175     LPWSTR cache_prefix; /* string that has to be prefixed for this container to be used */
00176     LPWSTR path; /* path to url container directory */
00177     HANDLE hMapping; /* handle of file mapping */
00178     DWORD file_size; /* size of file when mapping was opened */
00179     HANDLE hMutex; /* handle of mutex */
00180 } URLCACHECONTAINER;
00181 
00182 
00183 /* List of all containers available */
00184 static struct list UrlContainers = LIST_INIT(UrlContainers);
00185 BOOL bDefaultContainersAdded = FALSE;
00186 
00187 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash);
00188 
00189 /***********************************************************************
00190  *           URLCache_PathToObjectName (Internal)
00191  *
00192  *  Converts a path to a name suitable for use as a Win32 object name.
00193  * Replaces '\\' characters in-place with the specified character
00194  * (usually '_' or '!')
00195  *
00196  * RETURNS
00197  *    nothing
00198  *
00199  */
00200 static void URLCache_PathToObjectName(LPWSTR lpszPath, WCHAR replace)
00201 {
00202     for (; *lpszPath; lpszPath++)
00203     {
00204         if (*lpszPath == '\\')
00205             *lpszPath = replace;
00206     }
00207 }
00208 
00209 /***********************************************************************
00210  *           URLCacheContainer_OpenIndex (Internal)
00211  *
00212  *  Opens the index file and saves mapping handle in hCacheIndexMapping
00213  *
00214  * RETURNS
00215  *    ERROR_SUCCESS if succeeded
00216  *    Any other Win32 error code if failed
00217  *
00218  */
00219 static DWORD URLCacheContainer_OpenIndex(URLCACHECONTAINER * pContainer)
00220 {
00221     HANDLE hFile;
00222     WCHAR wszFilePath[MAX_PATH];
00223     DWORD dwFileSize;
00224 
00225     static const WCHAR wszIndex[] = {'i','n','d','e','x','.','d','a','t',0};
00226     static const WCHAR wszMappingFormat[] = {'%','s','%','s','_','%','l','u',0};
00227 
00228     WaitForSingleObject(pContainer->hMutex, INFINITE);
00229 
00230     if (pContainer->hMapping) {
00231         ReleaseMutex(pContainer->hMutex);
00232         return ERROR_SUCCESS;
00233     }
00234 
00235     strcpyW(wszFilePath, pContainer->path);
00236     strcatW(wszFilePath, wszIndex);
00237 
00238     hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
00239     if (hFile == INVALID_HANDLE_VALUE)
00240     {
00241     /* Maybe the directory wasn't there? Try to create it */
00242     if (CreateDirectoryW(pContainer->path, 0))
00243             hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
00244     }
00245     if (hFile == INVALID_HANDLE_VALUE)
00246     {
00247         TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(wszFilePath));
00248         ReleaseMutex(pContainer->hMutex);
00249         return GetLastError();
00250     }
00251 
00252     dwFileSize = GetFileSize(hFile, NULL);
00253     if (dwFileSize == INVALID_FILE_SIZE)
00254     {
00255     ReleaseMutex(pContainer->hMutex);
00256         return GetLastError();
00257     }
00258 
00259     if (dwFileSize == 0)
00260     {
00261         static const CHAR szCacheContent[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Cache\\Content";
00262     HKEY    key;
00263     char    achZeroes[0x1000];
00264     DWORD   dwOffset;
00265     DWORD dwError = ERROR_SUCCESS;
00266 
00267     /* Write zeroes to the entire file so we can safely map it without
00268      * fear of getting a SEGV because the disk is full.
00269      */
00270     memset(achZeroes, 0, sizeof(achZeroes));
00271     for (dwOffset = 0; dwOffset < NEWFILE_SIZE; dwOffset += sizeof(achZeroes))
00272     {
00273         DWORD dwWrite = sizeof(achZeroes);
00274         DWORD dwWritten;
00275 
00276         if (NEWFILE_SIZE - dwOffset < dwWrite)
00277         dwWrite = NEWFILE_SIZE - dwOffset;
00278         if (!WriteFile(hFile, achZeroes, dwWrite, &dwWritten, 0) ||
00279         dwWritten != dwWrite)
00280         {
00281         /* If we fail to write, we need to return the error that
00282          * cause the problem and also make sure the file is no
00283          * longer there, if possible.
00284          */
00285         dwError = GetLastError();
00286 
00287         break;
00288         }
00289     }
00290 
00291     if (dwError == ERROR_SUCCESS)
00292     {
00293         HANDLE hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, NEWFILE_SIZE, NULL);
00294 
00295         if (hMapping)
00296         {
00297         URLCACHE_HEADER *pHeader = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, NEWFILE_SIZE);
00298 
00299         if (pHeader)
00300         {
00301             WCHAR *pwchDir;
00302             WCHAR wszDirPath[MAX_PATH];
00303             FILETIME ft;
00304             int i, j;
00305                     HASH_CACHEFILE_ENTRY *pHashEntry;
00306 
00307             dwFileSize = NEWFILE_SIZE;
00308         
00309             /* First set some constants and defaults in the header */
00310             strcpy(pHeader->szSignature, "WINE URLCache Ver 0.2005001");
00311             pHeader->dwFileSize = dwFileSize;
00312             pHeader->dwIndexCapacityInBlocks = NEWFILE_NUM_BLOCKS;
00313             /* 127MB - taken from default for Windows 2000 */
00314                     pHeader->CacheLimit.QuadPart = 0x07ff5400;
00315             /* Copied from a Windows 2000 cache index */
00316             pHeader->DirectoryCount = 4;
00317         
00318             /* If the registry has a cache size set, use the registry value */
00319             if (RegOpenKeyA(HKEY_CURRENT_USER, szCacheContent, &key) == ERROR_SUCCESS)
00320             {
00321                 DWORD dw;
00322                 DWORD len = sizeof(dw);
00323                 DWORD keytype;
00324         
00325                 if (RegQueryValueExA(key, "CacheLimit", NULL, &keytype,
00326                          (BYTE *) &dw, &len) == ERROR_SUCCESS &&
00327                 keytype == REG_DWORD)
00328             {
00329                             pHeader->CacheLimit.QuadPart = (ULONGLONG)dw * 1024;
00330             }
00331             RegCloseKey(key);
00332             }
00333         
00334             URLCache_CreateHashTable(pHeader, NULL, &pHashEntry);
00335 
00336             /* Last step - create the directories */
00337     
00338             strcpyW(wszDirPath, pContainer->path);
00339             pwchDir = wszDirPath + strlenW(wszDirPath);
00340             pwchDir[8] = 0;
00341     
00342             GetSystemTimeAsFileTime(&ft);
00343     
00344             for (i = 0; !dwError && i < pHeader->DirectoryCount; ++i)
00345             {
00346             pHeader->directory_data[i].dwNumFiles = 0;
00347             for (j = 0;; ++j)
00348             {
00349                 int k;
00350                 ULONGLONG n = ft.dwHighDateTime;
00351     
00352                 /* Generate a file name to attempt to create.
00353                  * This algorithm will create what will appear
00354                  * to be random and unrelated directory names
00355                  * of up to 9 characters in length.
00356                  */
00357                 n <<= 32;
00358                 n += ft.dwLowDateTime;
00359                 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
00360     
00361                 for (k = 0; k < 8; ++k)
00362                 {
00363                 int r = (n % 36);
00364     
00365                 /* Dividing by a prime greater than 36 helps
00366                  * with the appearance of randomness
00367                  */
00368                 n /= 37;
00369     
00370                 if (r < 10)
00371                     pwchDir[k] = '0' + r;
00372                 else
00373                     pwchDir[k] = 'A' + (r - 10);
00374                 }
00375     
00376                 if (CreateDirectoryW(wszDirPath, 0))
00377                 {
00378                 /* The following is OK because we generated an
00379                  * 8 character directory name made from characters
00380                  * [A-Z0-9], which are equivalent for all code
00381                  * pages and for UTF-16
00382                  */
00383                 for (k = 0; k < 8; ++k)
00384                     pHeader->directory_data[i].filename[k] = pwchDir[k];
00385                 break;
00386                 }
00387                 else if (j >= 255)
00388                 {
00389                 /* Give up. The most likely cause of this
00390                  * is a full disk, but whatever the cause
00391                  * is, it should be more than apparent that
00392                  * we won't succeed.
00393                  */
00394                 dwError = GetLastError();
00395                 break;
00396                 }
00397             }
00398             }
00399         
00400             UnmapViewOfFile(pHeader);
00401         }
00402         else
00403         {
00404             dwError = GetLastError();
00405         }
00406         CloseHandle(hMapping);
00407         }
00408         else
00409         {
00410         dwError = GetLastError();
00411         }
00412     }
00413 
00414     if (dwError)
00415     {
00416         CloseHandle(hFile);
00417         DeleteFileW(wszFilePath);
00418         ReleaseMutex(pContainer->hMutex);
00419         return dwError;
00420     }
00421 
00422     }
00423 
00424     wsprintfW(wszFilePath, wszMappingFormat, pContainer->path, wszIndex, dwFileSize);
00425     URLCache_PathToObjectName(wszFilePath, '_');
00426     pContainer->hMapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, wszFilePath);
00427     if (!pContainer->hMapping)
00428         pContainer->hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, wszFilePath);
00429     CloseHandle(hFile);
00430     if (!pContainer->hMapping)
00431     {
00432         ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
00433         ReleaseMutex(pContainer->hMutex);
00434         return GetLastError();
00435     }
00436 
00437     ReleaseMutex(pContainer->hMutex);
00438 
00439     return ERROR_SUCCESS;
00440 }
00441 
00442 /***********************************************************************
00443  *           URLCacheContainer_CloseIndex (Internal)
00444  *
00445  *  Closes the index
00446  *
00447  * RETURNS
00448  *    nothing
00449  *
00450  */
00451 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
00452 {
00453     CloseHandle(pContainer->hMapping);
00454     pContainer->hMapping = NULL;
00455 }
00456 
00457 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix, LPCWSTR path, LPWSTR mutex_name)
00458 {
00459     URLCACHECONTAINER * pContainer = heap_alloc(sizeof(URLCACHECONTAINER));
00460     int cache_prefix_len = strlenW(cache_prefix);
00461 
00462     if (!pContainer)
00463     {
00464         return FALSE;
00465     }
00466 
00467     pContainer->hMapping = NULL;
00468     pContainer->file_size = 0;
00469 
00470     pContainer->path = heap_strdupW(path);
00471     if (!pContainer->path)
00472     {
00473         HeapFree(GetProcessHeap(), 0, pContainer);
00474         return FALSE;
00475     }
00476 
00477     pContainer->cache_prefix = heap_alloc((cache_prefix_len + 1) * sizeof(WCHAR));
00478     if (!pContainer->cache_prefix)
00479     {
00480         HeapFree(GetProcessHeap(), 0, pContainer->path);
00481         HeapFree(GetProcessHeap(), 0, pContainer);
00482         return FALSE;
00483     }
00484 
00485     memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
00486 
00487     CharLowerW(mutex_name);
00488     URLCache_PathToObjectName(mutex_name, '!');
00489 
00490     if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
00491     {
00492         ERR("couldn't create mutex (error is %d)\n", GetLastError());
00493         HeapFree(GetProcessHeap(), 0, pContainer->path);
00494         HeapFree(GetProcessHeap(), 0, pContainer);
00495         return FALSE;
00496     }
00497 
00498     list_add_head(&UrlContainers, &pContainer->entry);
00499 
00500     return TRUE;
00501 }
00502 
00503 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
00504 {
00505     list_remove(&pContainer->entry);
00506 
00507     URLCacheContainer_CloseIndex(pContainer);
00508     CloseHandle(pContainer->hMutex);
00509     HeapFree(GetProcessHeap(), 0, pContainer->path);
00510     HeapFree(GetProcessHeap(), 0, pContainer->cache_prefix);
00511     HeapFree(GetProcessHeap(), 0, pContainer);
00512 }
00513 
00514 void URLCacheContainers_CreateDefaults(void)
00515 {
00516     static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
00517     static const WCHAR UrlPrefix[] = {0};
00518     static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
00519     static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
00520     static const WCHAR CookieSuffix[] = {0};
00521     static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
00522     static const WCHAR UserProfile[] = {'U','S','E','R','P','R','O','F','I','L','E',0};
00523     static const struct
00524     {
00525         int nFolder; /* CSIDL_* constant */
00526         const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
00527         const WCHAR * cache_prefix; /* prefix used to reference the container */
00528     } DefaultContainerData[] = 
00529     {
00530         { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix },
00531         { CSIDL_HISTORY, HistorySuffix, HistoryPrefix },
00532         { CSIDL_COOKIES, CookieSuffix, CookiePrefix },
00533     };
00534     DWORD i;
00535 
00536     if (GetEnvironmentVariableW(UserProfile, NULL, 0) == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND)
00537     {
00538         TRACE("Environment variable 'USERPROFILE' does not exist!\n");
00539         return;
00540     }
00541 
00542     for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
00543     {
00544         WCHAR wszCachePath[MAX_PATH];
00545         WCHAR wszMutexName[MAX_PATH];
00546         int path_len, suffix_len;
00547 
00548         if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
00549         {
00550             ERR("Couldn't get path for default container %u\n", i);
00551             continue;
00552         }
00553         path_len = strlenW(wszCachePath);
00554         suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
00555 
00556         if (path_len + suffix_len + 2 > MAX_PATH)
00557         {
00558             ERR("Path too long\n");
00559             continue;
00560         }
00561 
00562         wszCachePath[path_len] = '\\';
00563         wszCachePath[path_len+1] = 0;
00564 
00565         strcpyW(wszMutexName, wszCachePath);
00566         
00567         if (suffix_len)
00568         {
00569             memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
00570             wszCachePath[path_len + suffix_len + 1] = '\\';
00571             wszCachePath[path_len + suffix_len + 2] = '\0';
00572         }
00573 
00574         URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath, wszMutexName);
00575     }
00576 }
00577 
00578 void URLCacheContainers_DeleteAll(void)
00579 {
00580     while(!list_empty(&UrlContainers))
00581         URLCacheContainer_DeleteContainer(
00582             LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
00583         );
00584 }
00585 
00586 static DWORD URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
00587 {
00588     URLCACHECONTAINER * pContainer;
00589 
00590     TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
00591 
00592     if(!lpwszUrl)
00593         return ERROR_INVALID_PARAMETER;
00594 
00595     if (!bDefaultContainersAdded)
00596         URLCacheContainers_CreateDefaults();
00597 
00598     LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
00599     {
00600         int prefix_len = strlenW(pContainer->cache_prefix);
00601         if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
00602         {
00603             TRACE("found container with prefix %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
00604             *ppContainer = pContainer;
00605             return ERROR_SUCCESS;
00606         }
00607     }
00608     ERR("no container found\n");
00609     return ERROR_FILE_NOT_FOUND;
00610 }
00611 
00612 static DWORD URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
00613 {
00614     LPWSTR url = NULL;
00615     DWORD ret;
00616 
00617     if (lpszUrl && !(url = heap_strdupAtoW(lpszUrl)))
00618         return ERROR_OUTOFMEMORY;
00619 
00620     ret = URLCacheContainers_FindContainerW(url, ppContainer);
00621     HeapFree(GetProcessHeap(), 0, url);
00622     return ret;
00623 }
00624 
00625 static BOOL URLCacheContainers_Enum(LPCWSTR lpwszSearchPattern, DWORD dwIndex, URLCACHECONTAINER ** ppContainer)
00626 {
00627     DWORD i = 0;
00628     URLCACHECONTAINER * pContainer;
00629 
00630     TRACE("searching for prefix: %s\n", debugstr_w(lpwszSearchPattern));
00631 
00632     /* non-NULL search pattern only returns one container ever */
00633     if (lpwszSearchPattern && dwIndex > 0)
00634         return FALSE;
00635 
00636     if (!bDefaultContainersAdded)
00637         URLCacheContainers_CreateDefaults();
00638 
00639     LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
00640     {
00641         if (lpwszSearchPattern)
00642         {
00643             if (!strcmpW(pContainer->cache_prefix, lpwszSearchPattern))
00644             {
00645                 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
00646                 *ppContainer = pContainer;
00647                 return TRUE;
00648             }
00649         }
00650         else
00651         {
00652             if (i == dwIndex)
00653             {
00654                 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
00655                 *ppContainer = pContainer;
00656                 return TRUE;
00657             }
00658         }
00659         i++;
00660     }
00661     return FALSE;
00662 }
00663 
00664 /***********************************************************************
00665  *           URLCacheContainer_LockIndex (Internal)
00666  *
00667  * Locks the index for system-wide exclusive access.
00668  *
00669  * RETURNS
00670  *  Cache file header if successful
00671  *  NULL if failed and calls SetLastError.
00672  */
00673 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
00674 {
00675     BYTE index;
00676     LPVOID pIndexData;
00677     URLCACHE_HEADER * pHeader;
00678     DWORD error;
00679 
00680     /* acquire mutex */
00681     WaitForSingleObject(pContainer->hMutex, INFINITE);
00682 
00683     pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
00684 
00685     if (!pIndexData)
00686     {
00687         ReleaseMutex(pContainer->hMutex);
00688         ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
00689         return NULL;
00690     }
00691     pHeader = (URLCACHE_HEADER *)pIndexData;
00692 
00693     /* file has grown - we need to remap to prevent us getting
00694      * access violations when we try and access beyond the end
00695      * of the memory mapped file */
00696     if (pHeader->dwFileSize != pContainer->file_size)
00697     {
00698         UnmapViewOfFile( pHeader );
00699         URLCacheContainer_CloseIndex(pContainer);
00700         error = URLCacheContainer_OpenIndex(pContainer);
00701         if (error != ERROR_SUCCESS)
00702         {
00703             ReleaseMutex(pContainer->hMutex);
00704             SetLastError(error);
00705             return NULL;
00706         }
00707         pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
00708 
00709         if (!pIndexData)
00710         {
00711             ReleaseMutex(pContainer->hMutex);
00712             ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
00713             return NULL;
00714         }
00715         pHeader = (URLCACHE_HEADER *)pIndexData;
00716     }
00717 
00718     TRACE("Signature: %s, file size: %d bytes\n", pHeader->szSignature, pHeader->dwFileSize);
00719 
00720     for (index = 0; index < pHeader->DirectoryCount; index++)
00721     {
00722         TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
00723     }
00724     
00725     return pHeader;
00726 }
00727 
00728 /***********************************************************************
00729  *           URLCacheContainer_UnlockIndex (Internal)
00730  *
00731  */
00732 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
00733 {
00734     /* release mutex */
00735     ReleaseMutex(pContainer->hMutex);
00736     return UnmapViewOfFile(pHeader);
00737 }
00738 
00739 
00740 #ifndef CHAR_BIT
00741 #define CHAR_BIT    (8 * sizeof(CHAR))
00742 #endif
00743 
00744 /***********************************************************************
00745  *           URLCache_Allocation_BlockIsFree (Internal)
00746  *
00747  *  Is the specified block number free?
00748  *
00749  * RETURNS
00750  *    zero if free
00751  *    non-zero otherwise
00752  *
00753  */
00754 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
00755 {
00756     BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
00757     return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
00758 }
00759 
00760 /***********************************************************************
00761  *           URLCache_Allocation_BlockFree (Internal)
00762  *
00763  *  Marks the specified block as free
00764  *
00765  * RETURNS
00766  *    nothing
00767  *
00768  */
00769 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
00770 {
00771     BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
00772     AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
00773 }
00774 
00775 /***********************************************************************
00776  *           URLCache_Allocation_BlockAllocate (Internal)
00777  *
00778  *  Marks the specified block as allocated
00779  *
00780  * RETURNS
00781  *    nothing
00782  *
00783  */
00784 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
00785 {
00786     BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
00787     AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
00788 }
00789 
00790 /***********************************************************************
00791  *           URLCache_FindFirstFreeEntry (Internal)
00792  *
00793  *  Finds and allocates the first block of free space big enough and
00794  * sets ppEntry to point to it.
00795  *
00796  * RETURNS
00797  *    TRUE if it had enough space
00798  *    FALSE if it couldn't find enough space
00799  *
00800  */
00801 static BOOL URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
00802 {
00803     LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
00804     DWORD dwBlockNumber;
00805     DWORD dwFreeCounter;
00806     for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
00807     {
00808         for (dwFreeCounter = 0; 
00809             dwFreeCounter < dwBlocksNeeded &&
00810               dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
00811               URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
00812             dwFreeCounter++)
00813                 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
00814 
00815         if (dwFreeCounter == dwBlocksNeeded)
00816         {
00817             DWORD index;
00818             TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
00819             for (index = 0; index < dwBlocksNeeded; index++)
00820                 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
00821             *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
00822             (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
00823             return TRUE;
00824         }
00825     }
00826     FIXME("Grow file\n");
00827     return FALSE;
00828 }
00829 
00830 /***********************************************************************
00831  *           URLCache_DeleteEntry (Internal)
00832  *
00833  *  Deletes the specified entry and frees the space allocated to it
00834  *
00835  * RETURNS
00836  *    TRUE if it succeeded
00837  *    FALSE if it failed
00838  *
00839  */
00840 static BOOL URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader, CACHEFILE_ENTRY * pEntry)
00841 {
00842     DWORD dwStartBlock;
00843     DWORD dwBlock;
00844     BYTE * AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
00845 
00846     /* update allocation table */
00847     dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader)) / BLOCKSIZE;
00848     for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
00849         URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
00850 
00851     ZeroMemory(pEntry, pEntry->dwBlocksUsed * BLOCKSIZE);
00852     return TRUE;
00853 }
00854 
00855 /***********************************************************************
00856  *           URLCache_LocalFileNameToPathW (Internal)
00857  *
00858  *  Copies the full path to the specified buffer given the local file
00859  * name and the index of the directory it is in. Always sets value in
00860  * lpBufferSize to the required buffer size (in bytes).
00861  *
00862  * RETURNS
00863  *    TRUE if the buffer was big enough
00864  *    FALSE if the buffer was too small
00865  *
00866  */
00867 static BOOL URLCache_LocalFileNameToPathW(
00868     const URLCACHECONTAINER * pContainer,
00869     LPCURLCACHE_HEADER pHeader,
00870     LPCSTR szLocalFileName,
00871     BYTE Directory,
00872     LPWSTR wszPath,
00873     LPLONG lpBufferSize)
00874 {
00875     LONG nRequired;
00876     int path_len = strlenW(pContainer->path);
00877     int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
00878     if (Directory >= pHeader->DirectoryCount)
00879     {
00880         *lpBufferSize = 0;
00881         return FALSE;
00882     }
00883 
00884     nRequired = (path_len + DIR_LENGTH + file_name_len + 1) * sizeof(WCHAR);
00885     if (nRequired <= *lpBufferSize)
00886     {
00887         int dir_len;
00888 
00889         memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
00890         dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
00891         wszPath[dir_len + path_len] = '\\';
00892         MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len + 1, file_name_len);
00893         *lpBufferSize = nRequired;
00894         return TRUE;
00895     }
00896     *lpBufferSize = nRequired;
00897     return FALSE;
00898 }
00899 
00900 /***********************************************************************
00901  *           URLCache_LocalFileNameToPathA (Internal)
00902  *
00903  *  Copies the full path to the specified buffer given the local file
00904  * name and the index of the directory it is in. Always sets value in
00905  * lpBufferSize to the required buffer size.
00906  *
00907  * RETURNS
00908  *    TRUE if the buffer was big enough
00909  *    FALSE if the buffer was too small
00910  *
00911  */
00912 static BOOL URLCache_LocalFileNameToPathA(
00913     const URLCACHECONTAINER * pContainer,
00914     LPCURLCACHE_HEADER pHeader,
00915     LPCSTR szLocalFileName,
00916     BYTE Directory,
00917     LPSTR szPath,
00918     LPLONG lpBufferSize)
00919 {
00920     LONG nRequired;
00921     int path_len, file_name_len, dir_len;
00922 
00923     if (Directory >= pHeader->DirectoryCount)
00924     {
00925         *lpBufferSize = 0;
00926         return FALSE;
00927     }
00928 
00929     path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
00930     file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
00931     dir_len = DIR_LENGTH;
00932 
00933     nRequired = (path_len + dir_len + 1 + file_name_len) * sizeof(char);
00934     if (nRequired < *lpBufferSize)
00935     {
00936         WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
00937         memcpy(szPath+path_len, pHeader->directory_data[Directory].filename, dir_len);
00938         szPath[path_len + dir_len] = '\\';
00939         memcpy(szPath + path_len + dir_len + 1, szLocalFileName, file_name_len);
00940         *lpBufferSize = nRequired;
00941         return TRUE;
00942     }
00943     *lpBufferSize = nRequired;
00944     return FALSE;
00945 }
00946 
00947 /* Just like DosDateTimeToFileTime, except that it also maps the special
00948  * case of a DOS date/time of (0,0) to a filetime of (0,0).
00949  */
00950 static void URLCache_DosDateTimeToFileTime(WORD fatdate, WORD fattime,
00951                                            FILETIME *ft)
00952 {
00953     if (!fatdate && !fattime)
00954         ft->dwLowDateTime = ft->dwHighDateTime = 0;
00955     else
00956         DosDateTimeToFileTime(fatdate, fattime, ft);
00957 }
00958 
00959 /***********************************************************************
00960  *           URLCache_CopyEntry (Internal)
00961  *
00962  *  Copies an entry from the cache index file to the Win32 structure
00963  *
00964  * RETURNS
00965  *    ERROR_SUCCESS if the buffer was big enough
00966  *    ERROR_INSUFFICIENT_BUFFER if the buffer was too small
00967  *
00968  */
00969 static DWORD URLCache_CopyEntry(
00970     URLCACHECONTAINER * pContainer,
00971     LPCURLCACHE_HEADER pHeader, 
00972     LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo, 
00973     LPDWORD lpdwBufferSize, 
00974     const URL_CACHEFILE_ENTRY * pUrlEntry,
00975     BOOL bUnicode)
00976 {
00977     int lenUrl;
00978     DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
00979 
00980     if (*lpdwBufferSize >= dwRequiredSize)
00981     {
00982         lpCacheEntryInfo->lpHeaderInfo = NULL;
00983         lpCacheEntryInfo->lpszFileExtension = NULL;
00984         lpCacheEntryInfo->lpszLocalFileName = NULL;
00985         lpCacheEntryInfo->lpszSourceUrlName = NULL;
00986         lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
00987         lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
00988         lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
00989         lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
00990         lpCacheEntryInfo->dwSizeHigh = pUrlEntry->size.u.HighPart;
00991         lpCacheEntryInfo->dwSizeLow = pUrlEntry->size.u.LowPart;
00992         lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
00993         lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
00994         URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
00995         lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
00996         lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
00997         lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
00998         lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
00999         URLCache_DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
01000     }
01001 
01002     if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
01003         ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
01004     dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
01005     if (bUnicode)
01006         lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, NULL, 0);
01007     else
01008         lenUrl = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
01009     dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
01010 
01011     /* FIXME: is source url optional? */
01012     if (*lpdwBufferSize >= dwRequiredSize)
01013     {
01014         DWORD lenUrlBytes = (lenUrl+1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
01015 
01016         lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrlBytes;
01017         if (bUnicode)
01018             MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
01019         else
01020             memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lenUrlBytes);
01021     }
01022 
01023     if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
01024         ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
01025     dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
01026 
01027     if (pUrlEntry->dwOffsetLocalName)
01028     {
01029         LONG nLocalFilePathSize;
01030         LPSTR lpszLocalFileName;
01031         lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
01032         nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
01033         if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
01034             (!bUnicode && URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize)))
01035         {
01036             lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
01037         }
01038         dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
01039 
01040         if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
01041             ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
01042         dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
01043     }
01044     dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
01045 
01046     if (*lpdwBufferSize >= dwRequiredSize)
01047     {
01048         lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
01049         memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
01050         ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
01051     }
01052     if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
01053         ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
01054     dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
01055 
01056     if (pUrlEntry->dwOffsetFileExtension)
01057     {
01058         int lenExtension;
01059 
01060         if (bUnicode)
01061             lenExtension = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, NULL, 0);
01062         else
01063             lenExtension = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension) + 1;
01064         dwRequiredSize += lenExtension * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
01065 
01066         if (*lpdwBufferSize >= dwRequiredSize)
01067         {
01068             lpCacheEntryInfo->lpszFileExtension = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenExtension;
01069             if (bUnicode)
01070                 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenExtension);
01071             else
01072                 memcpy(lpCacheEntryInfo->lpszFileExtension, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, lenExtension * sizeof(CHAR));
01073         }
01074 
01075         if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
01076             ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
01077         dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
01078     }
01079 
01080     if (dwRequiredSize > *lpdwBufferSize)
01081     {
01082         *lpdwBufferSize = dwRequiredSize;
01083         return ERROR_INSUFFICIENT_BUFFER;
01084     }
01085     *lpdwBufferSize = dwRequiredSize;
01086     return ERROR_SUCCESS;
01087 }
01088 
01089 /* Just like FileTimeToDosDateTime, except that it also maps the special
01090  * case of a filetime of (0,0) to a DOS date/time of (0,0).
01091  */
01092 static void URLCache_FileTimeToDosDateTime(const FILETIME *ft, WORD *fatdate,
01093                                            WORD *fattime)
01094 {
01095     if (!ft->dwLowDateTime && !ft->dwHighDateTime)
01096         *fatdate = *fattime = 0;
01097     else
01098         FileTimeToDosDateTime(ft, fatdate, fattime);
01099 }
01100 
01101 /***********************************************************************
01102  *           URLCache_SetEntryInfo (Internal)
01103  *
01104  *  Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
01105  * according to the flags set by dwFieldControl.
01106  *
01107  * RETURNS
01108  *    ERROR_SUCCESS if the buffer was big enough
01109  *    ERROR_INSUFFICIENT_BUFFER if the buffer was too small
01110  *
01111  */
01112 static DWORD URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
01113 {
01114     if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
01115         pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
01116     if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
01117         pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
01118     if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
01119         pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
01120     if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
01121         URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
01122     if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
01123         FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
01124     if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
01125         pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
01126     if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
01127         pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
01128     if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
01129         URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
01130 
01131     return ERROR_SUCCESS;
01132 }
01133 
01134 /***********************************************************************
01135  *           URLCache_HashKey (Internal)
01136  *
01137  *  Returns the hash key for a given string
01138  *
01139  * RETURNS
01140  *    hash key for the string
01141  *
01142  */
01143 static DWORD URLCache_HashKey(LPCSTR lpszKey)
01144 {
01145     /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
01146      * but the algorithm and result are not the same!
01147      */
01148     static const unsigned char lookupTable[256] = 
01149     {
01150         0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
01151         0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
01152         0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
01153         0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
01154         0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
01155         0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
01156         0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
01157         0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
01158         0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
01159         0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
01160         0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
01161         0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
01162         0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
01163         0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
01164         0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
01165         0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
01166         0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
01167         0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
01168         0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
01169         0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
01170         0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
01171         0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
01172         0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
01173         0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
01174         0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
01175         0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
01176         0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
01177         0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
01178         0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
01179         0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
01180         0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
01181         0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
01182     };
01183     BYTE key[4];
01184     DWORD i;
01185 
01186     for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
01187         key[i] = lookupTable[i];
01188 
01189     for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
01190     {
01191         for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
01192             key[i] = lookupTable[*lpszKey ^ key[i]];
01193     }
01194 
01195     return *(DWORD *)key;
01196 }
01197 
01198 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
01199 {
01200     return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
01201 }
01202 
01203 static inline BOOL URLCache_IsHashEntryValid(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY *pHashEntry)
01204 {
01205     /* check pHashEntry located within acceptable bounds in the URL cache mapping */
01206     return ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) >= ENTRY_START_OFFSET) &&
01207            ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) < pHeader->dwFileSize);
01208 }
01209 
01210 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
01211 {
01212     /* structure of hash table:
01213      *  448 entries divided into 64 blocks
01214      *  each block therefore contains a chain of 7 key/offset pairs
01215      * how position in table is calculated:
01216      *  1. the url is hashed in helper function
01217      *  2. the key % 64 * 8 is the offset
01218      *  3. the key in the hash table is the hash key aligned to 64
01219      *
01220      * note:
01221      *  there can be multiple hash tables in the file and the offset to
01222      *  the next one is stored in the header of the hash table
01223      */
01224     DWORD key = URLCache_HashKey(lpszUrl);
01225     DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
01226     HASH_CACHEFILE_ENTRY * pHashEntry;
01227     DWORD dwHashTableNumber = 0;
01228 
01229     key = (key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
01230 
01231     for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
01232          URLCache_IsHashEntryValid(pHeader, pHashEntry);
01233          pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
01234     {
01235         int i;
01236         if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
01237         {
01238             ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
01239             continue;
01240         }
01241         /* make sure that it is in fact a hash entry */
01242         if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
01243         {
01244             ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
01245             continue;
01246         }
01247 
01248         for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
01249         {
01250             struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
01251             if (key == (pHashElement->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES)
01252             {
01253                 /* FIXME: we should make sure that this is the right element
01254                  * before returning and claiming that it is. We can do this
01255                  * by doing a simple compare between the URL we were given
01256                  * and the URL stored in the entry. However, this assumes
01257                  * we know the format of all the entries stored in the
01258                  * hash table */
01259                 *ppHashEntry = pHashElement;
01260                 return TRUE;
01261             }
01262         }
01263     }
01264     return FALSE;
01265 }
01266 
01267 static BOOL URLCache_FindHashW(LPCURLCACHE_HEADER pHeader, LPCWSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
01268 {
01269     LPSTR urlA;
01270     BOOL ret;
01271 
01272     urlA = heap_strdupWtoA(lpszUrl);
01273     if (!urlA)
01274     {
01275         SetLastError(ERROR_OUTOFMEMORY);
01276         return FALSE;
01277     }
01278 
01279     ret = URLCache_FindHash(pHeader, urlA, ppHashEntry);
01280     HeapFree(GetProcessHeap(), 0, urlA);
01281     return ret;
01282 }
01283 
01284 /***********************************************************************
01285  *           URLCache_HashEntrySetUse (Internal)
01286  *
01287  *  Searches all the hash tables in the index for the given URL and
01288  * sets the use count (stored or'ed with key)
01289  *
01290  * RETURNS
01291  *    TRUE if the entry was found
01292  *    FALSE if the entry could not be found
01293  *
01294  */
01295 static BOOL URLCache_HashEntrySetUse(struct _HASH_ENTRY * pHashEntry, DWORD dwUseCount)
01296 {
01297     pHashEntry->dwHashKey = dwUseCount | (pHashEntry->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
01298     return TRUE;
01299 }
01300 
01301 /***********************************************************************
01302  *           URLCache_DeleteEntryFromHash (Internal)
01303  *
01304  *  Searches all the hash tables in the index for the given URL and
01305  * then if found deletes the entry.
01306  *
01307  * RETURNS
01308  *    TRUE if the entry was found
01309  *    FALSE if the entry could not be found
01310  *
01311  */
01312 static BOOL URLCache_DeleteEntryFromHash(struct _HASH_ENTRY * pHashEntry)
01313 {
01314     pHashEntry->dwHashKey = HASHTABLE_FREE;
01315     pHashEntry->dwOffsetEntry = HASHTABLE_FREE;
01316     return TRUE;
01317 }
01318 
01319 /***********************************************************************
01320  *           URLCache_AddEntryToHash (Internal)
01321  *
01322  *  Searches all the hash tables for a free slot based on the offset
01323  * generated from the hash key. If a free slot is found, the offset and
01324  * key are entered into the hash table.
01325  *
01326  * RETURNS
01327  *    ERROR_SUCCESS if the entry was added
01328  *    Any other Win32 error code if the entry could not be added
01329  *
01330  */
01331 static DWORD URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry)
01332 {
01333     /* see URLCache_FindEntryInHash for structure of hash tables */
01334 
01335     DWORD key = URLCache_HashKey(lpszUrl);
01336     DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
01337     HASH_CACHEFILE_ENTRY * pHashEntry;
01338     DWORD dwHashTableNumber = 0;
01339     DWORD error;
01340 
01341     key = (key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
01342 
01343     for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
01344          URLCache_IsHashEntryValid(pHeader, pHashEntry);
01345          pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
01346     {
01347         int i;
01348         if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
01349         {
01350             ERR("not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
01351             break;
01352         }
01353         /* make sure that it is in fact a hash entry */
01354         if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
01355         {
01356             ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
01357             break;
01358         }
01359 
01360         for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
01361         {
01362             struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
01363             if (pHashElement->dwHashKey == HASHTABLE_FREE) /* if the slot is free */
01364             {
01365                 pHashElement->dwHashKey = key;
01366                 pHashElement->dwOffsetEntry = dwOffsetEntry;
01367                 return ERROR_SUCCESS;
01368             }
01369         }
01370     }
01371     error = URLCache_CreateHashTable(pHeader, pHashEntry, &pHashEntry);
01372     if (error != ERROR_SUCCESS)
01373         return error;
01374 
01375     pHashEntry->HashTable[offset].dwHashKey = key;
01376     pHashEntry->HashTable[offset].dwOffsetEntry = dwOffsetEntry;
01377     return ERROR_SUCCESS;
01378 }
01379 
01380 /***********************************************************************
01381  *           URLCache_CreateHashTable (Internal)
01382  *
01383  *  Creates a new hash table in free space and adds it to the chain of existing
01384  * hash tables.
01385  *
01386  * RETURNS
01387  *    ERROR_SUCCESS if the hash table was created
01388  *    ERROR_DISK_FULL if the hash table could not be created
01389  *
01390  */
01391 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash)
01392 {
01393     DWORD dwOffset;
01394     int i;
01395 
01396     if (!URLCache_FindFirstFreeEntry(pHeader, 0x20, (CACHEFILE_ENTRY **)ppHash))
01397     {
01398         FIXME("no free space for hash table\n");
01399         return ERROR_DISK_FULL;
01400     }
01401 
01402     dwOffset = (BYTE *)*ppHash - (BYTE *)pHeader;
01403 
01404     if (pPrevHash)
01405         pPrevHash->dwAddressNext = dwOffset;
01406     else
01407         pHeader->dwOffsetFirstHashTable = dwOffset;
01408     (*ppHash)->CacheFileEntry.dwSignature = HASH_SIGNATURE;
01409     (*ppHash)->CacheFileEntry.dwBlocksUsed = 0x20;
01410     (*ppHash)->dwHashTableNumber = pPrevHash ? pPrevHash->dwHashTableNumber + 1 : 0;
01411     for (i = 0; i < HASHTABLE_SIZE; i++)
01412     {
01413         (*ppHash)->HashTable[i].dwOffsetEntry = 0;
01414         (*ppHash)->HashTable[i].dwHashKey = HASHTABLE_FREE;
01415     }
01416     return ERROR_SUCCESS;
01417 }
01418 
01419 /***********************************************************************
01420  *           URLCache_EnumHashTables (Internal)
01421  *
01422  *  Enumerates the hash tables in a container.
01423  *
01424  * RETURNS
01425  *    TRUE if an entry was found
01426  *    FALSE if there are no more tables to enumerate.
01427  *
01428  */
01429 static BOOL URLCache_EnumHashTables(LPCURLCACHE_HEADER pHeader, DWORD *pdwHashTableNumber, HASH_CACHEFILE_ENTRY ** ppHashEntry)
01430 {
01431     for (*ppHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
01432          URLCache_IsHashEntryValid(pHeader, *ppHashEntry);
01433          *ppHashEntry = URLCache_HashEntryFromOffset(pHeader, (*ppHashEntry)->dwAddressNext))
01434     {
01435         TRACE("looking at hash table number %d\n", (*ppHashEntry)->dwHashTableNumber);
01436         if ((*ppHashEntry)->dwHashTableNumber != *pdwHashTableNumber)
01437             continue;
01438         /* make sure that it is in fact a hash entry */
01439         if ((*ppHashEntry)->CacheFileEntry.dwSignature != HASH_SIGNATURE)
01440         {
01441             ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->CacheFileEntry.dwSignature);
01442             (*pdwHashTableNumber)++;
01443             continue;
01444         }
01445 
01446         TRACE("hash table number %d found\n", *pdwHashTableNumber);
01447         return TRUE;
01448     }
01449     return FALSE;
01450 }
01451 
01452 /***********************************************************************
01453  *           URLCache_EnumHashTableEntries (Internal)
01454  *
01455  *  Enumerates entries in a hash table and returns the next non-free entry.
01456  *
01457  * RETURNS
01458  *    TRUE if an entry was found
01459  *    FALSE if the hash table is empty or there are no more entries to
01460  *     enumerate.
01461  *
01462  */
01463 static BOOL URLCache_EnumHashTableEntries(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY * pHashEntry,
01464                                           DWORD * index, const struct _HASH_ENTRY ** ppHashEntry)
01465 {
01466     for (; *index < HASHTABLE_SIZE ; (*index)++)
01467     {
01468         if (pHashEntry->HashTable[*index].dwHashKey == HASHTABLE_FREE)
01469             continue;
01470 
01471         *ppHashEntry = &pHashEntry->HashTable[*index];
01472         TRACE("entry found %d\n", *index);
01473         return TRUE;
01474     }
01475     TRACE("no more entries (%d)\n", *index);
01476     return FALSE;
01477 }
01478 
01479 /***********************************************************************
01480  *           URLCache_DeleteCacheDirectory (Internal)
01481  *
01482  *  Erase a directory containing an URL cache.
01483  *
01484  * RETURNS
01485  *    TRUE success, FALSE failure/aborted.
01486  *
01487  */
01488 static BOOL URLCache_DeleteCacheDirectory(LPCWSTR lpszPath)
01489 {
01490     DWORD path_len;
01491     WCHAR path[MAX_PATH + 1];
01492     SHFILEOPSTRUCTW shfos;
01493     int ret;
01494 
01495     path_len = strlenW(lpszPath);
01496     if (path_len >= MAX_PATH)
01497         return FALSE;
01498     strcpyW(path, lpszPath);
01499     path[path_len + 1] = 0;  /* double-NUL-terminate path */
01500 
01501     shfos.hwnd = NULL;
01502     shfos.wFunc = FO_DELETE;
01503     shfos.pFrom = path;
01504     shfos.pTo = NULL;
01505     shfos.fFlags = 0;
01506     shfos.fAnyOperationsAborted = FALSE;
01507     ret = SHFileOperationW(&shfos);
01508     if (ret)
01509         ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path), ret);
01510     return !(ret || shfos.fAnyOperationsAborted);
01511 }
01512 
01513 /***********************************************************************
01514  *           FreeUrlCacheSpaceW (WININET.@)
01515  *
01516  * Frees up some cache.
01517  *
01518  * PARAMETERS
01519  *   lpszCachePath [I] Which volume to free up from, or NULL if you don't care.
01520  *   dwSize        [I] How much space to free up.
01521  *   dwSizeType    [I] How to interpret dwSize.
01522  *
01523  * RETURNS
01524  *   TRUE success. FALSE failure.
01525  *
01526  * IMPLEMENTATION
01527  *   This implementation just retrieves the path of the cache directory, and
01528  *   deletes its contents from the filesystem. The correct approach would
01529  *   probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
01530  */
01531 BOOL WINAPI FreeUrlCacheSpaceW(LPCWSTR lpszCachePath, DWORD dwSize, DWORD dwSizeType)
01532 {
01533     URLCACHECONTAINER * pContainer;
01534 
01535     if (lpszCachePath != NULL || dwSize != 100 || dwSizeType != FCS_PERCENT_CACHE_SPACE)
01536     {
01537         FIXME("(%s, %x, %x): partial stub!\n", debugstr_w(lpszCachePath), dwSize, dwSizeType);
01538         SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
01539         return FALSE;
01540     }
01541 
01542     if (!bDefaultContainersAdded)
01543         URLCacheContainers_CreateDefaults();
01544 
01545     LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
01546     {
01547         /* The URL cache has prefix L"" (unlike Cookies and History) */
01548         if (pContainer->cache_prefix[0] == 0)
01549         {
01550             BOOL ret_del;
01551             DWORD ret_open;
01552             WaitForSingleObject(pContainer->hMutex, INFINITE);
01553 
01554             /* unlock, delete, recreate and lock cache */
01555             URLCacheContainer_CloseIndex(pContainer);
01556             ret_del = URLCache_DeleteCacheDirectory(pContainer->path);
01557             ret_open = URLCacheContainer_OpenIndex(pContainer);
01558 
01559             ReleaseMutex(pContainer->hMutex);
01560             return ret_del && (ret_open == ERROR_SUCCESS);
01561         }
01562     }
01563     return FALSE;
01564 }
01565 
01566 /***********************************************************************
01567  *           FreeUrlCacheSpaceA (WININET.@)
01568  *
01569  * See FreeUrlCacheSpaceW.
01570  */
01571 BOOL WINAPI FreeUrlCacheSpaceA(LPCSTR lpszCachePath, DWORD dwSize, DWORD dwSizeType)
01572 {
01573     BOOL ret = FALSE;
01574     LPWSTR path = heap_strdupAtoW(lpszCachePath);
01575     if (lpszCachePath == NULL || path != NULL)
01576         ret = FreeUrlCacheSpaceW(path, dwSize, dwSizeType);
01577     heap_free(path);
01578     return ret;
01579 }
01580 
01581 /***********************************************************************
01582  *           GetUrlCacheEntryInfoExA (WININET.@)
01583  *
01584  */
01585 BOOL WINAPI GetUrlCacheEntryInfoExA(
01586     LPCSTR lpszUrl,
01587     LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
01588     LPDWORD lpdwCacheEntryInfoBufSize,
01589     LPSTR lpszReserved,
01590     LPDWORD lpdwReserved,
01591     LPVOID lpReserved,
01592     DWORD dwFlags)
01593 {
01594     TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
01595         debugstr_a(lpszUrl), 
01596         lpCacheEntryInfo,
01597         lpdwCacheEntryInfoBufSize,
01598         lpszReserved,
01599         lpdwReserved,
01600         lpReserved,
01601         dwFlags);
01602 
01603     if ((lpszReserved != NULL) ||
01604         (lpdwReserved != NULL) ||
01605         (lpReserved != NULL))
01606     {
01607         ERR("Reserved value was not 0\n");
01608         SetLastError(ERROR_INVALID_PARAMETER);
01609         return FALSE;
01610     }
01611     if (dwFlags != 0)
01612     {
01613         FIXME("Undocumented flag(s): %x\n", dwFlags);
01614         SetLastError(ERROR_FILE_NOT_FOUND);
01615         return FALSE;
01616     }
01617     return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
01618 }
01619 
01620 /***********************************************************************
01621  *           GetUrlCacheEntryInfoA (WININET.@)
01622  *
01623  */
01624 BOOL WINAPI GetUrlCacheEntryInfoA(
01625     IN LPCSTR lpszUrlName,
01626     IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
01627     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
01628 )
01629 {
01630     LPURLCACHE_HEADER pHeader;
01631     struct _HASH_ENTRY * pHashEntry;
01632     const CACHEFILE_ENTRY * pEntry;
01633     const URL_CACHEFILE_ENTRY * pUrlEntry;
01634     URLCACHECONTAINER * pContainer;
01635     DWORD error;
01636 
01637     TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
01638 
01639     error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
01640     if (error != ERROR_SUCCESS)
01641     {
01642         SetLastError(error);
01643         return FALSE;
01644     }
01645 
01646     error = URLCacheContainer_OpenIndex(pContainer);
01647     if (error != ERROR_SUCCESS)
01648     {
01649         SetLastError(error);
01650         return FALSE;
01651     }
01652 
01653     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
01654         return FALSE;
01655 
01656     if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
01657     {
01658         URLCacheContainer_UnlockIndex(pContainer, pHeader);
01659         WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
01660         SetLastError(ERROR_FILE_NOT_FOUND);
01661         return FALSE;
01662     }
01663 
01664     pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
01665     if (pEntry->dwSignature != URL_SIGNATURE)
01666     {
01667         URLCacheContainer_UnlockIndex(pContainer, pHeader);
01668         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
01669         SetLastError(ERROR_FILE_NOT_FOUND);
01670         return FALSE;
01671     }
01672 
01673     pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
01674     TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
01675     if (pUrlEntry->dwOffsetHeaderInfo)
01676         TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
01677 
01678     if (lpdwCacheEntryInfoBufferSize)
01679     {
01680         if (!lpCacheEntryInfo)
01681             *lpdwCacheEntryInfoBufferSize = 0;
01682 
01683         error = URLCache_CopyEntry(
01684             pContainer,
01685             pHeader,
01686             lpCacheEntryInfo,
01687             lpdwCacheEntryInfoBufferSize,
01688             pUrlEntry,
01689             FALSE /* ANSI */);
01690         if (error != ERROR_SUCCESS)
01691         {
01692             URLCacheContainer_UnlockIndex(pContainer, pHeader);
01693             SetLastError(error);
01694             return FALSE;
01695         }
01696         TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
01697     }
01698 
01699     URLCacheContainer_UnlockIndex(pContainer, pHeader);
01700 
01701     return TRUE;
01702 }
01703 
01704 /***********************************************************************
01705  *           GetUrlCacheEntryInfoW (WININET.@)
01706  *
01707  */
01708 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
01709   LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
01710   LPDWORD lpdwCacheEntryInfoBufferSize)
01711 {
01712     LPURLCACHE_HEADER pHeader;
01713     struct _HASH_ENTRY * pHashEntry;
01714     const CACHEFILE_ENTRY * pEntry;
01715     const URL_CACHEFILE_ENTRY * pUrlEntry;
01716     URLCACHECONTAINER * pContainer;
01717     DWORD error;
01718 
01719     TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
01720 
01721     error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
01722     if (error != ERROR_SUCCESS)
01723     {
01724         SetLastError(error);
01725         return FALSE;
01726     }
01727 
01728     error = URLCacheContainer_OpenIndex(pContainer);
01729     if (error != ERROR_SUCCESS)
01730     {
01731         SetLastError(error);
01732         return FALSE;
01733     }
01734 
01735     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
01736         return FALSE;
01737 
01738     if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
01739     {
01740         URLCacheContainer_UnlockIndex(pContainer, pHeader);
01741         WARN("entry %s not found!\n", debugstr_w(lpszUrl));
01742         SetLastError(ERROR_FILE_NOT_FOUND);
01743         return FALSE;
01744     }
01745 
01746     pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
01747     if (pEntry->dwSignature != URL_SIGNATURE)
01748     {
01749         URLCacheContainer_UnlockIndex(pContainer, pHeader);
01750         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
01751         SetLastError(ERROR_FILE_NOT_FOUND);
01752         return FALSE;
01753     }
01754 
01755     pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
01756     TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
01757     TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
01758 
01759     if (lpdwCacheEntryInfoBufferSize)
01760     {
01761         if (!lpCacheEntryInfo)
01762             *lpdwCacheEntryInfoBufferSize = 0;
01763 
01764         error = URLCache_CopyEntry(
01765             pContainer,
01766             pHeader,
01767             (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
01768             lpdwCacheEntryInfoBufferSize,
01769             pUrlEntry,
01770             TRUE /* UNICODE */);
01771         if (error != ERROR_SUCCESS)
01772         {
01773             URLCacheContainer_UnlockIndex(pContainer, pHeader);
01774             SetLastError(error);
01775             return FALSE;
01776         }
01777         TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
01778     }
01779 
01780     URLCacheContainer_UnlockIndex(pContainer, pHeader);
01781 
01782     return TRUE;
01783 }
01784 
01785 /***********************************************************************
01786  *           GetUrlCacheEntryInfoExW (WININET.@)
01787  *
01788  */
01789 BOOL WINAPI GetUrlCacheEntryInfoExW(
01790     LPCWSTR lpszUrl,
01791     LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
01792     LPDWORD lpdwCacheEntryInfoBufSize,
01793     LPWSTR lpszReserved,
01794     LPDWORD lpdwReserved,
01795     LPVOID lpReserved,
01796     DWORD dwFlags)
01797 {
01798     TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
01799         debugstr_w(lpszUrl), 
01800         lpCacheEntryInfo,
01801         lpdwCacheEntryInfoBufSize,
01802         lpszReserved,
01803         lpdwReserved,
01804         lpReserved,
01805         dwFlags);
01806 
01807     if ((lpszReserved != NULL) ||
01808         (lpdwReserved != NULL) ||
01809         (lpReserved != NULL))
01810     {
01811         ERR("Reserved value was not 0\n");
01812         SetLastError(ERROR_INVALID_PARAMETER);
01813         return FALSE;
01814     }
01815     if (dwFlags != 0)
01816     {
01817         FIXME("Undocumented flag(s): %x\n", dwFlags);
01818         SetLastError(ERROR_FILE_NOT_FOUND);
01819         return FALSE;
01820     }
01821     return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
01822 }
01823 
01824 /***********************************************************************
01825  *           SetUrlCacheEntryInfoA (WININET.@)
01826  */
01827 BOOL WINAPI SetUrlCacheEntryInfoA(
01828     LPCSTR lpszUrlName,
01829     LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
01830     DWORD dwFieldControl)
01831 {
01832     LPURLCACHE_HEADER pHeader;
01833     struct _HASH_ENTRY * pHashEntry;
01834     CACHEFILE_ENTRY * pEntry;
01835     URLCACHECONTAINER * pContainer;
01836     DWORD error;
01837 
01838     TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
01839 
01840     error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
01841     if (error != ERROR_SUCCESS)
01842     {
01843         SetLastError(error);
01844         return FALSE;
01845     }
01846 
01847     error = URLCacheContainer_OpenIndex(pContainer);
01848     if (error != ERROR_SUCCESS)
01849     {
01850         SetLastError(error);
01851         return FALSE;
01852     }
01853 
01854     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
01855         return FALSE;
01856 
01857     if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
01858     {
01859         URLCacheContainer_UnlockIndex(pContainer, pHeader);
01860         WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
01861         SetLastError(ERROR_FILE_NOT_FOUND);
01862         return FALSE;
01863     }
01864 
01865     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
01866     if (pEntry->dwSignature != URL_SIGNATURE)
01867     {
01868         URLCacheContainer_UnlockIndex(pContainer, pHeader);
01869         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
01870         SetLastError(ERROR_FILE_NOT_FOUND);
01871         return FALSE;
01872     }
01873 
01874     URLCache_SetEntryInfo(
01875         (URL_CACHEFILE_ENTRY *)pEntry,
01876         (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
01877         dwFieldControl);
01878 
01879     URLCacheContainer_UnlockIndex(pContainer, pHeader);
01880 
01881     return TRUE;
01882 }
01883 
01884 /***********************************************************************
01885  *           SetUrlCacheEntryInfoW (WININET.@)
01886  */
01887 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
01888 {
01889     LPURLCACHE_HEADER pHeader;
01890     struct _HASH_ENTRY * pHashEntry;
01891     CACHEFILE_ENTRY * pEntry;
01892     URLCACHECONTAINER * pContainer;
01893     DWORD error;
01894 
01895     TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
01896 
01897     error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
01898     if (error != ERROR_SUCCESS)
01899     {
01900         SetLastError(error);
01901         return FALSE;
01902     }
01903 
01904     error = URLCacheContainer_OpenIndex(pContainer);
01905     if (error != ERROR_SUCCESS)
01906     {
01907         SetLastError(error);
01908         return FALSE;
01909     }
01910 
01911     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
01912         return FALSE;
01913 
01914     if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
01915     {
01916         URLCacheContainer_UnlockIndex(pContainer, pHeader);
01917         WARN("entry %s not found!\n", debugstr_w(lpszUrl));
01918         SetLastError(ERROR_FILE_NOT_FOUND);
01919         return FALSE;
01920     }
01921 
01922     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
01923     if (pEntry->dwSignature != URL_SIGNATURE)
01924     {
01925         URLCacheContainer_UnlockIndex(pContainer, pHeader);
01926         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
01927         SetLastError(ERROR_FILE_NOT_FOUND);
01928         return FALSE;
01929     }
01930 
01931     URLCache_SetEntryInfo(
01932         (URL_CACHEFILE_ENTRY *)pEntry,
01933         lpCacheEntryInfo,
01934         dwFieldControl);
01935 
01936     URLCacheContainer_UnlockIndex(pContainer, pHeader);
01937 
01938     return TRUE;
01939 }
01940 
01941 /***********************************************************************
01942  *           RetrieveUrlCacheEntryFileA (WININET.@)
01943  *
01944  */
01945 BOOL WINAPI RetrieveUrlCacheEntryFileA(
01946     IN LPCSTR lpszUrlName,
01947     OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo, 
01948     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
01949     IN DWORD dwReserved
01950     )
01951 {
01952     LPURLCACHE_HEADER pHeader;
01953     struct _HASH_ENTRY * pHashEntry;
01954     CACHEFILE_ENTRY * pEntry;
01955     URL_CACHEFILE_ENTRY * pUrlEntry;
01956     URLCACHECONTAINER * pContainer;
01957     DWORD error;
01958 
01959     TRACE("(%s, %p, %p, 0x%08x)\n",
01960         debugstr_a(lpszUrlName),
01961         lpCacheEntryInfo,
01962         lpdwCacheEntryInfoBufferSize,
01963         dwReserved);
01964 
01965     if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
01966         (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
01967     {
01968         SetLastError(ERROR_INVALID_PARAMETER);
01969         return FALSE;
01970     }
01971 
01972     error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
01973     if (error != ERROR_SUCCESS)
01974     {
01975         SetLastError(error);
01976         return FALSE;
01977     }
01978 
01979     error = URLCacheContainer_OpenIndex(pContainer);
01980     if (error != ERROR_SUCCESS)
01981     {
01982         SetLastError(error);
01983         return FALSE;
01984     }
01985 
01986     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
01987         return FALSE;
01988 
01989     if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
01990     {
01991         URLCacheContainer_UnlockIndex(pContainer, pHeader);
01992         TRACE("entry %s not found!\n", lpszUrlName);
01993         SetLastError(ERROR_FILE_NOT_FOUND);
01994         return FALSE;
01995     }
01996 
01997     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
01998     if (pEntry->dwSignature != URL_SIGNATURE)
01999     {
02000         URLCacheContainer_UnlockIndex(pContainer, pHeader);
02001         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
02002         SetLastError(ERROR_FILE_NOT_FOUND);
02003         return FALSE;
02004     }
02005 
02006     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
02007     if (!pUrlEntry->dwOffsetLocalName)
02008     {
02009         URLCacheContainer_UnlockIndex(pContainer, pHeader);
02010         SetLastError(ERROR_INVALID_DATA);
02011         return FALSE;
02012     }
02013 
02014     TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
02015     TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
02016 
02017     error = URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo,
02018                                lpdwCacheEntryInfoBufferSize, pUrlEntry,
02019                                FALSE);
02020     if (error != ERROR_SUCCESS)
02021     {
02022         URLCacheContainer_UnlockIndex(pContainer, pHeader);
02023         SetLastError(error);
02024         return FALSE;
02025     }
02026     TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
02027 
02028     pUrlEntry->dwHitRate++;
02029     pUrlEntry->dwUseCount++;
02030     URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
02031     GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
02032 
02033     URLCacheContainer_UnlockIndex(pContainer, pHeader);
02034 
02035     return TRUE;
02036 }
02037 
02038 /***********************************************************************
02039  *           RetrieveUrlCacheEntryFileW (WININET.@)
02040  *
02041  */
02042 BOOL WINAPI RetrieveUrlCacheEntryFileW(
02043     IN LPCWSTR lpszUrlName,
02044     OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
02045     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
02046     IN DWORD dwReserved
02047     )
02048 {
02049     LPURLCACHE_HEADER pHeader;
02050     struct _HASH_ENTRY * pHashEntry;
02051     CACHEFILE_ENTRY * pEntry;
02052     URL_CACHEFILE_ENTRY * pUrlEntry;
02053     URLCACHECONTAINER * pContainer;
02054     DWORD error;
02055 
02056     TRACE("(%s, %p, %p, 0x%08x)\n",
02057         debugstr_w(lpszUrlName),
02058         lpCacheEntryInfo,
02059         lpdwCacheEntryInfoBufferSize,
02060         dwReserved);
02061 
02062     if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
02063         (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
02064     {
02065         SetLastError(ERROR_INVALID_PARAMETER);
02066         return FALSE;
02067     }
02068 
02069     error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
02070     if (error != ERROR_SUCCESS)
02071     {
02072         SetLastError(error);
02073         return FALSE;
02074     }
02075 
02076     error = URLCacheContainer_OpenIndex(pContainer);
02077     if (error != ERROR_SUCCESS)
02078     {
02079         SetLastError(error);
02080         return FALSE;
02081     }
02082 
02083     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
02084         return FALSE;
02085 
02086     if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
02087     {
02088         URLCacheContainer_UnlockIndex(pContainer, pHeader);
02089         TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
02090         SetLastError(ERROR_FILE_NOT_FOUND);
02091         return FALSE;
02092     }
02093 
02094     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
02095     if (pEntry->dwSignature != URL_SIGNATURE)
02096     {
02097         URLCacheContainer_UnlockIndex(pContainer, pHeader);
02098         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
02099         SetLastError(ERROR_FILE_NOT_FOUND);
02100         return FALSE;
02101     }
02102 
02103     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
02104     if (!pUrlEntry->dwOffsetLocalName)
02105     {
02106         URLCacheContainer_UnlockIndex(pContainer, pHeader);
02107         SetLastError(ERROR_INVALID_DATA);
02108         return FALSE;
02109     }
02110 
02111     TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
02112     TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
02113 
02114     error = URLCache_CopyEntry(
02115         pContainer,
02116         pHeader,
02117         (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
02118         lpdwCacheEntryInfoBufferSize,
02119         pUrlEntry,
02120         TRUE /* UNICODE */);
02121     if (error != ERROR_SUCCESS)
02122     {
02123         URLCacheContainer_UnlockIndex(pContainer, pHeader);
02124         SetLastError(error);
02125         return FALSE;
02126     }
02127     TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
02128 
02129     pUrlEntry->dwHitRate++;
02130     pUrlEntry->dwUseCount++;
02131     URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
02132     GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
02133 
02134     URLCacheContainer_UnlockIndex(pContainer, pHeader);
02135 
02136     return TRUE;
02137 }
02138 
02139 static BOOL DeleteUrlCacheEntryInternal(LPURLCACHE_HEADER pHeader,
02140         struct _HASH_ENTRY *pHashEntry)
02141 {
02142     CACHEFILE_ENTRY * pEntry;
02143     URL_CACHEFILE_ENTRY * pUrlEntry;
02144 
02145     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
02146     if (pEntry->dwSignature != URL_SIGNATURE)
02147     {
02148         FIXME("Trying to delete entry of unknown format %s\n",
02149               debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
02150         SetLastError(ERROR_FILE_NOT_FOUND);
02151         return FALSE;
02152     }
02153     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
02154     if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
02155     {
02156         if (pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles)
02157             pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles--;
02158     }
02159     if (pUrlEntry->CacheEntryType & STICKY_CACHE_ENTRY)
02160     {
02161         if (pUrlEntry->size.QuadPart < pHeader->ExemptUsage.QuadPart)
02162             pHeader->ExemptUsage.QuadPart -= pUrlEntry->size.QuadPart;
02163         else
02164             pHeader->ExemptUsage.QuadPart = 0;
02165     }
02166     else
02167     {
02168         if (pUrlEntry->size.QuadPart < pHeader->CacheUsage.QuadPart)
02169             pHeader->CacheUsage.QuadPart -= pUrlEntry->size.QuadPart;
02170         else
02171             pHeader->CacheUsage.QuadPart = 0;
02172     }
02173 
02174     URLCache_DeleteEntry(pHeader, pEntry);
02175 
02176     URLCache_DeleteEntryFromHash(pHashEntry);
02177     return TRUE;
02178 }
02179 
02180 /***********************************************************************
02181  *           UnlockUrlCacheEntryFileA (WININET.@)
02182  *
02183  */
02184 BOOL WINAPI UnlockUrlCacheEntryFileA(
02185     IN LPCSTR lpszUrlName, 
02186     IN DWORD dwReserved
02187     )
02188 {
02189     LPURLCACHE_HEADER pHeader;
02190     struct _HASH_ENTRY * pHashEntry;
02191     CACHEFILE_ENTRY * pEntry;
02192     URL_CACHEFILE_ENTRY * pUrlEntry;
02193     URLCACHECONTAINER * pContainer;
02194     DWORD error;
02195 
02196     TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
02197 
02198     if (dwReserved)
02199     {
02200         ERR("dwReserved != 0\n");
02201         SetLastError(ERROR_INVALID_PARAMETER);
02202         return FALSE;
02203     }
02204 
02205     error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
02206     if (error != ERROR_SUCCESS)
02207     {
02208        SetLastError(error);
02209        return FALSE;
02210     }
02211 
02212     error = URLCacheContainer_OpenIndex(pContainer);
02213     if (error != ERROR_SUCCESS)
02214     {
02215         SetLastError(error);
02216         return FALSE;
02217     }
02218 
02219     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
02220         return FALSE;
02221 
02222     if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
02223     {
02224         URLCacheContainer_UnlockIndex(pContainer, pHeader);
02225         TRACE("entry %s not found!\n", lpszUrlName);
02226         SetLastError(ERROR_FILE_NOT_FOUND);
02227         return FALSE;
02228     }
02229 
02230     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
02231     if (pEntry->dwSignature != URL_SIGNATURE)
02232     {
02233         URLCacheContainer_UnlockIndex(pContainer, pHeader);
02234         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
02235         SetLastError(ERROR_FILE_NOT_FOUND);
02236         return FALSE;
02237     }
02238 
02239     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
02240 
02241     if (pUrlEntry->dwUseCount == 0)
02242     {
02243         URLCacheContainer_UnlockIndex(pContainer, pHeader);
02244         return FALSE;
02245     }
02246     pUrlEntry->dwUseCount--;
02247     URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
02248 
02249     URLCacheContainer_UnlockIndex(pContainer, pHeader);
02250 
02251     return TRUE;
02252 }
02253 
02254 /***********************************************************************
02255  *           UnlockUrlCacheEntryFileW (WININET.@)
02256  *
02257  */
02258 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
02259 {
02260     LPURLCACHE_HEADER pHeader;
02261     struct _HASH_ENTRY * pHashEntry;
02262     CACHEFILE_ENTRY * pEntry;
02263     URL_CACHEFILE_ENTRY * pUrlEntry;
02264     URLCACHECONTAINER * pContainer;
02265     DWORD error;
02266 
02267     TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
02268 
02269     if (dwReserved)
02270     {
02271         ERR("dwReserved != 0\n");
02272         SetLastError(ERROR_INVALID_PARAMETER);
02273         return FALSE;
02274     }
02275 
02276     error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
02277     if (error != ERROR_SUCCESS)
02278     {
02279         SetLastError(error);
02280         return FALSE;
02281     }
02282 
02283     error = URLCacheContainer_OpenIndex(pContainer);
02284     if (error != ERROR_SUCCESS)
02285     {
02286         SetLastError(error);
02287         return FALSE;
02288     }
02289 
02290     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
02291         return FALSE;
02292 
02293     if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
02294     {
02295         URLCacheContainer_UnlockIndex(pContainer, pHeader);
02296         TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
02297         SetLastError(ERROR_FILE_NOT_FOUND);
02298         return FALSE;
02299     }
02300 
02301     pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
02302     if (pEntry->dwSignature != URL_SIGNATURE)
02303     {
02304         URLCacheContainer_UnlockIndex(pContainer, pHeader);
02305         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
02306         SetLastError(ERROR_FILE_NOT_FOUND);
02307         return FALSE;
02308     }
02309 
02310     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
02311 
02312     if (pUrlEntry->dwUseCount == 0)
02313     {
02314         URLCacheContainer_UnlockIndex(pContainer, pHeader);
02315         return FALSE;
02316     }
02317     pUrlEntry->dwUseCount--;
02318     URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
02319 
02320     URLCacheContainer_UnlockIndex(pContainer, pHeader);
02321 
02322     return TRUE;
02323 }
02324 
02325 /***********************************************************************
02326  *           CreateUrlCacheEntryA (WININET.@)
02327  *
02328  */
02329 BOOL WINAPI CreateUrlCacheEntryA(
02330     IN LPCSTR lpszUrlName,
02331     IN DWORD dwExpectedFileSize,
02332     IN LPCSTR lpszFileExtension,
02333     OUT LPSTR lpszFileName,
02334     IN DWORD dwReserved
02335 )
02336 {
02337     WCHAR *url_name;
02338     WCHAR *file_extension = NULL;
02339     WCHAR file_name[MAX_PATH];
02340     BOOL bSuccess = FALSE;
02341     DWORD dwError = 0;
02342 
02343     TRACE("(%s %d %s %p %d)\n", debugstr_a(lpszUrlName), dwExpectedFileSize,
02344             debugstr_a(lpszFileExtension), lpszFileName, dwReserved);
02345 
02346     if (lpszUrlName && (url_name = heap_strdupAtoW(lpszUrlName)))
02347     {
02348     if (!lpszFileExtension || (file_extension = heap_strdupAtoW(lpszFileExtension)))
02349     {
02350         if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
02351         {
02352         if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
02353         {
02354             bSuccess = TRUE;
02355         }
02356         else
02357         {
02358             dwError = GetLastError();
02359         }
02360         }
02361         else
02362         {
02363         dwError = GetLastError();
02364         }
02365         HeapFree(GetProcessHeap(), 0, file_extension);
02366     }
02367     else
02368     {
02369         dwError = GetLastError();
02370     }
02371         HeapFree(GetProcessHeap(), 0, url_name);
02372     if (!bSuccess)
02373         SetLastError(dwError);
02374     }
02375     return bSuccess;
02376 }
02377 /***********************************************************************
02378  *           CreateUrlCacheEntryW (WININET.@)
02379  *
02380  */
02381 BOOL WINAPI CreateUrlCacheEntryW(
02382     IN LPCWSTR lpszUrlName,
02383     IN DWORD dwExpectedFileSize,
02384     IN LPCWSTR lpszFileExtension,
02385     OUT LPWSTR lpszFileName,
02386     IN DWORD dwReserved
02387 )
02388 {
02389     URLCACHECONTAINER * pContainer;
02390     LPURLCACHE_HEADER pHeader;
02391     CHAR szFile[MAX_PATH];
02392     WCHAR szExtension[MAX_PATH];
02393     LPCWSTR lpszUrlPart;
02394     LPCWSTR lpszUrlEnd;
02395     LPCWSTR lpszFileNameExtension;
02396     LPWSTR lpszFileNameNoPath;
02397     int i;
02398     int countnoextension;
02399     BYTE CacheDir;
02400     LONG lBufferSize;
02401     BOOL bFound = FALSE;
02402     int count;
02403     DWORD error;
02404     HANDLE hFile;
02405     FILETIME ft;
02406 
02407     static const WCHAR szWWW[] = {'w','w','w',0};
02408     static const WCHAR fmt[] = {'%','0','8','X','%','s',0};
02409 
02410     TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
02411         debugstr_w(lpszUrlName),
02412         dwExpectedFileSize,
02413         debugstr_w(lpszFileExtension),
02414         lpszFileName,
02415         dwReserved);
02416 
02417     if (dwReserved)
02418         FIXME("dwReserved 0x%08x\n", dwReserved);
02419 
02420    lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
02421     
02422     if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
02423         lpszUrlEnd--;
02424 
02425     lpszUrlPart = memchrW(lpszUrlName, '?', lpszUrlEnd - lpszUrlName);
02426     if (!lpszUrlPart)
02427         lpszUrlPart = memchrW(lpszUrlName, '#', lpszUrlEnd - lpszUrlName);
02428     if (lpszUrlPart)
02429         lpszUrlEnd = lpszUrlPart;
02430 
02431     for (lpszUrlPart = lpszUrlEnd; 
02432         (lpszUrlPart >= lpszUrlName); 
02433         lpszUrlPart--)
02434     {
02435         if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
02436         {
02437             bFound = TRUE;
02438             lpszUrlPart++;
02439             break;
02440         }
02441     }
02442     if (!lstrcmpW(lpszUrlPart, szWWW))
02443     {
02444         lpszUrlPart += lstrlenW(szWWW);
02445     }
02446 
02447     count = lpszUrlEnd - lpszUrlPart;
02448 
02449     if (bFound && (count < MAX_PATH))
02450     {
02451     int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
02452     if (!len)
02453         return FALSE;
02454         szFile[len] = '\0';
02455         while(len && szFile[--len] == '/') szFile[len] = '\0';
02456 
02457         /* FIXME: get rid of illegal characters like \, / and : */
02458     }
02459     else
02460     {
02461         FIXME("need to generate a random filename\n");
02462     }
02463 
02464     TRACE("File name: %s\n", debugstr_a(szFile));
02465 
02466     error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
02467     if (error != ERROR_SUCCESS)
02468     {
02469         SetLastError(error);
02470         return FALSE;
02471     }
02472 
02473     error = URLCacheContainer_OpenIndex(pContainer);
02474     if (error != ERROR_SUCCESS)
02475     {
02476         SetLastError(error);
02477         return FALSE;
02478     }
02479 
02480     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
02481         return FALSE;
02482 
02483     CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
02484 
02485     lBufferSize = MAX_PATH * sizeof(WCHAR);
02486     if (!URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize))
02487     {
02488         WARN("Failed to get full path for filename %s, needed %u bytes.\n",
02489                 debugstr_a(szFile), lBufferSize);
02490         URLCacheContainer_UnlockIndex(pContainer, pHeader);
02491         return FALSE;
02492     }
02493 
02494     URLCacheContainer_UnlockIndex(pContainer, pHeader);
02495 
02496     for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
02497         lpszFileNameNoPath >= lpszFileName; 
02498         --lpszFileNameNoPath)
02499     {
02500         if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
02501             break;
02502     }
02503 
02504     countnoextension = lstrlenW(lpszFileNameNoPath);
02505     lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
02506     if (lpszFileNameExtension)
02507         countnoextension -= lstrlenW(lpszFileNameExtension);
02508     *szExtension = '\0';
02509 
02510     if (lpszFileExtension)
02511     {
02512         szExtension[0] = '.';
02513         lstrcpyW(szExtension+1, lpszFileExtension);
02514     }
02515 
02516     for (i = 0; i < 255; i++)
02517     {
02518         static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
02519         WCHAR *p;
02520 
02521         wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
02522         for (p = lpszFileNameNoPath + 1; *p; p++)
02523         {
02524             switch (*p)
02525             {
02526             case '<': case '>':
02527             case ':': case '"':
02528             case '/': case '\\':
02529             case '|': case '?':
02530             case '*':
02531                 *p = '_'; break;
02532             default: break;
02533             }
02534         }
02535         if (p[-1] == ' ' || p[-1] == '.') p[-1] = '_';
02536 
02537         TRACE("Trying: %s\n", debugstr_w(lpszFileName));
02538         hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
02539         if (hFile != INVALID_HANDLE_VALUE)
02540         {
02541             CloseHandle(hFile);
02542             return TRUE;
02543         }
02544     }
02545 
02546     GetSystemTimeAsFileTime(&ft);
02547     wsprintfW(lpszFileNameNoPath + countnoextension, fmt, ft.dwLowDateTime, szExtension);
02548 
02549     TRACE("Trying: %s\n", debugstr_w(lpszFileName));
02550     hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
02551     if (hFile != INVALID_HANDLE_VALUE)
02552     {
02553         CloseHandle(hFile);
02554         return TRUE;
02555     }
02556 
02557     WARN("Could not find a unique filename\n");
02558     return FALSE;
02559 }
02560 
02561 
02562 /***********************************************************************
02563  *           CommitUrlCacheEntryInternal (Compensates for an MS bug)
02564  *
02565  *   The bug we are compensating for is that some drongo at Microsoft
02566  *   used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
02567  *   As a consequence, CommitUrlCacheEntryA has been effectively
02568  *   redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
02569  *   is still defined as LPCWSTR. The result (other than madness) is
02570  *   that we always need to store lpHeaderInfo in CP_ACP rather than
02571  *   in UTF16, and we need to avoid converting lpHeaderInfo in
02572  *   CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
02573  *   result will lose data for arbitrary binary data.
02574  *
02575  */
02576 static BOOL CommitUrlCacheEntryInternal(
02577     IN LPCWSTR lpszUrlName,
02578     IN LPCWSTR lpszLocalFileName,
02579     IN FILETIME ExpireTime,
02580     IN FILETIME LastModifiedTime,
02581     IN DWORD CacheEntryType,
02582     IN LPBYTE lpHeaderInfo,
02583     IN DWORD dwHeaderSize,
02584     IN LPCWSTR lpszFileExtension,
02585     IN LPCWSTR lpszOriginalUrl
02586     )
02587 {
02588     URLCACHECONTAINER * pContainer;
02589     LPURLCACHE_HEADER pHeader;
02590     struct _HASH_ENTRY * pHashEntry;
02591     CACHEFILE_ENTRY * pEntry;
02592     URL_CACHEFILE_ENTRY * pUrlEntry;
02593     DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
02594     DWORD dwOffsetLocalFileName = 0;
02595     DWORD dwOffsetHeader = 0;
02596     DWORD dwOffsetFileExtension = 0;
02597     LARGE_INTEGER file_size;
02598     BYTE cDirectory = 0;
02599     char achFile[MAX_PATH];
02600     LPSTR lpszUrlNameA = NULL;
02601     LPSTR lpszFileExtensionA = NULL;
02602     char *pchLocalFileName = 0;
02603     DWORD error;
02604 
02605     TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
02606         debugstr_w(lpszUrlName),
02607         debugstr_w(lpszLocalFileName),
02608         CacheEntryType,
02609         lpHeaderInfo,
02610         dwHeaderSize,
02611         debugstr_w(lpszFileExtension),
02612         debugstr_w(lpszOriginalUrl));
02613 
02614     if (CacheEntryType & STICKY_CACHE_ENTRY && !lpszLocalFileName)
02615     {
02616         SetLastError(ERROR_INVALID_PARAMETER);
02617         return FALSE;
02618     }
02619     if (lpszOriginalUrl)
02620         WARN(": lpszOriginalUrl ignored\n");
02621 
02622     file_size.QuadPart = 0;
02623     if (lpszLocalFileName)
02624     {
02625         HANDLE hFile;
02626 
02627         hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
02628         if (hFile == INVALID_HANDLE_VALUE)
02629         {
02630             ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
02631             return FALSE;
02632         }
02633 
02634         /* Get file size */
02635         if (!GetFileSizeEx(hFile, &file_size))
02636         {
02637             ERR("couldn't get file size (error is %d)\n", GetLastError());
02638             CloseHandle(hFile);
02639             return FALSE;
02640         }
02641 
02642         CloseHandle(hFile);
02643     }
02644 
02645     error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
02646     if (error != ERROR_SUCCESS)
02647     {
02648         SetLastError(error);
02649         return FALSE;
02650     }
02651 
02652     error = URLCacheContainer_OpenIndex(pContainer);
02653     if (error != ERROR_SUCCESS)
02654     {
02655         SetLastError(error);
02656         return FALSE;
02657     }
02658 
02659     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
02660         return FALSE;
02661 
02662     lpszUrlNameA = heap_strdupWtoA(lpszUrlName);
02663     if (!lpszUrlNameA)
02664     {
02665         error = GetLastError();
02666         goto cleanup;
02667     }
02668 
02669     if (lpszFileExtension && !(lpszFileExtensionA = heap_strdupWtoA(lpszFileExtension)))
02670     {
02671         error = GetLastError();
02672         goto cleanup;
02673     }
02674 
02675     if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
02676     {
02677         FIXME("entry already in cache - don't know what to do!\n");
02678 /*
02679  *        SetLastError(ERROR_FILE_NOT_FOUND);
02680  *        return FALSE;
02681  */
02682         goto cleanup;
02683     }
02684 
02685     if (lpszLocalFileName)
02686     {
02687         BOOL bFound = FALSE;
02688 
02689         if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
02690         {
02691             ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
02692             error = ERROR_INVALID_PARAMETER;
02693             goto cleanup;
02694         }
02695 
02696         /* skip container path prefix */
02697         lpszLocalFileName += lstrlenW(pContainer->path);
02698 
02699         WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, MAX_PATH, NULL, NULL);
02700     pchLocalFileName = achFile;
02701 
02702         for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
02703         {
02704             if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
02705             {
02706                 bFound = TRUE;
02707                 break;
02708             }
02709         }
02710 
02711         if (!bFound)
02712         {
02713             ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
02714             error = ERROR_INVALID_PARAMETER;
02715             goto cleanup;
02716         }
02717 
02718         lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
02719     }
02720 
02721     dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
02722     if (lpszLocalFileName)
02723     {
02724         dwOffsetLocalFileName = dwBytesNeeded;
02725         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
02726     }
02727     if (lpHeaderInfo)
02728     {
02729         dwOffsetHeader = dwBytesNeeded;
02730         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
02731     }
02732     if (lpszFileExtensionA)
02733     {
02734         dwOffsetFileExtension = dwBytesNeeded;
02735         dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
02736     }
02737 
02738     /* round up to next block */
02739     if (dwBytesNeeded % BLOCKSIZE)
02740     {
02741         dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
02742         dwBytesNeeded += BLOCKSIZE;
02743     }
02744 
02745     if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
02746     {
02747         ERR("no free entries\n");
02748         error = ERROR_DISK_FULL;
02749         goto cleanup;
02750     }
02751 
02752     /* FindFirstFreeEntry fills in blocks used */
02753     pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
02754     pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
02755     pUrlEntry->CacheDir = cDirectory;
02756     pUrlEntry->CacheEntryType = CacheEntryType;
02757     pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
02758     if (CacheEntryType & STICKY_CACHE_ENTRY)
02759     {
02760         /* Sticky entries have a default exempt time of one day */
02761         pUrlEntry->dwExemptDelta = 86400;
02762     }
02763     else
02764         pUrlEntry->dwExemptDelta = 0;
02765     pUrlEntry->dwHitRate = 0;
02766     pUrlEntry->dwOffsetFileExtension = dwOffsetFileExtension;
02767     pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
02768     pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
02769     pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
02770     pUrlEntry->size.QuadPart = file_size.QuadPart;
02771     pUrlEntry->dwUseCount = 0;
02772     GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
02773     pUrlEntry->LastModifiedTime = LastModifiedTime;
02774     URLCache_FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
02775     URLCache_FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
02776     pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
02777     pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
02778 
02779     /*** Unknowns ***/
02780     pUrlEntry->dwUnknown1 = 0;
02781     pUrlEntry->dwUnknown2 = 0;
02782     pUrlEntry->dwUnknown3 = 0x60;
02783     pUrlEntry->Unknown4 = 0;
02784     pUrlEntry->wUnknown5 = 0x1010;
02785     pUrlEntry->dwUnknown7 = 0;
02786     pUrlEntry->dwUnknown8 = 0;
02787 
02788 
02789     strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
02790     if (dwOffsetLocalFileName)
02791         strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName + DIR_LENGTH + 1);
02792     if (dwOffsetHeader)
02793         memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
02794     if (dwOffsetFileExtension)
02795         strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
02796 
02797     error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA,
02798         (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader));
02799     if (error != ERROR_SUCCESS)
02800         URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
02801     else
02802     {
02803         if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
02804             pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles++;
02805         if (CacheEntryType & STICKY_CACHE_ENTRY)
02806             pHeader->ExemptUsage.QuadPart += file_size.QuadPart;
02807         else
02808             pHeader->CacheUsage.QuadPart += file_size.QuadPart;
02809         if (pHeader->CacheUsage.QuadPart + pHeader->ExemptUsage.QuadPart >
02810             pHeader->CacheLimit.QuadPart)
02811             FIXME("file of size %s bytes fills cache\n", wine_dbgstr_longlong(file_size.QuadPart));
02812     }
02813 
02814 cleanup:
02815     URLCacheContainer_UnlockIndex(pContainer, pHeader);
02816     HeapFree(GetProcessHeap(), 0, lpszUrlNameA);
02817     HeapFree(GetProcessHeap(), 0, lpszFileExtensionA);
02818 
02819     if (error == ERROR_SUCCESS)
02820         return TRUE;
02821     else
02822     {
02823         SetLastError(error);
02824         return FALSE;
02825     }
02826 }
02827 
02828 /***********************************************************************
02829  *           CommitUrlCacheEntryA (WININET.@)
02830  *
02831  */
02832 BOOL WINAPI CommitUrlCacheEntryA(
02833     IN LPCSTR lpszUrlName,
02834     IN LPCSTR lpszLocalFileName,
02835     IN FILETIME ExpireTime,
02836     IN FILETIME LastModifiedTime,
02837     IN DWORD CacheEntryType,
02838     IN LPBYTE lpHeaderInfo,
02839     IN DWORD dwHeaderSize,
02840     IN LPCSTR lpszFileExtension,
02841     IN LPCSTR lpszOriginalUrl
02842     )
02843 {
02844     WCHAR *url_name = NULL;
02845     WCHAR *local_file_name = NULL;
02846     WCHAR *original_url = NULL;
02847     WCHAR *file_extension = NULL;
02848     BOOL bSuccess = FALSE;
02849 
02850     TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
02851         debugstr_a(lpszUrlName),
02852         debugstr_a(lpszLocalFileName),
02853         CacheEntryType,
02854         lpHeaderInfo,
02855         dwHeaderSize,
02856         debugstr_a(lpszFileExtension),
02857         debugstr_a(lpszOriginalUrl));
02858 
02859     url_name = heap_strdupAtoW(lpszUrlName);
02860     if (!url_name)
02861         goto cleanup;
02862 
02863     if (lpszLocalFileName)
02864     {
02865         local_file_name = heap_strdupAtoW(lpszLocalFileName);
02866         if (!local_file_name)
02867             goto cleanup;
02868     }
02869     if (lpszFileExtension)
02870     {
02871         file_extension = heap_strdupAtoW(lpszFileExtension);
02872         if (!file_extension)
02873             goto cleanup;
02874     }
02875     if (lpszOriginalUrl)
02876     {
02877         original_url = heap_strdupAtoW(lpszOriginalUrl);
02878         if (!original_url)
02879             goto cleanup;
02880     }
02881 
02882     bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
02883                                            CacheEntryType, lpHeaderInfo, dwHeaderSize,
02884                                            file_extension, original_url);
02885 
02886 cleanup:
02887     HeapFree(GetProcessHeap(), 0, original_url);
02888     HeapFree(GetProcessHeap(), 0, file_extension);
02889     HeapFree(GetProcessHeap(), 0, local_file_name);
02890     HeapFree(GetProcessHeap(), 0, url_name);
02891 
02892     return bSuccess;
02893 }
02894 
02895 /***********************************************************************
02896  *           CommitUrlCacheEntryW (WININET.@)
02897  *
02898  */
02899 BOOL WINAPI CommitUrlCacheEntryW(
02900     IN LPCWSTR lpszUrlName,
02901     IN LPCWSTR lpszLocalFileName,
02902     IN FILETIME ExpireTime,
02903     IN FILETIME LastModifiedTime,
02904     IN DWORD CacheEntryType,
02905     IN LPWSTR lpHeaderInfo,
02906     IN DWORD dwHeaderSize,
02907     IN LPCWSTR lpszFileExtension,
02908     IN LPCWSTR lpszOriginalUrl
02909     )
02910 {
02911     DWORD dwError = 0;
02912     BOOL bSuccess = FALSE;
02913     DWORD len = 0;
02914     CHAR *header_info = NULL;
02915 
02916     TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
02917         debugstr_w(lpszUrlName),
02918         debugstr_w(lpszLocalFileName),
02919         CacheEntryType,
02920         lpHeaderInfo,
02921         dwHeaderSize,
02922         debugstr_w(lpszFileExtension),
02923         debugstr_w(lpszOriginalUrl));
02924 
02925     if (!lpHeaderInfo || (header_info = heap_strdupWtoA(lpHeaderInfo)))
02926     {
02927     if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
02928                 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
02929     {
02930         bSuccess = TRUE;
02931     }
02932     else
02933     {
02934         dwError = GetLastError();
02935     }
02936     if (header_info)
02937     {
02938         HeapFree(GetProcessHeap(), 0, header_info);
02939         if (!bSuccess)
02940         SetLastError(dwError);
02941     }
02942     }
02943     return bSuccess;
02944 }
02945 
02946 /***********************************************************************
02947  *           ReadUrlCacheEntryStream (WININET.@)
02948  *
02949  */
02950 BOOL WINAPI ReadUrlCacheEntryStream(
02951     IN HANDLE hUrlCacheStream,
02952     IN  DWORD dwLocation,
02953     IN OUT LPVOID lpBuffer,
02954     IN OUT LPDWORD lpdwLen,
02955     IN DWORD dwReserved
02956     )
02957 {
02958     /* Get handle to file from 'stream' */
02959     STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
02960 
02961     if (dwReserved != 0)
02962     {
02963         ERR("dwReserved != 0\n");
02964         SetLastError(ERROR_INVALID_PARAMETER);
02965         return FALSE;
02966     }
02967 
02968     if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
02969     {
02970         SetLastError(ERROR_INVALID_HANDLE);
02971         return FALSE;
02972     }
02973 
02974     if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
02975         return FALSE;
02976     return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
02977 }
02978 
02979 /***********************************************************************
02980  *           RetrieveUrlCacheEntryStreamA (WININET.@)
02981  *
02982  */
02983 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
02984     IN LPCSTR lpszUrlName,
02985     OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
02986     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
02987     IN BOOL fRandomRead,
02988     IN DWORD dwReserved
02989     )
02990 {
02991     /* NOTE: this is not the same as the way that the native
02992      * version allocates 'stream' handles. I did it this way
02993      * as it is much easier and no applications should depend
02994      * on this behaviour. (Native version appears to allocate
02995      * indices into a table)
02996      */
02997     STREAM_HANDLE * pStream;
02998     HANDLE hFile;
02999 
03000     TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
03001            lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
03002 
03003     if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
03004         lpCacheEntryInfo,
03005         lpdwCacheEntryInfoBufferSize,
03006         dwReserved))
03007     {
03008         return NULL;
03009     }
03010 
03011     hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
03012         GENERIC_READ,
03013         FILE_SHARE_READ,
03014         NULL,
03015         OPEN_EXISTING,
03016         fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
03017         NULL);
03018     if (hFile == INVALID_HANDLE_VALUE)
03019         return FALSE;
03020     
03021     /* allocate handle storage space */
03022     pStream = heap_alloc(sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
03023     if (!pStream)
03024     {
03025         CloseHandle(hFile);
03026         SetLastError(ERROR_OUTOFMEMORY);
03027         return FALSE;
03028     }
03029 
03030     pStream->hFile = hFile;
03031     strcpy(pStream->lpszUrl, lpszUrlName);
03032     return pStream;
03033 }
03034 
03035 /***********************************************************************
03036  *           RetrieveUrlCacheEntryStreamW (WININET.@)
03037  *
03038  */
03039 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
03040     IN LPCWSTR lpszUrlName,
03041     OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
03042     IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
03043     IN BOOL fRandomRead,
03044     IN DWORD dwReserved
03045     )
03046 {
03047     DWORD size;
03048     int url_len;
03049     /* NOTE: this is not the same as the way that the native
03050      * version allocates 'stream' handles. I did it this way
03051      * as it is much easier and no applications should depend
03052      * on this behaviour. (Native version appears to allocate
03053      * indices into a table)
03054      */
03055     STREAM_HANDLE * pStream;
03056     HANDLE hFile;
03057 
03058     TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
03059            lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
03060 
03061     if (!RetrieveUrlCacheEntryFileW(lpszUrlName,
03062         lpCacheEntryInfo,
03063         lpdwCacheEntryInfoBufferSize,
03064         dwReserved))
03065     {
03066         return NULL;
03067     }
03068 
03069     hFile = CreateFileW(lpCacheEntryInfo->lpszLocalFileName,
03070         GENERIC_READ,
03071         FILE_SHARE_READ,
03072         NULL,
03073         OPEN_EXISTING,
03074         fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
03075         NULL);
03076     if (hFile == INVALID_HANDLE_VALUE)
03077         return FALSE;
03078 
03079     /* allocate handle storage space */
03080     size = sizeof(STREAM_HANDLE);
03081     url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
03082     size += url_len;
03083     pStream = heap_alloc(size);
03084     if (!pStream)
03085     {
03086         CloseHandle(hFile);
03087         SetLastError(ERROR_OUTOFMEMORY);
03088         return FALSE;
03089     }
03090 
03091     pStream->hFile = hFile;
03092     WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, pStream->lpszUrl, url_len, NULL, NULL);
03093     return pStream;
03094 }
03095 
03096 /***********************************************************************
03097  *           UnlockUrlCacheEntryStream (WININET.@)
03098  *
03099  */
03100 BOOL WINAPI UnlockUrlCacheEntryStream(
03101     IN HANDLE hUrlCacheStream,
03102     IN DWORD dwReserved
03103 )
03104 {
03105     STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
03106 
03107     if (dwReserved != 0)
03108     {
03109         ERR("dwReserved != 0\n");
03110         SetLastError(ERROR_INVALID_PARAMETER);
03111         return FALSE;
03112     }
03113 
03114     if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
03115     {
03116         SetLastError(ERROR_INVALID_HANDLE);
03117         return FALSE;
03118     }
03119 
03120     if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
03121         return FALSE;
03122 
03123     /* close file handle */
03124     CloseHandle(pStream->hFile);
03125 
03126     /* free allocated space */
03127     HeapFree(GetProcessHeap(), 0, pStream);
03128 
03129     return TRUE;
03130 }
03131 
03132 
03133 /***********************************************************************
03134  *           DeleteUrlCacheEntryA (WININET.@)
03135  *
03136  */
03137 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
03138 {
03139     URLCACHECONTAINER * pContainer;
03140     LPURLCACHE_HEADER pHeader;
03141     struct _HASH_ENTRY * pHashEntry;
03142     DWORD error;
03143     BOOL ret;
03144 
03145     TRACE("(%s)\n", debugstr_a(lpszUrlName));
03146 
03147     error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
03148     if (error != ERROR_SUCCESS)
03149     {
03150         SetLastError(error);
03151         return FALSE;
03152     }
03153 
03154     error = URLCacheContainer_OpenIndex(pContainer);
03155     if (error != ERROR_SUCCESS)
03156     {
03157         SetLastError(error);
03158         return FALSE;
03159     }
03160 
03161     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
03162         return FALSE;
03163 
03164     if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
03165     {
03166         URLCacheContainer_UnlockIndex(pContainer, pHeader);
03167         TRACE("entry %s not found!\n", lpszUrlName);
03168         SetLastError(ERROR_FILE_NOT_FOUND);
03169         return FALSE;
03170     }
03171 
03172     ret = DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
03173 
03174     URLCacheContainer_UnlockIndex(pContainer, pHeader);
03175 
03176     return ret;
03177 }
03178 
03179 /***********************************************************************
03180  *           DeleteUrlCacheEntryW (WININET.@)
03181  *
03182  */
03183 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
03184 {
03185     URLCACHECONTAINER * pContainer;
03186     LPURLCACHE_HEADER pHeader;
03187     struct _HASH_ENTRY * pHashEntry;
03188     LPSTR urlA;
03189     DWORD error;
03190     BOOL ret;
03191 
03192     TRACE("(%s)\n", debugstr_w(lpszUrlName));
03193 
03194     urlA = heap_strdupWtoA(lpszUrlName);
03195     if (!urlA)
03196     {
03197         SetLastError(ERROR_OUTOFMEMORY);
03198         return FALSE;
03199     }
03200 
03201     error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
03202     if (error != ERROR_SUCCESS)
03203     {
03204         HeapFree(GetProcessHeap(), 0, urlA);
03205         SetLastError(error);
03206         return FALSE;
03207     }
03208 
03209     error = URLCacheContainer_OpenIndex(pContainer);
03210     if (error != ERROR_SUCCESS)
03211     {
03212         HeapFree(GetProcessHeap(), 0, urlA);
03213         SetLastError(error);
03214         return FALSE;
03215     }
03216 
03217     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
03218     {
03219         HeapFree(GetProcessHeap(), 0, urlA);
03220         return FALSE;
03221     }
03222 
03223     if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
03224     {
03225         URLCacheContainer_UnlockIndex(pContainer, pHeader);
03226         TRACE("entry %s not found!\n", debugstr_a(urlA));
03227         HeapFree(GetProcessHeap(), 0, urlA);
03228         SetLastError(ERROR_FILE_NOT_FOUND);
03229         return FALSE;
03230     }
03231 
03232     ret = DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
03233 
03234     URLCacheContainer_UnlockIndex(pContainer, pHeader);
03235 
03236     HeapFree(GetProcessHeap(), 0, urlA);
03237     return ret;
03238 }
03239 
03240 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
03241 {
03242     FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
03243     return TRUE;
03244 }
03245 
03246 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
03247 {
03248     FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
03249     return TRUE;
03250 }
03251 
03252 /***********************************************************************
03253  *           CreateCacheContainerA (WININET.@)
03254  */
03255 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
03256                                      DWORD d5, DWORD d6, DWORD d7, DWORD d8)
03257 {
03258     FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
03259           d1, d2, d3, d4, d5, d6, d7, d8);
03260     return TRUE;
03261 }
03262 
03263 /***********************************************************************
03264  *           CreateCacheContainerW (WININET.@)
03265  */
03266 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
03267                                      DWORD d5, DWORD d6, DWORD d7, DWORD d8)
03268 {
03269     FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
03270           d1, d2, d3, d4, d5, d6, d7, d8);
03271     return TRUE;
03272 }
03273 
03274 /***********************************************************************
03275  *           FindFirstUrlCacheContainerA (WININET.@)
03276  */
03277 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
03278 {
03279     FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
03280     return NULL;
03281 }
03282 
03283 /***********************************************************************
03284  *           FindFirstUrlCacheContainerW (WININET.@)
03285  */
03286 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
03287 {
03288     FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
03289     return NULL;
03290 }
03291 
03292 /***********************************************************************
03293  *           FindNextUrlCacheContainerA (WININET.@)
03294  */
03295 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
03296 {
03297     FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
03298     return FALSE;
03299 }
03300 
03301 /***********************************************************************
03302  *           FindNextUrlCacheContainerW (WININET.@)
03303  */
03304 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
03305 {
03306     FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
03307     return FALSE;
03308 }
03309 
03310 HANDLE WINAPI FindFirstUrlCacheEntryExA(
03311   LPCSTR lpszUrlSearchPattern,
03312   DWORD dwFlags,
03313   DWORD dwFilter,
03314   GROUPID GroupId,
03315   LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
03316   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
03317   LPVOID lpReserved,
03318   LPDWORD pcbReserved2,
03319   LPVOID lpReserved3
03320 )
03321 {
03322     FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
03323           dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
03324           lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
03325     SetLastError(ERROR_FILE_NOT_FOUND);
03326     return NULL;
03327 }
03328 
03329 HANDLE WINAPI FindFirstUrlCacheEntryExW(
03330   LPCWSTR lpszUrlSearchPattern,
03331   DWORD dwFlags,
03332   DWORD dwFilter,
03333   GROUPID GroupId,
03334   LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
03335   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
03336   LPVOID lpReserved,
03337   LPDWORD pcbReserved2,
03338   LPVOID lpReserved3
03339 )
03340 {
03341     FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
03342           dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
03343           lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
03344     SetLastError(ERROR_FILE_NOT_FOUND);
03345     return NULL;
03346 }
03347 
03348 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
03349 
03350 typedef struct URLCacheFindEntryHandle
03351 {
03352     DWORD dwMagic;
03353     LPWSTR lpszUrlSearchPattern;
03354     DWORD dwContainerIndex;
03355     DWORD dwHashTableIndex;
03356     DWORD dwHashEntryIndex;
03357 } URLCacheFindEntryHandle;
03358 
03359 /***********************************************************************
03360  *           FindFirstUrlCacheEntryA (WININET.@)
03361  *
03362  */
03363 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
03364  LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
03365 {
03366     URLCacheFindEntryHandle *pEntryHandle;
03367 
03368     TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
03369 
03370     pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
03371     if (!pEntryHandle)
03372         return NULL;
03373 
03374     pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
03375     if (lpszUrlSearchPattern)
03376     {
03377         pEntryHandle->lpszUrlSearchPattern = heap_strdupAtoW(lpszUrlSearchPattern);
03378         if (!pEntryHandle->lpszUrlSearchPattern)
03379         {
03380             HeapFree(GetProcessHeap(), 0, pEntryHandle);
03381             return NULL;
03382         }
03383     }
03384     else
03385         pEntryHandle->lpszUrlSearchPattern = NULL;
03386     pEntryHandle->dwContainerIndex = 0;
03387     pEntryHandle->dwHashTableIndex = 0;
03388     pEntryHandle->dwHashEntryIndex = 0;
03389 
03390     if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
03391     {
03392         HeapFree(GetProcessHeap(), 0, pEntryHandle);
03393         return NULL;
03394     }
03395     return pEntryHandle;
03396 }
03397 
03398 /***********************************************************************
03399  *           FindFirstUrlCacheEntryW (WININET.@)
03400  *
03401  */
03402 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
03403  LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
03404 {
03405     URLCacheFindEntryHandle *pEntryHandle;
03406 
03407     TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
03408 
03409     pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
03410     if (!pEntryHandle)
03411         return NULL;
03412 
03413     pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
03414     if (lpszUrlSearchPattern)
03415     {
03416         pEntryHandle->lpszUrlSearchPattern = heap_strdupW(lpszUrlSearchPattern);
03417         if (!pEntryHandle->lpszUrlSearchPattern)
03418         {
03419             HeapFree(GetProcessHeap(), 0, pEntryHandle);
03420             return NULL;
03421         }
03422     }
03423     else
03424         pEntryHandle->lpszUrlSearchPattern = NULL;
03425     pEntryHandle->dwContainerIndex = 0;
03426     pEntryHandle->dwHashTableIndex = 0;
03427     pEntryHandle->dwHashEntryIndex = 0;
03428 
03429     if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
03430     {
03431         HeapFree(GetProcessHeap(), 0, pEntryHandle);
03432         return NULL;
03433     }
03434     return pEntryHandle;
03435 }
03436 
03437 static BOOL FindNextUrlCacheEntryInternal(
03438   HANDLE hEnumHandle,
03439   LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
03440   LPDWORD lpdwNextCacheEntryInfoBufferSize,
03441   BOOL unicode)
03442 {
03443     URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
03444     URLCACHECONTAINER * pContainer;
03445 
03446     if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
03447     {
03448         SetLastError(ERROR_INVALID_HANDLE);
03449         return FALSE;
03450     }
03451 
03452     for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
03453          pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
03454     {
03455         LPURLCACHE_HEADER pHeader;
03456         HASH_CACHEFILE_ENTRY *pHashTableEntry;
03457         DWORD error;
03458 
03459         error = URLCacheContainer_OpenIndex(pContainer);
03460         if (error != ERROR_SUCCESS)
03461         {
03462             SetLastError(error);
03463             return FALSE;
03464         }
03465 
03466         if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
03467             return FALSE;
03468 
03469         for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
03470              pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
03471         {
03472             const struct _HASH_ENTRY *pHashEntry = NULL;
03473             for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
03474                  pEntryHandle->dwHashEntryIndex++)
03475             {
03476                 const URL_CACHEFILE_ENTRY *pUrlEntry;
03477                 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
03478 
03479                 if (pEntry->dwSignature != URL_SIGNATURE)
03480                     continue;
03481 
03482                 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
03483                 TRACE("Found URL: %s\n",
03484                       debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
03485                 TRACE("Header info: %s\n",
03486                       debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
03487 
03488                 error = URLCache_CopyEntry(
03489                     pContainer,
03490                     pHeader,
03491                     lpNextCacheEntryInfo,
03492                     lpdwNextCacheEntryInfoBufferSize,
03493                     pUrlEntry,
03494                     unicode);
03495                 if (error != ERROR_SUCCESS)
03496                 {
03497                     URLCacheContainer_UnlockIndex(pContainer, pHeader);
03498                     SetLastError(error);
03499                     return FALSE;
03500                 }
03501                 TRACE("Local File Name: %s\n",
03502                       debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
03503 
03504                 /* increment the current index so that next time the function
03505                  * is called the next entry is returned */
03506                 pEntryHandle->dwHashEntryIndex++;
03507                 URLCacheContainer_UnlockIndex(pContainer, pHeader);
03508                 return TRUE;
03509             }
03510         }
03511 
03512         URLCacheContainer_UnlockIndex(pContainer, pHeader);
03513     }
03514 
03515     SetLastError(ERROR_NO_MORE_ITEMS);
03516     return FALSE;
03517 }
03518 
03519 /***********************************************************************
03520  *           FindNextUrlCacheEntryA (WININET.@)
03521  */
03522 BOOL WINAPI FindNextUrlCacheEntryA(
03523   HANDLE hEnumHandle,
03524   LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
03525   LPDWORD lpdwNextCacheEntryInfoBufferSize)
03526 {
03527     TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
03528 
03529     return FindNextUrlCacheEntryInternal(hEnumHandle, lpNextCacheEntryInfo,
03530             lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
03531 }
03532 
03533 /***********************************************************************
03534  *           FindNextUrlCacheEntryW (WININET.@)
03535  */
03536 BOOL WINAPI FindNextUrlCacheEntryW(
03537   HANDLE hEnumHandle,
03538   LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
03539   LPDWORD lpdwNextCacheEntryInfoBufferSize
03540 )
03541 {
03542     TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
03543 
03544     return FindNextUrlCacheEntryInternal(hEnumHandle,
03545             (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
03546             lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
03547 }
03548 
03549 /***********************************************************************
03550  *           FindCloseUrlCache (WININET.@)
03551  */
03552 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
03553 {
03554     URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
03555 
03556     TRACE("(%p)\n", hEnumHandle);
03557 
03558     if (!pEntryHandle || pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
03559     {
03560         SetLastError(ERROR_INVALID_HANDLE);
03561         return FALSE;
03562     }
03563 
03564     pEntryHandle->dwMagic = 0;
03565     HeapFree(GetProcessHeap(), 0, pEntryHandle->lpszUrlSearchPattern);
03566     HeapFree(GetProcessHeap(), 0, pEntryHandle);
03567 
03568     return TRUE;
03569 }
03570 
03571 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
03572                                       DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
03573 {
03574     FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
03575           dwSearchCondition, lpGroupId, lpReserved);
03576     return NULL;
03577 }
03578 
03579 BOOL WINAPI FindNextUrlCacheEntryExA(
03580   HANDLE hEnumHandle,
03581   LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
03582   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
03583   LPVOID lpReserved,
03584   LPDWORD pcbReserved2,
03585   LPVOID lpReserved3
03586 )
03587 {
03588     FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
03589           lpReserved, pcbReserved2, lpReserved3);
03590     return FALSE;
03591 }
03592 
03593 BOOL WINAPI FindNextUrlCacheEntryExW(
03594   HANDLE hEnumHandle,
03595   LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
03596   LPDWORD lpdwFirstCacheEntryInfoBufferSize,
03597   LPVOID lpReserved,
03598   LPDWORD pcbReserved2,
03599   LPVOID lpReserved3
03600 )
03601 {
03602     FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
03603           lpReserved, pcbReserved2, lpReserved3);
03604     return FALSE;
03605 }
03606 
03607 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
03608 {
03609     FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
03610     return FALSE;
03611 }
03612 
03613 /***********************************************************************
03614  *           CreateUrlCacheGroup (WININET.@)
03615  *
03616  */
03617 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
03618 {
03619   FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
03620   return FALSE;
03621 }
03622 
03623 /***********************************************************************
03624  *           DeleteUrlCacheGroup (WININET.@)
03625  *
03626  */
03627 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
03628 {
03629     FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
03630           (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
03631     return FALSE;
03632 }
03633 
03634 /***********************************************************************
03635  *           SetUrlCacheEntryGroupA (WININET.@)
03636  *
03637  */
03638 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
03639   GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
03640   LPVOID lpReserved)
03641 {
03642     FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
03643           debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
03644           pbGroupAttributes, cbGroupAttributes, lpReserved);
03645     SetLastError(ERROR_FILE_NOT_FOUND);
03646     return FALSE;
03647 }
03648 
03649 /***********************************************************************
03650  *           SetUrlCacheEntryGroupW (WININET.@)
03651  *
03652  */
03653 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
03654   GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
03655   LPVOID lpReserved)
03656 {
03657     FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
03658           debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
03659           pbGroupAttributes, cbGroupAttributes, lpReserved);
03660     SetLastError(ERROR_FILE_NOT_FOUND);
03661     return FALSE;
03662 }
03663 
03664 /***********************************************************************
03665  *           GetUrlCacheConfigInfoW (WININET.@)
03666  */
03667 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
03668 {
03669     FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
03670     INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
03671     return FALSE;
03672 }
03673 
03674 /***********************************************************************
03675  *           GetUrlCacheConfigInfoA (WININET.@)
03676  */
03677 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
03678 {
03679     FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
03680     INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
03681     return FALSE;
03682 }
03683 
03684 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
03685                                         LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
03686                                         LPDWORD lpdwGroupInfo, LPVOID lpReserved )
03687 {
03688     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
03689           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
03690           lpdwGroupInfo, lpReserved);
03691     return FALSE;
03692 }
03693 
03694 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
03695                                         LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
03696                                         LPDWORD lpdwGroupInfo, LPVOID lpReserved )
03697 {
03698     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
03699           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
03700           lpdwGroupInfo, lpReserved);
03701     return FALSE;
03702 }
03703 
03704 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
03705                                         LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
03706 {
03707     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
03708           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
03709     return TRUE;
03710 }
03711 
03712 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
03713                                         LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
03714 {
03715     FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
03716           (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
03717     return TRUE;
03718 }
03719 
03720 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
03721 {
03722     FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
03723     return TRUE;
03724 }
03725 
03726 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
03727 {
03728     FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
03729     return TRUE;
03730 }
03731 
03732 /***********************************************************************
03733  *           DeleteIE3Cache (WININET.@)
03734  *
03735  * Deletes the files used by the IE3 URL caching system.
03736  *
03737  * PARAMS
03738  *   hWnd        [I] A dummy window.
03739  *   hInst       [I] Instance of process calling the function.
03740  *   lpszCmdLine [I] Options used by function.
03741  *   nCmdShow    [I] The nCmdShow value to use when showing windows created, if any.
03742  */
03743 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
03744 {
03745     FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
03746     return 0;
03747 }
03748 
03749 static BOOL IsUrlCacheEntryExpiredInternal(const URL_CACHEFILE_ENTRY *pUrlEntry,
03750         FILETIME *pftLastModified)
03751 {
03752     BOOL ret;
03753     FILETIME now, expired;
03754 
03755     *pftLastModified = pUrlEntry->LastModifiedTime;
03756     GetSystemTimeAsFileTime(&now);
03757     URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate,
03758             pUrlEntry->wExpiredTime, &expired);
03759     /* If the expired time is 0, it's interpreted as not expired */
03760     if (!expired.dwLowDateTime && !expired.dwHighDateTime)
03761         ret = FALSE;
03762     else
03763         ret = CompareFileTime(&expired, &now) < 0;
03764     return ret;
03765 }
03766 
03767 /***********************************************************************
03768  *           IsUrlCacheEntryExpiredA (WININET.@)
03769  *
03770  * PARAMS
03771  *   url             [I] Url
03772  *   dwFlags         [I] Unknown
03773  *   pftLastModified [O] Last modified time
03774  */
03775 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
03776 {
03777     LPURLCACHE_HEADER pHeader;
03778     struct _HASH_ENTRY * pHashEntry;
03779     const CACHEFILE_ENTRY * pEntry;
03780     const URL_CACHEFILE_ENTRY * pUrlEntry;
03781     URLCACHECONTAINER * pContainer;
03782     BOOL expired;
03783 
03784     TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
03785 
03786     if (!url || !pftLastModified)
03787         return TRUE;
03788     if (dwFlags)
03789         FIXME("unknown flags 0x%08x\n", dwFlags);
03790 
03791     /* Any error implies that the URL is expired, i.e. not in the cache */
03792     if (URLCacheContainers_FindContainerA(url, &pContainer))
03793     {
03794         memset(pftLastModified, 0, sizeof(*pftLastModified));
03795         return TRUE;
03796     }
03797 
03798     if (URLCacheContainer_OpenIndex(pContainer))
03799     {
03800         memset(pftLastModified, 0, sizeof(*pftLastModified));
03801         return TRUE;
03802     }
03803 
03804     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
03805     {
03806         memset(pftLastModified, 0, sizeof(*pftLastModified));
03807         return TRUE;
03808     }
03809 
03810     if (!URLCache_FindHash(pHeader, url, &pHashEntry))
03811     {
03812         URLCacheContainer_UnlockIndex(pContainer, pHeader);
03813         memset(pftLastModified, 0, sizeof(*pftLastModified));
03814         TRACE("entry %s not found!\n", url);
03815         return TRUE;
03816     }
03817 
03818     pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
03819     if (pEntry->dwSignature != URL_SIGNATURE)
03820     {
03821         URLCacheContainer_UnlockIndex(pContainer, pHeader);
03822         memset(pftLastModified, 0, sizeof(*pftLastModified));
03823         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
03824         return TRUE;
03825     }
03826 
03827     pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
03828     expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
03829 
03830     URLCacheContainer_UnlockIndex(pContainer, pHeader);
03831 
03832     return expired;
03833 }
03834 
03835 /***********************************************************************
03836  *           IsUrlCacheEntryExpiredW (WININET.@)
03837  *
03838  * PARAMS
03839  *   url             [I] Url
03840  *   dwFlags         [I] Unknown
03841  *   pftLastModified [O] Last modified time
03842  */
03843 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
03844 {
03845     LPURLCACHE_HEADER pHeader;
03846     struct _HASH_ENTRY * pHashEntry;
03847     const CACHEFILE_ENTRY * pEntry;
03848     const URL_CACHEFILE_ENTRY * pUrlEntry;
03849     URLCACHECONTAINER * pContainer;
03850     BOOL expired;
03851 
03852     TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
03853 
03854     if (!url || !pftLastModified)
03855         return TRUE;
03856     if (dwFlags)
03857         FIXME("unknown flags 0x%08x\n", dwFlags);
03858 
03859     /* Any error implies that the URL is expired, i.e. not in the cache */
03860     if (URLCacheContainers_FindContainerW(url, &pContainer))
03861     {
03862         memset(pftLastModified, 0, sizeof(*pftLastModified));
03863         return TRUE;
03864     }
03865 
03866     if (URLCacheContainer_OpenIndex(pContainer))
03867     {
03868         memset(pftLastModified, 0, sizeof(*pftLastModified));
03869         return TRUE;
03870     }
03871 
03872     if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
03873     {
03874         memset(pftLastModified, 0, sizeof(*pftLastModified));
03875         return TRUE;
03876     }
03877 
03878     if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
03879     {
03880         URLCacheContainer_UnlockIndex(pContainer, pHeader);
03881         memset(pftLastModified, 0, sizeof(*pftLastModified));
03882         TRACE("entry %s not found!\n", debugstr_w(url));
03883         return TRUE;
03884     }
03885 
03886     if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
03887     {
03888         URLCacheContainer_UnlockIndex(pContainer, pHeader);
03889         memset(pftLastModified, 0, sizeof(*pftLastModified));
03890         TRACE("entry %s not found!\n", debugstr_w(url));
03891         return TRUE;
03892     }
03893 
03894     pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
03895     if (pEntry->dwSignature != URL_SIGNATURE)
03896     {
03897         URLCacheContainer_UnlockIndex(pContainer, pHeader);
03898         memset(pftLastModified, 0, sizeof(*pftLastModified));
03899         FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
03900         return TRUE;
03901     }
03902 
03903     pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
03904     expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
03905 
03906     URLCacheContainer_UnlockIndex(pContainer, pHeader);
03907 
03908     return expired;
03909 }
03910 
03911 /***********************************************************************
03912  *           GetDiskInfoA (WININET.@)
03913  */
03914 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
03915 {
03916     BOOL ret;
03917     ULARGE_INTEGER bytes_free, bytes_total;
03918 
03919     TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
03920 
03921     if (!path)
03922     {
03923         SetLastError(ERROR_INVALID_PARAMETER);
03924         return FALSE;
03925     }
03926 
03927     if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
03928     {
03929         if (cluster_size) *cluster_size = 1;
03930         if (free) *free = bytes_free.QuadPart;
03931         if (total) *total = bytes_total.QuadPart;
03932     }
03933     return ret;
03934 }
03935 
03936 /***********************************************************************
03937  *           RegisterUrlCacheNotification (WININET.@)
03938  */
03939 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
03940 {
03941     FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
03942     return 0;
03943 }
03944 
03945 /***********************************************************************
03946  *           IncrementUrlCacheHeaderData (WININET.@)
03947  */
03948 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
03949 {
03950     FIXME("(%u, %p)\n", index, data);
03951     return FALSE;
03952 }

Generated on Sat May 26 2012 04:25:27 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.