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_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
68 #define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
69 #define ALLOCATION_TABLE_OFFSET 0x250
70 #define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
71 #define MIN_BLOCK_NO 0x80
72 #define MAX_BLOCK_NO (ALLOCATION_TABLE_SIZE * 8)
73 #define FILE_SIZE(blocks) ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
75 #define HASHTABLE_URL 0
76 #define HASHTABLE_DEL 1
77 #define HASHTABLE_LOCK 2
78 #define HASHTABLE_FREE 3
79 #define HASHTABLE_REDR 5
80 #define HASHTABLE_FLAG_BITS 5
82 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
83 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
84 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
85 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
86 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
88 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
90 typedef struct _CACHEFILE_ENTRY
94 DWORD dwSignature; /* e.g. "URL " */
95 /* CHAR szSignature[4];
97 DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
100 typedef struct _URL_CACHEFILE_ENTRY
102 CACHEFILE_ENTRY CacheFileEntry;
103 FILETIME LastModifiedTime;
104 FILETIME LastAccessTime;
105 WORD wExpiredDate; /* expire date in dos format */
106 WORD wExpiredTime; /* expire time in dos format */
107 DWORD dwUnknown1; /* usually zero */
108 ULARGE_INTEGER size; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
109 DWORD dwUnknown2; /* usually zero */
110 DWORD dwExemptDelta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
111 DWORD dwUnknown3; /* usually 0x60 */
112 DWORD dwOffsetUrl; /* offset of start of url from start of entry */
113 BYTE CacheDir; /* index of cache directory this url is stored in */
114 BYTE Unknown4; /* usually zero */
115 WORD wUnknown5; /* usually 0x1010 */
116 DWORD dwOffsetLocalName; /* offset of start of local filename from start of entry */
117 DWORD CacheEntryType; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
118 DWORD dwOffsetHeaderInfo; /* offset of start of header info from start of entry */
119 DWORD dwHeaderInfoSize;
120 DWORD dwOffsetFileExtension; /* offset of start of file extension from start of entry */
121 WORD wLastSyncDate; /* last sync date in dos format */
122 WORD wLastSyncTime; /* last sync time in dos format */
123 DWORD dwHitRate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
124 DWORD dwUseCount; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
125 WORD wUnknownDate; /* usually same as wLastSyncDate */
126 WORD wUnknownTime; /* usually same as wLastSyncTime */
127 DWORD dwUnknown7; /* usually zero */
128 DWORD dwUnknown8; /* usually zero */
129 /* packing to dword align start of next field */
130 /* CHAR szSourceUrlName[]; (url) */
131 /* packing to dword align start of next field */
132 /* CHAR szLocalFileName[]; (local file name excluding path) */
133 /* packing to dword align start of next field */
134 /* CHAR szHeaderInfo[]; (header info) */
135 } URL_CACHEFILE_ENTRY;
143 typedef struct _HASH_CACHEFILE_ENTRY
145 CACHEFILE_ENTRY CacheFileEntry;
147 DWORD dwHashTableNumber;
148 struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
149 } HASH_CACHEFILE_ENTRY;
151 typedef struct _DIRECTORY_DATA
154 char filename[DIR_LENGTH];
157 typedef struct _URLCACHE_HEADER
159 char szSignature[28];
161 DWORD dwOffsetFirstHashTable;
162 DWORD dwIndexCapacityInBlocks;
165 ULARGE_INTEGER CacheLimit;
166 ULARGE_INTEGER CacheUsage;
167 ULARGE_INTEGER ExemptUsage;
168 DWORD DirectoryCount; /* number of directory_data's */
169 DIRECTORY_DATA directory_data[1]; /* first directory entry */
170 } URLCACHE_HEADER, *LPURLCACHE_HEADER;
171 typedef const URLCACHE_HEADER *LPCURLCACHE_HEADER;
173 typedef struct _STREAM_HANDLE
179 typedef struct _URLCACHECONTAINER
181 struct list entry; /* part of a list */
182 LPWSTR cache_prefix; /* string that has to be prefixed for this container to be used */
183 LPWSTR path; /* path to url container directory */
184 HANDLE hMapping; /* handle of file mapping */
185 DWORD file_size; /* size of file when mapping was opened */
186 HANDLE hMutex; /* handle of mutex */
190 /* List of all containers available */
191 static struct list UrlContainers = LIST_INIT(UrlContainers);
193 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash);
195 /***********************************************************************
196 * URLCache_PathToObjectName (Internal)
198 * Converts a path to a name suitable for use as a Win32 object name.
199 * Replaces '\\' characters in-place with the specified character
200 * (usually '_' or '!')
206 static void URLCache_PathToObjectName(LPWSTR lpszPath, WCHAR replace)
208 for (; *lpszPath; lpszPath++)
210 if (*lpszPath == '\\')
215 /***********************************************************************
216 * URLCacheContainer_OpenIndex (Internal)
218 * Opens the index file and saves mapping handle in hCacheIndexMapping
221 * ERROR_SUCCESS if succeeded
222 * Any other Win32 error code if failed
225 static DWORD URLCacheContainer_OpenIndex(URLCACHECONTAINER * pContainer, DWORD blocks_no)
228 WCHAR wszFilePath[MAX_PATH];
229 DWORD dwFileSize, new_file_size;
231 static const WCHAR wszIndex[] = {'i','n','d','e','x','.','d','a','t',0};
232 static const WCHAR wszMappingFormat[] = {'%','s','%','s','_','%','l','u',0};
234 WaitForSingleObject(pContainer->hMutex, INFINITE);
236 if (pContainer->hMapping) {
237 ReleaseMutex(pContainer->hMutex);
238 return ERROR_SUCCESS;
241 strcpyW(wszFilePath, pContainer->path);
242 strcatW(wszFilePath, wszIndex);
244 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
245 if (hFile == INVALID_HANDLE_VALUE)
247 /* Maybe the directory wasn't there? Try to create it */
248 if (CreateDirectoryW(pContainer->path, 0))
249 hFile = CreateFileW(wszFilePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
251 if (hFile == INVALID_HANDLE_VALUE)
253 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(wszFilePath));
254 ReleaseMutex(pContainer->hMutex);
255 return GetLastError();
258 dwFileSize = GetFileSize(hFile, NULL);
259 if (dwFileSize == INVALID_FILE_SIZE)
261 ReleaseMutex(pContainer->hMutex);
262 return GetLastError();
265 if (blocks_no < MIN_BLOCK_NO)
266 blocks_no = MIN_BLOCK_NO;
267 else if (blocks_no > MAX_BLOCK_NO)
268 blocks_no = MAX_BLOCK_NO;
269 new_file_size = FILE_SIZE(blocks_no);
271 if (dwFileSize < new_file_size)
273 static const CHAR szCacheContent[] = "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Cache\\Content";
275 char achZeroes[0x1000];
277 DWORD dwError = ERROR_SUCCESS;
279 if (SetFilePointer(hFile, 0, NULL, FILE_END) == INVALID_SET_FILE_POINTER)
280 dwError = GetLastError();
282 /* Write zeroes to the entire file so we can safely map it without
283 * fear of getting a SEGV because the disk is full.
285 memset(achZeroes, 0, sizeof(achZeroes));
286 for (dwOffset = dwFileSize; dwOffset<new_file_size && dwError==ERROR_SUCCESS;
287 dwOffset += sizeof(achZeroes))
289 DWORD dwWrite = sizeof(achZeroes);
292 if (new_file_size - dwOffset < dwWrite)
293 dwWrite = new_file_size - dwOffset;
294 if (!WriteFile(hFile, achZeroes, dwWrite, &dwWritten, 0) ||
295 dwWritten != dwWrite)
297 /* If we fail to write, we need to return the error that
298 * cause the problem and also make sure the file is no
299 * longer there, if possible.
301 dwError = GetLastError();
305 if (dwError == ERROR_SUCCESS)
307 HANDLE hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
311 URLCACHE_HEADER *pHeader = MapViewOfFile(hMapping, FILE_MAP_WRITE, 0, 0, 0);
313 if (pHeader && dwFileSize)
315 pHeader->dwFileSize = new_file_size;
316 pHeader->dwIndexCapacityInBlocks = blocks_no;
321 WCHAR wszDirPath[MAX_PATH];
324 HASH_CACHEFILE_ENTRY *pHashEntry;
326 /* First set some constants and defaults in the header */
327 strcpy(pHeader->szSignature, "WINE URLCache Ver 0.2005001");
328 pHeader->dwFileSize = new_file_size;
329 pHeader->dwIndexCapacityInBlocks = blocks_no;
330 /* 127MB - taken from default for Windows 2000 */
331 pHeader->CacheLimit.QuadPart = 0x07ff5400;
332 /* Copied from a Windows 2000 cache index */
333 pHeader->DirectoryCount = 4;
335 /* If the registry has a cache size set, use the registry value */
336 if (RegOpenKeyA(HKEY_CURRENT_USER, szCacheContent, &key) == ERROR_SUCCESS)
339 DWORD len = sizeof(dw);
342 if (RegQueryValueExA(key, "CacheLimit", NULL, &keytype,
343 (BYTE *) &dw, &len) == ERROR_SUCCESS &&
344 keytype == REG_DWORD)
346 pHeader->CacheLimit.QuadPart = (ULONGLONG)dw * 1024;
351 URLCache_CreateHashTable(pHeader, NULL, &pHashEntry);
353 /* Last step - create the directories */
355 strcpyW(wszDirPath, pContainer->path);
356 pwchDir = wszDirPath + strlenW(wszDirPath);
359 GetSystemTimeAsFileTime(&ft);
361 for (i = 0; !dwError && i < pHeader->DirectoryCount; ++i)
363 pHeader->directory_data[i].dwNumFiles = 0;
367 ULONGLONG n = ft.dwHighDateTime;
369 /* Generate a file name to attempt to create.
370 * This algorithm will create what will appear
371 * to be random and unrelated directory names
372 * of up to 9 characters in length.
375 n += ft.dwLowDateTime;
376 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
378 for (k = 0; k < 8; ++k)
382 /* Dividing by a prime greater than 36 helps
383 * with the appearance of randomness
388 pwchDir[k] = '0' + r;
390 pwchDir[k] = 'A' + (r - 10);
393 if (CreateDirectoryW(wszDirPath, 0))
395 /* The following is OK because we generated an
396 * 8 character directory name made from characters
397 * [A-Z0-9], which are equivalent for all code
398 * pages and for UTF-16
400 for (k = 0; k < 8; ++k)
401 pHeader->directory_data[i].filename[k] = pwchDir[k];
406 /* Give up. The most likely cause of this
407 * is a full disk, but whatever the cause
408 * is, it should be more than apparent that
411 dwError = GetLastError();
417 UnmapViewOfFile(pHeader);
421 dwError = GetLastError();
423 dwFileSize = new_file_size;
424 CloseHandle(hMapping);
428 dwError = GetLastError();
435 DeleteFileW(wszFilePath);
436 ReleaseMutex(pContainer->hMutex);
442 pContainer->file_size = dwFileSize;
443 wsprintfW(wszFilePath, wszMappingFormat, pContainer->path, wszIndex, dwFileSize);
444 URLCache_PathToObjectName(wszFilePath, '_');
445 pContainer->hMapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, wszFilePath);
446 if (!pContainer->hMapping)
447 pContainer->hMapping = CreateFileMappingW(hFile, NULL, PAGE_READWRITE, 0, 0, wszFilePath);
449 if (!pContainer->hMapping)
451 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
452 ReleaseMutex(pContainer->hMutex);
453 return GetLastError();
456 ReleaseMutex(pContainer->hMutex);
458 return ERROR_SUCCESS;
461 /***********************************************************************
462 * URLCacheContainer_CloseIndex (Internal)
470 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
472 CloseHandle(pContainer->hMapping);
473 pContainer->hMapping = NULL;
476 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix, LPCWSTR path, LPWSTR mutex_name)
478 URLCACHECONTAINER * pContainer = heap_alloc(sizeof(URLCACHECONTAINER));
479 int cache_prefix_len = strlenW(cache_prefix);
486 pContainer->hMapping = NULL;
487 pContainer->file_size = 0;
489 pContainer->path = heap_strdupW(path);
490 if (!pContainer->path)
492 heap_free(pContainer);
496 pContainer->cache_prefix = heap_alloc((cache_prefix_len + 1) * sizeof(WCHAR));
497 if (!pContainer->cache_prefix)
499 heap_free(pContainer->path);
500 heap_free(pContainer);
504 memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
506 CharLowerW(mutex_name);
507 URLCache_PathToObjectName(mutex_name, '!');
509 if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
511 ERR("couldn't create mutex (error is %d)\n", GetLastError());
512 heap_free(pContainer->path);
513 heap_free(pContainer);
517 list_add_head(&UrlContainers, &pContainer->entry);
522 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
524 list_remove(&pContainer->entry);
526 URLCacheContainer_CloseIndex(pContainer);
527 CloseHandle(pContainer->hMutex);
528 heap_free(pContainer->path);
529 heap_free(pContainer->cache_prefix);
530 heap_free(pContainer);
533 void URLCacheContainers_CreateDefaults(void)
535 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
536 static const WCHAR UrlPrefix[] = {0};
537 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
538 static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
539 static const WCHAR CookieSuffix[] = {0};
540 static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
543 int nFolder; /* CSIDL_* constant */
544 const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
545 const WCHAR * cache_prefix; /* prefix used to reference the container */
546 } DefaultContainerData[] =
548 { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix },
549 { CSIDL_HISTORY, HistorySuffix, HistoryPrefix },
550 { CSIDL_COOKIES, CookieSuffix, CookiePrefix },
554 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
556 WCHAR wszCachePath[MAX_PATH];
557 WCHAR wszMutexName[MAX_PATH];
558 int path_len, suffix_len;
560 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
562 ERR("Couldn't get path for default container %u\n", i);
565 path_len = strlenW(wszCachePath);
566 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
568 if (path_len + suffix_len + 2 > MAX_PATH)
570 ERR("Path too long\n");
574 wszCachePath[path_len] = '\\';
575 wszCachePath[path_len+1] = 0;
577 strcpyW(wszMutexName, wszCachePath);
581 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
582 wszCachePath[path_len + suffix_len + 1] = '\\';
583 wszCachePath[path_len + suffix_len + 2] = '\0';
586 URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath, wszMutexName);
590 void URLCacheContainers_DeleteAll(void)
592 while(!list_empty(&UrlContainers))
593 URLCacheContainer_DeleteContainer(
594 LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
598 static DWORD URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
600 URLCACHECONTAINER * pContainer;
602 TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
605 return ERROR_INVALID_PARAMETER;
607 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
609 int prefix_len = strlenW(pContainer->cache_prefix);
610 if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
612 TRACE("found container with prefix %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
613 *ppContainer = pContainer;
614 return ERROR_SUCCESS;
617 ERR("no container found\n");
618 return ERROR_FILE_NOT_FOUND;
621 static DWORD URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
626 if (lpszUrl && !(url = heap_strdupAtoW(lpszUrl)))
627 return ERROR_OUTOFMEMORY;
629 ret = URLCacheContainers_FindContainerW(url, ppContainer);
634 static BOOL URLCacheContainers_Enum(LPCWSTR lpwszSearchPattern, DWORD dwIndex, URLCACHECONTAINER ** ppContainer)
637 URLCACHECONTAINER * pContainer;
639 TRACE("searching for prefix: %s\n", debugstr_w(lpwszSearchPattern));
641 /* non-NULL search pattern only returns one container ever */
642 if (lpwszSearchPattern && dwIndex > 0)
645 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
647 if (lpwszSearchPattern)
649 if (!strcmpW(pContainer->cache_prefix, lpwszSearchPattern))
651 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
652 *ppContainer = pContainer;
660 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
661 *ppContainer = pContainer;
670 /***********************************************************************
671 * URLCacheContainer_LockIndex (Internal)
673 * Locks the index for system-wide exclusive access.
676 * Cache file header if successful
677 * NULL if failed and calls SetLastError.
679 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
683 URLCACHE_HEADER * pHeader;
687 WaitForSingleObject(pContainer->hMutex, INFINITE);
689 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
693 ReleaseMutex(pContainer->hMutex);
694 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
697 pHeader = (URLCACHE_HEADER *)pIndexData;
699 /* file has grown - we need to remap to prevent us getting
700 * access violations when we try and access beyond the end
701 * of the memory mapped file */
702 if (pHeader->dwFileSize != pContainer->file_size)
704 UnmapViewOfFile( pHeader );
705 URLCacheContainer_CloseIndex(pContainer);
706 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
707 if (error != ERROR_SUCCESS)
709 ReleaseMutex(pContainer->hMutex);
713 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
717 ReleaseMutex(pContainer->hMutex);
718 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
721 pHeader = (URLCACHE_HEADER *)pIndexData;
724 TRACE("Signature: %s, file size: %d bytes\n", pHeader->szSignature, pHeader->dwFileSize);
726 for (index = 0; index < pHeader->DirectoryCount; index++)
728 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
734 /***********************************************************************
735 * URLCacheContainer_UnlockIndex (Internal)
738 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
741 ReleaseMutex(pContainer->hMutex);
742 return UnmapViewOfFile(pHeader);
745 /***********************************************************************
746 * URLCacheContainer_CleanIndex (Internal)
748 * This function is meant to make place in index file by removing old
749 * entries and resizing the file.
751 * CAUTION: file view may get mapped to new memory
752 * TODO: implement entries cleaning
755 * ERROR_SUCCESS when new memory is available
756 * error code otherwise
758 static DWORD URLCacheContainer_CleanIndex(URLCACHECONTAINER *container, URLCACHE_HEADER **file_view)
760 URLCACHE_HEADER *header = *file_view;
763 FIXME("(%s %s) semi-stub\n", debugstr_w(container->cache_prefix), debugstr_w(container->path));
765 if(header->dwFileSize >= ALLOCATION_TABLE_SIZE*8*BLOCKSIZE + ENTRY_START_OFFSET) {
766 WARN("index file has maximal size\n");
767 return ERROR_NOT_ENOUGH_MEMORY;
770 URLCacheContainer_CloseIndex(container);
771 ret = URLCacheContainer_OpenIndex(container, header->dwIndexCapacityInBlocks*2);
772 if(ret != ERROR_SUCCESS)
774 header = MapViewOfFile(container->hMapping, FILE_MAP_WRITE, 0, 0, 0);
776 return GetLastError();
778 UnmapViewOfFile(*file_view);
780 return ERROR_SUCCESS;
784 #define CHAR_BIT (8 * sizeof(CHAR))
787 /***********************************************************************
788 * URLCache_Allocation_BlockIsFree (Internal)
790 * Is the specified block number free?
797 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
799 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
800 return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
803 /***********************************************************************
804 * URLCache_Allocation_BlockFree (Internal)
806 * Marks the specified block as free
812 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
814 BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
815 AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
818 /***********************************************************************
819 * URLCache_Allocation_BlockAllocate (Internal)
821 * Marks the specified block as allocated
827 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
829 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
830 AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
833 /***********************************************************************
834 * URLCache_FindFirstFreeEntry (Internal)
836 * Finds and allocates the first block of free space big enough and
837 * sets ppEntry to point to it.
840 * ERROR_SUCCESS when free memory block was found
841 * Any other Win32 error code if the entry could not be added
844 static DWORD URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
846 LPBYTE AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
849 for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
851 for (dwFreeCounter = 0;
852 dwFreeCounter < dwBlocksNeeded &&
853 dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
854 URLCache_Allocation_BlockIsFree(AllocationTable, dwBlockNumber + dwFreeCounter);
856 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
858 if (dwFreeCounter == dwBlocksNeeded)
861 TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
862 for (index = 0; index < dwBlocksNeeded; index++)
863 URLCache_Allocation_BlockAllocate(AllocationTable, dwBlockNumber + index);
864 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
865 for (index = 0; index < dwBlocksNeeded * BLOCKSIZE / sizeof(DWORD); index++)
866 ((DWORD*)*ppEntry)[index] = 0xdeadbeef;
867 (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
868 return ERROR_SUCCESS;
872 return ERROR_HANDLE_DISK_FULL;
875 /***********************************************************************
876 * URLCache_DeleteEntry (Internal)
878 * Deletes the specified entry and frees the space allocated to it
881 * TRUE if it succeeded
885 static BOOL URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader, CACHEFILE_ENTRY * pEntry)
889 BYTE * AllocationTable = (LPBYTE)pHeader + ALLOCATION_TABLE_OFFSET;
891 /* update allocation table */
892 dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader) - ENTRY_START_OFFSET) / BLOCKSIZE;
893 for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
894 URLCache_Allocation_BlockFree(AllocationTable, dwBlock);
899 /***********************************************************************
900 * URLCache_LocalFileNameToPathW (Internal)
902 * Copies the full path to the specified buffer given the local file
903 * name and the index of the directory it is in. Always sets value in
904 * lpBufferSize to the required buffer size (in bytes).
907 * TRUE if the buffer was big enough
908 * FALSE if the buffer was too small
911 static BOOL URLCache_LocalFileNameToPathW(
912 const URLCACHECONTAINER * pContainer,
913 LPCURLCACHE_HEADER pHeader,
914 LPCSTR szLocalFileName,
920 int path_len = strlenW(pContainer->path);
921 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
922 if (Directory >= pHeader->DirectoryCount)
928 nRequired = (path_len + DIR_LENGTH + file_name_len + 1) * sizeof(WCHAR);
929 if (nRequired <= *lpBufferSize)
933 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
934 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
935 wszPath[dir_len + path_len] = '\\';
936 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len + 1, file_name_len);
937 *lpBufferSize = nRequired;
940 *lpBufferSize = nRequired;
944 /***********************************************************************
945 * URLCache_LocalFileNameToPathA (Internal)
947 * Copies the full path to the specified buffer given the local file
948 * name and the index of the directory it is in. Always sets value in
949 * lpBufferSize to the required buffer size.
952 * TRUE if the buffer was big enough
953 * FALSE if the buffer was too small
956 static BOOL URLCache_LocalFileNameToPathA(
957 const URLCACHECONTAINER * pContainer,
958 LPCURLCACHE_HEADER pHeader,
959 LPCSTR szLocalFileName,
965 int path_len, file_name_len, dir_len;
967 if (Directory >= pHeader->DirectoryCount)
973 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
974 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
975 dir_len = DIR_LENGTH;
977 nRequired = (path_len + dir_len + 1 + file_name_len) * sizeof(char);
978 if (nRequired < *lpBufferSize)
980 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
981 memcpy(szPath+path_len, pHeader->directory_data[Directory].filename, dir_len);
982 szPath[path_len + dir_len] = '\\';
983 memcpy(szPath + path_len + dir_len + 1, szLocalFileName, file_name_len);
984 *lpBufferSize = nRequired;
987 *lpBufferSize = nRequired;
991 /* Just like DosDateTimeToFileTime, except that it also maps the special
992 * case of a DOS date/time of (0,0) to a filetime of (0,0).
994 static void URLCache_DosDateTimeToFileTime(WORD fatdate, WORD fattime,
997 if (!fatdate && !fattime)
998 ft->dwLowDateTime = ft->dwHighDateTime = 0;
1000 DosDateTimeToFileTime(fatdate, fattime, ft);
1003 /***********************************************************************
1004 * URLCache_CopyEntry (Internal)
1006 * Copies an entry from the cache index file to the Win32 structure
1009 * ERROR_SUCCESS if the buffer was big enough
1010 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1013 static DWORD URLCache_CopyEntry(
1014 URLCACHECONTAINER * pContainer,
1015 LPCURLCACHE_HEADER pHeader,
1016 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1017 LPDWORD lpdwBufferSize,
1018 const URL_CACHEFILE_ENTRY * pUrlEntry,
1022 DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
1024 if (*lpdwBufferSize >= dwRequiredSize)
1026 lpCacheEntryInfo->lpHeaderInfo = NULL;
1027 lpCacheEntryInfo->lpszFileExtension = NULL;
1028 lpCacheEntryInfo->lpszLocalFileName = NULL;
1029 lpCacheEntryInfo->lpszSourceUrlName = NULL;
1030 lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
1031 lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
1032 lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
1033 lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
1034 lpCacheEntryInfo->dwSizeHigh = pUrlEntry->size.u.HighPart;
1035 lpCacheEntryInfo->dwSizeLow = pUrlEntry->size.u.LowPart;
1036 lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
1037 lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
1038 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
1039 lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
1040 lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
1041 lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
1042 lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
1043 URLCache_DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
1046 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1047 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1048 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1050 lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, NULL, 0);
1052 lenUrl = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1053 dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1055 /* FIXME: is source url optional? */
1056 if (*lpdwBufferSize >= dwRequiredSize)
1058 DWORD lenUrlBytes = (lenUrl+1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1060 lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrlBytes;
1062 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
1064 memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lenUrlBytes);
1067 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1068 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1069 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1071 if (pUrlEntry->dwOffsetLocalName)
1073 LONG nLocalFilePathSize;
1074 LPSTR lpszLocalFileName;
1075 lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
1076 nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
1077 if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
1078 (!bUnicode && URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize)))
1080 lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
1082 dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
1084 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1085 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1086 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1088 dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
1090 if (*lpdwBufferSize >= dwRequiredSize)
1092 lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
1093 memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
1094 ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
1096 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1097 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1098 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1100 if (pUrlEntry->dwOffsetFileExtension)
1105 lenExtension = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, NULL, 0);
1107 lenExtension = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension) + 1;
1108 dwRequiredSize += lenExtension * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1110 if (*lpdwBufferSize >= dwRequiredSize)
1112 lpCacheEntryInfo->lpszFileExtension = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenExtension;
1114 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenExtension);
1116 memcpy(lpCacheEntryInfo->lpszFileExtension, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, lenExtension * sizeof(CHAR));
1119 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1120 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1121 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1124 if (dwRequiredSize > *lpdwBufferSize)
1126 *lpdwBufferSize = dwRequiredSize;
1127 return ERROR_INSUFFICIENT_BUFFER;
1129 *lpdwBufferSize = dwRequiredSize;
1130 return ERROR_SUCCESS;
1133 /* Just like FileTimeToDosDateTime, except that it also maps the special
1134 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1136 static void URLCache_FileTimeToDosDateTime(const FILETIME *ft, WORD *fatdate,
1139 if (!ft->dwLowDateTime && !ft->dwHighDateTime)
1140 *fatdate = *fattime = 0;
1142 FileTimeToDosDateTime(ft, fatdate, fattime);
1145 /***********************************************************************
1146 * URLCache_SetEntryInfo (Internal)
1148 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1149 * according to the flags set by dwFieldControl.
1152 * ERROR_SUCCESS if the buffer was big enough
1153 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1156 static DWORD URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
1158 if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
1159 pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
1160 if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
1161 pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
1162 if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
1163 pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
1164 if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
1165 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
1166 if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
1167 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1168 if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
1169 pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
1170 if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
1171 pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
1172 if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
1173 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1175 return ERROR_SUCCESS;
1178 /***********************************************************************
1179 * URLCache_HashKey (Internal)
1181 * Returns the hash key for a given string
1184 * hash key for the string
1187 static DWORD URLCache_HashKey(LPCSTR lpszKey)
1189 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1190 * but the algorithm and result are not the same!
1192 static const unsigned char lookupTable[256] =
1194 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1195 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1196 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1197 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1198 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1199 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1200 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1201 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1202 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1203 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1204 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1205 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1206 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1207 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1208 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1209 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1210 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1211 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1212 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1213 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1214 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1215 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1216 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1217 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1218 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1219 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1220 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1221 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1222 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1223 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1224 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1225 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1230 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1231 key[i] = lookupTable[(*lpszKey + i) & 0xFF];
1233 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1235 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1236 key[i] = lookupTable[*lpszKey ^ key[i]];
1239 return *(DWORD *)key;
1242 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
1244 return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
1247 static inline BOOL URLCache_IsHashEntryValid(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY *pHashEntry)
1249 /* check pHashEntry located within acceptable bounds in the URL cache mapping */
1250 return ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) >= ENTRY_START_OFFSET) &&
1251 ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) < pHeader->dwFileSize);
1254 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1256 /* structure of hash table:
1257 * 448 entries divided into 64 blocks
1258 * each block therefore contains a chain of 7 key/offset pairs
1259 * how position in table is calculated:
1260 * 1. the url is hashed in helper function
1261 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1262 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1265 * there can be multiple hash tables in the file and the offset to
1266 * the next one is stored in the header of the hash table
1268 DWORD key = URLCache_HashKey(lpszUrl);
1269 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1270 HASH_CACHEFILE_ENTRY * pHashEntry;
1271 DWORD dwHashTableNumber = 0;
1273 key >>= HASHTABLE_FLAG_BITS;
1275 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1276 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1277 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1280 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1282 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1285 /* make sure that it is in fact a hash entry */
1286 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1288 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1292 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1294 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1295 if (key == pHashElement->dwHashKey>>HASHTABLE_FLAG_BITS)
1297 /* FIXME: we should make sure that this is the right element
1298 * before returning and claiming that it is. We can do this
1299 * by doing a simple compare between the URL we were given
1300 * and the URL stored in the entry. However, this assumes
1301 * we know the format of all the entries stored in the
1303 *ppHashEntry = pHashElement;
1311 static BOOL URLCache_FindHashW(LPCURLCACHE_HEADER pHeader, LPCWSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1316 urlA = heap_strdupWtoA(lpszUrl);
1319 SetLastError(ERROR_OUTOFMEMORY);
1323 ret = URLCache_FindHash(pHeader, urlA, ppHashEntry);
1328 /***********************************************************************
1329 * URLCache_HashEntrySetFlags (Internal)
1331 * Sets special bits in hash key
1337 static void URLCache_HashEntrySetFlags(struct _HASH_ENTRY * pHashEntry, DWORD dwFlag)
1339 pHashEntry->dwHashKey = (pHashEntry->dwHashKey >> HASHTABLE_FLAG_BITS << HASHTABLE_FLAG_BITS) | dwFlag;
1342 /***********************************************************************
1343 * URLCache_DeleteEntryFromHash (Internal)
1345 * Searches all the hash tables in the index for the given URL and
1346 * then if found deletes the entry.
1349 * TRUE if the entry was found
1350 * FALSE if the entry could not be found
1353 static BOOL URLCache_DeleteEntryFromHash(struct _HASH_ENTRY * pHashEntry)
1355 pHashEntry->dwHashKey = HASHTABLE_DEL;
1359 /***********************************************************************
1360 * URLCache_AddEntryToHash (Internal)
1362 * Searches all the hash tables for a free slot based on the offset
1363 * generated from the hash key. If a free slot is found, the offset and
1364 * key are entered into the hash table.
1367 * ERROR_SUCCESS if the entry was added
1368 * Any other Win32 error code if the entry could not be added
1371 static DWORD URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry, DWORD dwFieldType)
1373 /* see URLCache_FindEntryInHash for structure of hash tables */
1375 DWORD key = URLCache_HashKey(lpszUrl);
1376 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1377 HASH_CACHEFILE_ENTRY * pHashEntry, *pHashPrev = NULL;
1378 DWORD dwHashTableNumber = 0;
1381 key = ((key >> HASHTABLE_FLAG_BITS) << HASHTABLE_FLAG_BITS) + dwFieldType;
1383 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1384 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1385 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1388 pHashPrev = pHashEntry;
1390 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1392 ERR("not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1395 /* make sure that it is in fact a hash entry */
1396 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1398 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1402 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1404 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1405 if (pHashElement->dwHashKey==HASHTABLE_FREE || pHashElement->dwHashKey==HASHTABLE_DEL) /* if the slot is free */
1407 pHashElement->dwHashKey = key;
1408 pHashElement->dwOffsetEntry = dwOffsetEntry;
1409 return ERROR_SUCCESS;
1413 error = URLCache_CreateHashTable(pHeader, pHashPrev, &pHashEntry);
1414 if (error != ERROR_SUCCESS)
1417 pHashEntry->HashTable[offset].dwHashKey = key;
1418 pHashEntry->HashTable[offset].dwOffsetEntry = dwOffsetEntry;
1419 return ERROR_SUCCESS;
1422 /***********************************************************************
1423 * URLCache_CreateHashTable (Internal)
1425 * Creates a new hash table in free space and adds it to the chain of existing
1429 * ERROR_SUCCESS if the hash table was created
1430 * ERROR_DISK_FULL if the hash table could not be created
1433 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash)
1435 DWORD dwOffset, error;
1438 if ((error = URLCache_FindFirstFreeEntry(pHeader, 0x20, (CACHEFILE_ENTRY **)ppHash)) != ERROR_SUCCESS)
1441 dwOffset = (BYTE *)*ppHash - (BYTE *)pHeader;
1444 pPrevHash->dwAddressNext = dwOffset;
1446 pHeader->dwOffsetFirstHashTable = dwOffset;
1447 (*ppHash)->CacheFileEntry.dwSignature = HASH_SIGNATURE;
1448 (*ppHash)->CacheFileEntry.dwBlocksUsed = 0x20;
1449 (*ppHash)->dwAddressNext = 0;
1450 (*ppHash)->dwHashTableNumber = pPrevHash ? pPrevHash->dwHashTableNumber + 1 : 0;
1451 for (i = 0; i < HASHTABLE_SIZE; i++)
1453 (*ppHash)->HashTable[i].dwOffsetEntry = HASHTABLE_FREE;
1454 (*ppHash)->HashTable[i].dwHashKey = HASHTABLE_FREE;
1456 return ERROR_SUCCESS;
1459 /***********************************************************************
1460 * URLCache_EnumHashTables (Internal)
1462 * Enumerates the hash tables in a container.
1465 * TRUE if an entry was found
1466 * FALSE if there are no more tables to enumerate.
1469 static BOOL URLCache_EnumHashTables(LPCURLCACHE_HEADER pHeader, DWORD *pdwHashTableNumber, HASH_CACHEFILE_ENTRY ** ppHashEntry)
1471 for (*ppHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1472 URLCache_IsHashEntryValid(pHeader, *ppHashEntry);
1473 *ppHashEntry = URLCache_HashEntryFromOffset(pHeader, (*ppHashEntry)->dwAddressNext))
1475 TRACE("looking at hash table number %d\n", (*ppHashEntry)->dwHashTableNumber);
1476 if ((*ppHashEntry)->dwHashTableNumber != *pdwHashTableNumber)
1478 /* make sure that it is in fact a hash entry */
1479 if ((*ppHashEntry)->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1481 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->CacheFileEntry.dwSignature);
1482 (*pdwHashTableNumber)++;
1486 TRACE("hash table number %d found\n", *pdwHashTableNumber);
1492 /***********************************************************************
1493 * URLCache_EnumHashTableEntries (Internal)
1495 * Enumerates entries in a hash table and returns the next non-free entry.
1498 * TRUE if an entry was found
1499 * FALSE if the hash table is empty or there are no more entries to
1503 static BOOL URLCache_EnumHashTableEntries(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY * pHashEntry,
1504 DWORD * index, const struct _HASH_ENTRY ** ppHashEntry)
1506 for (; *index < HASHTABLE_SIZE ; (*index)++)
1508 if (pHashEntry->HashTable[*index].dwHashKey==HASHTABLE_FREE || pHashEntry->HashTable[*index].dwHashKey==HASHTABLE_DEL)
1511 *ppHashEntry = &pHashEntry->HashTable[*index];
1512 TRACE("entry found %d\n", *index);
1515 TRACE("no more entries (%d)\n", *index);
1519 /***********************************************************************
1520 * URLCache_DeleteCacheDirectory (Internal)
1522 * Erase a directory containing an URL cache.
1525 * TRUE success, FALSE failure/aborted.
1528 static BOOL URLCache_DeleteCacheDirectory(LPCWSTR lpszPath)
1531 WCHAR path[MAX_PATH + 1];
1532 SHFILEOPSTRUCTW shfos;
1535 path_len = strlenW(lpszPath);
1536 if (path_len >= MAX_PATH)
1538 strcpyW(path, lpszPath);
1539 path[path_len + 1] = 0; /* double-NUL-terminate path */
1542 shfos.wFunc = FO_DELETE;
1546 shfos.fAnyOperationsAborted = FALSE;
1547 ret = SHFileOperationW(&shfos);
1549 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path), ret);
1550 return !(ret || shfos.fAnyOperationsAborted);
1553 /***********************************************************************
1554 * FreeUrlCacheSpaceW (WININET.@)
1556 * Frees up some cache.
1559 * lpszCachePath [I] Which volume to free up from, or NULL if you don't care.
1560 * dwSize [I] How much space to free up.
1561 * dwSizeType [I] How to interpret dwSize.
1564 * TRUE success. FALSE failure.
1567 * This implementation just retrieves the path of the cache directory, and
1568 * deletes its contents from the filesystem. The correct approach would
1569 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
1571 BOOL WINAPI FreeUrlCacheSpaceW(LPCWSTR lpszCachePath, DWORD dwSize, DWORD dwSizeType)
1573 URLCACHECONTAINER * pContainer;
1575 if (lpszCachePath != NULL || dwSize != 100 || dwSizeType != FCS_PERCENT_CACHE_SPACE)
1577 FIXME("(%s, %x, %x): partial stub!\n", debugstr_w(lpszCachePath), dwSize, dwSizeType);
1578 SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
1582 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
1584 /* The URL cache has prefix L"" (unlike Cookies and History) */
1585 if (pContainer->cache_prefix[0] == 0)
1589 WaitForSingleObject(pContainer->hMutex, INFINITE);
1591 /* unlock, delete, recreate and lock cache */
1592 URLCacheContainer_CloseIndex(pContainer);
1593 ret_del = URLCache_DeleteCacheDirectory(pContainer->path);
1594 ret_open = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1596 ReleaseMutex(pContainer->hMutex);
1597 return ret_del && (ret_open == ERROR_SUCCESS);
1603 /***********************************************************************
1604 * FreeUrlCacheSpaceA (WININET.@)
1606 * See FreeUrlCacheSpaceW.
1608 BOOL WINAPI FreeUrlCacheSpaceA(LPCSTR lpszCachePath, DWORD dwSize, DWORD dwSizeType)
1611 LPWSTR path = heap_strdupAtoW(lpszCachePath);
1612 if (lpszCachePath == NULL || path != NULL)
1613 ret = FreeUrlCacheSpaceW(path, dwSize, dwSizeType);
1618 /***********************************************************************
1619 * GetUrlCacheEntryInfoExA (WININET.@)
1622 BOOL WINAPI GetUrlCacheEntryInfoExA(
1624 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1625 LPDWORD lpdwCacheEntryInfoBufSize,
1627 LPDWORD lpdwReserved,
1631 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1632 debugstr_a(lpszUrl),
1634 lpdwCacheEntryInfoBufSize,
1640 if ((lpszReserved != NULL) ||
1641 (lpdwReserved != NULL) ||
1642 (lpReserved != NULL))
1644 ERR("Reserved value was not 0\n");
1645 SetLastError(ERROR_INVALID_PARAMETER);
1650 FIXME("Undocumented flag(s): %x\n", dwFlags);
1651 SetLastError(ERROR_FILE_NOT_FOUND);
1654 return GetUrlCacheEntryInfoA(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1657 /***********************************************************************
1658 * GetUrlCacheEntryInfoA (WININET.@)
1661 BOOL WINAPI GetUrlCacheEntryInfoA(
1662 IN LPCSTR lpszUrlName,
1663 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1664 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1667 LPURLCACHE_HEADER pHeader;
1668 struct _HASH_ENTRY * pHashEntry;
1669 const CACHEFILE_ENTRY * pEntry;
1670 const URL_CACHEFILE_ENTRY * pUrlEntry;
1671 URLCACHECONTAINER * pContainer;
1674 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1676 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1677 if (error != ERROR_SUCCESS)
1679 SetLastError(error);
1683 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1684 if (error != ERROR_SUCCESS)
1686 SetLastError(error);
1690 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1693 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1695 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1696 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1697 SetLastError(ERROR_FILE_NOT_FOUND);
1701 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1702 if (pEntry->dwSignature != URL_SIGNATURE)
1704 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1705 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1706 SetLastError(ERROR_FILE_NOT_FOUND);
1710 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1711 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1712 if (pUrlEntry->dwOffsetHeaderInfo)
1713 TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1715 if (lpdwCacheEntryInfoBufferSize)
1717 if (!lpCacheEntryInfo)
1718 *lpdwCacheEntryInfoBufferSize = 0;
1720 error = URLCache_CopyEntry(
1724 lpdwCacheEntryInfoBufferSize,
1727 if (error != ERROR_SUCCESS)
1729 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1730 SetLastError(error);
1733 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1736 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1741 /***********************************************************************
1742 * GetUrlCacheEntryInfoW (WININET.@)
1745 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1746 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1747 LPDWORD lpdwCacheEntryInfoBufferSize)
1749 LPURLCACHE_HEADER pHeader;
1750 struct _HASH_ENTRY * pHashEntry;
1751 const CACHEFILE_ENTRY * pEntry;
1752 const URL_CACHEFILE_ENTRY * pUrlEntry;
1753 URLCACHECONTAINER * pContainer;
1756 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, lpdwCacheEntryInfoBufferSize);
1758 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1759 if (error != ERROR_SUCCESS)
1761 SetLastError(error);
1765 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1766 if (error != ERROR_SUCCESS)
1768 SetLastError(error);
1772 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1775 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1777 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1778 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1779 SetLastError(ERROR_FILE_NOT_FOUND);
1783 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1784 if (pEntry->dwSignature != URL_SIGNATURE)
1786 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1787 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1788 SetLastError(ERROR_FILE_NOT_FOUND);
1792 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1793 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1794 TRACE("Header info: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
1796 if (lpdwCacheEntryInfoBufferSize)
1798 if (!lpCacheEntryInfo)
1799 *lpdwCacheEntryInfoBufferSize = 0;
1801 error = URLCache_CopyEntry(
1804 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1805 lpdwCacheEntryInfoBufferSize,
1807 TRUE /* UNICODE */);
1808 if (error != ERROR_SUCCESS)
1810 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1811 SetLastError(error);
1814 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1817 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1822 /***********************************************************************
1823 * GetUrlCacheEntryInfoExW (WININET.@)
1826 BOOL WINAPI GetUrlCacheEntryInfoExW(
1828 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1829 LPDWORD lpdwCacheEntryInfoBufSize,
1830 LPWSTR lpszReserved,
1831 LPDWORD lpdwReserved,
1835 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1836 debugstr_w(lpszUrl),
1838 lpdwCacheEntryInfoBufSize,
1844 if ((lpszReserved != NULL) ||
1845 (lpdwReserved != NULL) ||
1846 (lpReserved != NULL))
1848 ERR("Reserved value was not 0\n");
1849 SetLastError(ERROR_INVALID_PARAMETER);
1854 FIXME("Undocumented flag(s): %x\n", dwFlags);
1855 SetLastError(ERROR_FILE_NOT_FOUND);
1858 return GetUrlCacheEntryInfoW(lpszUrl, lpCacheEntryInfo, lpdwCacheEntryInfoBufSize);
1861 /***********************************************************************
1862 * SetUrlCacheEntryInfoA (WININET.@)
1864 BOOL WINAPI SetUrlCacheEntryInfoA(
1866 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1867 DWORD dwFieldControl)
1869 LPURLCACHE_HEADER pHeader;
1870 struct _HASH_ENTRY * pHashEntry;
1871 CACHEFILE_ENTRY * pEntry;
1872 URLCACHECONTAINER * pContainer;
1875 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1877 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1878 if (error != ERROR_SUCCESS)
1880 SetLastError(error);
1884 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1885 if (error != ERROR_SUCCESS)
1887 SetLastError(error);
1891 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1894 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
1896 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1897 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
1898 SetLastError(ERROR_FILE_NOT_FOUND);
1902 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1903 if (pEntry->dwSignature != URL_SIGNATURE)
1905 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1906 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1907 SetLastError(ERROR_FILE_NOT_FOUND);
1911 URLCache_SetEntryInfo(
1912 (URL_CACHEFILE_ENTRY *)pEntry,
1913 (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
1916 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1921 /***********************************************************************
1922 * SetUrlCacheEntryInfoW (WININET.@)
1924 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
1926 LPURLCACHE_HEADER pHeader;
1927 struct _HASH_ENTRY * pHashEntry;
1928 CACHEFILE_ENTRY * pEntry;
1929 URLCACHECONTAINER * pContainer;
1932 TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
1934 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1935 if (error != ERROR_SUCCESS)
1937 SetLastError(error);
1941 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
1942 if (error != ERROR_SUCCESS)
1944 SetLastError(error);
1948 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1951 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1953 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1954 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1955 SetLastError(ERROR_FILE_NOT_FOUND);
1959 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1960 if (pEntry->dwSignature != URL_SIGNATURE)
1962 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1963 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
1964 SetLastError(ERROR_FILE_NOT_FOUND);
1968 URLCache_SetEntryInfo(
1969 (URL_CACHEFILE_ENTRY *)pEntry,
1973 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1978 /***********************************************************************
1979 * RetrieveUrlCacheEntryFileA (WININET.@)
1982 BOOL WINAPI RetrieveUrlCacheEntryFileA(
1983 IN LPCSTR lpszUrlName,
1984 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1985 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
1989 LPURLCACHE_HEADER pHeader;
1990 struct _HASH_ENTRY * pHashEntry;
1991 CACHEFILE_ENTRY * pEntry;
1992 URL_CACHEFILE_ENTRY * pUrlEntry;
1993 URLCACHECONTAINER * pContainer;
1996 TRACE("(%s, %p, %p, 0x%08x)\n",
1997 debugstr_a(lpszUrlName),
1999 lpdwCacheEntryInfoBufferSize,
2002 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
2003 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
2005 SetLastError(ERROR_INVALID_PARAMETER);
2009 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2010 if (error != ERROR_SUCCESS)
2012 SetLastError(error);
2016 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2017 if (error != ERROR_SUCCESS)
2019 SetLastError(error);
2023 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2026 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2028 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2029 TRACE("entry %s not found!\n", lpszUrlName);
2030 SetLastError(ERROR_FILE_NOT_FOUND);
2034 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2035 if (pEntry->dwSignature != URL_SIGNATURE)
2037 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2038 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2039 SetLastError(ERROR_FILE_NOT_FOUND);
2043 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2044 if (!pUrlEntry->dwOffsetLocalName)
2046 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2047 SetLastError(ERROR_INVALID_DATA);
2051 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
2052 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
2054 error = URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo,
2055 lpdwCacheEntryInfoBufferSize, pUrlEntry,
2057 if (error != ERROR_SUCCESS)
2059 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2060 SetLastError(error);
2063 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2065 pUrlEntry->dwHitRate++;
2066 pUrlEntry->dwUseCount++;
2067 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_LOCK);
2068 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2070 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2075 /***********************************************************************
2076 * RetrieveUrlCacheEntryFileW (WININET.@)
2079 BOOL WINAPI RetrieveUrlCacheEntryFileW(
2080 IN LPCWSTR lpszUrlName,
2081 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2082 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2086 LPURLCACHE_HEADER pHeader;
2087 struct _HASH_ENTRY * pHashEntry;
2088 CACHEFILE_ENTRY * pEntry;
2089 URL_CACHEFILE_ENTRY * pUrlEntry;
2090 URLCACHECONTAINER * pContainer;
2093 TRACE("(%s, %p, %p, 0x%08x)\n",
2094 debugstr_w(lpszUrlName),
2096 lpdwCacheEntryInfoBufferSize,
2099 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
2100 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
2102 SetLastError(ERROR_INVALID_PARAMETER);
2106 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2107 if (error != ERROR_SUCCESS)
2109 SetLastError(error);
2113 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2114 if (error != ERROR_SUCCESS)
2116 SetLastError(error);
2120 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2123 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2125 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2126 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2127 SetLastError(ERROR_FILE_NOT_FOUND);
2131 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2132 if (pEntry->dwSignature != URL_SIGNATURE)
2134 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2135 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2136 SetLastError(ERROR_FILE_NOT_FOUND);
2140 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2141 if (!pUrlEntry->dwOffsetLocalName)
2143 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2144 SetLastError(ERROR_INVALID_DATA);
2148 TRACE("Found URL: %s\n", (LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
2149 TRACE("Header info: %s\n", (LPBYTE)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo);
2151 error = URLCache_CopyEntry(
2154 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
2155 lpdwCacheEntryInfoBufferSize,
2157 TRUE /* UNICODE */);
2158 if (error != ERROR_SUCCESS)
2160 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2161 SetLastError(error);
2164 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2166 pUrlEntry->dwHitRate++;
2167 pUrlEntry->dwUseCount++;
2168 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_LOCK);
2169 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2171 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2176 static BOOL DeleteUrlCacheEntryInternal(LPURLCACHE_HEADER pHeader,
2177 struct _HASH_ENTRY *pHashEntry)
2179 CACHEFILE_ENTRY * pEntry;
2180 URL_CACHEFILE_ENTRY * pUrlEntry;
2182 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2183 if (pEntry->dwSignature != URL_SIGNATURE)
2185 FIXME("Trying to delete entry of unknown format %s\n",
2186 debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
2187 SetLastError(ERROR_FILE_NOT_FOUND);
2191 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2192 if ((pHashEntry->dwHashKey & ((1<<HASHTABLE_FLAG_BITS)-1)) == HASHTABLE_LOCK)
2194 /* FIXME: implement timeout object unlocking */
2195 TRACE("Trying to delete locked entry\n");
2196 pUrlEntry->CacheEntryType |= DELETED_CACHE_ENTRY;
2197 SetLastError(ERROR_SHARING_VIOLATION);
2201 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
2203 if (pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles)
2204 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles--;
2206 if (pUrlEntry->CacheEntryType & STICKY_CACHE_ENTRY)
2208 if (pUrlEntry->size.QuadPart < pHeader->ExemptUsage.QuadPart)
2209 pHeader->ExemptUsage.QuadPart -= pUrlEntry->size.QuadPart;
2211 pHeader->ExemptUsage.QuadPart = 0;
2215 if (pUrlEntry->size.QuadPart < pHeader->CacheUsage.QuadPart)
2216 pHeader->CacheUsage.QuadPart -= pUrlEntry->size.QuadPart;
2218 pHeader->CacheUsage.QuadPart = 0;
2221 URLCache_DeleteEntry(pHeader, pEntry);
2223 URLCache_DeleteEntryFromHash(pHashEntry);
2227 /***********************************************************************
2228 * UnlockUrlCacheEntryFileA (WININET.@)
2231 BOOL WINAPI UnlockUrlCacheEntryFileA(
2232 IN LPCSTR lpszUrlName,
2236 LPURLCACHE_HEADER pHeader;
2237 struct _HASH_ENTRY * pHashEntry;
2238 CACHEFILE_ENTRY * pEntry;
2239 URL_CACHEFILE_ENTRY * pUrlEntry;
2240 URLCACHECONTAINER * pContainer;
2243 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2247 ERR("dwReserved != 0\n");
2248 SetLastError(ERROR_INVALID_PARAMETER);
2252 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2253 if (error != ERROR_SUCCESS)
2255 SetLastError(error);
2259 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2260 if (error != ERROR_SUCCESS)
2262 SetLastError(error);
2266 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2269 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2271 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2272 TRACE("entry %s not found!\n", lpszUrlName);
2273 SetLastError(ERROR_FILE_NOT_FOUND);
2277 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2278 if (pEntry->dwSignature != URL_SIGNATURE)
2280 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2281 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2282 SetLastError(ERROR_FILE_NOT_FOUND);
2286 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2288 if (pUrlEntry->dwUseCount == 0)
2290 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2293 pUrlEntry->dwUseCount--;
2294 if (!pUrlEntry->dwUseCount)
2296 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_URL);
2297 if (pUrlEntry->CacheEntryType & DELETED_CACHE_ENTRY)
2298 DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
2301 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2306 /***********************************************************************
2307 * UnlockUrlCacheEntryFileW (WININET.@)
2310 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
2312 LPURLCACHE_HEADER pHeader;
2313 struct _HASH_ENTRY * pHashEntry;
2314 CACHEFILE_ENTRY * pEntry;
2315 URL_CACHEFILE_ENTRY * pUrlEntry;
2316 URLCACHECONTAINER * pContainer;
2319 TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
2323 ERR("dwReserved != 0\n");
2324 SetLastError(ERROR_INVALID_PARAMETER);
2328 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2329 if (error != ERROR_SUCCESS)
2331 SetLastError(error);
2335 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2336 if (error != ERROR_SUCCESS)
2338 SetLastError(error);
2342 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2345 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2347 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2348 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2349 SetLastError(ERROR_FILE_NOT_FOUND);
2353 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2354 if (pEntry->dwSignature != URL_SIGNATURE)
2356 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2357 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2358 SetLastError(ERROR_FILE_NOT_FOUND);
2362 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2364 if (pUrlEntry->dwUseCount == 0)
2366 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2369 pUrlEntry->dwUseCount--;
2370 if (!pUrlEntry->dwUseCount)
2371 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_URL);
2373 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2378 /***********************************************************************
2379 * CreateUrlCacheEntryA (WININET.@)
2382 BOOL WINAPI CreateUrlCacheEntryA(
2383 IN LPCSTR lpszUrlName,
2384 IN DWORD dwExpectedFileSize,
2385 IN LPCSTR lpszFileExtension,
2386 OUT LPSTR lpszFileName,
2391 WCHAR *file_extension = NULL;
2392 WCHAR file_name[MAX_PATH];
2393 BOOL bSuccess = FALSE;
2396 TRACE("(%s %d %s %p %d)\n", debugstr_a(lpszUrlName), dwExpectedFileSize,
2397 debugstr_a(lpszFileExtension), lpszFileName, dwReserved);
2399 if (lpszUrlName && (url_name = heap_strdupAtoW(lpszUrlName)))
2401 if (!lpszFileExtension || (file_extension = heap_strdupAtoW(lpszFileExtension)))
2403 if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
2405 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
2411 dwError = GetLastError();
2416 dwError = GetLastError();
2418 heap_free(file_extension);
2422 dwError = GetLastError();
2424 heap_free(url_name);
2425 if (!bSuccess) SetLastError(dwError);
2429 /***********************************************************************
2430 * CreateUrlCacheEntryW (WININET.@)
2433 BOOL WINAPI CreateUrlCacheEntryW(
2434 IN LPCWSTR lpszUrlName,
2435 IN DWORD dwExpectedFileSize,
2436 IN LPCWSTR lpszFileExtension,
2437 OUT LPWSTR lpszFileName,
2441 URLCACHECONTAINER * pContainer;
2442 LPURLCACHE_HEADER pHeader;
2443 CHAR szFile[MAX_PATH];
2444 WCHAR szExtension[MAX_PATH];
2445 LPCWSTR lpszUrlPart;
2447 LPCWSTR lpszFileNameExtension;
2448 LPWSTR lpszFileNameNoPath;
2450 int countnoextension;
2453 BOOL bFound = FALSE;
2459 static const WCHAR szWWW[] = {'w','w','w',0};
2460 static const WCHAR fmt[] = {'%','0','8','X','%','s',0};
2462 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2463 debugstr_w(lpszUrlName),
2465 debugstr_w(lpszFileExtension),
2470 FIXME("dwReserved 0x%08x\n", dwReserved);
2472 lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2474 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2477 lpszUrlPart = memchrW(lpszUrlName, '?', lpszUrlEnd - lpszUrlName);
2479 lpszUrlPart = memchrW(lpszUrlName, '#', lpszUrlEnd - lpszUrlName);
2481 lpszUrlEnd = lpszUrlPart;
2483 for (lpszUrlPart = lpszUrlEnd;
2484 (lpszUrlPart >= lpszUrlName);
2487 if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2494 if (!lstrcmpW(lpszUrlPart, szWWW))
2496 lpszUrlPart += lstrlenW(szWWW);
2499 count = lpszUrlEnd - lpszUrlPart;
2501 if (bFound && (count < MAX_PATH))
2503 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2507 while(len && szFile[--len] == '/') szFile[len] = '\0';
2509 /* FIXME: get rid of illegal characters like \, / and : */
2513 FIXME("need to generate a random filename\n");
2516 TRACE("File name: %s\n", debugstr_a(szFile));
2518 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2519 if (error != ERROR_SUCCESS)
2521 SetLastError(error);
2525 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2526 if (error != ERROR_SUCCESS)
2528 SetLastError(error);
2532 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2535 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2537 lBufferSize = MAX_PATH * sizeof(WCHAR);
2538 if (!URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize))
2540 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2541 debugstr_a(szFile), lBufferSize);
2542 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2546 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2548 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2549 lpszFileNameNoPath >= lpszFileName;
2550 --lpszFileNameNoPath)
2552 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2556 countnoextension = lstrlenW(lpszFileNameNoPath);
2557 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2558 if (lpszFileNameExtension)
2559 countnoextension -= lstrlenW(lpszFileNameExtension);
2560 *szExtension = '\0';
2562 if (lpszFileExtension)
2564 szExtension[0] = '.';
2565 lstrcpyW(szExtension+1, lpszFileExtension);
2568 for (i = 0; i < 255; i++)
2570 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2573 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2574 for (p = lpszFileNameNoPath + 1; *p; p++)
2580 case '/': case '\\':
2587 if (p[-1] == ' ' || p[-1] == '.') p[-1] = '_';
2589 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2590 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2591 if (hFile != INVALID_HANDLE_VALUE)
2598 GetSystemTimeAsFileTime(&ft);
2599 wsprintfW(lpszFileNameNoPath + countnoextension, fmt, ft.dwLowDateTime, szExtension);
2601 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2602 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2603 if (hFile != INVALID_HANDLE_VALUE)
2609 WARN("Could not find a unique filename\n");
2614 /***********************************************************************
2615 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
2617 * The bug we are compensating for is that some drongo at Microsoft
2618 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2619 * As a consequence, CommitUrlCacheEntryA has been effectively
2620 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2621 * is still defined as LPCWSTR. The result (other than madness) is
2622 * that we always need to store lpHeaderInfo in CP_ACP rather than
2623 * in UTF16, and we need to avoid converting lpHeaderInfo in
2624 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2625 * result will lose data for arbitrary binary data.
2628 static BOOL CommitUrlCacheEntryInternal(
2629 IN LPCWSTR lpszUrlName,
2630 IN LPCWSTR lpszLocalFileName,
2631 IN FILETIME ExpireTime,
2632 IN FILETIME LastModifiedTime,
2633 IN DWORD CacheEntryType,
2634 IN LPBYTE lpHeaderInfo,
2635 IN DWORD dwHeaderSize,
2636 IN LPCWSTR lpszFileExtension,
2637 IN LPCWSTR lpszOriginalUrl
2640 URLCACHECONTAINER * pContainer;
2641 LPURLCACHE_HEADER pHeader;
2642 struct _HASH_ENTRY * pHashEntry;
2643 CACHEFILE_ENTRY * pEntry;
2644 URL_CACHEFILE_ENTRY * pUrlEntry;
2645 DWORD url_entry_offset;
2646 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2647 DWORD dwOffsetLocalFileName = 0;
2648 DWORD dwOffsetHeader = 0;
2649 DWORD dwOffsetFileExtension = 0;
2650 LARGE_INTEGER file_size;
2651 BYTE cDirectory = 0;
2652 char achFile[MAX_PATH];
2653 LPSTR lpszUrlNameA = NULL;
2654 LPSTR lpszFileExtensionA = NULL;
2655 char *pchLocalFileName = 0;
2658 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2659 debugstr_w(lpszUrlName),
2660 debugstr_w(lpszLocalFileName),
2664 debugstr_w(lpszFileExtension),
2665 debugstr_w(lpszOriginalUrl));
2667 if (CacheEntryType & STICKY_CACHE_ENTRY && !lpszLocalFileName)
2669 SetLastError(ERROR_INVALID_PARAMETER);
2672 if (lpszOriginalUrl)
2673 WARN(": lpszOriginalUrl ignored\n");
2675 file_size.QuadPart = 0;
2676 if (lpszLocalFileName)
2680 hFile = CreateFileW(lpszLocalFileName, FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
2681 if (hFile == INVALID_HANDLE_VALUE)
2683 ERR("couldn't open file %s (error is %d)\n", debugstr_w(lpszLocalFileName), GetLastError());
2688 if (!GetFileSizeEx(hFile, &file_size))
2690 ERR("couldn't get file size (error is %d)\n", GetLastError());
2698 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2699 if (error != ERROR_SUCCESS)
2701 SetLastError(error);
2705 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
2706 if (error != ERROR_SUCCESS)
2708 SetLastError(error);
2712 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2715 lpszUrlNameA = heap_strdupWtoA(lpszUrlName);
2718 error = GetLastError();
2722 if (lpszFileExtension && !(lpszFileExtensionA = heap_strdupWtoA(lpszFileExtension)))
2724 error = GetLastError();
2728 if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
2730 if ((pHashEntry->dwHashKey & ((1<<HASHTABLE_FLAG_BITS)-1)) == HASHTABLE_LOCK)
2732 /* FIXME: implement timeout object unlocking */
2733 FIXME("Trying to overwrite locked entry\n");
2734 SetLastError(ERROR_SHARING_VIOLATION);
2738 FIXME("entry already in cache - don't know what to do!\n");
2740 * SetLastError(ERROR_FILE_NOT_FOUND);
2746 if (lpszLocalFileName)
2748 BOOL bFound = FALSE;
2750 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2752 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2753 error = ERROR_INVALID_PARAMETER;
2757 /* skip container path prefix */
2758 lpszLocalFileName += lstrlenW(pContainer->path);
2760 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, MAX_PATH, NULL, NULL);
2761 pchLocalFileName = achFile;
2763 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
2765 if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
2774 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2775 error = ERROR_INVALID_PARAMETER;
2779 lpszLocalFileName += DIR_LENGTH + 1;
2780 pchLocalFileName += DIR_LENGTH + 1;
2783 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
2784 if (lpszLocalFileName)
2786 dwOffsetLocalFileName = dwBytesNeeded;
2787 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
2791 dwOffsetHeader = dwBytesNeeded;
2792 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
2794 if (lpszFileExtensionA)
2796 dwOffsetFileExtension = dwBytesNeeded;
2797 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
2800 /* round up to next block */
2801 if (dwBytesNeeded % BLOCKSIZE)
2803 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
2804 dwBytesNeeded += BLOCKSIZE;
2807 error = URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry);
2808 while (error == ERROR_HANDLE_DISK_FULL)
2810 error = URLCacheContainer_CleanIndex(pContainer, &pHeader);
2811 if (error == ERROR_SUCCESS)
2812 error = URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry);
2814 if (error != ERROR_SUCCESS)
2817 /* FindFirstFreeEntry fills in blocks used */
2818 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2819 url_entry_offset = (LPBYTE)pUrlEntry - (LPBYTE)pHeader;
2820 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
2821 pUrlEntry->CacheDir = cDirectory;
2822 pUrlEntry->CacheEntryType = CacheEntryType;
2823 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
2824 if (CacheEntryType & STICKY_CACHE_ENTRY)
2826 /* Sticky entries have a default exempt time of one day */
2827 pUrlEntry->dwExemptDelta = 86400;
2830 pUrlEntry->dwExemptDelta = 0;
2831 pUrlEntry->dwHitRate = 0;
2832 pUrlEntry->dwOffsetFileExtension = dwOffsetFileExtension;
2833 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
2834 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
2835 pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
2836 pUrlEntry->size.QuadPart = file_size.QuadPart;
2837 pUrlEntry->dwUseCount = 0;
2838 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2839 pUrlEntry->LastModifiedTime = LastModifiedTime;
2840 URLCache_FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
2841 URLCache_FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
2842 pUrlEntry->wUnknownDate = pUrlEntry->wLastSyncDate;
2843 pUrlEntry->wUnknownTime = pUrlEntry->wLastSyncTime;
2846 pUrlEntry->dwUnknown1 = 0;
2847 pUrlEntry->dwUnknown2 = 0;
2848 pUrlEntry->dwUnknown3 = 0x60;
2849 pUrlEntry->Unknown4 = 0;
2850 pUrlEntry->wUnknown5 = 0x1010;
2851 pUrlEntry->dwUnknown7 = 0;
2852 pUrlEntry->dwUnknown8 = 0;
2855 strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
2856 if (dwOffsetLocalFileName)
2857 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName);
2859 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
2860 if (dwOffsetFileExtension)
2861 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
2863 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA, url_entry_offset, HASHTABLE_URL);
2864 while (error == ERROR_HANDLE_DISK_FULL)
2866 error = URLCacheContainer_CleanIndex(pContainer, &pHeader);
2867 if (error == ERROR_SUCCESS)
2869 pUrlEntry = (URL_CACHEFILE_ENTRY *)((LPBYTE)pHeader + url_entry_offset);
2870 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA,
2871 url_entry_offset, HASHTABLE_URL);
2874 if (error != ERROR_SUCCESS)
2875 URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
2878 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
2879 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles++;
2880 if (CacheEntryType & STICKY_CACHE_ENTRY)
2881 pHeader->ExemptUsage.QuadPart += file_size.QuadPart;
2883 pHeader->CacheUsage.QuadPart += file_size.QuadPart;
2884 if (pHeader->CacheUsage.QuadPart + pHeader->ExemptUsage.QuadPart >
2885 pHeader->CacheLimit.QuadPart)
2886 FIXME("file of size %s bytes fills cache\n", wine_dbgstr_longlong(file_size.QuadPart));
2890 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2891 heap_free(lpszUrlNameA);
2892 heap_free(lpszFileExtensionA);
2894 if (error == ERROR_SUCCESS)
2898 SetLastError(error);
2903 /***********************************************************************
2904 * CommitUrlCacheEntryA (WININET.@)
2907 BOOL WINAPI CommitUrlCacheEntryA(
2908 IN LPCSTR lpszUrlName,
2909 IN LPCSTR lpszLocalFileName,
2910 IN FILETIME ExpireTime,
2911 IN FILETIME LastModifiedTime,
2912 IN DWORD CacheEntryType,
2913 IN LPBYTE lpHeaderInfo,
2914 IN DWORD dwHeaderSize,
2915 IN LPCSTR lpszFileExtension,
2916 IN LPCSTR lpszOriginalUrl
2919 WCHAR *url_name = NULL;
2920 WCHAR *local_file_name = NULL;
2921 WCHAR *original_url = NULL;
2922 WCHAR *file_extension = NULL;
2923 BOOL bSuccess = FALSE;
2925 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2926 debugstr_a(lpszUrlName),
2927 debugstr_a(lpszLocalFileName),
2931 debugstr_a(lpszFileExtension),
2932 debugstr_a(lpszOriginalUrl));
2934 url_name = heap_strdupAtoW(lpszUrlName);
2938 if (lpszLocalFileName)
2940 local_file_name = heap_strdupAtoW(lpszLocalFileName);
2941 if (!local_file_name)
2944 if (lpszFileExtension)
2946 file_extension = heap_strdupAtoW(lpszFileExtension);
2947 if (!file_extension)
2950 if (lpszOriginalUrl)
2952 original_url = heap_strdupAtoW(lpszOriginalUrl);
2957 bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
2958 CacheEntryType, lpHeaderInfo, dwHeaderSize,
2959 file_extension, original_url);
2962 heap_free(original_url);
2963 heap_free(file_extension);
2964 heap_free(local_file_name);
2965 heap_free(url_name);
2969 /***********************************************************************
2970 * CommitUrlCacheEntryW (WININET.@)
2973 BOOL WINAPI CommitUrlCacheEntryW(
2974 IN LPCWSTR lpszUrlName,
2975 IN LPCWSTR lpszLocalFileName,
2976 IN FILETIME ExpireTime,
2977 IN FILETIME LastModifiedTime,
2978 IN DWORD CacheEntryType,
2979 IN LPWSTR lpHeaderInfo,
2980 IN DWORD dwHeaderSize,
2981 IN LPCWSTR lpszFileExtension,
2982 IN LPCWSTR lpszOriginalUrl
2986 BOOL bSuccess = FALSE;
2988 CHAR *header_info = NULL;
2990 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2991 debugstr_w(lpszUrlName),
2992 debugstr_w(lpszLocalFileName),
2996 debugstr_w(lpszFileExtension),
2997 debugstr_w(lpszOriginalUrl));
2999 if (!lpHeaderInfo || (header_info = heap_strdupWtoA(lpHeaderInfo)))
3001 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
3002 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
3008 dwError = GetLastError();
3012 heap_free(header_info);
3014 SetLastError(dwError);
3020 /***********************************************************************
3021 * ReadUrlCacheEntryStream (WININET.@)
3024 BOOL WINAPI ReadUrlCacheEntryStream(
3025 IN HANDLE hUrlCacheStream,
3026 IN DWORD dwLocation,
3027 IN OUT LPVOID lpBuffer,
3028 IN OUT LPDWORD lpdwLen,
3032 /* Get handle to file from 'stream' */
3033 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3035 if (dwReserved != 0)
3037 ERR("dwReserved != 0\n");
3038 SetLastError(ERROR_INVALID_PARAMETER);
3042 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3044 SetLastError(ERROR_INVALID_HANDLE);
3048 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
3050 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
3053 /***********************************************************************
3054 * RetrieveUrlCacheEntryStreamA (WININET.@)
3057 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
3058 IN LPCSTR lpszUrlName,
3059 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
3060 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3061 IN BOOL fRandomRead,
3065 /* NOTE: this is not the same as the way that the native
3066 * version allocates 'stream' handles. I did it this way
3067 * as it is much easier and no applications should depend
3068 * on this behaviour. (Native version appears to allocate
3069 * indices into a table)
3071 STREAM_HANDLE * pStream;
3074 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
3075 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3077 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
3079 lpdwCacheEntryInfoBufferSize,
3085 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
3090 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3092 if (hFile == INVALID_HANDLE_VALUE)
3095 /* allocate handle storage space */
3096 pStream = heap_alloc(sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
3100 SetLastError(ERROR_OUTOFMEMORY);
3104 pStream->hFile = hFile;
3105 strcpy(pStream->lpszUrl, lpszUrlName);
3109 /***********************************************************************
3110 * RetrieveUrlCacheEntryStreamW (WININET.@)
3113 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
3114 IN LPCWSTR lpszUrlName,
3115 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3116 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3117 IN BOOL fRandomRead,
3123 /* NOTE: this is not the same as the way that the native
3124 * version allocates 'stream' handles. I did it this way
3125 * as it is much easier and no applications should depend
3126 * on this behaviour. (Native version appears to allocate
3127 * indices into a table)
3129 STREAM_HANDLE * pStream;
3132 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3133 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3135 if (!RetrieveUrlCacheEntryFileW(lpszUrlName,
3137 lpdwCacheEntryInfoBufferSize,
3143 hFile = CreateFileW(lpCacheEntryInfo->lpszLocalFileName,
3148 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3150 if (hFile == INVALID_HANDLE_VALUE)
3153 /* allocate handle storage space */
3154 size = sizeof(STREAM_HANDLE);
3155 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
3157 pStream = heap_alloc(size);
3161 SetLastError(ERROR_OUTOFMEMORY);
3165 pStream->hFile = hFile;
3166 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, pStream->lpszUrl, url_len, NULL, NULL);
3170 /***********************************************************************
3171 * UnlockUrlCacheEntryStream (WININET.@)
3174 BOOL WINAPI UnlockUrlCacheEntryStream(
3175 IN HANDLE hUrlCacheStream,
3179 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3181 if (dwReserved != 0)
3183 ERR("dwReserved != 0\n");
3184 SetLastError(ERROR_INVALID_PARAMETER);
3188 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3190 SetLastError(ERROR_INVALID_HANDLE);
3194 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
3197 CloseHandle(pStream->hFile);
3203 /***********************************************************************
3204 * DeleteUrlCacheEntryA (WININET.@)
3207 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3209 URLCACHECONTAINER * pContainer;
3210 LPURLCACHE_HEADER pHeader;
3211 struct _HASH_ENTRY * pHashEntry;
3215 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3217 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
3218 if (error != ERROR_SUCCESS)
3220 SetLastError(error);
3224 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3225 if (error != ERROR_SUCCESS)
3227 SetLastError(error);
3231 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3234 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
3236 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3237 TRACE("entry %s not found!\n", lpszUrlName);
3238 SetLastError(ERROR_FILE_NOT_FOUND);
3242 ret = DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
3244 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3249 /***********************************************************************
3250 * DeleteUrlCacheEntryW (WININET.@)
3253 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3255 URLCACHECONTAINER * pContainer;
3256 LPURLCACHE_HEADER pHeader;
3257 struct _HASH_ENTRY * pHashEntry;
3262 TRACE("(%s)\n", debugstr_w(lpszUrlName));
3264 urlA = heap_strdupWtoA(lpszUrlName);
3267 SetLastError(ERROR_OUTOFMEMORY);
3271 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3272 if (error != ERROR_SUCCESS)
3275 SetLastError(error);
3279 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3280 if (error != ERROR_SUCCESS)
3283 SetLastError(error);
3287 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3293 if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
3295 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3296 TRACE("entry %s not found!\n", debugstr_a(urlA));
3298 SetLastError(ERROR_FILE_NOT_FOUND);
3302 ret = DeleteUrlCacheEntryInternal(pHeader, pHashEntry);
3304 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3309 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3311 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3315 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3317 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3321 /***********************************************************************
3322 * CreateCacheContainerA (WININET.@)
3324 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3325 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3327 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3328 d1, d2, d3, d4, d5, d6, d7, d8);
3332 /***********************************************************************
3333 * CreateCacheContainerW (WININET.@)
3335 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3336 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3338 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3339 d1, d2, d3, d4, d5, d6, d7, d8);
3343 /***********************************************************************
3344 * FindFirstUrlCacheContainerA (WININET.@)
3346 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3348 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3352 /***********************************************************************
3353 * FindFirstUrlCacheContainerW (WININET.@)
3355 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3357 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3361 /***********************************************************************
3362 * FindNextUrlCacheContainerA (WININET.@)
3364 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3366 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3370 /***********************************************************************
3371 * FindNextUrlCacheContainerW (WININET.@)
3373 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3375 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3379 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3380 LPCSTR lpszUrlSearchPattern,
3384 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3385 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3387 LPDWORD pcbReserved2,
3391 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3392 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3393 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3394 SetLastError(ERROR_FILE_NOT_FOUND);
3398 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3399 LPCWSTR lpszUrlSearchPattern,
3403 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3404 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3406 LPDWORD pcbReserved2,
3410 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3411 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3412 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3413 SetLastError(ERROR_FILE_NOT_FOUND);
3417 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
3419 typedef struct URLCacheFindEntryHandle
3422 LPWSTR lpszUrlSearchPattern;
3423 DWORD dwContainerIndex;
3424 DWORD dwHashTableIndex;
3425 DWORD dwHashEntryIndex;
3426 } URLCacheFindEntryHandle;
3428 /***********************************************************************
3429 * FindFirstUrlCacheEntryA (WININET.@)
3432 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3433 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3435 URLCacheFindEntryHandle *pEntryHandle;
3437 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3439 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3443 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3444 if (lpszUrlSearchPattern)
3446 pEntryHandle->lpszUrlSearchPattern = heap_strdupAtoW(lpszUrlSearchPattern);
3447 if (!pEntryHandle->lpszUrlSearchPattern)
3449 heap_free(pEntryHandle);
3454 pEntryHandle->lpszUrlSearchPattern = NULL;
3455 pEntryHandle->dwContainerIndex = 0;
3456 pEntryHandle->dwHashTableIndex = 0;
3457 pEntryHandle->dwHashEntryIndex = 0;
3459 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3461 heap_free(pEntryHandle);
3464 return pEntryHandle;
3467 /***********************************************************************
3468 * FindFirstUrlCacheEntryW (WININET.@)
3471 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3472 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3474 URLCacheFindEntryHandle *pEntryHandle;
3476 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3478 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3482 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3483 if (lpszUrlSearchPattern)
3485 pEntryHandle->lpszUrlSearchPattern = heap_strdupW(lpszUrlSearchPattern);
3486 if (!pEntryHandle->lpszUrlSearchPattern)
3488 heap_free(pEntryHandle);
3493 pEntryHandle->lpszUrlSearchPattern = NULL;
3494 pEntryHandle->dwContainerIndex = 0;
3495 pEntryHandle->dwHashTableIndex = 0;
3496 pEntryHandle->dwHashEntryIndex = 0;
3498 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3500 heap_free(pEntryHandle);
3503 return pEntryHandle;
3506 static BOOL FindNextUrlCacheEntryInternal(
3508 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3509 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3512 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3513 URLCACHECONTAINER * pContainer;
3515 if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3517 SetLastError(ERROR_INVALID_HANDLE);
3521 for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3522 pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3524 LPURLCACHE_HEADER pHeader;
3525 HASH_CACHEFILE_ENTRY *pHashTableEntry;
3528 error = URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO);
3529 if (error != ERROR_SUCCESS)
3531 SetLastError(error);
3535 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3538 for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3539 pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3541 const struct _HASH_ENTRY *pHashEntry = NULL;
3542 for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3543 pEntryHandle->dwHashEntryIndex++)
3545 const URL_CACHEFILE_ENTRY *pUrlEntry;
3546 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3548 if (pEntry->dwSignature != URL_SIGNATURE)
3551 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3552 TRACE("Found URL: %s\n",
3553 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
3554 TRACE("Header info: %s\n",
3555 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo));
3557 error = URLCache_CopyEntry(
3560 lpNextCacheEntryInfo,
3561 lpdwNextCacheEntryInfoBufferSize,
3564 if (error != ERROR_SUCCESS)
3566 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3567 SetLastError(error);
3570 TRACE("Local File Name: %s\n",
3571 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
3573 /* increment the current index so that next time the function
3574 * is called the next entry is returned */
3575 pEntryHandle->dwHashEntryIndex++;
3576 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3581 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3584 SetLastError(ERROR_NO_MORE_ITEMS);
3588 /***********************************************************************
3589 * FindNextUrlCacheEntryA (WININET.@)
3591 BOOL WINAPI FindNextUrlCacheEntryA(
3593 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3594 LPDWORD lpdwNextCacheEntryInfoBufferSize)
3596 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3598 return FindNextUrlCacheEntryInternal(hEnumHandle, lpNextCacheEntryInfo,
3599 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
3602 /***********************************************************************
3603 * FindNextUrlCacheEntryW (WININET.@)
3605 BOOL WINAPI FindNextUrlCacheEntryW(
3607 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3608 LPDWORD lpdwNextCacheEntryInfoBufferSize
3611 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3613 return FindNextUrlCacheEntryInternal(hEnumHandle,
3614 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
3615 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
3618 /***********************************************************************
3619 * FindCloseUrlCache (WININET.@)
3621 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3623 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3625 TRACE("(%p)\n", hEnumHandle);
3627 if (!pEntryHandle || pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3629 SetLastError(ERROR_INVALID_HANDLE);
3633 pEntryHandle->dwMagic = 0;
3634 heap_free(pEntryHandle->lpszUrlSearchPattern);
3635 heap_free(pEntryHandle);
3639 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3640 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3642 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3643 dwSearchCondition, lpGroupId, lpReserved);
3647 BOOL WINAPI FindNextUrlCacheEntryExA(
3649 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3650 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3652 LPDWORD pcbReserved2,
3656 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3657 lpReserved, pcbReserved2, lpReserved3);
3661 BOOL WINAPI FindNextUrlCacheEntryExW(
3663 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3664 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3666 LPDWORD pcbReserved2,
3670 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3671 lpReserved, pcbReserved2, lpReserved3);
3675 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3677 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3681 /***********************************************************************
3682 * CreateUrlCacheGroup (WININET.@)
3685 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3687 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3691 /***********************************************************************
3692 * DeleteUrlCacheGroup (WININET.@)
3695 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3697 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3698 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3702 /***********************************************************************
3703 * SetUrlCacheEntryGroupA (WININET.@)
3706 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3707 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3710 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3711 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3712 pbGroupAttributes, cbGroupAttributes, lpReserved);
3713 SetLastError(ERROR_FILE_NOT_FOUND);
3717 /***********************************************************************
3718 * SetUrlCacheEntryGroupW (WININET.@)
3721 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3722 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3725 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3726 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3727 pbGroupAttributes, cbGroupAttributes, lpReserved);
3728 SetLastError(ERROR_FILE_NOT_FOUND);
3732 /***********************************************************************
3733 * GetUrlCacheConfigInfoW (WININET.@)
3735 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3737 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3738 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3742 /***********************************************************************
3743 * GetUrlCacheConfigInfoA (WININET.@)
3745 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3747 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3748 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3752 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3753 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3754 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3756 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3757 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3758 lpdwGroupInfo, lpReserved);
3762 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3763 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3764 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3766 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3767 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3768 lpdwGroupInfo, lpReserved);
3772 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3773 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3775 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3776 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3780 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3781 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3783 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3784 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3788 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3790 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3794 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3796 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3800 /***********************************************************************
3801 * DeleteIE3Cache (WININET.@)
3803 * Deletes the files used by the IE3 URL caching system.
3806 * hWnd [I] A dummy window.
3807 * hInst [I] Instance of process calling the function.
3808 * lpszCmdLine [I] Options used by function.
3809 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3811 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3813 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3817 static BOOL IsUrlCacheEntryExpiredInternal(const URL_CACHEFILE_ENTRY *pUrlEntry,
3818 FILETIME *pftLastModified)
3821 FILETIME now, expired;
3823 *pftLastModified = pUrlEntry->LastModifiedTime;
3824 GetSystemTimeAsFileTime(&now);
3825 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate,
3826 pUrlEntry->wExpiredTime, &expired);
3827 /* If the expired time is 0, it's interpreted as not expired */
3828 if (!expired.dwLowDateTime && !expired.dwHighDateTime)
3831 ret = CompareFileTime(&expired, &now) < 0;
3835 /***********************************************************************
3836 * IsUrlCacheEntryExpiredA (WININET.@)
3840 * dwFlags [I] Unknown
3841 * pftLastModified [O] Last modified time
3843 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3845 LPURLCACHE_HEADER pHeader;
3846 struct _HASH_ENTRY * pHashEntry;
3847 const CACHEFILE_ENTRY * pEntry;
3848 const URL_CACHEFILE_ENTRY * pUrlEntry;
3849 URLCACHECONTAINER * pContainer;
3852 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3854 if (!url || !pftLastModified)
3857 FIXME("unknown flags 0x%08x\n", dwFlags);
3859 /* Any error implies that the URL is expired, i.e. not in the cache */
3860 if (URLCacheContainers_FindContainerA(url, &pContainer))
3862 memset(pftLastModified, 0, sizeof(*pftLastModified));
3866 if (URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO))
3868 memset(pftLastModified, 0, sizeof(*pftLastModified));
3872 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3874 memset(pftLastModified, 0, sizeof(*pftLastModified));
3878 if (!URLCache_FindHash(pHeader, url, &pHashEntry))
3880 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3881 memset(pftLastModified, 0, sizeof(*pftLastModified));
3882 TRACE("entry %s not found!\n", url);
3886 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3887 if (pEntry->dwSignature != URL_SIGNATURE)
3889 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3890 memset(pftLastModified, 0, sizeof(*pftLastModified));
3891 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3895 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3896 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
3898 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3903 /***********************************************************************
3904 * IsUrlCacheEntryExpiredW (WININET.@)
3908 * dwFlags [I] Unknown
3909 * pftLastModified [O] Last modified time
3911 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
3913 LPURLCACHE_HEADER pHeader;
3914 struct _HASH_ENTRY * pHashEntry;
3915 const CACHEFILE_ENTRY * pEntry;
3916 const URL_CACHEFILE_ENTRY * pUrlEntry;
3917 URLCACHECONTAINER * pContainer;
3920 TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
3922 if (!url || !pftLastModified)
3925 FIXME("unknown flags 0x%08x\n", dwFlags);
3927 /* Any error implies that the URL is expired, i.e. not in the cache */
3928 if (URLCacheContainers_FindContainerW(url, &pContainer))
3930 memset(pftLastModified, 0, sizeof(*pftLastModified));
3934 if (URLCacheContainer_OpenIndex(pContainer, MIN_BLOCK_NO))
3936 memset(pftLastModified, 0, sizeof(*pftLastModified));
3940 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3942 memset(pftLastModified, 0, sizeof(*pftLastModified));
3946 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3948 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3949 memset(pftLastModified, 0, sizeof(*pftLastModified));
3950 TRACE("entry %s not found!\n", debugstr_w(url));
3954 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
3956 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3957 memset(pftLastModified, 0, sizeof(*pftLastModified));
3958 TRACE("entry %s not found!\n", debugstr_w(url));
3962 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3963 if (pEntry->dwSignature != URL_SIGNATURE)
3965 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3966 memset(pftLastModified, 0, sizeof(*pftLastModified));
3967 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
3971 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3972 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
3974 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3979 /***********************************************************************
3980 * GetDiskInfoA (WININET.@)
3982 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
3985 ULARGE_INTEGER bytes_free, bytes_total;
3987 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
3991 SetLastError(ERROR_INVALID_PARAMETER);
3995 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
3997 if (cluster_size) *cluster_size = 1;
3998 if (free) *free = bytes_free.QuadPart;
3999 if (total) *total = bytes_total.QuadPart;
4004 /***********************************************************************
4005 * RegisterUrlCacheNotification (WININET.@)
4007 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
4009 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
4013 /***********************************************************************
4014 * IncrementUrlCacheHeaderData (WININET.@)
4016 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
4018 FIXME("(%u, %p)\n", index, data);
4022 /***********************************************************************
4023 * RunOnceUrlCache (WININET.@)
4026 DWORD WINAPI RunOnceUrlCache(HWND hwnd, HINSTANCE hinst, LPSTR cmd, int cmdshow)
4028 FIXME("(%p, %p, %s, %d): stub\n", hwnd, hinst, debugstr_a(cmd), cmdshow);