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 static const char urlcache_ver_prefix[] = "WINE URLCache Ver ";
64 static const char urlcache_ver[] = "0.2012001";
66 #define ENTRY_START_OFFSET 0x4000
68 #define MAX_DIR_NO 0x20
70 #define HASHTABLE_SIZE 448
71 #define HASHTABLE_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
72 #define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
73 #define ALLOCATION_TABLE_OFFSET 0x250
74 #define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
75 #define MIN_BLOCK_NO 0x80
76 #define MAX_BLOCK_NO (ALLOCATION_TABLE_SIZE * 8)
77 #define FILE_SIZE(blocks) ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
79 #define HASHTABLE_URL 0
80 #define HASHTABLE_DEL 1
81 #define HASHTABLE_LOCK 2
82 #define HASHTABLE_FREE 3
83 #define HASHTABLE_REDR 5
84 #define HASHTABLE_FLAG_BITS 6
86 #define PENDING_DELETE_CACHE_ENTRY 0x00400000
87 #define INSTALLED_CACHE_ENTRY 0x10000000
88 #define GET_INSTALLED_ENTRY 0x200
89 #define CACHE_CONTAINER_NO_SUBDIR 0xFE
91 #define CACHE_HEADER_DATA_ROOT_LEAK_OFFSET 0x16
93 #define FILETIME_SECOND 10000000
95 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
96 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
97 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
98 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
99 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
101 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
103 typedef struct _CACHEFILE_ENTRY
107 DWORD dwSignature; /* e.g. "URL " */
108 /* CHAR szSignature[4];
110 DWORD dwBlocksUsed; /* number of 128byte blocks used by this entry */
113 typedef struct _URL_CACHEFILE_ENTRY
115 CACHEFILE_ENTRY CacheFileEntry;
116 FILETIME LastModifiedTime;
117 FILETIME LastAccessTime;
118 WORD wExpiredDate; /* expire date in dos format */
119 WORD wExpiredTime; /* expire time in dos format */
120 DWORD dwUnknown1; /* usually zero */
121 ULARGE_INTEGER size; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
122 DWORD dwUnknown2; /* usually zero */
123 DWORD dwExemptDelta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
124 DWORD dwUnknown3; /* usually 0x60 */
125 DWORD dwOffsetUrl; /* offset of start of url from start of entry */
126 BYTE CacheDir; /* index of cache directory this url is stored in */
127 BYTE Unknown4; /* usually zero */
128 WORD wUnknown5; /* usually 0x1010 */
129 DWORD dwOffsetLocalName; /* offset of start of local filename from start of entry */
130 DWORD CacheEntryType; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
131 DWORD dwOffsetHeaderInfo; /* offset of start of header info from start of entry */
132 DWORD dwHeaderInfoSize;
133 DWORD dwOffsetFileExtension; /* offset of start of file extension from start of entry */
134 WORD wLastSyncDate; /* last sync date in dos format */
135 WORD wLastSyncTime; /* last sync time in dos format */
136 DWORD dwHitRate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
137 DWORD dwUseCount; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
140 DWORD dwUnknown7; /* usually zero */
141 DWORD dwUnknown8; /* usually zero */
142 /* packing to dword align start of next field */
143 /* CHAR szSourceUrlName[]; (url) */
144 /* packing to dword align start of next field */
145 /* CHAR szLocalFileName[]; (local file name excluding path) */
146 /* packing to dword align start of next field */
147 /* CHAR szHeaderInfo[]; (header info) */
148 } URL_CACHEFILE_ENTRY;
156 typedef struct _HASH_CACHEFILE_ENTRY
158 CACHEFILE_ENTRY CacheFileEntry;
160 DWORD dwHashTableNumber;
161 struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
162 } HASH_CACHEFILE_ENTRY;
164 typedef struct _DIRECTORY_DATA
167 char filename[DIR_LENGTH];
170 typedef struct _URLCACHE_HEADER
172 char szSignature[28];
174 DWORD dwOffsetFirstHashTable;
175 DWORD dwIndexCapacityInBlocks;
178 ULARGE_INTEGER CacheLimit;
179 ULARGE_INTEGER CacheUsage;
180 ULARGE_INTEGER ExemptUsage;
181 DWORD DirectoryCount;
182 DIRECTORY_DATA directory_data[MAX_DIR_NO];
184 BYTE allocation_table[ALLOCATION_TABLE_SIZE];
185 } URLCACHE_HEADER, *LPURLCACHE_HEADER;
186 typedef const URLCACHE_HEADER *LPCURLCACHE_HEADER;
188 typedef struct _STREAM_HANDLE
194 typedef struct _URLCACHECONTAINER
196 struct list entry; /* part of a list */
197 LPWSTR cache_prefix; /* string that has to be prefixed for this container to be used */
198 LPWSTR path; /* path to url container directory */
199 HANDLE hMapping; /* handle of file mapping */
200 DWORD file_size; /* size of file when mapping was opened */
201 HANDLE hMutex; /* handle of mutex */
202 DWORD default_entry_type;
206 /* List of all containers available */
207 static struct list UrlContainers = LIST_INIT(UrlContainers);
209 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash);
211 /***********************************************************************
212 * URLCache_PathToObjectName (Internal)
214 * Converts a path to a name suitable for use as a Win32 object name.
215 * Replaces '\\' characters in-place with the specified character
216 * (usually '_' or '!')
222 static void URLCache_PathToObjectName(LPWSTR lpszPath, WCHAR replace)
224 for (; *lpszPath; lpszPath++)
226 if (*lpszPath == '\\')
231 /* Caller must hold container lock */
232 static HANDLE cache_container_map_index(HANDLE file, const WCHAR *path, DWORD size, BOOL *validate)
234 static const WCHAR mapping_name_format[]
235 = {'%','s','i','n','d','e','x','.','d','a','t','_','%','l','u',0};
236 WCHAR mapping_name[MAX_PATH];
239 wsprintfW(mapping_name, mapping_name_format, path, size);
240 URLCache_PathToObjectName(mapping_name, '_');
242 mapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, mapping_name);
244 if(validate) *validate = FALSE;
248 if(validate) *validate = TRUE;
249 return CreateFileMappingW(file, NULL, PAGE_READWRITE, 0, 0, mapping_name);
252 /* Caller must hold container lock */
253 static DWORD cache_container_set_size(URLCACHECONTAINER *container, HANDLE file, DWORD blocks_no)
255 static const WCHAR cache_content_key[] = {'S','o','f','t','w','a','r','e','\\',
256 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
257 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
258 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s','\\',
259 'C','a','c','h','e','\\','C','o','n','t','e','n','t',0};
260 static const WCHAR cache_limit[] = {'C','a','c','h','e','L','i','m','i','t',0};
262 DWORD file_size = FILE_SIZE(blocks_no);
263 WCHAR dir_path[MAX_PATH], *dir_name;
264 HASH_CACHEFILE_ENTRY *hash_entry;
265 URLCACHE_HEADER *header;
271 if(SetFilePointer(file, file_size, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
272 return GetLastError();
274 if(!SetEndOfFile(file))
275 return GetLastError();
277 mapping = cache_container_map_index(file, container->path, file_size, NULL);
279 return GetLastError();
281 header = MapViewOfFile(mapping, FILE_MAP_WRITE, 0, 0, 0);
283 CloseHandle(mapping);
284 return GetLastError();
287 if(blocks_no != MIN_BLOCK_NO) {
288 if(file_size > header->dwFileSize)
289 memset((char*)header+header->dwFileSize, 0, file_size-header->dwFileSize);
290 header->dwFileSize = file_size;
291 header->dwIndexCapacityInBlocks = blocks_no;
293 UnmapViewOfFile(header);
294 CloseHandle(container->hMapping);
295 container->hMapping = mapping;
296 container->file_size = file_size;
297 return ERROR_SUCCESS;
300 memset(header, 0, file_size);
301 /* First set some constants and defaults in the header */
302 memcpy(header->szSignature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1);
303 memcpy(header->szSignature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1);
304 header->dwFileSize = file_size;
305 header->dwIndexCapacityInBlocks = blocks_no;
306 /* 127MB - taken from default for Windows 2000 */
307 header->CacheLimit.QuadPart = 0x07ff5400;
308 /* Copied from a Windows 2000 cache index */
309 header->DirectoryCount = container->default_entry_type==NORMAL_CACHE_ENTRY ? 4 : 0;
311 /* If the registry has a cache size set, use the registry value */
312 if(RegOpenKeyW(HKEY_CURRENT_USER, cache_content_key, &key) == ERROR_SUCCESS) {
313 DWORD dw, len = sizeof(dw), keytype;
315 if(RegQueryValueExW(key, cache_limit, NULL, &keytype, (BYTE*)&dw, &len) == ERROR_SUCCESS &&
316 keytype == REG_DWORD)
317 header->CacheLimit.QuadPart = (ULONGLONG)dw * 1024;
321 URLCache_CreateHashTable(header, NULL, &hash_entry);
323 /* Last step - create the directories */
324 strcpyW(dir_path, container->path);
325 dir_name = dir_path + strlenW(dir_path);
328 GetSystemTimeAsFileTime(&ft);
330 for(i=0; i<header->DirectoryCount; ++i) {
331 header->directory_data[i].dwNumFiles = 0;
333 ULONGLONG n = ft.dwHighDateTime;
336 /* Generate a file name to attempt to create.
337 * This algorithm will create what will appear
338 * to be random and unrelated directory names
339 * of up to 9 characters in length.
342 n += ft.dwLowDateTime;
343 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
345 for(k = 0; k < 8; ++k) {
348 /* Dividing by a prime greater than 36 helps
349 * with the appearance of randomness
354 dir_name[k] = '0' + r;
356 dir_name[k] = 'A' + (r - 10);
359 if(CreateDirectoryW(dir_path, 0)) {
360 /* The following is OK because we generated an
361 * 8 character directory name made from characters
362 * [A-Z0-9], which are equivalent for all code
363 * pages and for UTF-16
365 for (k = 0; k < 8; ++k)
366 header->directory_data[i].filename[k] = dir_name[k];
369 /* Give up. The most likely cause of this
370 * is a full disk, but whatever the cause
371 * is, it should be more than apparent that
374 UnmapViewOfFile(header);
375 CloseHandle(mapping);
376 return GetLastError();
381 UnmapViewOfFile(header);
382 CloseHandle(container->hMapping);
383 container->hMapping = mapping;
384 container->file_size = file_size;
385 return ERROR_SUCCESS;
388 static BOOL cache_container_is_valid(URLCACHE_HEADER *header, DWORD file_size)
390 DWORD allocation_size, count_bits, i;
392 if(file_size < FILE_SIZE(MIN_BLOCK_NO))
395 if(file_size != header->dwFileSize)
398 if (!memcmp(header->szSignature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1) &&
399 memcmp(header->szSignature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1))
402 if(FILE_SIZE(header->dwIndexCapacityInBlocks) != file_size)
406 for(i=0; i<header->dwIndexCapacityInBlocks/8; i++) {
407 for(count_bits = header->allocation_table[i]; count_bits!=0; count_bits>>=1) {
412 if(allocation_size != header->dwBlocksInUse)
415 for(; i<ALLOCATION_TABLE_SIZE; i++) {
416 if(header->allocation_table[i])
423 /***********************************************************************
424 * cache_container_open_index (Internal)
426 * Opens the index file and saves mapping handle in hMapping
429 * ERROR_SUCCESS if succeeded
430 * Any other Win32 error code if failed
433 static DWORD cache_container_open_index(URLCACHECONTAINER *container, DWORD blocks_no)
435 static const WCHAR index_dat[] = {'i','n','d','e','x','.','d','a','t',0};
438 WCHAR index_path[MAX_PATH];
442 WaitForSingleObject(container->hMutex, INFINITE);
444 if(container->hMapping) {
445 ReleaseMutex(container->hMutex);
446 return ERROR_SUCCESS;
449 strcpyW(index_path, container->path);
450 strcatW(index_path, index_dat);
452 file = CreateFileW(index_path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
453 if(file == INVALID_HANDLE_VALUE) {
454 /* Maybe the directory wasn't there? Try to create it */
455 if(CreateDirectoryW(container->path, 0))
456 file = CreateFileW(index_path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
458 if(file == INVALID_HANDLE_VALUE) {
459 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(index_path));
460 ReleaseMutex(container->hMutex);
461 return GetLastError();
464 file_size = GetFileSize(file, NULL);
465 if(file_size == INVALID_FILE_SIZE) {
467 ReleaseMutex(container->hMutex);
468 return GetLastError();
471 if(blocks_no < MIN_BLOCK_NO)
472 blocks_no = MIN_BLOCK_NO;
473 else if(blocks_no > MAX_BLOCK_NO)
474 blocks_no = MAX_BLOCK_NO;
476 if(file_size < FILE_SIZE(blocks_no)) {
477 DWORD ret = cache_container_set_size(container, file, blocks_no);
479 ReleaseMutex(container->hMutex);
483 container->file_size = file_size;
484 container->hMapping = cache_container_map_index(file, container->path, file_size, &validate);
486 if(container->hMapping && validate) {
487 URLCACHE_HEADER *header = MapViewOfFile(container->hMapping, FILE_MAP_WRITE, 0, 0, 0);
489 if(header && !cache_container_is_valid(header, file_size)) {
490 WARN("detected old or broken index.dat file\n");
491 UnmapViewOfFile(header);
492 FreeUrlCacheSpaceW(container->path, 100, 0);
494 UnmapViewOfFile(header);
496 CloseHandle(container->hMapping);
497 container->hMapping = NULL;
501 if(!container->hMapping)
503 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
504 ReleaseMutex(container->hMutex);
505 return GetLastError();
508 ReleaseMutex(container->hMutex);
509 return ERROR_SUCCESS;
512 /***********************************************************************
513 * URLCacheContainer_CloseIndex (Internal)
521 static void URLCacheContainer_CloseIndex(URLCACHECONTAINER * pContainer)
523 CloseHandle(pContainer->hMapping);
524 pContainer->hMapping = NULL;
527 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix,
528 LPCWSTR path, DWORD default_entry_type, LPWSTR mutex_name)
530 URLCACHECONTAINER * pContainer = heap_alloc(sizeof(URLCACHECONTAINER));
531 int cache_prefix_len = strlenW(cache_prefix);
538 pContainer->hMapping = NULL;
539 pContainer->file_size = 0;
540 pContainer->default_entry_type = default_entry_type;
542 pContainer->path = heap_strdupW(path);
543 if (!pContainer->path)
545 heap_free(pContainer);
549 pContainer->cache_prefix = heap_alloc((cache_prefix_len + 1) * sizeof(WCHAR));
550 if (!pContainer->cache_prefix)
552 heap_free(pContainer->path);
553 heap_free(pContainer);
557 memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
559 CharLowerW(mutex_name);
560 URLCache_PathToObjectName(mutex_name, '!');
562 if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
564 ERR("couldn't create mutex (error is %d)\n", GetLastError());
565 heap_free(pContainer->path);
566 heap_free(pContainer);
570 list_add_head(&UrlContainers, &pContainer->entry);
575 static void URLCacheContainer_DeleteContainer(URLCACHECONTAINER * pContainer)
577 list_remove(&pContainer->entry);
579 URLCacheContainer_CloseIndex(pContainer);
580 CloseHandle(pContainer->hMutex);
581 heap_free(pContainer->path);
582 heap_free(pContainer->cache_prefix);
583 heap_free(pContainer);
586 static void URLCacheContainers_CreateDefaults(void)
588 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
589 static const WCHAR UrlPrefix[] = {0};
590 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
591 static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
592 static const WCHAR CookieSuffix[] = {0};
593 static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
596 int nFolder; /* CSIDL_* constant */
597 const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
598 const WCHAR * cache_prefix; /* prefix used to reference the container */
599 DWORD default_entry_type;
600 } DefaultContainerData[] =
602 { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix, NORMAL_CACHE_ENTRY },
603 { CSIDL_HISTORY, HistorySuffix, HistoryPrefix, URLHISTORY_CACHE_ENTRY },
604 { CSIDL_COOKIES, CookieSuffix, CookiePrefix, COOKIE_CACHE_ENTRY },
608 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
610 WCHAR wszCachePath[MAX_PATH];
611 WCHAR wszMutexName[MAX_PATH];
612 int path_len, suffix_len;
614 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
616 ERR("Couldn't get path for default container %u\n", i);
619 path_len = strlenW(wszCachePath);
620 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
622 if (path_len + suffix_len + 2 > MAX_PATH)
624 ERR("Path too long\n");
628 wszCachePath[path_len] = '\\';
629 wszCachePath[path_len+1] = 0;
631 strcpyW(wszMutexName, wszCachePath);
635 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
636 wszCachePath[path_len + suffix_len + 1] = '\\';
637 wszCachePath[path_len + suffix_len + 2] = '\0';
640 URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath,
641 DefaultContainerData[i].default_entry_type, wszMutexName);
645 static void URLCacheContainers_DeleteAll(void)
647 while(!list_empty(&UrlContainers))
648 URLCacheContainer_DeleteContainer(
649 LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
653 static DWORD URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
655 URLCACHECONTAINER * pContainer;
657 TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
660 return ERROR_INVALID_PARAMETER;
662 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
664 int prefix_len = strlenW(pContainer->cache_prefix);
665 if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
667 TRACE("found container with prefix %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
668 *ppContainer = pContainer;
669 return ERROR_SUCCESS;
672 ERR("no container found\n");
673 return ERROR_FILE_NOT_FOUND;
676 static DWORD URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
681 if (lpszUrl && !(url = heap_strdupAtoW(lpszUrl)))
682 return ERROR_OUTOFMEMORY;
684 ret = URLCacheContainers_FindContainerW(url, ppContainer);
689 static BOOL URLCacheContainers_Enum(LPCWSTR lpwszSearchPattern, DWORD dwIndex, URLCACHECONTAINER ** ppContainer)
692 URLCACHECONTAINER * pContainer;
694 TRACE("searching for prefix: %s\n", debugstr_w(lpwszSearchPattern));
696 /* non-NULL search pattern only returns one container ever */
697 if (lpwszSearchPattern && dwIndex > 0)
700 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
702 if (lpwszSearchPattern)
704 if (!strcmpW(pContainer->cache_prefix, lpwszSearchPattern))
706 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
707 *ppContainer = pContainer;
715 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
716 *ppContainer = pContainer;
725 /***********************************************************************
726 * URLCacheContainer_LockIndex (Internal)
728 * Locks the index for system-wide exclusive access.
731 * Cache file header if successful
732 * NULL if failed and calls SetLastError.
734 static LPURLCACHE_HEADER URLCacheContainer_LockIndex(URLCACHECONTAINER * pContainer)
738 URLCACHE_HEADER * pHeader;
742 WaitForSingleObject(pContainer->hMutex, INFINITE);
744 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
748 ReleaseMutex(pContainer->hMutex);
749 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
752 pHeader = (URLCACHE_HEADER *)pIndexData;
754 /* file has grown - we need to remap to prevent us getting
755 * access violations when we try and access beyond the end
756 * of the memory mapped file */
757 if (pHeader->dwFileSize != pContainer->file_size)
759 UnmapViewOfFile( pHeader );
760 URLCacheContainer_CloseIndex(pContainer);
761 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
762 if (error != ERROR_SUCCESS)
764 ReleaseMutex(pContainer->hMutex);
768 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
772 ReleaseMutex(pContainer->hMutex);
773 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
776 pHeader = (URLCACHE_HEADER *)pIndexData;
779 TRACE("Signature: %s, file size: %d bytes\n", pHeader->szSignature, pHeader->dwFileSize);
781 for (index = 0; index < pHeader->DirectoryCount; index++)
783 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
789 /***********************************************************************
790 * URLCacheContainer_UnlockIndex (Internal)
793 static BOOL URLCacheContainer_UnlockIndex(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
796 ReleaseMutex(pContainer->hMutex);
797 return UnmapViewOfFile(pHeader);
801 #define CHAR_BIT (8 * sizeof(CHAR))
804 /***********************************************************************
805 * URLCache_Allocation_BlockIsFree (Internal)
807 * Is the specified block number free?
814 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
816 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
817 return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
820 /***********************************************************************
821 * URLCache_Allocation_BlockFree (Internal)
823 * Marks the specified block as free
826 * this function is not updating used blocks count
832 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
834 BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
835 AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
838 /***********************************************************************
839 * URLCache_Allocation_BlockAllocate (Internal)
841 * Marks the specified block as allocated
844 * this function is not updating used blocks count
850 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
852 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
853 AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
856 /***********************************************************************
857 * URLCache_FindFirstFreeEntry (Internal)
859 * Finds and allocates the first block of free space big enough and
860 * sets ppEntry to point to it.
863 * ERROR_SUCCESS when free memory block was found
864 * Any other Win32 error code if the entry could not be added
867 static DWORD URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, CACHEFILE_ENTRY ** ppEntry)
871 for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
873 for (dwFreeCounter = 0;
874 dwFreeCounter < dwBlocksNeeded &&
875 dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
876 URLCache_Allocation_BlockIsFree(pHeader->allocation_table, dwBlockNumber + dwFreeCounter);
878 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
880 if (dwFreeCounter == dwBlocksNeeded)
883 TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
884 for (index = 0; index < dwBlocksNeeded; index++)
885 URLCache_Allocation_BlockAllocate(pHeader->allocation_table, dwBlockNumber + index);
886 *ppEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
887 for (index = 0; index < dwBlocksNeeded * BLOCKSIZE / sizeof(DWORD); index++)
888 ((DWORD*)*ppEntry)[index] = 0xdeadbeef;
889 (*ppEntry)->dwBlocksUsed = dwBlocksNeeded;
890 pHeader->dwBlocksInUse += dwBlocksNeeded;
891 return ERROR_SUCCESS;
895 return ERROR_HANDLE_DISK_FULL;
898 /***********************************************************************
899 * URLCache_DeleteEntry (Internal)
901 * Deletes the specified entry and frees the space allocated to it
904 * TRUE if it succeeded
908 static BOOL URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader, CACHEFILE_ENTRY * pEntry)
913 /* update allocation table */
914 dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader) - ENTRY_START_OFFSET) / BLOCKSIZE;
915 for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->dwBlocksUsed; dwBlock++)
916 URLCache_Allocation_BlockFree(pHeader->allocation_table, dwBlock);
918 pHeader->dwBlocksInUse -= pEntry->dwBlocksUsed;
922 /***********************************************************************
923 * URLCache_LocalFileNameToPathW (Internal)
925 * Copies the full path to the specified buffer given the local file
926 * name and the index of the directory it is in. Always sets value in
927 * lpBufferSize to the required buffer size (in bytes).
930 * TRUE if the buffer was big enough
931 * FALSE if the buffer was too small
934 static BOOL URLCache_LocalFileNameToPathW(
935 const URLCACHECONTAINER * pContainer,
936 LPCURLCACHE_HEADER pHeader,
937 LPCSTR szLocalFileName,
943 int path_len = strlenW(pContainer->path);
944 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
945 if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->DirectoryCount)
951 nRequired = (path_len + file_name_len) * sizeof(WCHAR);
952 if(Directory != CACHE_CONTAINER_NO_SUBDIR)
953 nRequired += (DIR_LENGTH + 1) * sizeof(WCHAR);
954 if (nRequired <= *lpBufferSize)
958 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
959 if (Directory != CACHE_CONTAINER_NO_SUBDIR)
961 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
962 wszPath[dir_len + path_len] = '\\';
969 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len, file_name_len);
970 *lpBufferSize = nRequired;
973 *lpBufferSize = nRequired;
977 /***********************************************************************
978 * URLCache_LocalFileNameToPathA (Internal)
980 * Copies the full path to the specified buffer given the local file
981 * name and the index of the directory it is in. Always sets value in
982 * lpBufferSize to the required buffer size.
985 * TRUE if the buffer was big enough
986 * FALSE if the buffer was too small
989 static BOOL URLCache_LocalFileNameToPathA(
990 const URLCACHECONTAINER * pContainer,
991 LPCURLCACHE_HEADER pHeader,
992 LPCSTR szLocalFileName,
998 int path_len, file_name_len, dir_len;
1000 if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->DirectoryCount)
1006 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
1007 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
1008 if (Directory!=CACHE_CONTAINER_NO_SUBDIR)
1009 dir_len = DIR_LENGTH+1;
1013 nRequired = (path_len + dir_len + file_name_len) * sizeof(char);
1014 if (nRequired < *lpBufferSize)
1016 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
1018 memcpy(szPath+path_len, pHeader->directory_data[Directory].filename, dir_len-1);
1019 szPath[path_len + dir_len-1] = '\\';
1021 memcpy(szPath + path_len + dir_len, szLocalFileName, file_name_len);
1022 *lpBufferSize = nRequired;
1025 *lpBufferSize = nRequired;
1029 /* Just like FileTimeToDosDateTime, except that it also maps the special
1030 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1032 static void URLCache_FileTimeToDosDateTime(const FILETIME *ft, WORD *fatdate,
1035 if (!ft->dwLowDateTime && !ft->dwHighDateTime)
1036 *fatdate = *fattime = 0;
1038 FileTimeToDosDateTime(ft, fatdate, fattime);
1041 /***********************************************************************
1042 * URLCache_DeleteFile (Internal)
1044 static DWORD URLCache_DeleteFile(const URLCACHECONTAINER *container,
1045 URLCACHE_HEADER *header, URL_CACHEFILE_ENTRY *url_entry)
1047 WIN32_FILE_ATTRIBUTE_DATA attr;
1048 WCHAR path[MAX_PATH];
1049 LONG path_size = sizeof(path);
1053 if(!url_entry->dwOffsetLocalName)
1056 if(!URLCache_LocalFileNameToPathW(container, header,
1057 (LPCSTR)url_entry+url_entry->dwOffsetLocalName,
1058 url_entry->CacheDir, path, &path_size))
1061 if(!GetFileAttributesExW(path, GetFileExInfoStandard, &attr))
1063 URLCache_FileTimeToDosDateTime(&attr.ftLastWriteTime, &date, &time);
1064 if(date != url_entry->LastWriteDate || time != url_entry->LastWriteTime)
1067 err = (DeleteFileW(path) ? ERROR_SUCCESS : GetLastError());
1068 if(err == ERROR_ACCESS_DENIED || err == ERROR_SHARING_VIOLATION)
1072 if (url_entry->CacheDir < header->DirectoryCount)
1074 if (header->directory_data[url_entry->CacheDir].dwNumFiles)
1075 header->directory_data[url_entry->CacheDir].dwNumFiles--;
1077 if (url_entry->CacheEntryType & STICKY_CACHE_ENTRY)
1079 if (url_entry->size.QuadPart < header->ExemptUsage.QuadPart)
1080 header->ExemptUsage.QuadPart -= url_entry->size.QuadPart;
1082 header->ExemptUsage.QuadPart = 0;
1086 if (url_entry->size.QuadPart < header->CacheUsage.QuadPart)
1087 header->CacheUsage.QuadPart -= url_entry->size.QuadPart;
1089 header->CacheUsage.QuadPart = 0;
1092 return ERROR_SUCCESS;
1095 static BOOL urlcache_clean_leaked_entries(URLCACHECONTAINER *container, URLCACHE_HEADER *header)
1100 leak_off = &header->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
1102 URL_CACHEFILE_ENTRY *url_entry = (URL_CACHEFILE_ENTRY*)((LPBYTE)header + *leak_off);
1104 if(SUCCEEDED(URLCache_DeleteFile(container, header, url_entry))) {
1105 *leak_off = url_entry->dwExemptDelta;
1106 URLCache_DeleteEntry(header, &url_entry->CacheFileEntry);
1109 leak_off = &url_entry->dwExemptDelta;
1116 /***********************************************************************
1117 * URLCacheContainer_CleanIndex (Internal)
1119 * This function is meant to make place in index file by removing leaked
1120 * files entries and resizing the file.
1122 * CAUTION: file view may get mapped to new memory
1125 * ERROR_SUCCESS when new memory is available
1126 * error code otherwise
1128 static DWORD URLCacheContainer_CleanIndex(URLCACHECONTAINER *container, URLCACHE_HEADER **file_view)
1130 URLCACHE_HEADER *header = *file_view;
1133 TRACE("(%s %s)\n", debugstr_w(container->cache_prefix), debugstr_w(container->path));
1135 if(urlcache_clean_leaked_entries(container, header))
1136 return ERROR_SUCCESS;
1138 if(header->dwFileSize >= ALLOCATION_TABLE_SIZE*8*BLOCKSIZE + ENTRY_START_OFFSET) {
1139 WARN("index file has maximal size\n");
1140 return ERROR_NOT_ENOUGH_MEMORY;
1143 URLCacheContainer_CloseIndex(container);
1144 ret = cache_container_open_index(container, header->dwIndexCapacityInBlocks*2);
1145 if(ret != ERROR_SUCCESS)
1147 header = MapViewOfFile(container->hMapping, FILE_MAP_WRITE, 0, 0, 0);
1149 return GetLastError();
1151 UnmapViewOfFile(*file_view);
1152 *file_view = header;
1153 return ERROR_SUCCESS;
1156 /* Just like DosDateTimeToFileTime, except that it also maps the special
1157 * case of a DOS date/time of (0,0) to a filetime of (0,0).
1159 static void URLCache_DosDateTimeToFileTime(WORD fatdate, WORD fattime,
1162 if (!fatdate && !fattime)
1163 ft->dwLowDateTime = ft->dwHighDateTime = 0;
1165 DosDateTimeToFileTime(fatdate, fattime, ft);
1168 /***********************************************************************
1169 * URLCache_CopyEntry (Internal)
1171 * Copies an entry from the cache index file to the Win32 structure
1174 * ERROR_SUCCESS if the buffer was big enough
1175 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1178 static DWORD URLCache_CopyEntry(
1179 URLCACHECONTAINER * pContainer,
1180 LPCURLCACHE_HEADER pHeader,
1181 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1182 LPDWORD lpdwBufferSize,
1183 const URL_CACHEFILE_ENTRY * pUrlEntry,
1187 DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
1189 if (*lpdwBufferSize >= dwRequiredSize)
1191 lpCacheEntryInfo->lpHeaderInfo = NULL;
1192 lpCacheEntryInfo->lpszFileExtension = NULL;
1193 lpCacheEntryInfo->lpszLocalFileName = NULL;
1194 lpCacheEntryInfo->lpszSourceUrlName = NULL;
1195 lpCacheEntryInfo->CacheEntryType = pUrlEntry->CacheEntryType;
1196 lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->dwExemptDelta;
1197 lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->dwHeaderInfoSize;
1198 lpCacheEntryInfo->dwHitRate = pUrlEntry->dwHitRate;
1199 lpCacheEntryInfo->dwSizeHigh = pUrlEntry->size.u.HighPart;
1200 lpCacheEntryInfo->dwSizeLow = pUrlEntry->size.u.LowPart;
1201 lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
1202 lpCacheEntryInfo->dwUseCount = pUrlEntry->dwUseCount;
1203 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate, pUrlEntry->wExpiredTime, &lpCacheEntryInfo->ExpireTime);
1204 lpCacheEntryInfo->LastAccessTime.dwHighDateTime = pUrlEntry->LastAccessTime.dwHighDateTime;
1205 lpCacheEntryInfo->LastAccessTime.dwLowDateTime = pUrlEntry->LastAccessTime.dwLowDateTime;
1206 lpCacheEntryInfo->LastModifiedTime.dwHighDateTime = pUrlEntry->LastModifiedTime.dwHighDateTime;
1207 lpCacheEntryInfo->LastModifiedTime.dwLowDateTime = pUrlEntry->LastModifiedTime.dwLowDateTime;
1208 URLCache_DosDateTimeToFileTime(pUrlEntry->wLastSyncDate, pUrlEntry->wLastSyncTime, &lpCacheEntryInfo->LastSyncTime);
1211 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1212 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1213 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1215 lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, NULL, 0);
1217 lenUrl = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl);
1218 dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1220 /* FIXME: is source url optional? */
1221 if (*lpdwBufferSize >= dwRequiredSize)
1223 DWORD lenUrlBytes = (lenUrl+1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1225 lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrlBytes;
1227 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
1229 memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lenUrlBytes);
1232 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1233 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1234 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1236 if (pUrlEntry->dwOffsetLocalName)
1238 LONG nLocalFilePathSize;
1239 LPSTR lpszLocalFileName;
1240 lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
1241 nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
1242 if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
1243 (!bUnicode && URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName, pUrlEntry->CacheDir, lpszLocalFileName, &nLocalFilePathSize)))
1245 lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
1247 dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
1249 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1250 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1251 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1253 dwRequiredSize += pUrlEntry->dwHeaderInfoSize + 1;
1255 if (*lpdwBufferSize >= dwRequiredSize)
1257 lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->dwHeaderInfoSize - 1;
1258 memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize);
1259 ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
1261 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1262 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1263 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1265 if (pUrlEntry->dwOffsetFileExtension)
1270 lenExtension = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, NULL, 0);
1272 lenExtension = strlen((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension) + 1;
1273 dwRequiredSize += lenExtension * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1275 if (*lpdwBufferSize >= dwRequiredSize)
1277 lpCacheEntryInfo->lpszFileExtension = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenExtension;
1279 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenExtension);
1281 memcpy(lpCacheEntryInfo->lpszFileExtension, (LPCSTR)pUrlEntry + pUrlEntry->dwOffsetFileExtension, lenExtension * sizeof(CHAR));
1284 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1285 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1286 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1289 if (dwRequiredSize > *lpdwBufferSize)
1291 *lpdwBufferSize = dwRequiredSize;
1292 return ERROR_INSUFFICIENT_BUFFER;
1294 *lpdwBufferSize = dwRequiredSize;
1295 return ERROR_SUCCESS;
1298 /***********************************************************************
1299 * URLCache_SetEntryInfo (Internal)
1301 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1302 * according to the flags set by dwFieldControl.
1305 * ERROR_SUCCESS if the buffer was big enough
1306 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1309 static DWORD URLCache_SetEntryInfo(URL_CACHEFILE_ENTRY * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
1311 if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
1312 pUrlEntry->LastAccessTime = lpCacheEntryInfo->LastAccessTime;
1313 if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
1314 pUrlEntry->CacheEntryType = lpCacheEntryInfo->CacheEntryType;
1315 if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
1316 pUrlEntry->dwExemptDelta = lpCacheEntryInfo->u.dwExemptDelta;
1317 if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
1318 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
1319 if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
1320 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1321 if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
1322 pUrlEntry->dwHitRate = lpCacheEntryInfo->dwHitRate;
1323 if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
1324 pUrlEntry->LastModifiedTime = lpCacheEntryInfo->LastModifiedTime;
1325 if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
1326 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
1328 return ERROR_SUCCESS;
1331 /***********************************************************************
1332 * URLCache_HashKey (Internal)
1334 * Returns the hash key for a given string
1337 * hash key for the string
1340 static DWORD URLCache_HashKey(LPCSTR lpszKey)
1342 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1343 * but the algorithm and result are not the same!
1345 static const unsigned char lookupTable[256] =
1347 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1348 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1349 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1350 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1351 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1352 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1353 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1354 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1355 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1356 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1357 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1358 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1359 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1360 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1361 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1362 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1363 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1364 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1365 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1366 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1367 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1368 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1369 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1370 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1371 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1372 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1373 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1374 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1375 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1376 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1377 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1378 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1383 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1384 key[i] = lookupTable[(*lpszKey + i) & 0xFF];
1386 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1388 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1389 key[i] = lookupTable[*lpszKey ^ key[i]];
1392 return *(DWORD *)key;
1395 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
1397 return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
1400 static inline BOOL URLCache_IsHashEntryValid(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY *pHashEntry)
1402 /* check pHashEntry located within acceptable bounds in the URL cache mapping */
1403 return ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) >= ENTRY_START_OFFSET) &&
1404 ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) < pHeader->dwFileSize);
1407 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1409 /* structure of hash table:
1410 * 448 entries divided into 64 blocks
1411 * each block therefore contains a chain of 7 key/offset pairs
1412 * how position in table is calculated:
1413 * 1. the url is hashed in helper function
1414 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1415 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1418 * there can be multiple hash tables in the file and the offset to
1419 * the next one is stored in the header of the hash table
1421 DWORD key = URLCache_HashKey(lpszUrl);
1422 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1423 HASH_CACHEFILE_ENTRY * pHashEntry;
1424 DWORD dwHashTableNumber = 0;
1426 key >>= HASHTABLE_FLAG_BITS;
1428 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1429 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1430 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1433 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1435 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1438 /* make sure that it is in fact a hash entry */
1439 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1441 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1445 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1447 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1448 if (key == pHashElement->dwHashKey>>HASHTABLE_FLAG_BITS)
1450 /* FIXME: we should make sure that this is the right element
1451 * before returning and claiming that it is. We can do this
1452 * by doing a simple compare between the URL we were given
1453 * and the URL stored in the entry. However, this assumes
1454 * we know the format of all the entries stored in the
1456 *ppHashEntry = pHashElement;
1464 static BOOL URLCache_FindHashW(LPCURLCACHE_HEADER pHeader, LPCWSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1469 urlA = heap_strdupWtoA(lpszUrl);
1472 SetLastError(ERROR_OUTOFMEMORY);
1476 ret = URLCache_FindHash(pHeader, urlA, ppHashEntry);
1481 /***********************************************************************
1482 * URLCache_HashEntrySetFlags (Internal)
1484 * Sets special bits in hash key
1490 static void URLCache_HashEntrySetFlags(struct _HASH_ENTRY * pHashEntry, DWORD dwFlag)
1492 pHashEntry->dwHashKey = (pHashEntry->dwHashKey >> HASHTABLE_FLAG_BITS << HASHTABLE_FLAG_BITS) | dwFlag;
1495 /***********************************************************************
1496 * URLCache_DeleteEntryFromHash (Internal)
1498 * Searches all the hash tables in the index for the given URL and
1499 * then if found deletes the entry.
1502 * TRUE if the entry was found
1503 * FALSE if the entry could not be found
1506 static BOOL URLCache_DeleteEntryFromHash(struct _HASH_ENTRY * pHashEntry)
1508 pHashEntry->dwHashKey = HASHTABLE_DEL;
1512 /***********************************************************************
1513 * URLCache_AddEntryToHash (Internal)
1515 * Searches all the hash tables for a free slot based on the offset
1516 * generated from the hash key. If a free slot is found, the offset and
1517 * key are entered into the hash table.
1520 * ERROR_SUCCESS if the entry was added
1521 * Any other Win32 error code if the entry could not be added
1524 static DWORD URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry, DWORD dwFieldType)
1526 /* see URLCache_FindEntryInHash for structure of hash tables */
1528 DWORD key = URLCache_HashKey(lpszUrl);
1529 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1530 HASH_CACHEFILE_ENTRY * pHashEntry, *pHashPrev = NULL;
1531 DWORD dwHashTableNumber = 0;
1534 key = ((key >> HASHTABLE_FLAG_BITS) << HASHTABLE_FLAG_BITS) + dwFieldType;
1536 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1537 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1538 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1541 pHashPrev = pHashEntry;
1543 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1545 ERR("not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1548 /* make sure that it is in fact a hash entry */
1549 if (pHashEntry->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1551 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.dwSignature);
1555 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1557 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1558 if (pHashElement->dwHashKey==HASHTABLE_FREE || pHashElement->dwHashKey==HASHTABLE_DEL) /* if the slot is free */
1560 pHashElement->dwHashKey = key;
1561 pHashElement->dwOffsetEntry = dwOffsetEntry;
1562 return ERROR_SUCCESS;
1566 error = URLCache_CreateHashTable(pHeader, pHashPrev, &pHashEntry);
1567 if (error != ERROR_SUCCESS)
1570 pHashEntry->HashTable[offset].dwHashKey = key;
1571 pHashEntry->HashTable[offset].dwOffsetEntry = dwOffsetEntry;
1572 return ERROR_SUCCESS;
1575 /***********************************************************************
1576 * URLCache_CreateHashTable (Internal)
1578 * Creates a new hash table in free space and adds it to the chain of existing
1582 * ERROR_SUCCESS if the hash table was created
1583 * ERROR_DISK_FULL if the hash table could not be created
1586 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash)
1588 DWORD dwOffset, error;
1591 if ((error = URLCache_FindFirstFreeEntry(pHeader, 0x20, (CACHEFILE_ENTRY **)ppHash)) != ERROR_SUCCESS)
1594 dwOffset = (BYTE *)*ppHash - (BYTE *)pHeader;
1597 pPrevHash->dwAddressNext = dwOffset;
1599 pHeader->dwOffsetFirstHashTable = dwOffset;
1600 (*ppHash)->CacheFileEntry.dwSignature = HASH_SIGNATURE;
1601 (*ppHash)->CacheFileEntry.dwBlocksUsed = 0x20;
1602 (*ppHash)->dwAddressNext = 0;
1603 (*ppHash)->dwHashTableNumber = pPrevHash ? pPrevHash->dwHashTableNumber + 1 : 0;
1604 for (i = 0; i < HASHTABLE_SIZE; i++)
1606 (*ppHash)->HashTable[i].dwOffsetEntry = HASHTABLE_FREE;
1607 (*ppHash)->HashTable[i].dwHashKey = HASHTABLE_FREE;
1609 return ERROR_SUCCESS;
1612 /***********************************************************************
1613 * URLCache_EnumHashTables (Internal)
1615 * Enumerates the hash tables in a container.
1618 * TRUE if an entry was found
1619 * FALSE if there are no more tables to enumerate.
1622 static BOOL URLCache_EnumHashTables(LPCURLCACHE_HEADER pHeader, DWORD *pdwHashTableNumber, HASH_CACHEFILE_ENTRY ** ppHashEntry)
1624 for (*ppHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1625 URLCache_IsHashEntryValid(pHeader, *ppHashEntry);
1626 *ppHashEntry = URLCache_HashEntryFromOffset(pHeader, (*ppHashEntry)->dwAddressNext))
1628 TRACE("looking at hash table number %d\n", (*ppHashEntry)->dwHashTableNumber);
1629 if ((*ppHashEntry)->dwHashTableNumber != *pdwHashTableNumber)
1631 /* make sure that it is in fact a hash entry */
1632 if ((*ppHashEntry)->CacheFileEntry.dwSignature != HASH_SIGNATURE)
1634 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->CacheFileEntry.dwSignature);
1635 (*pdwHashTableNumber)++;
1639 TRACE("hash table number %d found\n", *pdwHashTableNumber);
1645 /***********************************************************************
1646 * URLCache_EnumHashTableEntries (Internal)
1648 * Enumerates entries in a hash table and returns the next non-free entry.
1651 * TRUE if an entry was found
1652 * FALSE if the hash table is empty or there are no more entries to
1656 static BOOL URLCache_EnumHashTableEntries(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY * pHashEntry,
1657 DWORD * index, const struct _HASH_ENTRY ** ppHashEntry)
1659 for (; *index < HASHTABLE_SIZE ; (*index)++)
1661 if (pHashEntry->HashTable[*index].dwHashKey==HASHTABLE_FREE || pHashEntry->HashTable[*index].dwHashKey==HASHTABLE_DEL)
1664 *ppHashEntry = &pHashEntry->HashTable[*index];
1665 TRACE("entry found %d\n", *index);
1668 TRACE("no more entries (%d)\n", *index);
1672 /***********************************************************************
1673 * URLCache_DeleteCacheDirectory (Internal)
1675 * Erase a directory containing an URL cache.
1678 * TRUE success, FALSE failure/aborted.
1681 static BOOL URLCache_DeleteCacheDirectory(LPCWSTR lpszPath)
1684 WCHAR path[MAX_PATH + 1];
1685 SHFILEOPSTRUCTW shfos;
1688 path_len = strlenW(lpszPath);
1689 if (path_len >= MAX_PATH)
1691 strcpyW(path, lpszPath);
1692 path[path_len + 1] = 0; /* double-NUL-terminate path */
1695 shfos.wFunc = FO_DELETE;
1698 shfos.fFlags = FOF_NOCONFIRMATION;
1699 shfos.fAnyOperationsAborted = FALSE;
1700 ret = SHFileOperationW(&shfos);
1702 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path), ret);
1703 return !(ret || shfos.fAnyOperationsAborted);
1706 /***********************************************************************
1707 * URLCache_IsLocked (Internal)
1709 * Checks if entry is locked. Unlocks it if possible.
1711 static BOOL URLCache_IsLocked(struct _HASH_ENTRY *hash_entry, URL_CACHEFILE_ENTRY *url_entry)
1714 ULARGE_INTEGER acc_time, time;
1716 if ((hash_entry->dwHashKey & ((1<<HASHTABLE_FLAG_BITS)-1)) != HASHTABLE_LOCK)
1719 GetSystemTimeAsFileTime(&cur_time);
1720 time.u.LowPart = cur_time.dwLowDateTime;
1721 time.u.HighPart = cur_time.dwHighDateTime;
1723 acc_time.u.LowPart = url_entry->LastAccessTime.dwLowDateTime;
1724 acc_time.u.HighPart = url_entry->LastAccessTime.dwHighDateTime;
1726 time.QuadPart -= acc_time.QuadPart;
1728 /* check if entry was locked for at least a day */
1729 if(time.QuadPart > (ULONGLONG)24*60*60*FILETIME_SECOND) {
1730 URLCache_HashEntrySetFlags(hash_entry, HASHTABLE_URL);
1731 url_entry->dwUseCount = 0;
1738 /***********************************************************************
1739 * GetUrlCacheEntryInfoExA (WININET.@)
1742 BOOL WINAPI GetUrlCacheEntryInfoExA(
1744 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1745 LPDWORD lpdwCacheEntryInfoBufSize,
1747 LPDWORD lpdwReserved,
1751 LPURLCACHE_HEADER pHeader;
1752 struct _HASH_ENTRY * pHashEntry;
1753 const CACHEFILE_ENTRY * pEntry;
1754 const URL_CACHEFILE_ENTRY * pUrlEntry;
1755 URLCACHECONTAINER * pContainer;
1758 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1759 debugstr_a(lpszUrl),
1761 lpdwCacheEntryInfoBufSize,
1767 if ((lpszReserved != NULL) ||
1768 (lpdwReserved != NULL) ||
1769 (lpReserved != NULL))
1771 ERR("Reserved value was not 0\n");
1772 SetLastError(ERROR_INVALID_PARAMETER);
1775 if (dwFlags & ~GET_INSTALLED_ENTRY)
1776 FIXME("ignoring unsupported flags: %x\n", dwFlags);
1778 error = URLCacheContainers_FindContainerA(lpszUrl, &pContainer);
1779 if (error != ERROR_SUCCESS)
1781 SetLastError(error);
1785 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
1786 if (error != ERROR_SUCCESS)
1788 SetLastError(error);
1792 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1795 if (!URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1797 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1798 WARN("entry %s not found!\n", debugstr_a(lpszUrl));
1799 SetLastError(ERROR_FILE_NOT_FOUND);
1803 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1804 if (pEntry->dwSignature != URL_SIGNATURE)
1806 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1807 FIXME("Trying to retrieve entry of unknown format %s\n",
1808 debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1809 SetLastError(ERROR_FILE_NOT_FOUND);
1813 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1814 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1815 TRACE("Header info: %s\n", debugstr_an((LPCSTR)pUrlEntry +
1816 pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize));
1818 if((dwFlags & GET_INSTALLED_ENTRY) && !(pUrlEntry->CacheEntryType & INSTALLED_CACHE_ENTRY))
1820 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1821 SetLastError(ERROR_FILE_NOT_FOUND);
1825 if (lpdwCacheEntryInfoBufSize)
1827 if (!lpCacheEntryInfo)
1828 *lpdwCacheEntryInfoBufSize = 0;
1830 error = URLCache_CopyEntry(
1834 lpdwCacheEntryInfoBufSize,
1837 if (error != ERROR_SUCCESS)
1839 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1840 SetLastError(error);
1843 if(pUrlEntry->dwOffsetLocalName)
1844 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1847 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1852 /***********************************************************************
1853 * GetUrlCacheEntryInfoA (WININET.@)
1856 BOOL WINAPI GetUrlCacheEntryInfoA(
1857 IN LPCSTR lpszUrlName,
1858 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1859 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1862 return GetUrlCacheEntryInfoExA(lpszUrlName, lpCacheEntryInfo,
1863 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
1866 /***********************************************************************
1867 * GetUrlCacheEntryInfoW (WININET.@)
1870 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1871 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1872 LPDWORD lpdwCacheEntryInfoBufferSize)
1874 return GetUrlCacheEntryInfoExW(lpszUrl, lpCacheEntryInfo,
1875 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
1878 /***********************************************************************
1879 * GetUrlCacheEntryInfoExW (WININET.@)
1882 BOOL WINAPI GetUrlCacheEntryInfoExW(
1884 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1885 LPDWORD lpdwCacheEntryInfoBufSize,
1886 LPWSTR lpszReserved,
1887 LPDWORD lpdwReserved,
1891 LPURLCACHE_HEADER pHeader;
1892 struct _HASH_ENTRY * pHashEntry;
1893 const CACHEFILE_ENTRY * pEntry;
1894 const URL_CACHEFILE_ENTRY * pUrlEntry;
1895 URLCACHECONTAINER * pContainer;
1898 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1899 debugstr_w(lpszUrl),
1901 lpdwCacheEntryInfoBufSize,
1907 /* Ignore GET_INSTALLED_ENTRY flag in unicode version of function */
1908 dwFlags &= ~GET_INSTALLED_ENTRY;
1910 if ((lpszReserved != NULL) ||
1911 (lpdwReserved != NULL) ||
1912 (lpReserved != NULL))
1914 ERR("Reserved value was not 0\n");
1915 SetLastError(ERROR_INVALID_PARAMETER);
1919 FIXME("ignoring unsupported flags: %x\n", dwFlags);
1921 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1922 if (error != ERROR_SUCCESS)
1924 SetLastError(error);
1928 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
1929 if (error != ERROR_SUCCESS)
1931 SetLastError(error);
1935 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
1938 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1940 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1941 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1942 SetLastError(ERROR_FILE_NOT_FOUND);
1946 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1947 if (pEntry->dwSignature != URL_SIGNATURE)
1949 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1950 FIXME("Trying to retrieve entry of unknown format %s\n",
1951 debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
1952 SetLastError(ERROR_FILE_NOT_FOUND);
1956 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
1957 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
1958 TRACE("Header info: %s\n", debugstr_an((LPCSTR)pUrlEntry +
1959 pUrlEntry->dwOffsetHeaderInfo, pUrlEntry->dwHeaderInfoSize));
1961 if (lpdwCacheEntryInfoBufSize)
1963 if (!lpCacheEntryInfo)
1964 *lpdwCacheEntryInfoBufSize = 0;
1966 error = URLCache_CopyEntry(
1969 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1970 lpdwCacheEntryInfoBufSize,
1972 TRUE /* UNICODE */);
1973 if (error != ERROR_SUCCESS)
1975 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1976 SetLastError(error);
1979 if(pUrlEntry->dwOffsetLocalName)
1980 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
1983 URLCacheContainer_UnlockIndex(pContainer, pHeader);
1988 /***********************************************************************
1989 * SetUrlCacheEntryInfoA (WININET.@)
1991 BOOL WINAPI SetUrlCacheEntryInfoA(
1993 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1994 DWORD dwFieldControl)
1996 LPURLCACHE_HEADER pHeader;
1997 struct _HASH_ENTRY * pHashEntry;
1998 CACHEFILE_ENTRY * pEntry;
1999 URLCACHECONTAINER * pContainer;
2002 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
2004 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2005 if (error != ERROR_SUCCESS)
2007 SetLastError(error);
2011 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2012 if (error != ERROR_SUCCESS)
2014 SetLastError(error);
2018 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2021 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2023 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2024 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
2025 SetLastError(ERROR_FILE_NOT_FOUND);
2029 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2030 if (pEntry->dwSignature != URL_SIGNATURE)
2032 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2033 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2034 SetLastError(ERROR_FILE_NOT_FOUND);
2038 URLCache_SetEntryInfo(
2039 (URL_CACHEFILE_ENTRY *)pEntry,
2040 (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
2043 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2048 /***********************************************************************
2049 * SetUrlCacheEntryInfoW (WININET.@)
2051 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
2053 LPURLCACHE_HEADER pHeader;
2054 struct _HASH_ENTRY * pHashEntry;
2055 CACHEFILE_ENTRY * pEntry;
2056 URLCACHECONTAINER * pContainer;
2059 TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
2061 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
2062 if (error != ERROR_SUCCESS)
2064 SetLastError(error);
2068 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2069 if (error != ERROR_SUCCESS)
2071 SetLastError(error);
2075 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2078 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
2080 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2081 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
2082 SetLastError(ERROR_FILE_NOT_FOUND);
2086 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2087 if (pEntry->dwSignature != URL_SIGNATURE)
2089 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2090 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2091 SetLastError(ERROR_FILE_NOT_FOUND);
2095 URLCache_SetEntryInfo(
2096 (URL_CACHEFILE_ENTRY *)pEntry,
2100 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2105 /***********************************************************************
2106 * RetrieveUrlCacheEntryFileA (WININET.@)
2109 BOOL WINAPI RetrieveUrlCacheEntryFileA(
2110 IN LPCSTR lpszUrlName,
2111 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2112 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2116 LPURLCACHE_HEADER pHeader;
2117 struct _HASH_ENTRY * pHashEntry;
2118 CACHEFILE_ENTRY * pEntry;
2119 URL_CACHEFILE_ENTRY * pUrlEntry;
2120 URLCACHECONTAINER * pContainer;
2123 TRACE("(%s, %p, %p, 0x%08x)\n",
2124 debugstr_a(lpszUrlName),
2126 lpdwCacheEntryInfoBufferSize,
2129 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
2130 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
2132 SetLastError(ERROR_INVALID_PARAMETER);
2136 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2137 if (error != ERROR_SUCCESS)
2139 SetLastError(error);
2143 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2144 if (error != ERROR_SUCCESS)
2146 SetLastError(error);
2150 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2153 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2155 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2156 TRACE("entry %s not found!\n", lpszUrlName);
2157 SetLastError(ERROR_FILE_NOT_FOUND);
2161 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2162 if (pEntry->dwSignature != URL_SIGNATURE)
2164 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2165 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2166 SetLastError(ERROR_FILE_NOT_FOUND);
2170 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2171 if (!pUrlEntry->dwOffsetLocalName)
2173 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2174 SetLastError(ERROR_INVALID_DATA);
2178 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
2179 TRACE("Header info: %s\n", debugstr_an((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo,
2180 pUrlEntry->dwHeaderInfoSize));
2182 error = URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo,
2183 lpdwCacheEntryInfoBufferSize, pUrlEntry,
2185 if (error != ERROR_SUCCESS)
2187 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2188 SetLastError(error);
2191 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2193 pUrlEntry->dwHitRate++;
2194 pUrlEntry->dwUseCount++;
2195 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_LOCK);
2196 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2198 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2203 /***********************************************************************
2204 * RetrieveUrlCacheEntryFileW (WININET.@)
2207 BOOL WINAPI RetrieveUrlCacheEntryFileW(
2208 IN LPCWSTR lpszUrlName,
2209 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2210 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2214 LPURLCACHE_HEADER pHeader;
2215 struct _HASH_ENTRY * pHashEntry;
2216 CACHEFILE_ENTRY * pEntry;
2217 URL_CACHEFILE_ENTRY * pUrlEntry;
2218 URLCACHECONTAINER * pContainer;
2221 TRACE("(%s, %p, %p, 0x%08x)\n",
2222 debugstr_w(lpszUrlName),
2224 lpdwCacheEntryInfoBufferSize,
2227 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
2228 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
2230 SetLastError(ERROR_INVALID_PARAMETER);
2234 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2235 if (error != ERROR_SUCCESS)
2237 SetLastError(error);
2241 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2242 if (error != ERROR_SUCCESS)
2244 SetLastError(error);
2248 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2251 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2253 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2254 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2255 SetLastError(ERROR_FILE_NOT_FOUND);
2259 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2260 if (pEntry->dwSignature != URL_SIGNATURE)
2262 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2263 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2264 SetLastError(ERROR_FILE_NOT_FOUND);
2268 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2269 if (!pUrlEntry->dwOffsetLocalName)
2271 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2272 SetLastError(ERROR_INVALID_DATA);
2276 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
2277 TRACE("Header info: %s\n", debugstr_an((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo,
2278 pUrlEntry->dwHeaderInfoSize));
2280 error = URLCache_CopyEntry(
2283 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
2284 lpdwCacheEntryInfoBufferSize,
2286 TRUE /* UNICODE */);
2287 if (error != ERROR_SUCCESS)
2289 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2290 SetLastError(error);
2293 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
2295 pUrlEntry->dwHitRate++;
2296 pUrlEntry->dwUseCount++;
2297 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_LOCK);
2298 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
2300 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2305 static BOOL DeleteUrlCacheEntryInternal(const URLCACHECONTAINER * pContainer,
2306 LPURLCACHE_HEADER pHeader, struct _HASH_ENTRY *pHashEntry)
2308 CACHEFILE_ENTRY * pEntry;
2309 URL_CACHEFILE_ENTRY * pUrlEntry;
2311 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2312 if (pEntry->dwSignature != URL_SIGNATURE)
2314 FIXME("Trying to delete entry of unknown format %s\n",
2315 debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
2316 SetLastError(ERROR_FILE_NOT_FOUND);
2320 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2321 if(URLCache_IsLocked(pHashEntry, pUrlEntry))
2323 TRACE("Trying to delete locked entry\n");
2324 pUrlEntry->CacheEntryType |= PENDING_DELETE_CACHE_ENTRY;
2325 SetLastError(ERROR_SHARING_VIOLATION);
2329 if(!URLCache_DeleteFile(pContainer, pHeader, pUrlEntry))
2331 URLCache_DeleteEntry(pHeader, pEntry);
2335 /* Add entry to leaked files list */
2336 pUrlEntry->CacheFileEntry.dwSignature = LEAK_SIGNATURE;
2337 pUrlEntry->dwExemptDelta = pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
2338 pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET] = pHashEntry->dwOffsetEntry;
2341 URLCache_DeleteEntryFromHash(pHashEntry);
2345 static HANDLE free_cache_running;
2346 static HANDLE dll_unload_event;
2347 static DWORD WINAPI handle_full_cache_worker(void *param)
2349 FreeUrlCacheSpaceW(NULL, 20, 0);
2350 ReleaseSemaphore(free_cache_running, 1, NULL);
2354 static void handle_full_cache(void)
2356 if(WaitForSingleObject(free_cache_running, 0) == WAIT_OBJECT_0) {
2357 if(!QueueUserWorkItem(handle_full_cache_worker, NULL, 0))
2358 ReleaseSemaphore(free_cache_running, 1, NULL);
2362 /* Enumerates entries in cache, allows cache unlocking between calls. */
2363 static BOOL urlcache_next_entry(URLCACHE_HEADER *header, DWORD *hash_table_off, DWORD *hash_table_entry,
2364 struct _HASH_ENTRY **hash_entry, CACHEFILE_ENTRY **entry)
2366 HASH_CACHEFILE_ENTRY *hashtable_entry;
2371 if(!*hash_table_off) {
2372 *hash_table_off = header->dwOffsetFirstHashTable;
2373 *hash_table_entry = 0;
2375 hashtable_entry = URLCache_HashEntryFromOffset(header, *hash_table_off);
2377 if(*hash_table_off >= header->dwFileSize) {
2378 *hash_table_off = 0;
2382 hashtable_entry = URLCache_HashEntryFromOffset(header, *hash_table_off);
2385 if(hashtable_entry->CacheFileEntry.dwSignature != HASH_SIGNATURE) {
2386 *hash_table_off = 0;
2391 if(*hash_table_entry >= HASHTABLE_SIZE) {
2392 *hash_table_off = hashtable_entry->dwAddressNext;
2393 if(!*hash_table_off) {
2394 *hash_table_off = 0;
2398 hashtable_entry = URLCache_HashEntryFromOffset(header, *hash_table_off);
2399 *hash_table_entry = 0;
2402 if(hashtable_entry->HashTable[*hash_table_entry].dwHashKey != HASHTABLE_DEL &&
2403 hashtable_entry->HashTable[*hash_table_entry].dwHashKey != HASHTABLE_FREE) {
2404 *hash_entry = &hashtable_entry->HashTable[*hash_table_entry];
2405 *entry = (CACHEFILE_ENTRY*)((LPBYTE)header + hashtable_entry->HashTable[*hash_table_entry].dwOffsetEntry);
2406 (*hash_table_entry)++;
2410 (*hash_table_entry)++;
2413 *hash_table_off = 0;
2417 /* Rates an urlcache entry to determine if it can be deleted.
2419 * Score 0 means that entry can safely be removed, the bigger rating
2420 * the smaller chance of entry being removed.
2421 * DWORD_MAX means that entry can't be deleted at all.
2423 * Rating system is currently not fully compatible with native implementation.
2425 static DWORD urlcache_rate_entry(URL_CACHEFILE_ENTRY *url_entry, FILETIME *cur_time)
2427 ULARGE_INTEGER time, access_time;
2430 access_time.u.LowPart = url_entry->LastAccessTime.dwLowDateTime;
2431 access_time.u.HighPart = url_entry->LastAccessTime.dwHighDateTime;
2433 time.u.LowPart = cur_time->dwLowDateTime;
2434 time.u.HighPart = cur_time->dwHighDateTime;
2436 /* Don't touch entries that were added less than 10 minutes ago */
2437 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)10*60*FILETIME_SECOND)
2440 if(url_entry->CacheEntryType & STICKY_CACHE_ENTRY)
2441 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)url_entry->dwExemptDelta*FILETIME_SECOND)
2444 time.QuadPart = (time.QuadPart-access_time.QuadPart)/FILETIME_SECOND;
2445 rating = 400*60*60*24/(60*60*24+time.QuadPart);
2447 if(url_entry->dwHitRate > 100)
2450 rating += url_entry->dwHitRate;
2455 static int dword_cmp(const void *p1, const void *p2)
2457 return *(const DWORD*)p1 - *(const DWORD*)p2;
2460 /***********************************************************************
2461 * FreeUrlCacheSpaceW (WININET.@)
2463 * Frees up some cache.
2466 * cache_path [I] Which volume to free up from, or NULL if you don't care.
2467 * size [I] Percentage of the cache that should be free.
2468 * filter [I] Which entries can't be deleted (CacheEntryType)
2471 * TRUE success. FALSE failure.
2474 * This implementation just retrieves the path of the cache directory, and
2475 * deletes its contents from the filesystem. The correct approach would
2476 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
2478 BOOL WINAPI FreeUrlCacheSpaceW(LPCWSTR cache_path, DWORD size, DWORD filter)
2480 URLCACHECONTAINER *container;
2481 DWORD path_len, err;
2483 TRACE("(%s, %x, %x)\n", debugstr_w(cache_path), size, filter);
2485 if(size<1 || size>100) {
2486 SetLastError(ERROR_INVALID_PARAMETER);
2491 path_len = strlenW(cache_path);
2492 if(cache_path[path_len-1] == '\\')
2498 if(size==100 && !filter) {
2499 LIST_FOR_EACH_ENTRY(container, &UrlContainers, URLCACHECONTAINER, entry)
2501 /* When cache_path==NULL only clean Temporary Internet Files */
2502 if((!path_len && container->cache_prefix[0]==0) ||
2503 (path_len && !strncmpiW(container->path, cache_path, path_len) &&
2504 (container->path[path_len]=='\0' || container->path[path_len]=='\\')))
2508 WaitForSingleObject(container->hMutex, INFINITE);
2510 /* unlock, delete, recreate and lock cache */
2511 URLCacheContainer_CloseIndex(container);
2512 ret_del = URLCache_DeleteCacheDirectory(container->path);
2513 err = cache_container_open_index(container, MIN_BLOCK_NO);
2515 ReleaseMutex(container->hMutex);
2516 if(!ret_del || (err != ERROR_SUCCESS))
2524 LIST_FOR_EACH_ENTRY(container, &UrlContainers, URLCACHECONTAINER, entry)
2526 URLCACHE_HEADER *header;
2527 struct _HASH_ENTRY *hash_entry;
2528 CACHEFILE_ENTRY *entry;
2529 URL_CACHEFILE_ENTRY *url_entry;
2530 ULONGLONG desired_size, cur_size;
2531 DWORD delete_factor, hash_table_off, hash_table_entry;
2532 DWORD rate[100], rate_no;
2535 if((path_len || container->cache_prefix[0]!=0) &&
2536 (!path_len || strncmpiW(container->path, cache_path, path_len) ||
2537 (container->path[path_len]!='\0' && container->path[path_len]!='\\')))
2540 err = cache_container_open_index(container, MIN_BLOCK_NO);
2541 if(err != ERROR_SUCCESS)
2544 header = URLCacheContainer_LockIndex(container);
2548 urlcache_clean_leaked_entries(container, header);
2550 desired_size = header->CacheLimit.QuadPart*(100-size)/100;
2551 cur_size = header->CacheUsage.QuadPart+header->ExemptUsage.QuadPart;
2552 if(cur_size <= desired_size)
2555 delete_factor = (cur_size-desired_size)*100/cur_size;
2557 if(!delete_factor) {
2558 URLCacheContainer_UnlockIndex(container, header);
2563 hash_table_entry = 0;
2565 GetSystemTimeAsFileTime(&cur_time);
2566 while(rate_no<sizeof(rate)/sizeof(*rate) &&
2567 urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2568 if(entry->dwSignature != URL_SIGNATURE) {
2569 WARN("only url entries are currently supported\n");
2573 url_entry = (URL_CACHEFILE_ENTRY*)entry;
2574 if(url_entry->CacheEntryType & filter)
2577 rate[rate_no] = urlcache_rate_entry(url_entry, &cur_time);
2578 if(rate[rate_no] != -1)
2583 TRACE("nothing to delete\n");
2584 URLCacheContainer_UnlockIndex(container, header);
2588 qsort(rate, rate_no, sizeof(DWORD), dword_cmp);
2590 delete_factor = delete_factor*rate_no/100;
2591 delete_factor = rate[delete_factor];
2592 TRACE("deleting files with rating %d or less\n", delete_factor);
2595 while(urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2596 if(entry->dwSignature != URL_SIGNATURE)
2599 url_entry = (URL_CACHEFILE_ENTRY*)entry;
2600 if(url_entry->CacheEntryType & filter)
2603 if(urlcache_rate_entry(url_entry, &cur_time) <= delete_factor) {
2604 TRACE("deleting file: %s\n", debugstr_a((char*)url_entry+url_entry->dwOffsetLocalName));
2605 DeleteUrlCacheEntryInternal(container, header, hash_entry);
2607 if(header->CacheUsage.QuadPart+header->ExemptUsage.QuadPart <= desired_size)
2610 /* Allow other threads to use cache while cleaning */
2611 URLCacheContainer_UnlockIndex(container, header);
2612 if(WaitForSingleObject(dll_unload_event, 0) == WAIT_OBJECT_0) {
2613 TRACE("got dll_unload_event - finishing\n");
2617 header = URLCacheContainer_LockIndex(container);
2621 TRACE("cache size after cleaning 0x%s/0x%s\n",
2622 wine_dbgstr_longlong(header->CacheUsage.QuadPart+header->ExemptUsage.QuadPart),
2623 wine_dbgstr_longlong(header->CacheLimit.QuadPart));
2624 URLCacheContainer_UnlockIndex(container, header);
2630 /***********************************************************************
2631 * FreeUrlCacheSpaceA (WININET.@)
2633 * See FreeUrlCacheSpaceW.
2635 BOOL WINAPI FreeUrlCacheSpaceA(LPCSTR lpszCachePath, DWORD dwSize, DWORD dwFilter)
2638 LPWSTR path = heap_strdupAtoW(lpszCachePath);
2639 if (lpszCachePath == NULL || path != NULL)
2640 ret = FreeUrlCacheSpaceW(path, dwSize, dwFilter);
2645 /***********************************************************************
2646 * UnlockUrlCacheEntryFileA (WININET.@)
2649 BOOL WINAPI UnlockUrlCacheEntryFileA(
2650 IN LPCSTR lpszUrlName,
2654 LPURLCACHE_HEADER pHeader;
2655 struct _HASH_ENTRY * pHashEntry;
2656 CACHEFILE_ENTRY * pEntry;
2657 URL_CACHEFILE_ENTRY * pUrlEntry;
2658 URLCACHECONTAINER * pContainer;
2661 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2665 ERR("dwReserved != 0\n");
2666 SetLastError(ERROR_INVALID_PARAMETER);
2670 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2671 if (error != ERROR_SUCCESS)
2673 SetLastError(error);
2677 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2678 if (error != ERROR_SUCCESS)
2680 SetLastError(error);
2684 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2687 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2689 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2690 TRACE("entry %s not found!\n", lpszUrlName);
2691 SetLastError(ERROR_FILE_NOT_FOUND);
2695 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2696 if (pEntry->dwSignature != URL_SIGNATURE)
2698 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2699 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2700 SetLastError(ERROR_FILE_NOT_FOUND);
2704 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2706 if (pUrlEntry->dwUseCount == 0)
2708 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2711 pUrlEntry->dwUseCount--;
2712 if (!pUrlEntry->dwUseCount)
2714 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_URL);
2715 if (pUrlEntry->CacheEntryType & PENDING_DELETE_CACHE_ENTRY)
2716 DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
2719 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2724 /***********************************************************************
2725 * UnlockUrlCacheEntryFileW (WININET.@)
2728 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
2730 LPURLCACHE_HEADER pHeader;
2731 struct _HASH_ENTRY * pHashEntry;
2732 CACHEFILE_ENTRY * pEntry;
2733 URL_CACHEFILE_ENTRY * pUrlEntry;
2734 URLCACHECONTAINER * pContainer;
2737 TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
2741 ERR("dwReserved != 0\n");
2742 SetLastError(ERROR_INVALID_PARAMETER);
2746 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2747 if (error != ERROR_SUCCESS)
2749 SetLastError(error);
2753 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2754 if (error != ERROR_SUCCESS)
2756 SetLastError(error);
2760 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2763 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2765 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2766 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2767 SetLastError(ERROR_FILE_NOT_FOUND);
2771 pEntry = (CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2772 if (pEntry->dwSignature != URL_SIGNATURE)
2774 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2775 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->dwSignature, sizeof(DWORD)));
2776 SetLastError(ERROR_FILE_NOT_FOUND);
2780 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
2782 if (pUrlEntry->dwUseCount == 0)
2784 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2787 pUrlEntry->dwUseCount--;
2788 if (!pUrlEntry->dwUseCount)
2789 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_URL);
2791 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2796 /***********************************************************************
2797 * CreateUrlCacheEntryA (WININET.@)
2800 BOOL WINAPI CreateUrlCacheEntryA(
2801 IN LPCSTR lpszUrlName,
2802 IN DWORD dwExpectedFileSize,
2803 IN LPCSTR lpszFileExtension,
2804 OUT LPSTR lpszFileName,
2809 WCHAR *file_extension = NULL;
2810 WCHAR file_name[MAX_PATH];
2811 BOOL bSuccess = FALSE;
2814 TRACE("(%s %d %s %p %d)\n", debugstr_a(lpszUrlName), dwExpectedFileSize,
2815 debugstr_a(lpszFileExtension), lpszFileName, dwReserved);
2817 if (lpszUrlName && (url_name = heap_strdupAtoW(lpszUrlName)))
2819 if (!lpszFileExtension || (file_extension = heap_strdupAtoW(lpszFileExtension)))
2821 if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
2823 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
2829 dwError = GetLastError();
2834 dwError = GetLastError();
2836 heap_free(file_extension);
2840 dwError = GetLastError();
2842 heap_free(url_name);
2843 if (!bSuccess) SetLastError(dwError);
2847 /***********************************************************************
2848 * CreateUrlCacheEntryW (WININET.@)
2851 BOOL WINAPI CreateUrlCacheEntryW(
2852 IN LPCWSTR lpszUrlName,
2853 IN DWORD dwExpectedFileSize,
2854 IN LPCWSTR lpszFileExtension,
2855 OUT LPWSTR lpszFileName,
2859 URLCACHECONTAINER * pContainer;
2860 LPURLCACHE_HEADER pHeader;
2861 CHAR szFile[MAX_PATH];
2862 WCHAR szExtension[MAX_PATH];
2863 LPCWSTR lpszUrlPart;
2865 LPCWSTR lpszFileNameExtension;
2866 LPWSTR lpszFileNameNoPath;
2868 int countnoextension;
2871 BOOL bFound = FALSE;
2872 BOOL generate_name = FALSE;
2878 static const WCHAR szWWW[] = {'w','w','w',0};
2880 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2881 debugstr_w(lpszUrlName),
2883 debugstr_w(lpszFileExtension),
2888 FIXME("dwReserved 0x%08x\n", dwReserved);
2890 lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2892 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2895 lpszUrlPart = memchrW(lpszUrlName, '?', lpszUrlEnd - lpszUrlName);
2897 lpszUrlPart = memchrW(lpszUrlName, '#', lpszUrlEnd - lpszUrlName);
2899 lpszUrlEnd = lpszUrlPart;
2901 for (lpszUrlPart = lpszUrlEnd;
2902 (lpszUrlPart >= lpszUrlName);
2905 if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2915 if (!lstrcmpW(lpszUrlPart, szWWW))
2917 lpszUrlPart += lstrlenW(szWWW);
2920 count = lpszUrlEnd - lpszUrlPart;
2922 if (bFound && (count < MAX_PATH))
2924 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2928 while(len && szFile[--len] == '/') szFile[len] = '\0';
2930 /* FIXME: get rid of illegal characters like \, / and : */
2931 TRACE("File name: %s\n", debugstr_a(szFile));
2935 generate_name = TRUE;
2939 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2940 if (error != ERROR_SUCCESS)
2942 SetLastError(error);
2946 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2947 if (error != ERROR_SUCCESS)
2949 SetLastError(error);
2953 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
2956 if(pHeader->DirectoryCount)
2957 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2959 CacheDir = CACHE_CONTAINER_NO_SUBDIR;
2961 lBufferSize = MAX_PATH * sizeof(WCHAR);
2962 if (!URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize))
2964 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2965 debugstr_a(szFile), lBufferSize);
2966 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2970 URLCacheContainer_UnlockIndex(pContainer, pHeader);
2972 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2973 lpszFileNameNoPath >= lpszFileName;
2974 --lpszFileNameNoPath)
2976 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2980 countnoextension = lstrlenW(lpszFileNameNoPath);
2981 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2982 if (lpszFileNameExtension)
2983 countnoextension -= lstrlenW(lpszFileNameExtension);
2984 *szExtension = '\0';
2986 if (lpszFileExtension)
2988 szExtension[0] = '.';
2989 lstrcpyW(szExtension+1, lpszFileExtension);
2992 for (i = 0; i<255 && !generate_name; i++)
2994 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2997 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2998 for (p = lpszFileNameNoPath + 1; *p; p++)
3004 case '/': case '\\':
3011 if (p[-1] == ' ' || p[-1] == '.') p[-1] = '_';
3013 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
3014 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
3015 if (hFile != INVALID_HANDLE_VALUE)
3022 /* Try to generate random name */
3023 GetSystemTimeAsFileTime(&ft);
3024 strcpyW(lpszFileNameNoPath+countnoextension+8, szExtension);
3026 for(i=0; i<255; i++)
3029 ULONGLONG n = ft.dwHighDateTime;
3031 n += ft.dwLowDateTime;
3032 n ^= (ULONGLONG)i<<48;
3038 lpszFileNameNoPath[countnoextension+j] = (r < 10 ? '0' + r : 'A' + r - 10);
3041 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
3042 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
3043 if (hFile != INVALID_HANDLE_VALUE)
3050 WARN("Could not find a unique filename\n");
3054 /***********************************************************************
3055 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
3057 * The bug we are compensating for is that some drongo at Microsoft
3058 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
3059 * As a consequence, CommitUrlCacheEntryA has been effectively
3060 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
3061 * is still defined as LPCWSTR. The result (other than madness) is
3062 * that we always need to store lpHeaderInfo in CP_ACP rather than
3063 * in UTF16, and we need to avoid converting lpHeaderInfo in
3064 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
3065 * result will lose data for arbitrary binary data.
3068 static BOOL CommitUrlCacheEntryInternal(
3069 IN LPCWSTR lpszUrlName,
3070 IN LPCWSTR lpszLocalFileName,
3071 IN FILETIME ExpireTime,
3072 IN FILETIME LastModifiedTime,
3073 IN DWORD CacheEntryType,
3074 IN LPBYTE lpHeaderInfo,
3075 IN DWORD dwHeaderSize,
3076 IN LPCWSTR lpszFileExtension,
3077 IN LPCWSTR lpszOriginalUrl
3080 URLCACHECONTAINER * pContainer;
3081 LPURLCACHE_HEADER pHeader;
3082 struct _HASH_ENTRY * pHashEntry;
3083 CACHEFILE_ENTRY * pEntry;
3084 URL_CACHEFILE_ENTRY * pUrlEntry;
3085 DWORD url_entry_offset;
3086 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
3087 DWORD dwOffsetLocalFileName = 0;
3088 DWORD dwOffsetHeader = 0;
3089 DWORD dwOffsetFileExtension = 0;
3090 WIN32_FILE_ATTRIBUTE_DATA file_attr;
3091 LARGE_INTEGER file_size;
3093 char achFile[MAX_PATH];
3094 LPSTR lpszUrlNameA = NULL;
3095 LPSTR lpszFileExtensionA = NULL;
3096 char *pchLocalFileName = 0;
3098 DWORD exempt_delta = 0;
3101 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3102 debugstr_w(lpszUrlName),
3103 debugstr_w(lpszLocalFileName),
3107 debugstr_w(lpszFileExtension),
3108 debugstr_w(lpszOriginalUrl));
3110 if (CacheEntryType & STICKY_CACHE_ENTRY && !lpszLocalFileName)
3112 SetLastError(ERROR_INVALID_PARAMETER);
3115 if (lpszOriginalUrl)
3116 WARN(": lpszOriginalUrl ignored\n");
3118 memset(&file_attr, 0, sizeof(file_attr));
3119 if (lpszLocalFileName)
3121 if(!GetFileAttributesExW(lpszLocalFileName, GetFileExInfoStandard, &file_attr))
3124 file_size.u.LowPart = file_attr.nFileSizeLow;
3125 file_size.u.HighPart = file_attr.nFileSizeHigh;
3127 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3128 if (error != ERROR_SUCCESS)
3130 SetLastError(error);
3134 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3135 if (error != ERROR_SUCCESS)
3137 SetLastError(error);
3141 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3144 lpszUrlNameA = heap_strdupWtoA(lpszUrlName);
3147 error = GetLastError();
3151 if (lpszFileExtension && !(lpszFileExtensionA = heap_strdupWtoA(lpszFileExtension)))
3153 error = GetLastError();
3157 if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
3159 URL_CACHEFILE_ENTRY *pUrlEntry = (URL_CACHEFILE_ENTRY*)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3160 if (URLCache_IsLocked(pHashEntry, pUrlEntry))
3162 TRACE("Trying to overwrite locked entry\n");
3163 error = ERROR_SHARING_VIOLATION;
3167 hit_rate = pUrlEntry->dwHitRate;
3168 exempt_delta = pUrlEntry->dwExemptDelta;
3169 DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
3172 if (pHeader->DirectoryCount)
3175 cDirectory = CACHE_CONTAINER_NO_SUBDIR;
3177 if (lpszLocalFileName)
3179 BOOL bFound = FALSE;
3181 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
3183 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
3184 error = ERROR_INVALID_PARAMETER;
3188 /* skip container path prefix */
3189 lpszLocalFileName += lstrlenW(pContainer->path);
3191 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, MAX_PATH, NULL, NULL);
3192 pchLocalFileName = achFile;
3194 if(pHeader->DirectoryCount)
3196 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
3198 if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
3207 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
3208 error = ERROR_INVALID_PARAMETER;
3212 lpszLocalFileName += DIR_LENGTH + 1;
3213 pchLocalFileName += DIR_LENGTH + 1;
3217 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
3218 if (lpszLocalFileName)
3220 dwOffsetLocalFileName = dwBytesNeeded;
3221 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
3225 dwOffsetHeader = dwBytesNeeded;
3226 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
3228 if (lpszFileExtensionA)
3230 dwOffsetFileExtension = dwBytesNeeded;
3231 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
3234 /* round up to next block */
3235 if (dwBytesNeeded % BLOCKSIZE)
3237 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
3238 dwBytesNeeded += BLOCKSIZE;
3241 error = URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry);
3242 while (error == ERROR_HANDLE_DISK_FULL)
3244 error = URLCacheContainer_CleanIndex(pContainer, &pHeader);
3245 if (error == ERROR_SUCCESS)
3246 error = URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry);
3248 if (error != ERROR_SUCCESS)
3251 /* FindFirstFreeEntry fills in blocks used */
3252 pUrlEntry = (URL_CACHEFILE_ENTRY *)pEntry;
3253 url_entry_offset = (LPBYTE)pUrlEntry - (LPBYTE)pHeader;
3254 pUrlEntry->CacheFileEntry.dwSignature = URL_SIGNATURE;
3255 pUrlEntry->CacheDir = cDirectory;
3256 pUrlEntry->CacheEntryType = CacheEntryType | pContainer->default_entry_type;
3257 pUrlEntry->dwHeaderInfoSize = dwHeaderSize;
3258 if ((CacheEntryType & STICKY_CACHE_ENTRY) && !exempt_delta)
3260 /* Sticky entries have a default exempt time of one day */
3261 exempt_delta = 86400;
3263 pUrlEntry->dwExemptDelta = exempt_delta;
3264 pUrlEntry->dwHitRate = hit_rate+1;
3265 pUrlEntry->dwOffsetFileExtension = dwOffsetFileExtension;
3266 pUrlEntry->dwOffsetHeaderInfo = dwOffsetHeader;
3267 pUrlEntry->dwOffsetLocalName = dwOffsetLocalFileName;
3268 pUrlEntry->dwOffsetUrl = DWORD_ALIGN(sizeof(*pUrlEntry));
3269 pUrlEntry->size.QuadPart = file_size.QuadPart;
3270 pUrlEntry->dwUseCount = 0;
3271 GetSystemTimeAsFileTime(&pUrlEntry->LastAccessTime);
3272 pUrlEntry->LastModifiedTime = LastModifiedTime;
3273 URLCache_FileTimeToDosDateTime(&pUrlEntry->LastAccessTime, &pUrlEntry->wLastSyncDate, &pUrlEntry->wLastSyncTime);
3274 URLCache_FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->wExpiredDate, &pUrlEntry->wExpiredTime);
3275 URLCache_FileTimeToDosDateTime(&file_attr.ftLastWriteTime, &pUrlEntry->LastWriteDate, &pUrlEntry->LastWriteTime);
3278 pUrlEntry->dwUnknown1 = 0;
3279 pUrlEntry->dwUnknown2 = 0;
3280 pUrlEntry->dwUnknown3 = 0x60;
3281 pUrlEntry->Unknown4 = 0;
3282 pUrlEntry->wUnknown5 = 0x1010;
3283 pUrlEntry->dwUnknown7 = 0;
3284 pUrlEntry->dwUnknown8 = 0;
3287 strcpy((LPSTR)pUrlEntry + pUrlEntry->dwOffsetUrl, lpszUrlNameA);
3288 if (dwOffsetLocalFileName)
3289 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName);
3291 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
3292 if (dwOffsetFileExtension)
3293 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
3295 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA, url_entry_offset, HASHTABLE_URL);
3296 while (error == ERROR_HANDLE_DISK_FULL)
3298 error = URLCacheContainer_CleanIndex(pContainer, &pHeader);
3299 if (error == ERROR_SUCCESS)
3301 pUrlEntry = (URL_CACHEFILE_ENTRY *)((LPBYTE)pHeader + url_entry_offset);
3302 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA,
3303 url_entry_offset, HASHTABLE_URL);
3306 if (error != ERROR_SUCCESS)
3307 URLCache_DeleteEntry(pHeader, &pUrlEntry->CacheFileEntry);
3310 if (pUrlEntry->CacheDir < pHeader->DirectoryCount)
3311 pHeader->directory_data[pUrlEntry->CacheDir].dwNumFiles++;
3312 if (CacheEntryType & STICKY_CACHE_ENTRY)
3313 pHeader->ExemptUsage.QuadPart += file_size.QuadPart;
3315 pHeader->CacheUsage.QuadPart += file_size.QuadPart;
3316 if (pHeader->CacheUsage.QuadPart + pHeader->ExemptUsage.QuadPart >
3317 pHeader->CacheLimit.QuadPart)
3318 handle_full_cache();
3322 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3323 heap_free(lpszUrlNameA);
3324 heap_free(lpszFileExtensionA);
3326 if (error == ERROR_SUCCESS)
3330 SetLastError(error);
3335 /***********************************************************************
3336 * CommitUrlCacheEntryA (WININET.@)
3339 BOOL WINAPI CommitUrlCacheEntryA(
3340 IN LPCSTR lpszUrlName,
3341 IN LPCSTR lpszLocalFileName,
3342 IN FILETIME ExpireTime,
3343 IN FILETIME LastModifiedTime,
3344 IN DWORD CacheEntryType,
3345 IN LPBYTE lpHeaderInfo,
3346 IN DWORD dwHeaderSize,
3347 IN LPCSTR lpszFileExtension,
3348 IN LPCSTR lpszOriginalUrl
3351 WCHAR *url_name = NULL;
3352 WCHAR *local_file_name = NULL;
3353 WCHAR *original_url = NULL;
3354 WCHAR *file_extension = NULL;
3355 BOOL bSuccess = FALSE;
3357 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3358 debugstr_a(lpszUrlName),
3359 debugstr_a(lpszLocalFileName),
3363 debugstr_a(lpszFileExtension),
3364 debugstr_a(lpszOriginalUrl));
3366 url_name = heap_strdupAtoW(lpszUrlName);
3370 if (lpszLocalFileName)
3372 local_file_name = heap_strdupAtoW(lpszLocalFileName);
3373 if (!local_file_name)
3376 if (lpszFileExtension)
3378 file_extension = heap_strdupAtoW(lpszFileExtension);
3379 if (!file_extension)
3382 if (lpszOriginalUrl)
3384 original_url = heap_strdupAtoW(lpszOriginalUrl);
3389 bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
3390 CacheEntryType, lpHeaderInfo, dwHeaderSize,
3391 file_extension, original_url);
3394 heap_free(original_url);
3395 heap_free(file_extension);
3396 heap_free(local_file_name);
3397 heap_free(url_name);
3401 /***********************************************************************
3402 * CommitUrlCacheEntryW (WININET.@)
3405 BOOL WINAPI CommitUrlCacheEntryW(
3406 IN LPCWSTR lpszUrlName,
3407 IN LPCWSTR lpszLocalFileName,
3408 IN FILETIME ExpireTime,
3409 IN FILETIME LastModifiedTime,
3410 IN DWORD CacheEntryType,
3411 IN LPWSTR lpHeaderInfo,
3412 IN DWORD dwHeaderSize,
3413 IN LPCWSTR lpszFileExtension,
3414 IN LPCWSTR lpszOriginalUrl
3418 BOOL bSuccess = FALSE;
3420 CHAR *header_info = NULL;
3422 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3423 debugstr_w(lpszUrlName),
3424 debugstr_w(lpszLocalFileName),
3428 debugstr_w(lpszFileExtension),
3429 debugstr_w(lpszOriginalUrl));
3431 if (!lpHeaderInfo || (header_info = heap_strdupWtoA(lpHeaderInfo)))
3434 len = strlen(header_info);
3435 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
3436 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
3442 dwError = GetLastError();
3446 heap_free(header_info);
3448 SetLastError(dwError);
3454 /***********************************************************************
3455 * ReadUrlCacheEntryStream (WININET.@)
3458 BOOL WINAPI ReadUrlCacheEntryStream(
3459 IN HANDLE hUrlCacheStream,
3460 IN DWORD dwLocation,
3461 IN OUT LPVOID lpBuffer,
3462 IN OUT LPDWORD lpdwLen,
3466 /* Get handle to file from 'stream' */
3467 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3469 if (dwReserved != 0)
3471 ERR("dwReserved != 0\n");
3472 SetLastError(ERROR_INVALID_PARAMETER);
3476 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3478 SetLastError(ERROR_INVALID_HANDLE);
3482 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
3484 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
3487 /***********************************************************************
3488 * RetrieveUrlCacheEntryStreamA (WININET.@)
3491 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
3492 IN LPCSTR lpszUrlName,
3493 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
3494 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3495 IN BOOL fRandomRead,
3499 /* NOTE: this is not the same as the way that the native
3500 * version allocates 'stream' handles. I did it this way
3501 * as it is much easier and no applications should depend
3502 * on this behaviour. (Native version appears to allocate
3503 * indices into a table)
3505 STREAM_HANDLE * pStream;
3508 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
3509 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3511 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
3513 lpdwCacheEntryInfoBufferSize,
3519 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
3524 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3526 if (hFile == INVALID_HANDLE_VALUE)
3529 /* allocate handle storage space */
3530 pStream = heap_alloc(sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
3534 SetLastError(ERROR_OUTOFMEMORY);
3538 pStream->hFile = hFile;
3539 strcpy(pStream->lpszUrl, lpszUrlName);
3543 /***********************************************************************
3544 * RetrieveUrlCacheEntryStreamW (WININET.@)
3547 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
3548 IN LPCWSTR lpszUrlName,
3549 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3550 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3551 IN BOOL fRandomRead,
3557 /* NOTE: this is not the same as the way that the native
3558 * version allocates 'stream' handles. I did it this way
3559 * as it is much easier and no applications should depend
3560 * on this behaviour. (Native version appears to allocate
3561 * indices into a table)
3563 STREAM_HANDLE * pStream;
3566 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3567 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3569 if (!RetrieveUrlCacheEntryFileW(lpszUrlName,
3571 lpdwCacheEntryInfoBufferSize,
3577 hFile = CreateFileW(lpCacheEntryInfo->lpszLocalFileName,
3582 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3584 if (hFile == INVALID_HANDLE_VALUE)
3587 /* allocate handle storage space */
3588 size = sizeof(STREAM_HANDLE);
3589 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
3591 pStream = heap_alloc(size);
3595 SetLastError(ERROR_OUTOFMEMORY);
3599 pStream->hFile = hFile;
3600 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, pStream->lpszUrl, url_len, NULL, NULL);
3604 /***********************************************************************
3605 * UnlockUrlCacheEntryStream (WININET.@)
3608 BOOL WINAPI UnlockUrlCacheEntryStream(
3609 IN HANDLE hUrlCacheStream,
3613 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3615 if (dwReserved != 0)
3617 ERR("dwReserved != 0\n");
3618 SetLastError(ERROR_INVALID_PARAMETER);
3622 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3624 SetLastError(ERROR_INVALID_HANDLE);
3628 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
3631 CloseHandle(pStream->hFile);
3637 /***********************************************************************
3638 * DeleteUrlCacheEntryA (WININET.@)
3641 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3643 URLCACHECONTAINER * pContainer;
3644 LPURLCACHE_HEADER pHeader;
3645 struct _HASH_ENTRY * pHashEntry;
3649 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3651 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
3652 if (error != ERROR_SUCCESS)
3654 SetLastError(error);
3658 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3659 if (error != ERROR_SUCCESS)
3661 SetLastError(error);
3665 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3668 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
3670 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3671 TRACE("entry %s not found!\n", lpszUrlName);
3672 SetLastError(ERROR_FILE_NOT_FOUND);
3676 ret = DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
3678 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3683 /***********************************************************************
3684 * DeleteUrlCacheEntryW (WININET.@)
3687 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3689 URLCACHECONTAINER * pContainer;
3690 LPURLCACHE_HEADER pHeader;
3691 struct _HASH_ENTRY * pHashEntry;
3696 TRACE("(%s)\n", debugstr_w(lpszUrlName));
3698 urlA = heap_strdupWtoA(lpszUrlName);
3701 SetLastError(ERROR_OUTOFMEMORY);
3705 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3706 if (error != ERROR_SUCCESS)
3709 SetLastError(error);
3713 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3714 if (error != ERROR_SUCCESS)
3717 SetLastError(error);
3721 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3727 if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
3729 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3730 TRACE("entry %s not found!\n", debugstr_a(urlA));
3732 SetLastError(ERROR_FILE_NOT_FOUND);
3736 ret = DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
3738 URLCacheContainer_UnlockIndex(pContainer, pHeader);
3743 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3745 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3749 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3751 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3755 /***********************************************************************
3756 * CreateCacheContainerA (WININET.@)
3758 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3759 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3761 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3762 d1, d2, d3, d4, d5, d6, d7, d8);
3766 /***********************************************************************
3767 * CreateCacheContainerW (WININET.@)
3769 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3770 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3772 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3773 d1, d2, d3, d4, d5, d6, d7, d8);
3777 /***********************************************************************
3778 * FindFirstUrlCacheContainerA (WININET.@)
3780 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3782 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3786 /***********************************************************************
3787 * FindFirstUrlCacheContainerW (WININET.@)
3789 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3791 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3795 /***********************************************************************
3796 * FindNextUrlCacheContainerA (WININET.@)
3798 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3800 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3804 /***********************************************************************
3805 * FindNextUrlCacheContainerW (WININET.@)
3807 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3809 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3813 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3814 LPCSTR lpszUrlSearchPattern,
3818 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3819 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3821 LPDWORD pcbReserved2,
3825 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3826 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3827 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3828 SetLastError(ERROR_FILE_NOT_FOUND);
3832 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3833 LPCWSTR lpszUrlSearchPattern,
3837 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3838 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3840 LPDWORD pcbReserved2,
3844 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3845 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3846 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3847 SetLastError(ERROR_FILE_NOT_FOUND);
3851 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
3853 typedef struct URLCacheFindEntryHandle
3856 LPWSTR lpszUrlSearchPattern;
3857 DWORD dwContainerIndex;
3858 DWORD dwHashTableIndex;
3859 DWORD dwHashEntryIndex;
3860 } URLCacheFindEntryHandle;
3862 /***********************************************************************
3863 * FindFirstUrlCacheEntryA (WININET.@)
3866 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3867 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3869 URLCacheFindEntryHandle *pEntryHandle;
3871 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3873 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3877 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3878 if (lpszUrlSearchPattern)
3880 pEntryHandle->lpszUrlSearchPattern = heap_strdupAtoW(lpszUrlSearchPattern);
3881 if (!pEntryHandle->lpszUrlSearchPattern)
3883 heap_free(pEntryHandle);
3888 pEntryHandle->lpszUrlSearchPattern = NULL;
3889 pEntryHandle->dwContainerIndex = 0;
3890 pEntryHandle->dwHashTableIndex = 0;
3891 pEntryHandle->dwHashEntryIndex = 0;
3893 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3895 heap_free(pEntryHandle);
3898 return pEntryHandle;
3901 /***********************************************************************
3902 * FindFirstUrlCacheEntryW (WININET.@)
3905 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3906 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3908 URLCacheFindEntryHandle *pEntryHandle;
3910 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3912 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3916 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3917 if (lpszUrlSearchPattern)
3919 pEntryHandle->lpszUrlSearchPattern = heap_strdupW(lpszUrlSearchPattern);
3920 if (!pEntryHandle->lpszUrlSearchPattern)
3922 heap_free(pEntryHandle);
3927 pEntryHandle->lpszUrlSearchPattern = NULL;
3928 pEntryHandle->dwContainerIndex = 0;
3929 pEntryHandle->dwHashTableIndex = 0;
3930 pEntryHandle->dwHashEntryIndex = 0;
3932 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3934 heap_free(pEntryHandle);
3937 return pEntryHandle;
3940 static BOOL FindNextUrlCacheEntryInternal(
3942 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3943 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3946 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3947 URLCACHECONTAINER * pContainer;
3949 if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3951 SetLastError(ERROR_INVALID_HANDLE);
3955 for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3956 pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3958 LPURLCACHE_HEADER pHeader;
3959 HASH_CACHEFILE_ENTRY *pHashTableEntry;
3962 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3963 if (error != ERROR_SUCCESS)
3965 SetLastError(error);
3969 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
3972 for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3973 pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3975 const struct _HASH_ENTRY *pHashEntry = NULL;
3976 for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3977 pEntryHandle->dwHashEntryIndex++)
3979 const URL_CACHEFILE_ENTRY *pUrlEntry;
3980 const CACHEFILE_ENTRY *pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3982 if (pEntry->dwSignature != URL_SIGNATURE)
3985 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
3986 TRACE("Found URL: %s\n",
3987 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetUrl));
3988 TRACE("Header info: %s\n",
3989 debugstr_an((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetHeaderInfo,
3990 pUrlEntry->dwHeaderInfoSize));
3992 error = URLCache_CopyEntry(
3995 lpNextCacheEntryInfo,
3996 lpdwNextCacheEntryInfoBufferSize,
3999 if (error != ERROR_SUCCESS)
4001 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4002 SetLastError(error);
4005 if(pUrlEntry->dwOffsetLocalName)
4006 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->dwOffsetLocalName));
4008 /* increment the current index so that next time the function
4009 * is called the next entry is returned */
4010 pEntryHandle->dwHashEntryIndex++;
4011 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4016 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4019 SetLastError(ERROR_NO_MORE_ITEMS);
4023 /***********************************************************************
4024 * FindNextUrlCacheEntryA (WININET.@)
4026 BOOL WINAPI FindNextUrlCacheEntryA(
4028 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
4029 LPDWORD lpdwNextCacheEntryInfoBufferSize)
4031 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
4033 return FindNextUrlCacheEntryInternal(hEnumHandle, lpNextCacheEntryInfo,
4034 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
4037 /***********************************************************************
4038 * FindNextUrlCacheEntryW (WININET.@)
4040 BOOL WINAPI FindNextUrlCacheEntryW(
4042 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
4043 LPDWORD lpdwNextCacheEntryInfoBufferSize
4046 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
4048 return FindNextUrlCacheEntryInternal(hEnumHandle,
4049 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
4050 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
4053 /***********************************************************************
4054 * FindCloseUrlCache (WININET.@)
4056 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
4058 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
4060 TRACE("(%p)\n", hEnumHandle);
4062 if (!pEntryHandle || pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
4064 SetLastError(ERROR_INVALID_HANDLE);
4068 pEntryHandle->dwMagic = 0;
4069 heap_free(pEntryHandle->lpszUrlSearchPattern);
4070 heap_free(pEntryHandle);
4074 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
4075 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
4077 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
4078 dwSearchCondition, lpGroupId, lpReserved);
4082 BOOL WINAPI FindNextUrlCacheEntryExA(
4084 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
4085 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
4087 LPDWORD pcbReserved2,
4091 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
4092 lpReserved, pcbReserved2, lpReserved3);
4096 BOOL WINAPI FindNextUrlCacheEntryExW(
4098 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
4099 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
4101 LPDWORD pcbReserved2,
4105 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
4106 lpReserved, pcbReserved2, lpReserved3);
4110 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
4112 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
4116 /***********************************************************************
4117 * CreateUrlCacheGroup (WININET.@)
4120 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
4122 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
4126 /***********************************************************************
4127 * DeleteUrlCacheGroup (WININET.@)
4130 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
4132 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
4133 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
4137 /***********************************************************************
4138 * DeleteWpadCacheForNetworks (WININET.@)
4139 * Undocumented, added in IE8
4141 BOOL WINAPI DeleteWpadCacheForNetworks(DWORD unk1)
4143 FIXME("(%d) stub\n", unk1);
4147 /***********************************************************************
4148 * SetUrlCacheEntryGroupA (WININET.@)
4151 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
4152 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
4155 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
4156 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
4157 pbGroupAttributes, cbGroupAttributes, lpReserved);
4158 SetLastError(ERROR_FILE_NOT_FOUND);
4162 /***********************************************************************
4163 * SetUrlCacheEntryGroupW (WININET.@)
4166 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
4167 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
4170 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
4171 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
4172 pbGroupAttributes, cbGroupAttributes, lpReserved);
4173 SetLastError(ERROR_FILE_NOT_FOUND);
4177 /***********************************************************************
4178 * GetUrlCacheConfigInfoW (WININET.@)
4180 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
4182 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
4183 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4187 /***********************************************************************
4188 * GetUrlCacheConfigInfoA (WININET.@)
4190 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
4192 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
4193 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4197 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4198 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
4199 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
4201 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
4202 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
4203 lpdwGroupInfo, lpReserved);
4207 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4208 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
4209 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
4211 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
4212 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
4213 lpdwGroupInfo, lpReserved);
4217 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4218 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
4220 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
4221 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
4225 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4226 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
4228 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
4229 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
4233 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
4235 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
4239 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
4241 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
4245 /***********************************************************************
4246 * DeleteIE3Cache (WININET.@)
4248 * Deletes the files used by the IE3 URL caching system.
4251 * hWnd [I] A dummy window.
4252 * hInst [I] Instance of process calling the function.
4253 * lpszCmdLine [I] Options used by function.
4254 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
4256 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
4258 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
4262 static BOOL IsUrlCacheEntryExpiredInternal(const URL_CACHEFILE_ENTRY *pUrlEntry,
4263 FILETIME *pftLastModified)
4266 FILETIME now, expired;
4268 *pftLastModified = pUrlEntry->LastModifiedTime;
4269 GetSystemTimeAsFileTime(&now);
4270 URLCache_DosDateTimeToFileTime(pUrlEntry->wExpiredDate,
4271 pUrlEntry->wExpiredTime, &expired);
4272 /* If the expired time is 0, it's interpreted as not expired */
4273 if (!expired.dwLowDateTime && !expired.dwHighDateTime)
4276 ret = CompareFileTime(&expired, &now) < 0;
4280 /***********************************************************************
4281 * IsUrlCacheEntryExpiredA (WININET.@)
4285 * dwFlags [I] Unknown
4286 * pftLastModified [O] Last modified time
4288 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
4290 LPURLCACHE_HEADER pHeader;
4291 struct _HASH_ENTRY * pHashEntry;
4292 const CACHEFILE_ENTRY * pEntry;
4293 const URL_CACHEFILE_ENTRY * pUrlEntry;
4294 URLCACHECONTAINER * pContainer;
4297 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
4299 if (!url || !pftLastModified)
4302 FIXME("unknown flags 0x%08x\n", dwFlags);
4304 /* Any error implies that the URL is expired, i.e. not in the cache */
4305 if (URLCacheContainers_FindContainerA(url, &pContainer))
4307 memset(pftLastModified, 0, sizeof(*pftLastModified));
4311 if (cache_container_open_index(pContainer, MIN_BLOCK_NO))
4313 memset(pftLastModified, 0, sizeof(*pftLastModified));
4317 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
4319 memset(pftLastModified, 0, sizeof(*pftLastModified));
4323 if (!URLCache_FindHash(pHeader, url, &pHashEntry))
4325 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4326 memset(pftLastModified, 0, sizeof(*pftLastModified));
4327 TRACE("entry %s not found!\n", url);
4331 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
4332 if (pEntry->dwSignature != URL_SIGNATURE)
4334 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4335 memset(pftLastModified, 0, sizeof(*pftLastModified));
4336 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
4340 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
4341 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
4343 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4348 /***********************************************************************
4349 * IsUrlCacheEntryExpiredW (WININET.@)
4353 * dwFlags [I] Unknown
4354 * pftLastModified [O] Last modified time
4356 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
4358 LPURLCACHE_HEADER pHeader;
4359 struct _HASH_ENTRY * pHashEntry;
4360 const CACHEFILE_ENTRY * pEntry;
4361 const URL_CACHEFILE_ENTRY * pUrlEntry;
4362 URLCACHECONTAINER * pContainer;
4365 TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
4367 if (!url || !pftLastModified)
4370 FIXME("unknown flags 0x%08x\n", dwFlags);
4372 /* Any error implies that the URL is expired, i.e. not in the cache */
4373 if (URLCacheContainers_FindContainerW(url, &pContainer))
4375 memset(pftLastModified, 0, sizeof(*pftLastModified));
4379 if (cache_container_open_index(pContainer, MIN_BLOCK_NO))
4381 memset(pftLastModified, 0, sizeof(*pftLastModified));
4385 if (!(pHeader = URLCacheContainer_LockIndex(pContainer)))
4387 memset(pftLastModified, 0, sizeof(*pftLastModified));
4391 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
4393 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4394 memset(pftLastModified, 0, sizeof(*pftLastModified));
4395 TRACE("entry %s not found!\n", debugstr_w(url));
4399 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
4401 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4402 memset(pftLastModified, 0, sizeof(*pftLastModified));
4403 TRACE("entry %s not found!\n", debugstr_w(url));
4407 pEntry = (const CACHEFILE_ENTRY *)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
4408 if (pEntry->dwSignature != URL_SIGNATURE)
4410 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4411 memset(pftLastModified, 0, sizeof(*pftLastModified));
4412 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->dwSignature, sizeof(DWORD)));
4416 pUrlEntry = (const URL_CACHEFILE_ENTRY *)pEntry;
4417 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
4419 URLCacheContainer_UnlockIndex(pContainer, pHeader);
4424 /***********************************************************************
4425 * GetDiskInfoA (WININET.@)
4427 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
4430 ULARGE_INTEGER bytes_free, bytes_total;
4432 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
4436 SetLastError(ERROR_INVALID_PARAMETER);
4440 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
4442 if (cluster_size) *cluster_size = 1;
4443 if (free) *free = bytes_free.QuadPart;
4444 if (total) *total = bytes_total.QuadPart;
4449 /***********************************************************************
4450 * RegisterUrlCacheNotification (WININET.@)
4452 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
4454 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
4458 /***********************************************************************
4459 * IncrementUrlCacheHeaderData (WININET.@)
4461 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
4463 FIXME("(%u, %p)\n", index, data);
4467 /***********************************************************************
4468 * RunOnceUrlCache (WININET.@)
4471 DWORD WINAPI RunOnceUrlCache(HWND hwnd, HINSTANCE hinst, LPSTR cmd, int cmdshow)
4473 FIXME("(%p, %p, %s, %d): stub\n", hwnd, hinst, debugstr_a(cmd), cmdshow);
4477 BOOL init_urlcache(void)
4479 dll_unload_event = CreateEventW(NULL, FALSE, FALSE, NULL);
4480 if(!dll_unload_event)
4483 free_cache_running = CreateSemaphoreW(NULL, 1, 1, NULL);
4484 if(!free_cache_running) {
4485 CloseHandle(dll_unload_event);
4489 URLCacheContainers_CreateDefaults();
4493 void free_urlcache(void)
4495 SetEvent(dll_unload_event);
4496 WaitForSingleObject(free_cache_running, INFINITE);
4497 ReleaseSemaphore(free_cache_running, 1, NULL);
4498 CloseHandle(free_cache_running);
4499 CloseHandle(dll_unload_event);
4501 URLCacheContainers_DeleteAll();
4504 /***********************************************************************
4505 * LoadUrlCacheContent (WININET.@)
4507 BOOL WINAPI LoadUrlCacheContent(void)