2 * Wininet - Url Cache functions
4 * Copyright 2001,2002 CodeWeavers
5 * Copyright 2003-2008 Robert Shearman
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include "wine/port.h"
28 #define NONAMELESSUNION
29 #define NONAMELESSSTRUCT
31 #if defined(__MINGW32__) || defined (_MSC_VER)
39 #include <sys/types.h>
40 #ifdef HAVE_SYS_SOCKET_H
41 # include <sys/socket.h>
58 #include "wine/unicode.h"
59 #include "wine/debug.h"
61 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
63 #define ENTRY_START_OFFSET 0x4000
66 #define HASHTABLE_SIZE 448
67 #define HASHTABLE_BLOCKSIZE 7
68 #define HASHTABLE_FREE 3
69 #define ALLOCATION_TABLE_OFFSET 0x250
70 #define ALLOCATION_TABLE_SIZE (0x1000 - ALLOCATION_TABLE_OFFSET)
71 #define HASHTABLE_NUM_ENTRIES (HASHTABLE_SIZE / HASHTABLE_BLOCKSIZE)
72 #define NEWFILE_NUM_BLOCKS 0xd80
73 #define NEWFILE_SIZE (NEWFILE_NUM_BLOCKS * BLOCKSIZE + ENTRY_START_OFFSET)
75 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
76 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
77 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
78 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
79 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
81 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
83 typedef struct _CACHEFILE_ENTRY
87 DWORD dwSignature; /* e.g. "URL " */
88 /* CHAR szSignature[4];
90 DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
93 typedef struct _URL_CACHEFILE_ENTRY
95 CACHEFILE_ENTRY CacheFileEntry;
96 FILETIME LastModifiedTime;
97 FILETIME LastAccessTime;
98 WORD wExpiredDate; /* expire date in dos format */
99 WORD wExpiredTime; /* expire time in dos format */
100 DWORD dwUnknown1; /* usually zero */
101 ULARGE_INTEGER size; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
102 DWORD dwUnknown2; /* usually zero */
103 DWORD dwExemptDelta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
104 DWORD dwUnknown3; /* usually 0x60 */
105 DWORD dwOffsetUrl; /* offset of start of url from start of entry */
106 BYTE CacheDir; /* index of cache directory this url is stored in */
107 BYTE Unknown4; /* usually zero */
108 WORD wUnknown5; /* usually 0x1010 */
109 DWORD dwOffsetLocalName; /* offset of start of local filename from start of entry */
110 DWORD CacheEntryType; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
111 DWORD dwOffsetHeaderInfo; /* offset of start of header info from start of entry */
112 DWORD dwHeaderInfoSize;
113 DWORD dwOffsetFileExtension; /* offset of start of file extension from start of entry */
114 WORD wLastSyncDate; /* last sync date in dos format */
115 WORD wLastSyncTime; /* last sync time in dos format */
116 DWORD dwHitRate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
117 DWORD dwUseCount; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
118 WORD wUnknownDate; /* usually same as wLastSyncDate */
119 WORD wUnknownTime; /* usually same as wLastSyncTime */
120 DWORD dwUnknown7; /* usually zero */
121 DWORD dwUnknown8; /* usually zero */
122 /* packing to dword align start of next field */
123 /* CHAR szSourceUrlName[]; (url) */
124 /* packing to dword align start of next field */
125 /* CHAR szLocalFileName[]; (local file name excluding path) */
126 /* packing to dword align start of next field */
127 /* CHAR szHeaderInfo[]; (header info) */
128 } URL_CACHEFILE_ENTRY;
136 typedef struct _HASH_CACHEFILE_ENTRY
138 CACHEFILE_ENTRY CacheFileEntry;
140 DWORD dwHashTableNumber;
141 struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
142 } HASH_CACHEFILE_ENTRY;
144 typedef struct _DIRECTORY_DATA
147 char filename[DIR_LENGTH];
150 typedef struct _URLCACHE_HEADER
152 char szSignature[28];
154 DWORD dwOffsetFirstHashTable;
155 DWORD dwIndexCapacityInBlocks;
158 ULARGE_INTEGER CacheLimit;
159 ULARGE_INTEGER CacheUsage;
160 ULARGE_INTEGER ExemptUsage;
161 DWORD DirectoryCount; /* number of directory_data's */
162 DIRECTORY_DATA directory_data[1]; /* first directory entry */
163 } URLCACHE_HEADER, *LPURLCACHE_HEADER;
164 typedef const URLCACHE_HEADER *LPCURLCACHE_HEADER;
166 typedef struct _STREAM_HANDLE
172 typedef struct _URLCACHECONTAINER
174 struct list entry; /* part of a list */
175 LPWSTR cache_prefix; /* string that has to be prefixed for this container to be used */
176 LPWSTR path; /* path to url container directory */
177 HANDLE hMapping; /* handle of file mapping */
178 DWORD file_size; /* size of file when mapping was opened */
179 HANDLE hMutex; /* handle of mutex */
183 /* List of all containers available */
184 static struct list UrlContainers = LIST_INIT(UrlContainers);
186 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash);
188 /***********************************************************************
189 * URLCache_PathToObjectName (Internal)
191 * Converts a path to a name suitable for use as a Win32 object name.
192 * Replaces '\\' characters in-place with the specified character
193 * (usually '_' or '!')
199 static void URLCache_PathToObjectName(LPWSTR lpszPath, WCHAR replace)
201 for (; *lpszPath; lpszPath++)
203 if (*lpszPath == '\\')
208 /***********************************************************************
209 * URLCacheContainer_OpenIndex (Internal)
211 * Opens the index file and saves mapping handle in hCacheIndexMapping
214 * ERROR_SUCCESS if succeeded
215 * Any other Win32 error code if failed
218 static DWORD URLCacheContainer_OpenIndex(URLCACHECONTAINER * pContainer)
221 WCHAR wszFilePath[MAX_PATH];
224 static const WCHAR wszIndex[] = {'i','n','d','e','x','.','d','a','t',0};
225 static const WCHAR wszMappingFormat[] = {'%','s','%','s','_','%','l','u',0};
227 if (pContainer->hMapping)
228 return ERROR_SUCCESS;
230 strcpyW(wszFilePath, pContainer->path);
231 strcatW(wszFilePath, wszIndex);
233 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
234 if (hFile == INVALID_HANDLE_VALUE)
236 /* Maybe the directory wasn't there? Try to create it */
237 if (CreateDirectoryW(pContainer->path, 0))
238 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
240 if (hFile == INVALID_HANDLE_VALUE)
242 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(wszFilePath));
243 return GetLastError();
246 /* At this stage we need the mutex because we may be about to create the
249 WaitForSingleObject(pContainer->hMutex, INFINITE);
251 dwFileSize = GetFileSize(hFile, NULL);
252 if (dwFileSize == INVALID_FILE_SIZE)
254 ReleaseMutex(pContainer->hMutex);
255 return GetLastError();
260 static const CHAR szCacheContent[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Cache\\Content";
262 char achZeroes[0x1000];
264 DWORD dwError = ERROR_SUCCESS;
266 /* Write zeroes to the entire file so we can safely map it without
267 * fear of getting a SEGV because the disk is full.
269 memset(achZeroes, 0, sizeof(achZeroes));
270 for (dwOffset = 0; dwOffset < NEWFILE_SIZE; dwOffset += sizeof(achZeroes))
272 DWORD dwWrite = sizeof(achZeroes);
275 if (NEWFILE_SIZE - dwOffset < dwWrite)
276 dwWrite = NEWFILE_SIZE - dwOffset;
277 if (!WriteFile(hFile, achZeroes, dwWrite, &dwWritten, 0) ||
278 dwWritten != dwWrite)
280 /* If we fail to write, we need to return the error that
281 * cause the problem and also make sure the file is no
282 * longer there, if possible.
284 dwError = GetLastError();
290 if (dwError == ERROR_SUCCESS)
292 HANDLE hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, NEWFILE_SIZE, NULL);
296 URLCACHE_HEADER *pHeader = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, NEWFILE_SIZE);
301 WCHAR wszDirPath[MAX_PATH];
304 HASH_CACHEFILE_ENTRY *pHashEntry;
306 dwFileSize = NEWFILE_SIZE;
308 /* First set some constants and defaults in the header */
309 strcpy(pHeader->szSignature, "WINE URLCache Ver 0.2005001");
310 pHeader->dwFileSize = dwFileSize;
311 pHeader->dwIndexCapacityInBlocks = NEWFILE_NUM_BLOCKS;
312 /* 127MB - taken from default for Windows 2000 */
313 pHeader->CacheLimit.QuadPart = 0x07ff5400;
314 /* Copied from a Windows 2000 cache index */
315 pHeader->DirectoryCount = 4;
317 /* If the registry has a cache size set, use the registry value */
318 if (RegOpenKeyA(HKEY_CURRENT_USER, szCacheContent, &key) == ERROR_SUCCESS)
321 DWORD len = sizeof(dw);
324 if (RegQueryValueExA(key, "CacheLimit", NULL, &keytype,
325 (BYTE *) &dw, &len) == ERROR_SUCCESS &&
326 keytype == REG_DWORD)
328 pHeader->CacheLimit.QuadPart = (ULONGLONG)dw * 1024;
333 URLCache_CreateHashTable(pHeader, NULL, &pHashEntry);
335 /* Last step - create the directories */
337 strcpyW(wszDirPath, pContainer->path);
338 pwchDir = wszDirPath + strlenW(wszDirPath);
341 GetSystemTimeAsFileTime(&ft);
343 for (i = 0; !dwError && i < pHeader->DirectoryCount; ++i)
345 pHeader->directory_data[i].dwNumFiles = 0;
349 ULONGLONG n = ft.dwHighDateTime;
351 /* Generate a file name to attempt to create.
352 * This algorithm will create what will appear
353 * to be random and unrelated directory names
354 * of up to 9 characters in length.
357 n += ft.dwLowDateTime;
358 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
360 for (k = 0; k < 8; ++k)
364 /* Dividing by a prime greater than 36 helps
365 * with the appearance of randomness
370 pwchDir[k] = '0' + r;
372 pwchDir[k] = 'A' + (r - 10);
375 if (CreateDirectoryW(wszDirPath, 0))
377 /* The following is OK because we generated an
378 * 8 character directory name made from characters
379 * [A-Z0-9], which are equivalent for all code
380 * pages and for UTF-16
382 for (k = 0; k < 8; ++k)
383 pHeader->directory_data[i].filename[k] = pwchDir[k];
388 /* Give up. The most likely cause of this
389 * is a full disk, but whatever the cause
390 * is, it should be more than apparent that
393 dwError = GetLastError();
399 UnmapViewOfFile(pHeader);
403 dwError = GetLastError();
405 CloseHandle(hMapping);
409 dwError = GetLastError();
416 DeleteFileW(wszFilePath);
417 ReleaseMutex(pContainer->hMutex);
423 ReleaseMutex(pContainer->hMutex);
425 wsprintfW(wszFilePath, wszMappingFormat, pContainer->path, wszIndex, dwFileSize);
426 URLCache_PathToObjectName(wszFilePath, '_');
427 pContainer->hMapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, wszFilePath);
428 if (!pContainer->hMapping)
429 pContainer->hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, wszFilePath);
431 if (!pContainer->hMapping)
433 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
434 return GetLastError();
437 return ERROR_SUCCESS;
440 /***********************************************************************
441 * URLCacheContainer_CloseIndex (Internal)
449 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
451 CloseHandle(pContainer->hMapping);
452 pContainer->hMapping = NULL;
455 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix, LPCWSTR path, LPWSTR mutex_name)
457 URLCACHECONTAINER * pContainer = HeapAlloc(GetProcessHeap(), 0, sizeof(URLCACHECONTAINER));
458 int cache_prefix_len = strlenW(cache_prefix);
465 pContainer->hMapping = NULL;
466 pContainer->file_size = 0;
468 pContainer->path = heap_strdupW(path);
469 if (!pContainer->path)
471 HeapFree(GetProcessHeap(), 0, pContainer);
475 pContainer->cache_prefix = HeapAlloc(GetProcessHeap(), 0, (cache_prefix_len + 1) * sizeof(WCHAR));
476 if (!pContainer->cache_prefix)
478 HeapFree(GetProcessHeap(), 0, pContainer->path);
479 HeapFree(GetProcessHeap(), 0, pContainer);
483 memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
485 CharLowerW(mutex_name);
486 URLCache_PathToObjectName(mutex_name, '!');
488 if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
490 ERR("couldn't create mutex (error is %d)\n", GetLastError());
491 HeapFree(GetProcessHeap(), 0, pContainer->path);
492 HeapFree(GetProcessHeap(), 0, pContainer);
496 list_add_head(&UrlContainers, &pContainer->entry);
501 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
503 list_remove(&pContainer->entry);
505 URLCacheContainer_CloseIndex(pContainer);
506 CloseHandle(pContainer->hMutex);
507 HeapFree(GetProcessHeap(), 0, pContainer->path);
508 HeapFree(GetProcessHeap(), 0, pContainer->cache_prefix);
509 HeapFree(GetProcessHeap(), 0, pContainer);
512 void URLCacheContainers_CreateDefaults(void)
514 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
515 static const WCHAR UrlPrefix[] = {0};
516 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
517 static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
518 static const WCHAR CookieSuffix[] = {0};
519 static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
522 int nFolder; /* CSIDL_* constant */
523 const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
524 const WCHAR * cache_prefix; /* prefix used to reference the container */
525 } DefaultContainerData[] =
527 { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix },
528 { CSIDL_HISTORY, HistorySuffix, HistoryPrefix },
529 { CSIDL_COOKIES, CookieSuffix, CookiePrefix },
533 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
535 WCHAR wszCachePath[MAX_PATH];
536 WCHAR wszMutexName[MAX_PATH];
537 int path_len, suffix_len;
539 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
541 ERR("Couldn't get path for default container %u\n", i);
544 path_len = strlenW(wszCachePath);
545 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
547 if (path_len + suffix_len + 2 > MAX_PATH)
549 ERR("Path too long\n");
553 wszCachePath[path_len] = '\\';
554 wszCachePath[path_len+1] = 0;
556 strcpyW(wszMutexName, wszCachePath);
560 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
561 wszCachePath[path_len + suffix_len + 1] = '\\';
562 wszCachePath[path_len + suffix_len + 2] = '\0';
565 URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath, wszMutexName);
569 void URLCacheContainers_DeleteAll(void)
571 while(!list_empty(&UrlContainers))
572 URLCacheContainer_DeleteContainer(
573 LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
577 static DWORD URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
579 URLCACHECONTAINER * pContainer;
581 TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
584 return ERROR_INVALID_PARAMETER;
586 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
588 int prefix_len = strlenW(pContainer->cache_prefix);
589 if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
591 TRACE("found container with prefix %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
592 *ppContainer = pContainer;
593 return ERROR_SUCCESS;
596 ERR("no container found\n");
597 return ERROR_FILE_NOT_FOUND;
600 static DWORD URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
605 if (lpszUrl && !(url = heap_strdupAtoW(lpszUrl)))
606 return ERROR_OUTOFMEMORY;
608 ret = URLCacheContainers_FindContainerW(url, ppContainer);
609 HeapFree(GetProcessHeap(), 0, url);
613 static BOOL URLCacheContainers_Enum(LPCWSTR lpwszSearchPattern, DWORD dwIndex, URLCACHECONTAINER ** ppContainer)
616 URLCACHECONTAINER * pContainer;
618 TRACE("searching for prefix: %s\n", debugstr_w(lpwszSearchPattern));
620 /* non-NULL search pattern only returns one container ever */
621 if (lpwszSearchPattern && dwIndex > 0)
624 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
626 if (lpwszSearchPattern)
628 if (!strcmpW(pContainer->cache_prefix, lpwszSearchPattern))
630 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
631 *ppContainer = pContainer;
639 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
640 *ppContainer = pContainer;
649 /***********************************************************************
650 * URLCacheContainer_LockIndex (Internal)
652 * Locks the index for system-wide exclusive access.
655 * Cache file header if successful
656 * NULL if failed and calls SetLastError.
658 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
662 URLCACHE_HEADER * pHeader;
666 WaitForSingleObject(pContainer->hMutex, INFINITE);
668 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
672 ReleaseMutex(pContainer->hMutex);
673 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
676 pHeader = (URLCACHE_HEADER *)pIndexData;
678 /* file has grown - we need to remap to prevent us getting
679 * access violations when we try and access beyond the end
680 * of the memory mapped file */
681 if (pHeader->dwFileSize != pContainer->file_size)
683 UnmapViewOfFile( pHeader );
684 URLCacheContainer_CloseIndex(pContainer);
685 error = URLCacheContainer_OpenIndex(pContainer);
686 if (error != ERROR_SUCCESS)
688 ReleaseMutex(pContainer->hMutex);
692 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
696 ReleaseMutex(pContainer->hMutex);
697 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
700 pHeader = (URLCACHE_HEADER *)pIndexData;
703 TRACE("Signature: %s, file size: %d bytes\n", pHeader->szSignature, pHeader->dwFileSize);
705 for (index = 0; index < pHeader->DirectoryCount; index++)
707 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
713 /***********************************************************************
714 * URLCacheContainer_UnlockIndex (Internal)
717 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
720 ReleaseMutex(pContainer->hMutex);
721 return UnmapViewOfFile(pHeader);
726 #define CHAR_BIT (8 * sizeof(CHAR))
729 /***********************************************************************
730 * URLCache_Allocation_BlockIsFree (Internal)
732 * Is the specified block number free?
739 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
741 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
742 return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
745 /***********************************************************************
746 * URLCache_Allocation_BlockFree (Internal)
748 * Marks the specified block as free
754 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
756 BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
757 AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
760 /***********************************************************************
761 * URLCache_Allocation_BlockAllocate (Internal)
763 * Marks the specified block as allocated
769 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
771 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
772 AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
775 /***********************************************************************
776 * URLCache_FindFirstFreeEntry (Internal)
778 * Finds and allocates the first block of free space big enough and
779 * sets ppEntry to point to it.
782 * TRUE if it had enough space
783 * FALSE if it couldn't find enough space
786 static BOOL URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
788 LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
791 for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
793 for (dwFreeCounter = 0;
794 dwFreeCounter < dwBlocksNeeded &&
795 dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
796 URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
798 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
800 if (dwFreeCounter == dwBlocksNeeded)
803 TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
804 for (index = 0; index < dwBlocksNeeded; index++)
805 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
806 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
807 (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
811 FIXME("Grow file\n");
815 /***********************************************************************
816 * URLCache_DeleteEntry (Internal)
818 * Deletes the specified entry and frees the space allocated to it
821 * TRUE if it succeeded
825 static BOOL URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader, CACHEFILE_ENTRY * pEntry)
829 BYTE * AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
831 /* update allocation table */
832 dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader)) / BLOCKSIZE;
833 for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
834 URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
836 ZeroMemory(pEntry, pEntry->dwBlocksUsed * BLOCKSIZE);
840 /***********************************************************************
841 * URLCache_LocalFileNameToPathW (Internal)
843 * Copies the full path to the specified buffer given the local file
844 * name and the index of the directory it is in. Always sets value in
845 * lpBufferSize to the required buffer size (in bytes).
848 * TRUE if the buffer was big enough
849 * FALSE if the buffer was too small
852 static BOOL URLCache_LocalFileNameToPathW(
853 const URLCACHECONTAINER * pContainer,
854 LPCURLCACHE_HEADER pHeader,
855 LPCSTR szLocalFileName,
861 int path_len = strlenW(pContainer->path);
862 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
863 if (Directory >= pHeader->DirectoryCount)
869 nRequired = (path_len + DIR_LENGTH + file_name_len + 1) * sizeof(WCHAR);
870 if (nRequired <= *lpBufferSize)
874 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
875 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
876 wszPath[dir_len + path_len] = '\\';
877 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len + 1, file_name_len);
878 *lpBufferSize = nRequired;
881 *lpBufferSize = nRequired;
885 /***********************************************************************
886 * URLCache_LocalFileNameToPathA (Internal)
888 * Copies the full path to the specified buffer given the local file
889 * name and the index of the directory it is in. Always sets value in
890 * lpBufferSize to the required buffer size.
893 * TRUE if the buffer was big enough
894 * FALSE if the buffer was too small
897 static BOOL URLCache_LocalFileNameToPathA(
898 const URLCACHECONTAINER * pContainer,
899 LPCURLCACHE_HEADER pHeader,
900 LPCSTR szLocalFileName,
906 int path_len, file_name_len, dir_len;
908 if (Directory >= pHeader->DirectoryCount)
914 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
915 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
916 dir_len = DIR_LENGTH;
918 nRequired = (path_len + dir_len + 1 + file_name_len) * sizeof(char);
919 if (nRequired < *lpBufferSize)
921 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
922 memcpy(szPath+path_len, pHeader->directory_data[Directory].filename, dir_len);
923 szPath[path_len + dir_len] = '\\';
924 memcpy(szPath + path_len + dir_len + 1, szLocalFileName, file_name_len);
925 *lpBufferSize = nRequired;
928 *lpBufferSize = nRequired;
932 /* Just like DosDateTimeToFileTime, except that it also maps the special
933 * case of a DOS date/time of (0,0) to a filetime of (0,0).
935 static void URLCache_DosDateTimeToFileTime(WORD fatdate, WORD fattime,
938 if (!fatdate && !fattime)
939 ft->dwLowDateTime = ft->dwHighDateTime = 0;
941 DosDateTimeToFileTime(fatdate, fattime, ft);
944 /***********************************************************************
945 * URLCache_CopyEntry (Internal)
947 * Copies an entry from the cache index file to the Win32 structure
950 * ERROR_SUCCESS if the buffer was big enough
951 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
954 static DWORD URLCache_CopyEntry(
955 URLCACHECONTAINER * pContainer,
956 LPCURLCACHE_HEADER pHeader,
957 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
958 LPDWORD lpdwBufferSize,
959 const URL_CACHEFILE_ENTRY * pUrlEntry,
963 DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
965 if (*lpdwBufferSize >= dwRequiredSize)
967 lpCacheEntryInfo->lpHeaderInfo = NULL;
968 lpCacheEntryInfo->lpszFileExtension = NULL;
969 lpCacheEntryInfo->lpszLocalFileName = NULL;
970 lpCacheEntryInfo->lpszSourceUrlName = NULL;
971 lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
972 lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
973 lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
974 lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
975 lpCacheEntryInfo->dwSizeHigh = pUrlEntry->size.u.HighPart;
976 lpCacheEntryInfo->dwSizeLow = pUrlEntry->size.u.LowPart;
977 lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
978 lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
979 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
980 lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
981 lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
982 lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
983 lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
984 URLCache_DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
987 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
988 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
989 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
991 lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, NULL, 0);
993 lenUrl = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
994 dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
996 /* FIXME: is source url optional? */
997 if (*lpdwBufferSize >= dwRequiredSize)
999 DWORD lenUrlBytes = (lenUrl+1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1001 lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrlBytes;
1003 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
1005 memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lenUrlBytes);
1008 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1009 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1010 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1012 if (pUrlEntry->dwOffsetLocalName)
1014 LONG nLocalFilePathSize;
1015 LPSTR lpszLocalFileName;
1016 lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
1017 nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
1018 if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
1019 (!bUnicode && URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize)))
1021 lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
1023 dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
1025 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1026 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1027 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1029 dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
1031 if (*lpdwBufferSize >= dwRequiredSize)
1033 lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
1034 memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
1035 ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
1037 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1038 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1039 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1041 if (pUrlEntry->dwOffsetFileExtension)
1046 lenExtension = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, NULL, 0);
1048 lenExtension = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension) + 1;
1049 dwRequiredSize += lenExtension * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1051 if (*lpdwBufferSize >= dwRequiredSize)
1053 lpCacheEntryInfo->lpszFileExtension = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenExtension;
1055 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenExtension);
1057 memcpy(lpCacheEntryInfo->lpszFileExtension, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, lenExtension * sizeof(CHAR));
1060 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1061 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1062 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1065 if (dwRequiredSize > *lpdwBufferSize)
1067 *lpdwBufferSize = dwRequiredSize;
1068 return ERROR_INSUFFICIENT_BUFFER;
1070 *lpdwBufferSize = dwRequiredSize;
1071 return ERROR_SUCCESS;
1074 /* Just like FileTimeToDosDateTime, except that it also maps the special
1075 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1077 static void URLCache_FileTimeToDosDateTime(const FILETIME *ft, WORD *fatdate,
1080 if (!ft->dwLowDateTime && !ft->dwHighDateTime)
1081 *fatdate = *fattime = 0;
1083 FileTimeToDosDateTime(ft, fatdate, fattime);
1086 /***********************************************************************
1087 * URLCache_SetEntryInfo (Internal)
1089 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1090 * according to the flags set by dwFieldControl.
1093 * ERROR_SUCCESS if the buffer was big enough
1094 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1097 static DWORD URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
1099 if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
1100 pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
1101 if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
1102 pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
1103 if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
1104 pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
1105 if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
1106 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
1107 if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
1108 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1109 if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
1110 pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
1111 if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
1112 pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
1113 if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
1114 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1116 return ERROR_SUCCESS;
1119 /***********************************************************************
1120 * URLCache_HashKey (Internal)
1122 * Returns the hash key for a given string
1125 * hash key for the string
1128 static DWORD URLCache_HashKey(LPCSTR lpszKey)
1130 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1131 * but the algorithm and result are not the same!
1133 static const unsigned char lookupTable[256] =
1135 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1136 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1137 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1138 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1139 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1140 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1141 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1142 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1143 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1144 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1145 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1146 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1147 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1148 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1149 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1150 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1151 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1152 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1153 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1154 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1155 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1156 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1157 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1158 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1159 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1160 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1161 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1162 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1163 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1164 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1165 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1166 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1171 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1172 key[i] = lookupTable[i];
1174 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1176 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1177 key[i] = lookupTable[*lpszKey ^ key[i]];
1180 return *(DWORD *)key;
1183 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
1185 return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
1188 static inline BOOL URLCache_IsHashEntryValid(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY *pHashEntry)
1190 /* check pHashEntry located within acceptable bounds in the URL cache mapping */
1191 return ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) >= ENTRY_START_OFFSET) &&
1192 ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) < pHeader->dwFileSize);
1195 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1197 /* structure of hash table:
1198 * 448 entries divided into 64 blocks
1199 * each block therefore contains a chain of 7 key/offset pairs
1200 * how position in table is calculated:
1201 * 1. the url is hashed in helper function
1202 * 2. the key % 64 * 8 is the offset
1203 * 3. the key in the hash table is the hash key aligned to 64
1206 * there can be multiple hash tables in the file and the offset to
1207 * the next one is stored in the header of the hash table
1209 DWORD key = URLCache_HashKey(lpszUrl);
1210 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1211 HASH_CACHEFILE_ENTRY * pHashEntry;
1212 DWORD dwHashTableNumber = 0;
1214 key = (key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1216 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1217 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1218 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1221 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1223 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1226 /* make sure that it is in fact a hash entry */
1227 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1229 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1233 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1235 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1236 if (key == (pHashElement->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES)
1238 /* FIXME: we should make sure that this is the right element
1239 * before returning and claiming that it is. We can do this
1240 * by doing a simple compare between the URL we were given
1241 * and the URL stored in the entry. However, this assumes
1242 * we know the format of all the entries stored in the
1244 *ppHashEntry = pHashElement;
1252 static BOOL URLCache_FindHashW(LPCURLCACHE_HEADER pHeader, LPCWSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1257 urlA = heap_strdupWtoA(lpszUrl);
1260 SetLastError(ERROR_OUTOFMEMORY);
1264 ret = URLCache_FindHash(pHeader, urlA, ppHashEntry);
1265 HeapFree(GetProcessHeap(), 0, urlA);
1269 /***********************************************************************
1270 * URLCache_HashEntrySetUse (Internal)
1272 * Searches all the hash tables in the index for the given URL and
1273 * sets the use count (stored or'ed with key)
1276 * TRUE if the entry was found
1277 * FALSE if the entry could not be found
1280 static BOOL URLCache_HashEntrySetUse(struct _HASH_ENTRY * pHashEntry, DWORD dwUseCount)
1282 pHashEntry->dwHashKey = dwUseCount | (pHashEntry->dwHashKey / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1286 /***********************************************************************
1287 * URLCache_DeleteEntryFromHash (Internal)
1289 * Searches all the hash tables in the index for the given URL and
1290 * then if found deletes the entry.
1293 * TRUE if the entry was found
1294 * FALSE if the entry could not be found
1297 static BOOL URLCache_DeleteEntryFromHash(struct _HASH_ENTRY * pHashEntry)
1299 pHashEntry->dwHashKey = HASHTABLE_FREE;
1300 pHashEntry->dwOffsetEntry = HASHTABLE_FREE;
1304 /***********************************************************************
1305 * URLCache_AddEntryToHash (Internal)
1307 * Searches all the hash tables for a free slot based on the offset
1308 * generated from the hash key. If a free slot is found, the offset and
1309 * key are entered into the hash table.
1312 * ERROR_SUCCESS if the entry was added
1313 * Any other Win32 error code if the entry could not be added
1316 static DWORD URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry)
1318 /* see URLCache_FindEntryInHash for structure of hash tables */
1320 DWORD key = URLCache_HashKey(lpszUrl);
1321 DWORD offset = (key % HASHTABLE_NUM_ENTRIES) * sizeof(struct _HASH_ENTRY);
1322 HASH_CACHEFILE_ENTRY * pHashEntry;
1323 DWORD dwHashTableNumber = 0;
1326 key = (key / HASHTABLE_NUM_ENTRIES) * HASHTABLE_NUM_ENTRIES;
1328 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1329 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1330 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1333 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1335 ERR("not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1338 /* make sure that it is in fact a hash entry */
1339 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1341 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1345 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1347 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1348 if (pHashElement->dwHashKey == HASHTABLE_FREE) /* if the slot is free */
1350 pHashElement->dwHashKey = key;
1351 pHashElement->dwOffsetEntry = dwOffsetEntry;
1352 return ERROR_SUCCESS;
1356 error = URLCache_CreateHashTable(pHeader, pHashEntry, &pHashEntry);
1357 if (error != ERROR_SUCCESS)
1360 pHashEntry->HashTable[offset].dwHashKey = key;
1361 pHashEntry->HashTable[offset].dwOffsetEntry = dwOffsetEntry;
1362 return ERROR_SUCCESS;
1365 /***********************************************************************
1366 * URLCache_CreateHashTable (Internal)
1368 * Creates a new hash table in free space and adds it to the chain of existing
1372 * ERROR_SUCCESS if the hash table was created
1373 * ERROR_DISK_FULL if the hash table could not be created
1376 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash)
1381 if (!URLCache_FindFirstFreeEntry(pHeader, 0x20, (CACHEFILE_ENTRY **)ppHash))
1383 FIXME("no free space for hash table\n");
1384 return ERROR_DISK_FULL;
1387 dwOffset = (BYTE *)*ppHash - (BYTE *)pHeader;
1390 pPrevHash->dwAddressNext = dwOffset;
1392 pHeader->dwOffsetFirstHashTable = dwOffset;
1393 (*ppHash)->CacheFileEntry.dwSignature = HASH_SIGNATURE;
1394 (*ppHash)->CacheFileEntry.dwBlocksUsed = 0x20;
1395 (*ppHash)->dwHashTableNumber = pPrevHash ? pPrevHash->dwHashTableNumber + 1 : 0;
1396 for (i = 0; i < HASHTABLE_SIZE; i++)
1398 (*ppHash)->HashTable[i].dwOffsetEntry = 0;
1399 (*ppHash)->HashTable[i].dwHashKey = HASHTABLE_FREE;
1401 return ERROR_SUCCESS;
1404 /***********************************************************************
1405 * URLCache_EnumHashTables (Internal)
1407 * Enumerates the hash tables in a container.
1410 * TRUE if an entry was found
1411 * FALSE if there are no more tables to enumerate.
1414 static BOOL URLCache_EnumHashTables(LPCURLCACHE_HEADER pHeader, DWORD *pdwHashTableNumber, HASH_CACHEFILE_ENTRY ** ppHashEntry)
1416 for (*ppHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1417 URLCache_IsHashEntryValid(pHeader, *ppHashEntry);
1418 *ppHashEntry = URLCache_HashEntryFromOffset(pHeader, (*ppHashEntry)->dwAddressNext))
1420 TRACE("looking at hash table number %d\n", (*ppHashEntry)->dwHashTableNumber);
1421 if ((*ppHashEntry)->dwHashTableNumber != *pdwHashTableNumber)
1423 /* make sure that it is in fact a hash entry */
1424 if ((*ppHashEntry)->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1426 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->CacheFileEntry.dwSignature);
1427 (*pdwHashTableNumber)++;
1431 TRACE("hash table number %d found\n", *pdwHashTableNumber);
1437 /***********************************************************************
1438 * URLCache_EnumHashTableEntries (Internal)
1440 * Enumerates entries in a hash table and returns the next non-free entry.
1443 * TRUE if an entry was found
1444 * FALSE if the hash table is empty or there are no more entries to
1448 static BOOL URLCache_EnumHashTableEntries(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY * pHashEntry,
1449 DWORD * index, const struct _HASH_ENTRY ** ppHashEntry)
1451 for (; *index < HASHTABLE_SIZE ; (*index)++)
1453 if (pHashEntry->HashTable[*index].dwHashKey == HASHTABLE_FREE)
1456 *ppHashEntry = &pHashEntry->HashTable[*index];
1457 TRACE("entry found %d\n", *index);
1460 TRACE("no more entries (%d)\n", *index);
1464 /***********************************************************************
1465 * URLCache_DeleteCacheDirectory (Internal)
1467 * Erase a directory containing an URL cache.
1470 * TRUE success, FALSE failure/aborted.
1473 static BOOL URLCache_DeleteCacheDirectory(LPCWSTR lpszPath)
1476 WCHAR path[MAX_PATH + 1];
1477 SHFILEOPSTRUCTW shfos;
1480 path_len = strlenW(lpszPath);
1481 if (path_len >= MAX_PATH)
1483 strcpyW(path, lpszPath);
1484 path[path_len + 1] = 0; /* double-NUL-terminate path */
1487 shfos.wFunc = FO_DELETE;
1491 shfos.fAnyOperationsAborted = FALSE;
1492 ret = SHFileOperationW(&shfos);
1494 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path), ret);
1495 return !(ret || shfos.fAnyOperationsAborted);
1498 /***********************************************************************
1499 * FreeUrlCacheSpaceW (WININET.@)
1501 * Frees up some cache.
1504 * lpszCachePath [I] Which volume to free up from, or NULL if you don't care.
1505 * dwSize [I] How much space to free up.
1506 * dwSizeType [I] How to interpret dwSize.
1509 * TRUE success. FALSE failure.
1512 * This implementation just retrieves the path of the cache directory, and
1513 * deletes its contents from the filesystem. The correct approach would
1514 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
1516 BOOL WINAPI FreeUrlCacheSpaceW(LPCWSTR lpszCachePath, DWORD dwSize, DWORD dwSizeType)
1518 URLCACHECONTAINER * pContainer;
1520 if (lpszCachePath != NULL || dwSize != 100 || dwSizeType != FCS_PERCENT_CACHE_SPACE)
1522 FIXME("(%s, %x, %x): partial stub!\n", debugstr_w(lpszCachePath), dwSize, dwSizeType);
1523 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1527 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
1529 /* The URL cache has prefix L"" (unlike Cookies and History) */
1530 if (pContainer->cache_prefix[0] == 0)
1534 WaitForSingleObject(pContainer->hMutex, INFINITE);
1536 /* unlock, delete, recreate and lock cache */
1537 URLCacheContainer_CloseIndex(pContainer);
1538 ret_del = URLCache_DeleteCacheDirectory(pContainer->path);
1539 ret_open = URLCacheContainer_OpenIndex(pContainer);
1541 ReleaseMutex(pContainer->hMutex);
1542 return ret_del && (ret_open == ERROR_SUCCESS);
1548 /***********************************************************************
1549 * FreeUrlCacheSpaceA (WININET.@)
1551 * See FreeUrlCacheSpaceW.
1553 BOOL WINAPI FreeUrlCacheSpaceA(LPCSTR lpszCachePath, DWORD dwSize, DWORD dwSizeType)
1556 LPWSTR path = heap_strdupAtoW(lpszCachePath);
1557 if (lpszCachePath == NULL || path != NULL)
1558 ret = FreeUrlCacheSpaceW(path, dwSize, dwSizeType);
1563 /***********************************************************************
1564 * GetUrlCacheEntryInfoExA (WININET.@)
1567 BOOL WINAPI GetUrlCacheEntryInfoExA(
1569 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1570 LPDWORD lpdwCacheEntryInfoBufSize,
1572 LPDWORD lpdwReserved,
1576 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1577 debugstr_a(lpszUrl),
1579 lpdwCacheEntryInfoBufSize,
1585 if ((lpszReserved != NULL) ||
1586 (lpdwReserved != NULL) ||
1587 (lpReserved != NULL))
1589 ERR("Reserved value was not 0\n");
1590 SetLastError(ERROR_INVALID_PARAMETER);
1595 FIXME("Undocumented flag(s): %x\n", dwFlags);
1596 SetLastError(ERROR_FILE_NOT_FOUND);
1599 return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1602 /***********************************************************************
1603 * GetUrlCacheEntryInfoA (WININET.@)
1606 BOOL WINAPI GetUrlCacheEntryInfoA(
1607 IN LPCSTR lpszUrlName,
1608 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1609 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1612 LPURLCACHE_HEADER pHeader;
1613 struct _HASH_ENTRY * pHashEntry;
1614 const CACHEFILE_ENTRY * pEntry;
1615 const URL_CACHEFILE_ENTRY * pUrlEntry;
1616 URLCACHECONTAINER * pContainer;
1619 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1621 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1622 if (error != ERROR_SUCCESS)
1624 SetLastError(error);
1628 error = URLCacheContainer_OpenIndex(pContainer);
1629 if (error != ERROR_SUCCESS)
1631 SetLastError(error);
1635 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1638 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1640 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1641 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1642 SetLastError(ERROR_FILE_NOT_FOUND);
1646 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1647 if (pEntry->dwSignature != URL_SIGNATURE)
1649 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1650 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1651 SetLastError(ERROR_FILE_NOT_FOUND);
1655 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1656 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1657 if (pUrlEntry->dwOffsetHeaderInfo)
1658 TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1660 if (lpdwCacheEntryInfoBufferSize)
1662 if (!lpCacheEntryInfo)
1663 *lpdwCacheEntryInfoBufferSize = 0;
1665 error = URLCache_CopyEntry(
1669 lpdwCacheEntryInfoBufferSize,
1672 if (error != ERROR_SUCCESS)
1674 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1675 SetLastError(error);
1678 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1681 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1686 /***********************************************************************
1687 * GetUrlCacheEntryInfoW (WININET.@)
1690 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1691 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1692 LPDWORD lpdwCacheEntryInfoBufferSize)
1694 LPURLCACHE_HEADER pHeader;
1695 struct _HASH_ENTRY * pHashEntry;
1696 const CACHEFILE_ENTRY * pEntry;
1697 const URL_CACHEFILE_ENTRY * pUrlEntry;
1698 URLCACHECONTAINER * pContainer;
1701 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1703 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1704 if (error != ERROR_SUCCESS)
1706 SetLastError(error);
1710 error = URLCacheContainer_OpenIndex(pContainer);
1711 if (error != ERROR_SUCCESS)
1713 SetLastError(error);
1717 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1720 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1722 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1723 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1724 SetLastError(ERROR_FILE_NOT_FOUND);
1728 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1729 if (pEntry->dwSignature != URL_SIGNATURE)
1731 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1732 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1733 SetLastError(ERROR_FILE_NOT_FOUND);
1737 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1738 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1739 TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1741 if (lpdwCacheEntryInfoBufferSize)
1743 if (!lpCacheEntryInfo)
1744 *lpdwCacheEntryInfoBufferSize = 0;
1746 error = URLCache_CopyEntry(
1749 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1750 lpdwCacheEntryInfoBufferSize,
1752 TRUE /* UNICODE */);
1753 if (error != ERROR_SUCCESS)
1755 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1756 SetLastError(error);
1759 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1762 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1767 /***********************************************************************
1768 * GetUrlCacheEntryInfoExW (WININET.@)
1771 BOOL WINAPI GetUrlCacheEntryInfoExW(
1773 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1774 LPDWORD lpdwCacheEntryInfoBufSize,
1775 LPWSTR lpszReserved,
1776 LPDWORD lpdwReserved,
1780 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1781 debugstr_w(lpszUrl),
1783 lpdwCacheEntryInfoBufSize,
1789 if ((lpszReserved != NULL) ||
1790 (lpdwReserved != NULL) ||
1791 (lpReserved != NULL))
1793 ERR("Reserved value was not 0\n");
1794 SetLastError(ERROR_INVALID_PARAMETER);
1799 FIXME("Undocumented flag(s): %x\n", dwFlags);
1800 SetLastError(ERROR_FILE_NOT_FOUND);
1803 return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1806 /***********************************************************************
1807 * SetUrlCacheEntryInfoA (WININET.@)
1809 BOOL WINAPI SetUrlCacheEntryInfoA(
1811 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1812 DWORD dwFieldControl)
1814 LPURLCACHE_HEADER pHeader;
1815 struct _HASH_ENTRY * pHashEntry;
1816 CACHEFILE_ENTRY * pEntry;
1817 URLCACHECONTAINER * pContainer;
1820 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1822 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1823 if (error != ERROR_SUCCESS)
1825 SetLastError(error);
1829 error = URLCacheContainer_OpenIndex(pContainer);
1830 if (error != ERROR_SUCCESS)
1832 SetLastError(error);
1836 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1839 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1841 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1842 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1843 SetLastError(ERROR_FILE_NOT_FOUND);
1847 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1848 if (pEntry->dwSignature != URL_SIGNATURE)
1850 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1851 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1852 SetLastError(ERROR_FILE_NOT_FOUND);
1856 URLCache_SetEntryInfo(
1857 (URL_CACHEFILE_ENTRY *)pEntry,
1858 (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
1861 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1866 /***********************************************************************
1867 * SetUrlCacheEntryInfoW (WININET.@)
1869 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1871 LPURLCACHE_HEADER pHeader;
1872 struct _HASH_ENTRY * pHashEntry;
1873 CACHEFILE_ENTRY * pEntry;
1874 URLCACHECONTAINER * pContainer;
1877 TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
1879 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1880 if (error != ERROR_SUCCESS)
1882 SetLastError(error);
1886 error = URLCacheContainer_OpenIndex(pContainer);
1887 if (error != ERROR_SUCCESS)
1889 SetLastError(error);
1893 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1896 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1898 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1899 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1900 SetLastError(ERROR_FILE_NOT_FOUND);
1904 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1905 if (pEntry->dwSignature != URL_SIGNATURE)
1907 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1908 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1909 SetLastError(ERROR_FILE_NOT_FOUND);
1913 URLCache_SetEntryInfo(
1914 (URL_CACHEFILE_ENTRY *)pEntry,
1918 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1923 /***********************************************************************
1924 * RetrieveUrlCacheEntryFileA (WININET.@)
1927 BOOL WINAPI RetrieveUrlCacheEntryFileA(
1928 IN LPCSTR lpszUrlName,
1929 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1930 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1934 LPURLCACHE_HEADER pHeader;
1935 struct _HASH_ENTRY * pHashEntry;
1936 CACHEFILE_ENTRY * pEntry;
1937 URL_CACHEFILE_ENTRY * pUrlEntry;
1938 URLCACHECONTAINER * pContainer;
1941 TRACE("(%s, %p, %p, 0x%08x)\n",
1942 debugstr_a(lpszUrlName),
1944 lpdwCacheEntryInfoBufferSize,
1947 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
1948 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
1950 SetLastError(ERROR_INVALID_PARAMETER);
1954 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1955 if (error != ERROR_SUCCESS)
1957 SetLastError(error);
1961 error = URLCacheContainer_OpenIndex(pContainer);
1962 if (error != ERROR_SUCCESS)
1964 SetLastError(error);
1968 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1971 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1973 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1974 TRACE("entry %s not found!\n", lpszUrlName);
1975 SetLastError(ERROR_FILE_NOT_FOUND);
1979 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1980 if (pEntry->dwSignature != URL_SIGNATURE)
1982 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1983 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1984 SetLastError(ERROR_FILE_NOT_FOUND);
1988 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
1989 if (!pUrlEntry->dwOffsetLocalName)
1991 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1992 SetLastError(ERROR_INVALID_DATA);
1996 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1997 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
1999 error = URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo,
2000 lpdwCacheEntryInfoBufferSize, pUrlEntry,
2002 if (error != ERROR_SUCCESS)
2004 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2005 SetLastError(error);
2008 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2010 pUrlEntry->dwHitRate++;
2011 pUrlEntry->dwUseCount++;
2012 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2013 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2015 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2020 /***********************************************************************
2021 * RetrieveUrlCacheEntryFileW (WININET.@)
2024 BOOL WINAPI RetrieveUrlCacheEntryFileW(
2025 IN LPCWSTR lpszUrlName,
2026 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2027 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2031 LPURLCACHE_HEADER pHeader;
2032 struct _HASH_ENTRY * pHashEntry;
2033 CACHEFILE_ENTRY * pEntry;
2034 URL_CACHEFILE_ENTRY * pUrlEntry;
2035 URLCACHECONTAINER * pContainer;
2038 TRACE("(%s, %p, %p, 0x%08x)\n",
2039 debugstr_w(lpszUrlName),
2041 lpdwCacheEntryInfoBufferSize,
2044 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
2045 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
2047 SetLastError(ERROR_INVALID_PARAMETER);
2051 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2052 if (error != ERROR_SUCCESS)
2054 SetLastError(error);
2058 error = URLCacheContainer_OpenIndex(pContainer);
2059 if (error != ERROR_SUCCESS)
2061 SetLastError(error);
2065 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2068 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2070 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2071 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2072 SetLastError(ERROR_FILE_NOT_FOUND);
2076 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2077 if (pEntry->dwSignature != URL_SIGNATURE)
2079 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2080 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2081 SetLastError(ERROR_FILE_NOT_FOUND);
2085 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2086 if (!pUrlEntry->dwOffsetLocalName)
2088 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2089 SetLastError(ERROR_INVALID_DATA);
2093 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
2094 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
2096 error = URLCache_CopyEntry(
2099 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
2100 lpdwCacheEntryInfoBufferSize,
2102 TRUE /* UNICODE */);
2103 if (error != ERROR_SUCCESS)
2105 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2106 SetLastError(error);
2109 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2111 pUrlEntry->dwHitRate++;
2112 pUrlEntry->dwUseCount++;
2113 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2114 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2116 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2121 static BOOL DeleteUrlCacheEntryInternal(LPURLCACHE_HEADER pHeader,
2122 struct _HASH_ENTRY *pHashEntry)
2124 CACHEFILE_ENTRY * pEntry;
2125 URL_CACHEFILE_ENTRY * pUrlEntry;
2127 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2128 if (pEntry->dwSignature != URL_SIGNATURE)
2130 FIXME("Trying to delete entry of unknown format %s\n",
2131 debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
2132 SetLastError(ERROR_FILE_NOT_FOUND);
2135 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2136 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
2138 if (pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles)
2139 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles--;
2141 if (pUrlEntry->CacheEntryType & STICKY_CACHE_ENTRY)
2143 if (pUrlEntry->size.QuadPart < pHeader->ExemptUsage.QuadPart)
2144 pHeader->ExemptUsage.QuadPart -= pUrlEntry->size.QuadPart;
2146 pHeader->ExemptUsage.QuadPart = 0;
2150 if (pUrlEntry->size.QuadPart < pHeader->CacheUsage.QuadPart)
2151 pHeader->CacheUsage.QuadPart -= pUrlEntry->size.QuadPart;
2153 pHeader->CacheUsage.QuadPart = 0;
2156 URLCache_DeleteEntry(pHeader, pEntry);
2158 URLCache_DeleteEntryFromHash(pHashEntry);
2162 /***********************************************************************
2163 * UnlockUrlCacheEntryFileA (WININET.@)
2166 BOOL WINAPI UnlockUrlCacheEntryFileA(
2167 IN LPCSTR lpszUrlName,
2171 LPURLCACHE_HEADER pHeader;
2172 struct _HASH_ENTRY * pHashEntry;
2173 CACHEFILE_ENTRY * pEntry;
2174 URL_CACHEFILE_ENTRY * pUrlEntry;
2175 URLCACHECONTAINER * pContainer;
2178 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2182 ERR("dwReserved != 0\n");
2183 SetLastError(ERROR_INVALID_PARAMETER);
2187 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2188 if (error != ERROR_SUCCESS)
2190 SetLastError(error);
2194 error = URLCacheContainer_OpenIndex(pContainer);
2195 if (error != ERROR_SUCCESS)
2197 SetLastError(error);
2201 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2204 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2206 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2207 TRACE("entry %s not found!\n", lpszUrlName);
2208 SetLastError(ERROR_FILE_NOT_FOUND);
2212 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2213 if (pEntry->dwSignature != URL_SIGNATURE)
2215 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2216 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2217 SetLastError(ERROR_FILE_NOT_FOUND);
2221 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2223 if (pUrlEntry->dwUseCount == 0)
2225 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2228 pUrlEntry->dwUseCount--;
2229 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2231 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2236 /***********************************************************************
2237 * UnlockUrlCacheEntryFileW (WININET.@)
2240 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
2242 LPURLCACHE_HEADER pHeader;
2243 struct _HASH_ENTRY * pHashEntry;
2244 CACHEFILE_ENTRY * pEntry;
2245 URL_CACHEFILE_ENTRY * pUrlEntry;
2246 URLCACHECONTAINER * pContainer;
2249 TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
2253 ERR("dwReserved != 0\n");
2254 SetLastError(ERROR_INVALID_PARAMETER);
2258 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2259 if (error != ERROR_SUCCESS)
2261 SetLastError(error);
2265 error = URLCacheContainer_OpenIndex(pContainer);
2266 if (error != ERROR_SUCCESS)
2268 SetLastError(error);
2272 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2275 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2277 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2278 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2279 SetLastError(ERROR_FILE_NOT_FOUND);
2283 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2284 if (pEntry->dwSignature != URL_SIGNATURE)
2286 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2287 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2288 SetLastError(ERROR_FILE_NOT_FOUND);
2292 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2294 if (pUrlEntry->dwUseCount == 0)
2296 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2299 pUrlEntry->dwUseCount--;
2300 URLCache_HashEntrySetUse(pHashEntry, pUrlEntry->dwUseCount);
2302 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2307 /***********************************************************************
2308 * CreateUrlCacheEntryA (WININET.@)
2311 BOOL WINAPI CreateUrlCacheEntryA(
2312 IN LPCSTR lpszUrlName,
2313 IN DWORD dwExpectedFileSize,
2314 IN LPCSTR lpszFileExtension,
2315 OUT LPSTR lpszFileName,
2320 WCHAR *file_extension = NULL;
2321 WCHAR file_name[MAX_PATH];
2322 BOOL bSuccess = FALSE;
2325 TRACE("(%s %d %s %p %d)\n", debugstr_a(lpszUrlName), dwExpectedFileSize,
2326 debugstr_a(lpszFileExtension), lpszFileName, dwReserved);
2328 if (lpszUrlName && (url_name = heap_strdupAtoW(lpszUrlName)))
2330 if (!lpszFileExtension || (file_extension = heap_strdupAtoW(lpszFileExtension)))
2332 if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
2334 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
2340 dwError = GetLastError();
2345 dwError = GetLastError();
2347 HeapFree(GetProcessHeap(), 0, file_extension);
2351 dwError = GetLastError();
2353 HeapFree(GetProcessHeap(), 0, url_name);
2355 SetLastError(dwError);
2359 /***********************************************************************
2360 * CreateUrlCacheEntryW (WININET.@)
2363 BOOL WINAPI CreateUrlCacheEntryW(
2364 IN LPCWSTR lpszUrlName,
2365 IN DWORD dwExpectedFileSize,
2366 IN LPCWSTR lpszFileExtension,
2367 OUT LPWSTR lpszFileName,
2371 URLCACHECONTAINER * pContainer;
2372 LPURLCACHE_HEADER pHeader;
2373 CHAR szFile[MAX_PATH];
2374 WCHAR szExtension[MAX_PATH];
2375 LPCWSTR lpszUrlPart;
2377 LPCWSTR lpszFileNameExtension;
2378 LPWSTR lpszFileNameNoPath;
2380 int countnoextension;
2383 BOOL bFound = FALSE;
2389 static const WCHAR szWWW[] = {'w','w','w',0};
2390 static const WCHAR fmt[] = {'%','0','8','X','%','s',0};
2392 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2393 debugstr_w(lpszUrlName),
2395 debugstr_w(lpszFileExtension),
2400 FIXME("dwReserved 0x%08x\n", dwReserved);
2402 lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2404 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2407 lpszUrlPart = memchrW(lpszUrlName, '?', lpszUrlEnd - lpszUrlName);
2409 lpszUrlPart = memchrW(lpszUrlName, '#', lpszUrlEnd - lpszUrlName);
2411 lpszUrlEnd = lpszUrlPart;
2413 for (lpszUrlPart = lpszUrlEnd;
2414 (lpszUrlPart >= lpszUrlName);
2417 if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2424 if (!lstrcmpW(lpszUrlPart, szWWW))
2426 lpszUrlPart += lstrlenW(szWWW);
2429 count = lpszUrlEnd - lpszUrlPart;
2431 if (bFound && (count < MAX_PATH))
2433 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2437 while(len && szFile[--len] == '/') szFile[len] = '\0';
2439 /* FIXME: get rid of illegal characters like \, / and : */
2443 FIXME("need to generate a random filename\n");
2446 TRACE("File name: %s\n", debugstr_a(szFile));
2448 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2449 if (error != ERROR_SUCCESS)
2451 SetLastError(error);
2455 error = URLCacheContainer_OpenIndex(pContainer);
2456 if (error != ERROR_SUCCESS)
2458 SetLastError(error);
2462 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2465 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2467 lBufferSize = MAX_PATH * sizeof(WCHAR);
2468 if (!URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize))
2470 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2471 debugstr_a(szFile), lBufferSize);
2472 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2476 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2478 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2479 lpszFileNameNoPath >= lpszFileName;
2480 --lpszFileNameNoPath)
2482 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2486 countnoextension = lstrlenW(lpszFileNameNoPath);
2487 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2488 if (lpszFileNameExtension)
2489 countnoextension -= lstrlenW(lpszFileNameExtension);
2490 *szExtension = '\0';
2492 if (lpszFileExtension)
2494 szExtension[0] = '.';
2495 lstrcpyW(szExtension+1, lpszFileExtension);
2498 for (i = 0; i < 255; i++)
2500 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2503 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2504 for (p = lpszFileNameNoPath + 1; *p; p++)
2510 case '/': case '\\':
2517 if (p[-1] == ' ' || p[-1] == '.') p[-1] = '_';
2519 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2520 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2521 if (hFile != INVALID_HANDLE_VALUE)
2528 GetSystemTimeAsFileTime(&ft);
2529 wsprintfW(lpszFileNameNoPath + countnoextension, fmt, ft.dwLowDateTime, szExtension);
2531 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2532 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2533 if (hFile != INVALID_HANDLE_VALUE)
2539 WARN("Could not find a unique filename\n");
2544 /***********************************************************************
2545 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
2547 * The bug we are compensating for is that some drongo at Microsoft
2548 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2549 * As a consequence, CommitUrlCacheEntryA has been effectively
2550 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2551 * is still defined as LPCWSTR. The result (other than madness) is
2552 * that we always need to store lpHeaderInfo in CP_ACP rather than
2553 * in UTF16, and we need to avoid converting lpHeaderInfo in
2554 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2555 * result will lose data for arbitrary binary data.
2558 static BOOL CommitUrlCacheEntryInternal(
2559 IN LPCWSTR lpszUrlName,
2560 IN LPCWSTR lpszLocalFileName,
2561 IN FILETIME ExpireTime,
2562 IN FILETIME LastModifiedTime,
2563 IN DWORD CacheEntryType,
2564 IN LPBYTE lpHeaderInfo,
2565 IN DWORD dwHeaderSize,
2566 IN LPCWSTR lpszFileExtension,
2567 IN LPCWSTR lpszOriginalUrl
2570 URLCACHECONTAINER * pContainer;
2571 LPURLCACHE_HEADER pHeader;
2572 struct _HASH_ENTRY * pHashEntry;
2573 CACHEFILE_ENTRY * pEntry;
2574 URL_CACHEFILE_ENTRY * pUrlEntry;
2575 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2576 DWORD dwOffsetLocalFileName = 0;
2577 DWORD dwOffsetHeader = 0;
2578 DWORD dwOffsetFileExtension = 0;
2579 LARGE_INTEGER file_size;
2580 BYTE cDirectory = 0;
2581 char achFile[MAX_PATH];
2582 LPSTR lpszUrlNameA = NULL;
2583 LPSTR lpszFileExtensionA = NULL;
2584 char *pchLocalFileName = 0;
2587 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2588 debugstr_w(lpszUrlName),
2589 debugstr_w(lpszLocalFileName),
2593 debugstr_w(lpszFileExtension),
2594 debugstr_w(lpszOriginalUrl));
2596 if (CacheEntryType & STICKY_CACHE_ENTRY && !lpszLocalFileName)
2598 SetLastError(ERROR_INVALID_PARAMETER);
2601 if (lpszOriginalUrl)
2602 WARN(": lpszOriginalUrl ignored\n");
2604 file_size.QuadPart = 0;
2605 if (lpszLocalFileName)
2609 hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2610 if (hFile == INVALID_HANDLE_VALUE)
2612 ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
2617 if (!GetFileSizeEx(hFile, &file_size))
2619 ERR("couldn't get file size (error is %d)\n", GetLastError());
2627 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2628 if (error != ERROR_SUCCESS)
2630 SetLastError(error);
2634 error = URLCacheContainer_OpenIndex(pContainer);
2635 if (error != ERROR_SUCCESS)
2637 SetLastError(error);
2641 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2644 lpszUrlNameA = heap_strdupWtoA(lpszUrlName);
2647 error = GetLastError();
2651 if (lpszFileExtension && !(lpszFileExtensionA = heap_strdupWtoA(lpszFileExtension)))
2653 error = GetLastError();
2657 if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
2659 FIXME("entry already in cache - don't know what to do!\n");
2661 * SetLastError(ERROR_FILE_NOT_FOUND);
2667 if (lpszLocalFileName)
2669 BOOL bFound = FALSE;
2671 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2673 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2674 error = ERROR_INVALID_PARAMETER;
2678 /* skip container path prefix */
2679 lpszLocalFileName += lstrlenW(pContainer->path);
2681 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, MAX_PATH, NULL, NULL);
2682 pchLocalFileName = achFile;
2684 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2686 if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2695 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2696 error = ERROR_INVALID_PARAMETER;
2700 lpszLocalFileName += (DIR_LENGTH + 1); /* "1234WXYZ\" */
2703 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
2704 if (lpszLocalFileName)
2706 dwOffsetLocalFileName = dwBytesNeeded;
2707 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2711 dwOffsetHeader = dwBytesNeeded;
2712 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2714 if (lpszFileExtensionA)
2716 dwOffsetFileExtension = dwBytesNeeded;
2717 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
2720 /* round up to next block */
2721 if (dwBytesNeeded % BLOCKSIZE)
2723 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2724 dwBytesNeeded += BLOCKSIZE;
2727 if (!URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry))
2729 ERR("no free entries\n");
2730 error = ERROR_DISK_FULL;
2734 /* FindFirstFreeEntry fills in blocks used */
2735 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2736 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2737 pUrlEntry->CacheDir = cDirectory;
2738 pUrlEntry->CacheEntryType = CacheEntryType;
2739 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2740 if (CacheEntryType & STICKY_CACHE_ENTRY)
2742 /* Sticky entries have a default exempt time of one day */
2743 pUrlEntry->dwExemptDelta = 86400;
2746 pUrlEntry->dwExemptDelta = 0;
2747 pUrlEntry->dwHitRate = 0;
2748 pUrlEntry->dwOffsetFileExtension = dwOffsetFileExtension;
2749 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2750 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2751 pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2752 pUrlEntry->size.QuadPart = file_size.QuadPart;
2753 pUrlEntry->dwUseCount = 0;
2754 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2755 pUrlEntry->LastModifiedTime = LastModifiedTime;
2756 URLCache_FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2757 URLCache_FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2758 pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2759 pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2762 pUrlEntry->dwUnknown1 = 0;
2763 pUrlEntry->dwUnknown2 = 0;
2764 pUrlEntry->dwUnknown3 = 0x60;
2765 pUrlEntry->Unknown4 = 0;
2766 pUrlEntry->wUnknown5 = 0x1010;
2767 pUrlEntry->dwUnknown7 = 0;
2768 pUrlEntry->dwUnknown8 = 0;
2771 strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
2772 if (dwOffsetLocalFileName)
2773 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName + DIR_LENGTH + 1);
2775 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2776 if (dwOffsetFileExtension)
2777 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
2779 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA,
2780 (DWORD)((LPBYTE)pUrlEntry - (LPBYTE)pHeader));
2781 if (error != ERROR_SUCCESS)
2782 URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2785 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
2786 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles++;
2787 if (CacheEntryType & STICKY_CACHE_ENTRY)
2788 pHeader->ExemptUsage.QuadPart += file_size.QuadPart;
2790 pHeader->CacheUsage.QuadPart += file_size.QuadPart;
2791 if (pHeader->CacheUsage.QuadPart + pHeader->ExemptUsage.QuadPart >
2792 pHeader->CacheLimit.QuadPart)
2793 FIXME("file of size %s bytes fills cache\n", wine_dbgstr_longlong(file_size.QuadPart));
2797 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2798 HeapFree(GetProcessHeap(), 0, lpszUrlNameA);
2799 HeapFree(GetProcessHeap(), 0, lpszFileExtensionA);
2801 if (error == ERROR_SUCCESS)
2805 SetLastError(error);
2810 /***********************************************************************
2811 * CommitUrlCacheEntryA (WININET.@)
2814 BOOL WINAPI CommitUrlCacheEntryA(
2815 IN LPCSTR lpszUrlName,
2816 IN LPCSTR lpszLocalFileName,
2817 IN FILETIME ExpireTime,
2818 IN FILETIME LastModifiedTime,
2819 IN DWORD CacheEntryType,
2820 IN LPBYTE lpHeaderInfo,
2821 IN DWORD dwHeaderSize,
2822 IN LPCSTR lpszFileExtension,
2823 IN LPCSTR lpszOriginalUrl
2826 WCHAR *url_name = NULL;
2827 WCHAR *local_file_name = NULL;
2828 WCHAR *original_url = NULL;
2829 WCHAR *file_extension = NULL;
2830 BOOL bSuccess = FALSE;
2832 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2833 debugstr_a(lpszUrlName),
2834 debugstr_a(lpszLocalFileName),
2838 debugstr_a(lpszFileExtension),
2839 debugstr_a(lpszOriginalUrl));
2841 url_name = heap_strdupAtoW(lpszUrlName);
2845 if (lpszLocalFileName)
2847 local_file_name = heap_strdupAtoW(lpszLocalFileName);
2848 if (!local_file_name)
2851 if (lpszFileExtension)
2853 file_extension = heap_strdupAtoW(lpszFileExtension);
2854 if (!file_extension)
2857 if (lpszOriginalUrl)
2859 original_url = heap_strdupAtoW(lpszOriginalUrl);
2864 bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2865 CacheEntryType, lpHeaderInfo, dwHeaderSize,
2866 file_extension, original_url);
2869 HeapFree(GetProcessHeap(), 0, original_url);
2870 HeapFree(GetProcessHeap(), 0, file_extension);
2871 HeapFree(GetProcessHeap(), 0, local_file_name);
2872 HeapFree(GetProcessHeap(), 0, url_name);
2877 /***********************************************************************
2878 * CommitUrlCacheEntryW (WININET.@)
2881 BOOL WINAPI CommitUrlCacheEntryW(
2882 IN LPCWSTR lpszUrlName,
2883 IN LPCWSTR lpszLocalFileName,
2884 IN FILETIME ExpireTime,
2885 IN FILETIME LastModifiedTime,
2886 IN DWORD CacheEntryType,
2887 IN LPWSTR lpHeaderInfo,
2888 IN DWORD dwHeaderSize,
2889 IN LPCWSTR lpszFileExtension,
2890 IN LPCWSTR lpszOriginalUrl
2894 BOOL bSuccess = FALSE;
2896 CHAR *header_info = NULL;
2898 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2899 debugstr_w(lpszUrlName),
2900 debugstr_w(lpszLocalFileName),
2904 debugstr_w(lpszFileExtension),
2905 debugstr_w(lpszOriginalUrl));
2907 if (!lpHeaderInfo || (header_info = heap_strdupWtoA(lpHeaderInfo)))
2909 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
2910 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
2916 dwError = GetLastError();
2920 HeapFree(GetProcessHeap(), 0, header_info);
2922 SetLastError(dwError);
2928 /***********************************************************************
2929 * ReadUrlCacheEntryStream (WININET.@)
2932 BOOL WINAPI ReadUrlCacheEntryStream(
2933 IN HANDLE hUrlCacheStream,
2934 IN DWORD dwLocation,
2935 IN OUT LPVOID lpBuffer,
2936 IN OUT LPDWORD lpdwLen,
2940 /* Get handle to file from 'stream' */
2941 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
2943 if (dwReserved != 0)
2945 ERR("dwReserved != 0\n");
2946 SetLastError(ERROR_INVALID_PARAMETER);
2950 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
2952 SetLastError(ERROR_INVALID_HANDLE);
2956 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
2958 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
2961 /***********************************************************************
2962 * RetrieveUrlCacheEntryStreamA (WININET.@)
2965 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
2966 IN LPCSTR lpszUrlName,
2967 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2968 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2969 IN BOOL fRandomRead,
2973 /* NOTE: this is not the same as the way that the native
2974 * version allocates 'stream' handles. I did it this way
2975 * as it is much easier and no applications should depend
2976 * on this behaviour. (Native version appears to allocate
2977 * indices into a table)
2979 STREAM_HANDLE * pStream;
2982 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
2983 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
2985 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
2987 lpdwCacheEntryInfoBufferSize,
2993 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
2998 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3000 if (hFile == INVALID_HANDLE_VALUE)
3003 /* allocate handle storage space */
3004 pStream = HeapAlloc(GetProcessHeap(), 0, sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
3008 SetLastError(ERROR_OUTOFMEMORY);
3012 pStream->hFile = hFile;
3013 strcpy(pStream->lpszUrl, lpszUrlName);
3017 /***********************************************************************
3018 * RetrieveUrlCacheEntryStreamW (WININET.@)
3021 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
3022 IN LPCWSTR lpszUrlName,
3023 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3024 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3025 IN BOOL fRandomRead,
3031 /* NOTE: this is not the same as the way that the native
3032 * version allocates 'stream' handles. I did it this way
3033 * as it is much easier and no applications should depend
3034 * on this behaviour. (Native version appears to allocate
3035 * indices into a table)
3037 STREAM_HANDLE * pStream;
3040 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3041 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3043 if (!RetrieveUrlCacheEntryFileW(lpszUrlName,
3045 lpdwCacheEntryInfoBufferSize,
3051 hFile = CreateFileW(lpCacheEntryInfo->lpszLocalFileName,
3056 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3058 if (hFile == INVALID_HANDLE_VALUE)
3061 /* allocate handle storage space */
3062 size = sizeof(STREAM_HANDLE);
3063 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
3065 pStream = HeapAlloc(GetProcessHeap(), 0, size);
3069 SetLastError(ERROR_OUTOFMEMORY);
3073 pStream->hFile = hFile;
3074 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, pStream->lpszUrl, url_len, NULL, NULL);
3078 /***********************************************************************
3079 * UnlockUrlCacheEntryStream (WININET.@)
3082 BOOL WINAPI UnlockUrlCacheEntryStream(
3083 IN HANDLE hUrlCacheStream,
3087 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3089 if (dwReserved != 0)
3091 ERR("dwReserved != 0\n");
3092 SetLastError(ERROR_INVALID_PARAMETER);
3096 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3098 SetLastError(ERROR_INVALID_HANDLE);
3102 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
3105 /* close file handle */
3106 CloseHandle(pStream->hFile);
3108 /* free allocated space */
3109 HeapFree(GetProcessHeap(), 0, pStream);
3115 /***********************************************************************
3116 * DeleteUrlCacheEntryA (WININET.@)
3119 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3121 URLCACHECONTAINER * pContainer;
3122 LPURLCACHE_HEADER pHeader;
3123 struct _HASH_ENTRY * pHashEntry;
3127 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3129 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
3130 if (error != ERROR_SUCCESS)
3132 SetLastError(error);
3136 error = URLCacheContainer_OpenIndex(pContainer);
3137 if (error != ERROR_SUCCESS)
3139 SetLastError(error);
3143 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3146 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
3148 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3149 TRACE("entry %s not found!\n", lpszUrlName);
3150 SetLastError(ERROR_FILE_NOT_FOUND);
3154 ret = DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
3156 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3161 /***********************************************************************
3162 * DeleteUrlCacheEntryW (WININET.@)
3165 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3167 URLCACHECONTAINER * pContainer;
3168 LPURLCACHE_HEADER pHeader;
3169 struct _HASH_ENTRY * pHashEntry;
3174 TRACE("(%s)\n", debugstr_w(lpszUrlName));
3176 urlA = heap_strdupWtoA(lpszUrlName);
3179 SetLastError(ERROR_OUTOFMEMORY);
3183 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3184 if (error != ERROR_SUCCESS)
3186 HeapFree(GetProcessHeap(), 0, urlA);
3187 SetLastError(error);
3191 error = URLCacheContainer_OpenIndex(pContainer);
3192 if (error != ERROR_SUCCESS)
3194 HeapFree(GetProcessHeap(), 0, urlA);
3195 SetLastError(error);
3199 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3201 HeapFree(GetProcessHeap(), 0, urlA);
3205 if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
3207 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3208 TRACE("entry %s not found!\n", debugstr_a(urlA));
3209 HeapFree(GetProcessHeap(), 0, urlA);
3210 SetLastError(ERROR_FILE_NOT_FOUND);
3214 ret = DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
3216 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3218 HeapFree(GetProcessHeap(), 0, urlA);
3222 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3224 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3228 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3230 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3234 /***********************************************************************
3235 * CreateCacheContainerA (WININET.@)
3237 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3238 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3240 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3241 d1, d2, d3, d4, d5, d6, d7, d8);
3245 /***********************************************************************
3246 * CreateCacheContainerW (WININET.@)
3248 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3249 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3251 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3252 d1, d2, d3, d4, d5, d6, d7, d8);
3256 /***********************************************************************
3257 * FindFirstUrlCacheContainerA (WININET.@)
3259 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3261 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3265 /***********************************************************************
3266 * FindFirstUrlCacheContainerW (WININET.@)
3268 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3270 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3274 /***********************************************************************
3275 * FindNextUrlCacheContainerA (WININET.@)
3277 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3279 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3283 /***********************************************************************
3284 * FindNextUrlCacheContainerW (WININET.@)
3286 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3288 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3292 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3293 LPCSTR lpszUrlSearchPattern,
3297 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3298 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3300 LPDWORD pcbReserved2,
3304 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3305 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3306 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3307 SetLastError(ERROR_FILE_NOT_FOUND);
3311 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3312 LPCWSTR lpszUrlSearchPattern,
3316 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3317 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3319 LPDWORD pcbReserved2,
3323 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3324 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3325 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3326 SetLastError(ERROR_FILE_NOT_FOUND);
3330 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
3332 typedef struct URLCacheFindEntryHandle
3335 LPWSTR lpszUrlSearchPattern;
3336 DWORD dwContainerIndex;
3337 DWORD dwHashTableIndex;
3338 DWORD dwHashEntryIndex;
3339 } URLCacheFindEntryHandle;
3341 /***********************************************************************
3342 * FindFirstUrlCacheEntryA (WININET.@)
3345 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3346 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3348 URLCacheFindEntryHandle *pEntryHandle;
3350 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3352 pEntryHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(*pEntryHandle));
3356 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3357 if (lpszUrlSearchPattern)
3359 pEntryHandle->lpszUrlSearchPattern = heap_strdupAtoW(lpszUrlSearchPattern);
3360 if (!pEntryHandle->lpszUrlSearchPattern)
3362 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3367 pEntryHandle->lpszUrlSearchPattern = NULL;
3368 pEntryHandle->dwContainerIndex = 0;
3369 pEntryHandle->dwHashTableIndex = 0;
3370 pEntryHandle->dwHashEntryIndex = 0;
3372 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3374 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3377 return pEntryHandle;
3380 /***********************************************************************
3381 * FindFirstUrlCacheEntryW (WININET.@)
3384 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3385 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3387 URLCacheFindEntryHandle *pEntryHandle;
3389 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3391 pEntryHandle = HeapAlloc(GetProcessHeap(), 0, sizeof(*pEntryHandle));
3395 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3396 if (lpszUrlSearchPattern)
3398 pEntryHandle->lpszUrlSearchPattern = heap_strdupW(lpszUrlSearchPattern);
3399 if (!pEntryHandle->lpszUrlSearchPattern)
3401 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3406 pEntryHandle->lpszUrlSearchPattern = NULL;
3407 pEntryHandle->dwContainerIndex = 0;
3408 pEntryHandle->dwHashTableIndex = 0;
3409 pEntryHandle->dwHashEntryIndex = 0;
3411 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3413 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3416 return pEntryHandle;
3419 static BOOL FindNextUrlCacheEntryInternal(
3421 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3422 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3425 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3426 URLCACHECONTAINER * pContainer;
3428 if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3430 SetLastError(ERROR_INVALID_HANDLE);
3434 for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3435 pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3437 LPURLCACHE_HEADER pHeader;
3438 HASH_CACHEFILE_ENTRY *pHashTableEntry;
3441 error = URLCacheContainer_OpenIndex(pContainer);
3442 if (error != ERROR_SUCCESS)
3444 SetLastError(error);
3448 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3451 for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3452 pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3454 const struct _HASH_ENTRY *pHashEntry = NULL;
3455 for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3456 pEntryHandle->dwHashEntryIndex++)
3458 const URL_CACHEFILE_ENTRY *pUrlEntry;
3459 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3461 if (pEntry->dwSignature != URL_SIGNATURE)
3464 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3465 TRACE("Found URL: %s\n",
3466 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
3467 TRACE("Header info: %s\n",
3468 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
3470 error = URLCache_CopyEntry(
3473 lpNextCacheEntryInfo,
3474 lpdwNextCacheEntryInfoBufferSize,
3477 if (error != ERROR_SUCCESS)
3479 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3480 SetLastError(error);
3483 TRACE("Local File Name: %s\n",
3484 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
3486 /* increment the current index so that next time the function
3487 * is called the next entry is returned */
3488 pEntryHandle->dwHashEntryIndex++;
3489 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3494 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3497 SetLastError(ERROR_NO_MORE_ITEMS);
3501 /***********************************************************************
3502 * FindNextUrlCacheEntryA (WININET.@)
3504 BOOL WINAPI FindNextUrlCacheEntryA(
3506 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3507 LPDWORD lpdwNextCacheEntryInfoBufferSize)
3509 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3511 return FindNextUrlCacheEntryInternal(hEnumHandle, lpNextCacheEntryInfo,
3512 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
3515 /***********************************************************************
3516 * FindNextUrlCacheEntryW (WININET.@)
3518 BOOL WINAPI FindNextUrlCacheEntryW(
3520 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3521 LPDWORD lpdwNextCacheEntryInfoBufferSize
3524 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3526 return FindNextUrlCacheEntryInternal(hEnumHandle,
3527 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
3528 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
3531 /***********************************************************************
3532 * FindCloseUrlCache (WININET.@)
3534 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3536 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3538 TRACE("(%p)\n", hEnumHandle);
3540 if (!pEntryHandle || pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3542 SetLastError(ERROR_INVALID_HANDLE);
3546 pEntryHandle->dwMagic = 0;
3547 HeapFree(GetProcessHeap(), 0, pEntryHandle->lpszUrlSearchPattern);
3548 HeapFree(GetProcessHeap(), 0, pEntryHandle);
3553 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3554 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3556 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3557 dwSearchCondition, lpGroupId, lpReserved);
3561 BOOL WINAPI FindNextUrlCacheEntryExA(
3563 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3564 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3566 LPDWORD pcbReserved2,
3570 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3571 lpReserved, pcbReserved2, lpReserved3);
3575 BOOL WINAPI FindNextUrlCacheEntryExW(
3577 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3578 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3580 LPDWORD pcbReserved2,
3584 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3585 lpReserved, pcbReserved2, lpReserved3);
3589 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3591 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3595 /***********************************************************************
3596 * CreateUrlCacheGroup (WININET.@)
3599 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3601 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3605 /***********************************************************************
3606 * DeleteUrlCacheGroup (WININET.@)
3609 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3611 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3612 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3616 /***********************************************************************
3617 * SetUrlCacheEntryGroupA (WININET.@)
3620 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3621 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3624 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3625 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3626 pbGroupAttributes, cbGroupAttributes, lpReserved);
3627 SetLastError(ERROR_FILE_NOT_FOUND);
3631 /***********************************************************************
3632 * SetUrlCacheEntryGroupW (WININET.@)
3635 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3636 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3639 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3640 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3641 pbGroupAttributes, cbGroupAttributes, lpReserved);
3642 SetLastError(ERROR_FILE_NOT_FOUND);
3646 /***********************************************************************
3647 * GetUrlCacheConfigInfoW (WININET.@)
3649 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3651 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3652 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3656 /***********************************************************************
3657 * GetUrlCacheConfigInfoA (WININET.@)
3659 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3661 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3662 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3666 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3667 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3668 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3670 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3671 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3672 lpdwGroupInfo, lpReserved);
3676 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3677 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3678 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3680 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3681 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3682 lpdwGroupInfo, lpReserved);
3686 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3687 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3689 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3690 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3694 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3695 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3697 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3698 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3702 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3704 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3708 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3710 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3714 /***********************************************************************
3715 * DeleteIE3Cache (WININET.@)
3717 * Deletes the files used by the IE3 URL caching system.
3720 * hWnd [I] A dummy window.
3721 * hInst [I] Instance of process calling the function.
3722 * lpszCmdLine [I] Options used by function.
3723 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3725 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3727 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3731 static BOOL IsUrlCacheEntryExpiredInternal(const URL_CACHEFILE_ENTRY *pUrlEntry,
3732 FILETIME *pftLastModified)
3735 FILETIME now, expired;
3737 *pftLastModified = pUrlEntry->LastModifiedTime;
3738 GetSystemTimeAsFileTime(&now);
3739 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate,
3740 pUrlEntry->wExpiredTime, &expired);
3741 /* If the expired time is 0, it's interpreted as not expired */
3742 if (!expired.dwLowDateTime && !expired.dwHighDateTime)
3745 ret = CompareFileTime(&expired, &now) < 0;
3749 /***********************************************************************
3750 * IsUrlCacheEntryExpiredA (WININET.@)
3754 * dwFlags [I] Unknown
3755 * pftLastModified [O] Last modified time
3757 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3759 LPURLCACHE_HEADER pHeader;
3760 struct _HASH_ENTRY * pHashEntry;
3761 const CACHEFILE_ENTRY * pEntry;
3762 const URL_CACHEFILE_ENTRY * pUrlEntry;
3763 URLCACHECONTAINER * pContainer;
3766 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3768 if (!url || !pftLastModified)
3771 FIXME("unknown flags 0x%08x\n", dwFlags);
3773 /* Any error implies that the URL is expired, i.e. not in the cache */
3774 if (URLCacheContainers_FindContainerA(url, &pContainer))
3776 memset(pftLastModified, 0, sizeof(*pftLastModified));
3780 if (URLCacheContainer_OpenIndex(pContainer))
3782 memset(pftLastModified, 0, sizeof(*pftLastModified));
3786 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3788 memset(pftLastModified, 0, sizeof(*pftLastModified));
3792 if (!URLCache_FindHash(pHeader, url, &pHashEntry))
3794 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3795 memset(pftLastModified, 0, sizeof(*pftLastModified));
3796 TRACE("entry %s not found!\n", url);
3800 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3801 if (pEntry->dwSignature != URL_SIGNATURE)
3803 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3804 memset(pftLastModified, 0, sizeof(*pftLastModified));
3805 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3809 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3810 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
3812 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3817 /***********************************************************************
3818 * IsUrlCacheEntryExpiredW (WININET.@)
3822 * dwFlags [I] Unknown
3823 * pftLastModified [O] Last modified time
3825 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3827 LPURLCACHE_HEADER pHeader;
3828 struct _HASH_ENTRY * pHashEntry;
3829 const CACHEFILE_ENTRY * pEntry;
3830 const URL_CACHEFILE_ENTRY * pUrlEntry;
3831 URLCACHECONTAINER * pContainer;
3834 TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
3836 if (!url || !pftLastModified)
3839 FIXME("unknown flags 0x%08x\n", dwFlags);
3841 /* Any error implies that the URL is expired, i.e. not in the cache */
3842 if (URLCacheContainers_FindContainerW(url, &pContainer))
3844 memset(pftLastModified, 0, sizeof(*pftLastModified));
3848 if (URLCacheContainer_OpenIndex(pContainer))
3850 memset(pftLastModified, 0, sizeof(*pftLastModified));
3854 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3856 memset(pftLastModified, 0, sizeof(*pftLastModified));
3860 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3862 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3863 memset(pftLastModified, 0, sizeof(*pftLastModified));
3864 TRACE("entry %s not found!\n", debugstr_w(url));
3868 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3870 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3871 memset(pftLastModified, 0, sizeof(*pftLastModified));
3872 TRACE("entry %s not found!\n", debugstr_w(url));
3876 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3877 if (pEntry->dwSignature != URL_SIGNATURE)
3879 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3880 memset(pftLastModified, 0, sizeof(*pftLastModified));
3881 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3885 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3886 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
3888 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3893 /***********************************************************************
3894 * GetDiskInfoA (WININET.@)
3896 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
3899 ULARGE_INTEGER bytes_free, bytes_total;
3901 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
3905 SetLastError(ERROR_INVALID_PARAMETER);
3909 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
3911 if (cluster_size) *cluster_size = 1;
3912 if (free) *free = bytes_free.QuadPart;
3913 if (total) *total = bytes_total.QuadPart;
3918 /***********************************************************************
3919 * RegisterUrlCacheNotification (WININET.@)
3921 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
3923 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
3927 /***********************************************************************
3928 * IncrementUrlCacheHeaderData (WININET.@)
3930 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
3932 FIXME("(%u, %p)\n", index, data);