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) )
106 DWORD blocks_used; /* number of 128byte blocks used by this entry */
112 FILETIME modification_time;
113 FILETIME access_time;
114 WORD expire_date; /* expire date in dos format */
115 WORD expire_time; /* expire time in dos format */
116 DWORD unk1; /* usually zero */
117 ULARGE_INTEGER size; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
118 DWORD unk2; /* usually zero */
119 DWORD exempt_delta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
120 DWORD unk3; /* usually 0x60 */
121 DWORD url_off; /* offset of start of url from start of entry */
122 BYTE cache_dir; /* index of cache directory this url is stored in */
123 BYTE unk4; /* usually zero */
124 WORD unk5; /* usually 0x1010 */
125 DWORD local_name_off; /* offset of start of local filename from start of entry */
126 DWORD cache_entry_type; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
127 DWORD header_info_off; /* offset of start of header info from start of entry */
128 DWORD header_info_size;
129 DWORD file_extension_off; /* offset of start of file extension from start of entry */
130 WORD sync_date; /* last sync date in dos format */
131 WORD sync_time; /* last sync time in dos format */
132 DWORD hit_rate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
133 DWORD use_count; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
136 DWORD unk7; /* usually zero */
137 DWORD unk8; /* usually zero */
138 /* packing to dword align start of next field */
139 /* CHAR szSourceUrlName[]; (url) */
140 /* packing to dword align start of next field */
141 /* CHAR szLocalFileName[]; (local file name excluding path) */
142 /* packing to dword align start of next field */
143 /* CHAR szHeaderInfo[]; (header info) */
152 typedef struct _HASH_CACHEFILE_ENTRY
154 entry_header CacheFileEntry;
156 DWORD dwHashTableNumber;
157 struct _HASH_ENTRY HashTable[HASHTABLE_SIZE];
158 } HASH_CACHEFILE_ENTRY;
160 typedef struct _DIRECTORY_DATA
163 char filename[DIR_LENGTH];
166 typedef struct _URLCACHE_HEADER
168 char szSignature[28];
170 DWORD dwOffsetFirstHashTable;
171 DWORD dwIndexCapacityInBlocks;
174 ULARGE_INTEGER CacheLimit;
175 ULARGE_INTEGER CacheUsage;
176 ULARGE_INTEGER ExemptUsage;
177 DWORD DirectoryCount;
178 DIRECTORY_DATA directory_data[MAX_DIR_NO];
180 BYTE allocation_table[ALLOCATION_TABLE_SIZE];
181 } URLCACHE_HEADER, *LPURLCACHE_HEADER;
182 typedef const URLCACHE_HEADER *LPCURLCACHE_HEADER;
184 typedef struct _STREAM_HANDLE
190 typedef struct _URLCACHECONTAINER
192 struct list entry; /* part of a list */
193 LPWSTR cache_prefix; /* string that has to be prefixed for this container to be used */
194 LPWSTR path; /* path to url container directory */
195 HANDLE hMapping; /* handle of file mapping */
196 DWORD file_size; /* size of file when mapping was opened */
197 HANDLE hMutex; /* handle of mutex */
198 DWORD default_entry_type;
202 /* List of all containers available */
203 static struct list UrlContainers = LIST_INIT(UrlContainers);
205 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash);
207 /***********************************************************************
208 * URLCache_PathToObjectName (Internal)
210 * Converts a path to a name suitable for use as a Win32 object name.
211 * Replaces '\\' characters in-place with the specified character
212 * (usually '_' or '!')
218 static void URLCache_PathToObjectName(LPWSTR lpszPath, WCHAR replace)
220 for (; *lpszPath; lpszPath++)
222 if (*lpszPath == '\\')
227 /* Caller must hold container lock */
228 static HANDLE cache_container_map_index(HANDLE file, const WCHAR *path, DWORD size, BOOL *validate)
230 static const WCHAR mapping_name_format[]
231 = {'%','s','i','n','d','e','x','.','d','a','t','_','%','l','u',0};
232 WCHAR mapping_name[MAX_PATH];
235 wsprintfW(mapping_name, mapping_name_format, path, size);
236 URLCache_PathToObjectName(mapping_name, '_');
238 mapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, mapping_name);
240 if(validate) *validate = FALSE;
244 if(validate) *validate = TRUE;
245 return CreateFileMappingW(file, NULL, PAGE_READWRITE, 0, 0, mapping_name);
248 /* Caller must hold container lock */
249 static DWORD cache_container_set_size(URLCACHECONTAINER *container, HANDLE file, DWORD blocks_no)
251 static const WCHAR cache_content_key[] = {'S','o','f','t','w','a','r','e','\\',
252 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
253 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
254 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s','\\',
255 'C','a','c','h','e','\\','C','o','n','t','e','n','t',0};
256 static const WCHAR cache_limit[] = {'C','a','c','h','e','L','i','m','i','t',0};
258 DWORD file_size = FILE_SIZE(blocks_no);
259 WCHAR dir_path[MAX_PATH], *dir_name;
260 HASH_CACHEFILE_ENTRY *hash_entry;
261 URLCACHE_HEADER *header;
267 if(SetFilePointer(file, file_size, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
268 return GetLastError();
270 if(!SetEndOfFile(file))
271 return GetLastError();
273 mapping = cache_container_map_index(file, container->path, file_size, NULL);
275 return GetLastError();
277 header = MapViewOfFile(mapping, FILE_MAP_WRITE, 0, 0, 0);
279 CloseHandle(mapping);
280 return GetLastError();
283 if(blocks_no != MIN_BLOCK_NO) {
284 if(file_size > header->dwFileSize)
285 memset((char*)header+header->dwFileSize, 0, file_size-header->dwFileSize);
286 header->dwFileSize = file_size;
287 header->dwIndexCapacityInBlocks = blocks_no;
289 UnmapViewOfFile(header);
290 CloseHandle(container->hMapping);
291 container->hMapping = mapping;
292 container->file_size = file_size;
293 return ERROR_SUCCESS;
296 memset(header, 0, file_size);
297 /* First set some constants and defaults in the header */
298 memcpy(header->szSignature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1);
299 memcpy(header->szSignature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1);
300 header->dwFileSize = file_size;
301 header->dwIndexCapacityInBlocks = blocks_no;
302 /* 127MB - taken from default for Windows 2000 */
303 header->CacheLimit.QuadPart = 0x07ff5400;
304 /* Copied from a Windows 2000 cache index */
305 header->DirectoryCount = container->default_entry_type==NORMAL_CACHE_ENTRY ? 4 : 0;
307 /* If the registry has a cache size set, use the registry value */
308 if(RegOpenKeyW(HKEY_CURRENT_USER, cache_content_key, &key) == ERROR_SUCCESS) {
309 DWORD dw, len = sizeof(dw), keytype;
311 if(RegQueryValueExW(key, cache_limit, NULL, &keytype, (BYTE*)&dw, &len) == ERROR_SUCCESS &&
312 keytype == REG_DWORD)
313 header->CacheLimit.QuadPart = (ULONGLONG)dw * 1024;
317 URLCache_CreateHashTable(header, NULL, &hash_entry);
319 /* Last step - create the directories */
320 strcpyW(dir_path, container->path);
321 dir_name = dir_path + strlenW(dir_path);
324 GetSystemTimeAsFileTime(&ft);
326 for(i=0; i<header->DirectoryCount; ++i) {
327 header->directory_data[i].dwNumFiles = 0;
329 ULONGLONG n = ft.dwHighDateTime;
332 /* Generate a file name to attempt to create.
333 * This algorithm will create what will appear
334 * to be random and unrelated directory names
335 * of up to 9 characters in length.
338 n += ft.dwLowDateTime;
339 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
341 for(k = 0; k < 8; ++k) {
344 /* Dividing by a prime greater than 36 helps
345 * with the appearance of randomness
350 dir_name[k] = '0' + r;
352 dir_name[k] = 'A' + (r - 10);
355 if(CreateDirectoryW(dir_path, 0)) {
356 /* The following is OK because we generated an
357 * 8 character directory name made from characters
358 * [A-Z0-9], which are equivalent for all code
359 * pages and for UTF-16
361 for (k = 0; k < 8; ++k)
362 header->directory_data[i].filename[k] = dir_name[k];
365 /* Give up. The most likely cause of this
366 * is a full disk, but whatever the cause
367 * is, it should be more than apparent that
370 UnmapViewOfFile(header);
371 CloseHandle(mapping);
372 return GetLastError();
377 UnmapViewOfFile(header);
378 CloseHandle(container->hMapping);
379 container->hMapping = mapping;
380 container->file_size = file_size;
381 return ERROR_SUCCESS;
384 static BOOL cache_container_is_valid(URLCACHE_HEADER *header, DWORD file_size)
386 DWORD allocation_size, count_bits, i;
388 if(file_size < FILE_SIZE(MIN_BLOCK_NO))
391 if(file_size != header->dwFileSize)
394 if (!memcmp(header->szSignature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1) &&
395 memcmp(header->szSignature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1))
398 if(FILE_SIZE(header->dwIndexCapacityInBlocks) != file_size)
402 for(i=0; i<header->dwIndexCapacityInBlocks/8; i++) {
403 for(count_bits = header->allocation_table[i]; count_bits!=0; count_bits>>=1) {
408 if(allocation_size != header->dwBlocksInUse)
411 for(; i<ALLOCATION_TABLE_SIZE; i++) {
412 if(header->allocation_table[i])
419 /***********************************************************************
420 * cache_container_open_index (Internal)
422 * Opens the index file and saves mapping handle in hMapping
425 * ERROR_SUCCESS if succeeded
426 * Any other Win32 error code if failed
429 static DWORD cache_container_open_index(URLCACHECONTAINER *container, DWORD blocks_no)
431 static const WCHAR index_dat[] = {'i','n','d','e','x','.','d','a','t',0};
434 WCHAR index_path[MAX_PATH];
438 WaitForSingleObject(container->hMutex, INFINITE);
440 if(container->hMapping) {
441 ReleaseMutex(container->hMutex);
442 return ERROR_SUCCESS;
445 strcpyW(index_path, container->path);
446 strcatW(index_path, index_dat);
448 file = CreateFileW(index_path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
449 if(file == INVALID_HANDLE_VALUE) {
450 /* Maybe the directory wasn't there? Try to create it */
451 if(CreateDirectoryW(container->path, 0))
452 file = CreateFileW(index_path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
454 if(file == INVALID_HANDLE_VALUE) {
455 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(index_path));
456 ReleaseMutex(container->hMutex);
457 return GetLastError();
460 file_size = GetFileSize(file, NULL);
461 if(file_size == INVALID_FILE_SIZE) {
463 ReleaseMutex(container->hMutex);
464 return GetLastError();
467 if(blocks_no < MIN_BLOCK_NO)
468 blocks_no = MIN_BLOCK_NO;
469 else if(blocks_no > MAX_BLOCK_NO)
470 blocks_no = MAX_BLOCK_NO;
472 if(file_size < FILE_SIZE(blocks_no)) {
473 DWORD ret = cache_container_set_size(container, file, blocks_no);
475 ReleaseMutex(container->hMutex);
479 container->file_size = file_size;
480 container->hMapping = cache_container_map_index(file, container->path, file_size, &validate);
482 if(container->hMapping && validate) {
483 URLCACHE_HEADER *header = MapViewOfFile(container->hMapping, FILE_MAP_WRITE, 0, 0, 0);
485 if(header && !cache_container_is_valid(header, file_size)) {
486 WARN("detected old or broken index.dat file\n");
487 UnmapViewOfFile(header);
488 FreeUrlCacheSpaceW(container->path, 100, 0);
490 UnmapViewOfFile(header);
492 CloseHandle(container->hMapping);
493 container->hMapping = NULL;
497 if(!container->hMapping)
499 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
500 ReleaseMutex(container->hMutex);
501 return GetLastError();
504 ReleaseMutex(container->hMutex);
505 return ERROR_SUCCESS;
508 /***********************************************************************
509 * cache_container_close_index (Internal)
517 static void cache_container_close_index(URLCACHECONTAINER * pContainer)
519 CloseHandle(pContainer->hMapping);
520 pContainer->hMapping = NULL;
523 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix,
524 LPCWSTR path, DWORD default_entry_type, LPWSTR mutex_name)
526 URLCACHECONTAINER * pContainer = heap_alloc(sizeof(URLCACHECONTAINER));
527 int cache_prefix_len = strlenW(cache_prefix);
534 pContainer->hMapping = NULL;
535 pContainer->file_size = 0;
536 pContainer->default_entry_type = default_entry_type;
538 pContainer->path = heap_strdupW(path);
539 if (!pContainer->path)
541 heap_free(pContainer);
545 pContainer->cache_prefix = heap_alloc((cache_prefix_len + 1) * sizeof(WCHAR));
546 if (!pContainer->cache_prefix)
548 heap_free(pContainer->path);
549 heap_free(pContainer);
553 memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
555 CharLowerW(mutex_name);
556 URLCache_PathToObjectName(mutex_name, '!');
558 if ((pContainer->hMutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
560 ERR("couldn't create mutex (error is %d)\n", GetLastError());
561 heap_free(pContainer->path);
562 heap_free(pContainer);
566 list_add_head(&UrlContainers, &pContainer->entry);
571 static void cache_container_delete_container(URLCACHECONTAINER * pContainer)
573 list_remove(&pContainer->entry);
575 cache_container_close_index(pContainer);
576 CloseHandle(pContainer->hMutex);
577 heap_free(pContainer->path);
578 heap_free(pContainer->cache_prefix);
579 heap_free(pContainer);
582 static void URLCacheContainers_CreateDefaults(void)
584 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
585 static const WCHAR UrlPrefix[] = {0};
586 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
587 static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
588 static const WCHAR CookieSuffix[] = {0};
589 static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
592 int nFolder; /* CSIDL_* constant */
593 const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
594 const WCHAR * cache_prefix; /* prefix used to reference the container */
595 DWORD default_entry_type;
596 } DefaultContainerData[] =
598 { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix, NORMAL_CACHE_ENTRY },
599 { CSIDL_HISTORY, HistorySuffix, HistoryPrefix, URLHISTORY_CACHE_ENTRY },
600 { CSIDL_COOKIES, CookieSuffix, CookiePrefix, COOKIE_CACHE_ENTRY },
604 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
606 WCHAR wszCachePath[MAX_PATH];
607 WCHAR wszMutexName[MAX_PATH];
608 int path_len, suffix_len;
610 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
612 ERR("Couldn't get path for default container %u\n", i);
615 path_len = strlenW(wszCachePath);
616 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
618 if (path_len + suffix_len + 2 > MAX_PATH)
620 ERR("Path too long\n");
624 wszCachePath[path_len] = '\\';
625 wszCachePath[path_len+1] = 0;
627 strcpyW(wszMutexName, wszCachePath);
631 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
632 wszCachePath[path_len + suffix_len + 1] = '\\';
633 wszCachePath[path_len + suffix_len + 2] = '\0';
636 URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath,
637 DefaultContainerData[i].default_entry_type, wszMutexName);
641 static void URLCacheContainers_DeleteAll(void)
643 while(!list_empty(&UrlContainers))
644 cache_container_delete_container(
645 LIST_ENTRY(list_head(&UrlContainers), URLCACHECONTAINER, entry)
649 static DWORD URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, URLCACHECONTAINER ** ppContainer)
651 URLCACHECONTAINER * pContainer;
653 TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
656 return ERROR_INVALID_PARAMETER;
658 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
660 int prefix_len = strlenW(pContainer->cache_prefix);
661 if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
663 TRACE("found container with prefix %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
664 *ppContainer = pContainer;
665 return ERROR_SUCCESS;
668 ERR("no container found\n");
669 return ERROR_FILE_NOT_FOUND;
672 static DWORD URLCacheContainers_FindContainerA(LPCSTR lpszUrl, URLCACHECONTAINER ** ppContainer)
677 if (lpszUrl && !(url = heap_strdupAtoW(lpszUrl)))
678 return ERROR_OUTOFMEMORY;
680 ret = URLCacheContainers_FindContainerW(url, ppContainer);
685 static BOOL URLCacheContainers_Enum(LPCWSTR lpwszSearchPattern, DWORD dwIndex, URLCACHECONTAINER ** ppContainer)
688 URLCACHECONTAINER * pContainer;
690 TRACE("searching for prefix: %s\n", debugstr_w(lpwszSearchPattern));
692 /* non-NULL search pattern only returns one container ever */
693 if (lpwszSearchPattern && dwIndex > 0)
696 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, URLCACHECONTAINER, entry)
698 if (lpwszSearchPattern)
700 if (!strcmpW(pContainer->cache_prefix, lpwszSearchPattern))
702 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
703 *ppContainer = pContainer;
711 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
712 *ppContainer = pContainer;
721 /***********************************************************************
722 * cache_container_lock_index (Internal)
724 * Locks the index for system-wide exclusive access.
727 * Cache file header if successful
728 * NULL if failed and calls SetLastError.
730 static LPURLCACHE_HEADER cache_container_lock_index(URLCACHECONTAINER * pContainer)
734 URLCACHE_HEADER * pHeader;
738 WaitForSingleObject(pContainer->hMutex, INFINITE);
740 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
744 ReleaseMutex(pContainer->hMutex);
745 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
748 pHeader = (URLCACHE_HEADER *)pIndexData;
750 /* file has grown - we need to remap to prevent us getting
751 * access violations when we try and access beyond the end
752 * of the memory mapped file */
753 if (pHeader->dwFileSize != pContainer->file_size)
755 UnmapViewOfFile( pHeader );
756 cache_container_close_index(pContainer);
757 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
758 if (error != ERROR_SUCCESS)
760 ReleaseMutex(pContainer->hMutex);
764 pIndexData = MapViewOfFile(pContainer->hMapping, FILE_MAP_WRITE, 0, 0, 0);
768 ReleaseMutex(pContainer->hMutex);
769 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
772 pHeader = (URLCACHE_HEADER *)pIndexData;
775 TRACE("Signature: %s, file size: %d bytes\n", pHeader->szSignature, pHeader->dwFileSize);
777 for (index = 0; index < pHeader->DirectoryCount; index++)
779 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].filename);
785 /***********************************************************************
786 * cache_container_unlock_index (Internal)
789 static BOOL cache_container_unlock_index(URLCACHECONTAINER * pContainer, LPURLCACHE_HEADER pHeader)
792 ReleaseMutex(pContainer->hMutex);
793 return UnmapViewOfFile(pHeader);
797 #define CHAR_BIT (8 * sizeof(CHAR))
800 /***********************************************************************
801 * URLCache_Allocation_BlockIsFree (Internal)
803 * Is the specified block number free?
810 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
812 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
813 return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
816 /***********************************************************************
817 * URLCache_Allocation_BlockFree (Internal)
819 * Marks the specified block as free
822 * this function is not updating used blocks count
828 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
830 BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
831 AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
834 /***********************************************************************
835 * URLCache_Allocation_BlockAllocate (Internal)
837 * Marks the specified block as allocated
840 * this function is not updating used blocks count
846 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
848 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
849 AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
852 /***********************************************************************
853 * URLCache_FindFirstFreeEntry (Internal)
855 * Finds and allocates the first block of free space big enough and
856 * sets ppEntry to point to it.
859 * ERROR_SUCCESS when free memory block was found
860 * Any other Win32 error code if the entry could not be added
863 static DWORD URLCache_FindFirstFreeEntry(URLCACHE_HEADER * pHeader, DWORD dwBlocksNeeded, entry_header **ppEntry)
867 for (dwBlockNumber = 0; dwBlockNumber < pHeader->dwIndexCapacityInBlocks; dwBlockNumber++)
869 for (dwFreeCounter = 0;
870 dwFreeCounter < dwBlocksNeeded &&
871 dwFreeCounter + dwBlockNumber < pHeader->dwIndexCapacityInBlocks &&
872 URLCache_Allocation_BlockIsFree(pHeader->allocation_table, dwBlockNumber + dwFreeCounter);
874 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
876 if (dwFreeCounter == dwBlocksNeeded)
879 TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
880 for (index = 0; index < dwBlocksNeeded; index++)
881 URLCache_Allocation_BlockAllocate(pHeader->allocation_table, dwBlockNumber + index);
882 *ppEntry = (entry_header*)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
883 for (index = 0; index < dwBlocksNeeded * BLOCKSIZE / sizeof(DWORD); index++)
884 ((DWORD*)*ppEntry)[index] = 0xdeadbeef;
885 (*ppEntry)->blocks_used = dwBlocksNeeded;
886 pHeader->dwBlocksInUse += dwBlocksNeeded;
887 return ERROR_SUCCESS;
891 return ERROR_HANDLE_DISK_FULL;
894 /***********************************************************************
895 * URLCache_DeleteEntry (Internal)
897 * Deletes the specified entry and frees the space allocated to it
900 * TRUE if it succeeded
904 static BOOL URLCache_DeleteEntry(LPURLCACHE_HEADER pHeader, entry_header *pEntry)
909 /* update allocation table */
910 dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader) - ENTRY_START_OFFSET) / BLOCKSIZE;
911 for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->blocks_used; dwBlock++)
912 URLCache_Allocation_BlockFree(pHeader->allocation_table, dwBlock);
914 pHeader->dwBlocksInUse -= pEntry->blocks_used;
918 /***********************************************************************
919 * URLCache_LocalFileNameToPathW (Internal)
921 * Copies the full path to the specified buffer given the local file
922 * name and the index of the directory it is in. Always sets value in
923 * lpBufferSize to the required buffer size (in bytes).
926 * TRUE if the buffer was big enough
927 * FALSE if the buffer was too small
930 static BOOL URLCache_LocalFileNameToPathW(
931 const URLCACHECONTAINER * pContainer,
932 LPCURLCACHE_HEADER pHeader,
933 LPCSTR szLocalFileName,
939 int path_len = strlenW(pContainer->path);
940 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
941 if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->DirectoryCount)
947 nRequired = (path_len + file_name_len) * sizeof(WCHAR);
948 if(Directory != CACHE_CONTAINER_NO_SUBDIR)
949 nRequired += (DIR_LENGTH + 1) * sizeof(WCHAR);
950 if (nRequired <= *lpBufferSize)
954 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
955 if (Directory != CACHE_CONTAINER_NO_SUBDIR)
957 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].filename, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
958 wszPath[dir_len + path_len] = '\\';
965 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len, file_name_len);
966 *lpBufferSize = nRequired;
969 *lpBufferSize = nRequired;
973 /***********************************************************************
974 * URLCache_LocalFileNameToPathA (Internal)
976 * Copies the full path to the specified buffer given the local file
977 * name and the index of the directory it is in. Always sets value in
978 * lpBufferSize to the required buffer size.
981 * TRUE if the buffer was big enough
982 * FALSE if the buffer was too small
985 static BOOL URLCache_LocalFileNameToPathA(
986 const URLCACHECONTAINER * pContainer,
987 LPCURLCACHE_HEADER pHeader,
988 LPCSTR szLocalFileName,
994 int path_len, file_name_len, dir_len;
996 if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->DirectoryCount)
1002 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
1003 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
1004 if (Directory!=CACHE_CONTAINER_NO_SUBDIR)
1005 dir_len = DIR_LENGTH+1;
1009 nRequired = (path_len + dir_len + file_name_len) * sizeof(char);
1010 if (nRequired < *lpBufferSize)
1012 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
1014 memcpy(szPath+path_len, pHeader->directory_data[Directory].filename, dir_len-1);
1015 szPath[path_len + dir_len-1] = '\\';
1017 memcpy(szPath + path_len + dir_len, szLocalFileName, file_name_len);
1018 *lpBufferSize = nRequired;
1021 *lpBufferSize = nRequired;
1025 /* Just like FileTimeToDosDateTime, except that it also maps the special
1026 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1028 static void URLCache_FileTimeToDosDateTime(const FILETIME *ft, WORD *fatdate,
1031 if (!ft->dwLowDateTime && !ft->dwHighDateTime)
1032 *fatdate = *fattime = 0;
1034 FileTimeToDosDateTime(ft, fatdate, fattime);
1037 /***********************************************************************
1038 * URLCache_DeleteFile (Internal)
1040 static DWORD URLCache_DeleteFile(const URLCACHECONTAINER *container,
1041 URLCACHE_HEADER *header, entry_url *url_entry)
1043 WIN32_FILE_ATTRIBUTE_DATA attr;
1044 WCHAR path[MAX_PATH];
1045 LONG path_size = sizeof(path);
1049 if(!url_entry->local_name_off)
1052 if(!URLCache_LocalFileNameToPathW(container, header,
1053 (LPCSTR)url_entry+url_entry->local_name_off,
1054 url_entry->cache_dir, path, &path_size))
1057 if(!GetFileAttributesExW(path, GetFileExInfoStandard, &attr))
1059 URLCache_FileTimeToDosDateTime(&attr.ftLastWriteTime, &date, &time);
1060 if(date != url_entry->write_date || time != url_entry->write_time)
1063 err = (DeleteFileW(path) ? ERROR_SUCCESS : GetLastError());
1064 if(err == ERROR_ACCESS_DENIED || err == ERROR_SHARING_VIOLATION)
1068 if (url_entry->cache_dir < header->DirectoryCount)
1070 if (header->directory_data[url_entry->cache_dir].dwNumFiles)
1071 header->directory_data[url_entry->cache_dir].dwNumFiles--;
1073 if (url_entry->cache_entry_type & STICKY_CACHE_ENTRY)
1075 if (url_entry->size.QuadPart < header->ExemptUsage.QuadPart)
1076 header->ExemptUsage.QuadPart -= url_entry->size.QuadPart;
1078 header->ExemptUsage.QuadPart = 0;
1082 if (url_entry->size.QuadPart < header->CacheUsage.QuadPart)
1083 header->CacheUsage.QuadPart -= url_entry->size.QuadPart;
1085 header->CacheUsage.QuadPart = 0;
1088 return ERROR_SUCCESS;
1091 static BOOL urlcache_clean_leaked_entries(URLCACHECONTAINER *container, URLCACHE_HEADER *header)
1096 leak_off = &header->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
1098 entry_url *url_entry = (entry_url*)((LPBYTE)header + *leak_off);
1100 if(SUCCEEDED(URLCache_DeleteFile(container, header, url_entry))) {
1101 *leak_off = url_entry->exempt_delta;
1102 URLCache_DeleteEntry(header, &url_entry->header);
1105 leak_off = &url_entry->exempt_delta;
1112 /***********************************************************************
1113 * cache_container_clean_index (Internal)
1115 * This function is meant to make place in index file by removing leaked
1116 * files entries and resizing the file.
1118 * CAUTION: file view may get mapped to new memory
1121 * ERROR_SUCCESS when new memory is available
1122 * error code otherwise
1124 static DWORD cache_container_clean_index(URLCACHECONTAINER *container, URLCACHE_HEADER **file_view)
1126 URLCACHE_HEADER *header = *file_view;
1129 TRACE("(%s %s)\n", debugstr_w(container->cache_prefix), debugstr_w(container->path));
1131 if(urlcache_clean_leaked_entries(container, header))
1132 return ERROR_SUCCESS;
1134 if(header->dwFileSize >= ALLOCATION_TABLE_SIZE*8*BLOCKSIZE + ENTRY_START_OFFSET) {
1135 WARN("index file has maximal size\n");
1136 return ERROR_NOT_ENOUGH_MEMORY;
1139 cache_container_close_index(container);
1140 ret = cache_container_open_index(container, header->dwIndexCapacityInBlocks*2);
1141 if(ret != ERROR_SUCCESS)
1143 header = MapViewOfFile(container->hMapping, FILE_MAP_WRITE, 0, 0, 0);
1145 return GetLastError();
1147 UnmapViewOfFile(*file_view);
1148 *file_view = header;
1149 return ERROR_SUCCESS;
1152 /* Just like DosDateTimeToFileTime, except that it also maps the special
1153 * case of a DOS date/time of (0,0) to a filetime of (0,0).
1155 static void URLCache_DosDateTimeToFileTime(WORD fatdate, WORD fattime,
1158 if (!fatdate && !fattime)
1159 ft->dwLowDateTime = ft->dwHighDateTime = 0;
1161 DosDateTimeToFileTime(fatdate, fattime, ft);
1164 /***********************************************************************
1165 * URLCache_CopyEntry (Internal)
1167 * Copies an entry from the cache index file to the Win32 structure
1170 * ERROR_SUCCESS if the buffer was big enough
1171 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1174 static DWORD URLCache_CopyEntry(
1175 URLCACHECONTAINER * pContainer,
1176 LPCURLCACHE_HEADER pHeader,
1177 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1178 LPDWORD lpdwBufferSize,
1179 const entry_url * pUrlEntry,
1183 DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
1185 if (*lpdwBufferSize >= dwRequiredSize)
1187 lpCacheEntryInfo->lpHeaderInfo = NULL;
1188 lpCacheEntryInfo->lpszFileExtension = NULL;
1189 lpCacheEntryInfo->lpszLocalFileName = NULL;
1190 lpCacheEntryInfo->lpszSourceUrlName = NULL;
1191 lpCacheEntryInfo->CacheEntryType = pUrlEntry->cache_entry_type;
1192 lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->exempt_delta;
1193 lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->header_info_size;
1194 lpCacheEntryInfo->dwHitRate = pUrlEntry->hit_rate;
1195 lpCacheEntryInfo->dwSizeHigh = pUrlEntry->size.u.HighPart;
1196 lpCacheEntryInfo->dwSizeLow = pUrlEntry->size.u.LowPart;
1197 lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
1198 lpCacheEntryInfo->dwUseCount = pUrlEntry->use_count;
1199 URLCache_DosDateTimeToFileTime(pUrlEntry->expire_date, pUrlEntry->expire_time, &lpCacheEntryInfo->ExpireTime);
1200 lpCacheEntryInfo->LastAccessTime = pUrlEntry->access_time;
1201 lpCacheEntryInfo->LastModifiedTime = pUrlEntry->modification_time;
1202 URLCache_DosDateTimeToFileTime(pUrlEntry->sync_date, pUrlEntry->sync_time, &lpCacheEntryInfo->LastSyncTime);
1205 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1206 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1207 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1209 lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->url_off, -1, NULL, 0);
1211 lenUrl = strlen((LPCSTR)pUrlEntry + pUrlEntry->url_off);
1212 dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1214 /* FIXME: is source url optional? */
1215 if (*lpdwBufferSize >= dwRequiredSize)
1217 DWORD lenUrlBytes = (lenUrl+1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1219 lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrlBytes;
1221 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->url_off, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
1223 memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPCSTR)pUrlEntry + pUrlEntry->url_off, lenUrlBytes);
1226 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1227 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1228 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1230 if (pUrlEntry->local_name_off)
1232 LONG nLocalFilePathSize;
1233 LPSTR lpszLocalFileName;
1234 lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
1235 nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
1236 if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->local_name_off, pUrlEntry->cache_dir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
1237 (!bUnicode && URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->local_name_off, pUrlEntry->cache_dir, lpszLocalFileName, &nLocalFilePathSize)))
1239 lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
1241 dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
1243 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1244 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1245 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1247 dwRequiredSize += pUrlEntry->header_info_size + 1;
1249 if (*lpdwBufferSize >= dwRequiredSize)
1251 lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->header_info_size - 1;
1252 memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPCSTR)pUrlEntry + pUrlEntry->header_info_off, pUrlEntry->header_info_size);
1253 ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
1255 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1256 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1257 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1259 if (pUrlEntry->file_extension_off)
1264 lenExtension = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->file_extension_off, -1, NULL, 0);
1266 lenExtension = strlen((LPCSTR)pUrlEntry + pUrlEntry->file_extension_off) + 1;
1267 dwRequiredSize += lenExtension * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1269 if (*lpdwBufferSize >= dwRequiredSize)
1271 lpCacheEntryInfo->lpszFileExtension = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenExtension;
1273 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->file_extension_off, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenExtension);
1275 memcpy(lpCacheEntryInfo->lpszFileExtension, (LPCSTR)pUrlEntry + pUrlEntry->file_extension_off, lenExtension * sizeof(CHAR));
1278 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1279 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1280 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1283 if (dwRequiredSize > *lpdwBufferSize)
1285 *lpdwBufferSize = dwRequiredSize;
1286 return ERROR_INSUFFICIENT_BUFFER;
1288 *lpdwBufferSize = dwRequiredSize;
1289 return ERROR_SUCCESS;
1292 /***********************************************************************
1293 * URLCache_SetEntryInfo (Internal)
1295 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1296 * according to the flags set by dwFieldControl.
1299 * ERROR_SUCCESS if the buffer was big enough
1300 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1303 static DWORD URLCache_SetEntryInfo(entry_url * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
1305 if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
1306 pUrlEntry->access_time = lpCacheEntryInfo->LastAccessTime;
1307 if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
1308 pUrlEntry->cache_entry_type = lpCacheEntryInfo->CacheEntryType;
1309 if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
1310 pUrlEntry->exempt_delta = lpCacheEntryInfo->u.dwExemptDelta;
1311 if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
1312 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->ExpireTime, &pUrlEntry->expire_date, &pUrlEntry->expire_time);
1313 if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
1314 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1315 if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
1316 pUrlEntry->hit_rate = lpCacheEntryInfo->dwHitRate;
1317 if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
1318 pUrlEntry->modification_time = lpCacheEntryInfo->LastModifiedTime;
1319 if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
1320 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->sync_date, &pUrlEntry->sync_time);
1322 return ERROR_SUCCESS;
1325 /***********************************************************************
1326 * URLCache_HashKey (Internal)
1328 * Returns the hash key for a given string
1331 * hash key for the string
1334 static DWORD URLCache_HashKey(LPCSTR lpszKey)
1336 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1337 * but the algorithm and result are not the same!
1339 static const unsigned char lookupTable[256] =
1341 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1342 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1343 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1344 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1345 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1346 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1347 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1348 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1349 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1350 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1351 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1352 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1353 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1354 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1355 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1356 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1357 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1358 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1359 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1360 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1361 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1362 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1363 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1364 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1365 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1366 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1367 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1368 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1369 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1370 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1371 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1372 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1377 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1378 key[i] = lookupTable[(*lpszKey + i) & 0xFF];
1380 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1382 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1383 key[i] = lookupTable[*lpszKey ^ key[i]];
1386 return *(DWORD *)key;
1389 static inline HASH_CACHEFILE_ENTRY * URLCache_HashEntryFromOffset(LPCURLCACHE_HEADER pHeader, DWORD dwOffset)
1391 return (HASH_CACHEFILE_ENTRY *)((LPBYTE)pHeader + dwOffset);
1394 static inline BOOL URLCache_IsHashEntryValid(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY *pHashEntry)
1396 /* check pHashEntry located within acceptable bounds in the URL cache mapping */
1397 return ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) >= ENTRY_START_OFFSET) &&
1398 ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) < pHeader->dwFileSize);
1401 static BOOL URLCache_FindHash(LPCURLCACHE_HEADER pHeader, LPCSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1403 /* structure of hash table:
1404 * 448 entries divided into 64 blocks
1405 * each block therefore contains a chain of 7 key/offset pairs
1406 * how position in table is calculated:
1407 * 1. the url is hashed in helper function
1408 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1409 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1412 * there can be multiple hash tables in the file and the offset to
1413 * the next one is stored in the header of the hash table
1415 DWORD key = URLCache_HashKey(lpszUrl);
1416 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1417 HASH_CACHEFILE_ENTRY * pHashEntry;
1418 DWORD dwHashTableNumber = 0;
1420 key >>= HASHTABLE_FLAG_BITS;
1422 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1423 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1424 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1427 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1429 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1432 /* make sure that it is in fact a hash entry */
1433 if (pHashEntry->CacheFileEntry.signature != HASH_SIGNATURE)
1435 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.signature);
1439 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1441 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1442 if (key == pHashElement->dwHashKey>>HASHTABLE_FLAG_BITS)
1444 /* FIXME: we should make sure that this is the right element
1445 * before returning and claiming that it is. We can do this
1446 * by doing a simple compare between the URL we were given
1447 * and the URL stored in the entry. However, this assumes
1448 * we know the format of all the entries stored in the
1450 *ppHashEntry = pHashElement;
1458 static BOOL URLCache_FindHashW(LPCURLCACHE_HEADER pHeader, LPCWSTR lpszUrl, struct _HASH_ENTRY ** ppHashEntry)
1463 urlA = heap_strdupWtoA(lpszUrl);
1466 SetLastError(ERROR_OUTOFMEMORY);
1470 ret = URLCache_FindHash(pHeader, urlA, ppHashEntry);
1475 /***********************************************************************
1476 * URLCache_HashEntrySetFlags (Internal)
1478 * Sets special bits in hash key
1484 static void URLCache_HashEntrySetFlags(struct _HASH_ENTRY * pHashEntry, DWORD dwFlag)
1486 pHashEntry->dwHashKey = (pHashEntry->dwHashKey >> HASHTABLE_FLAG_BITS << HASHTABLE_FLAG_BITS) | dwFlag;
1489 /***********************************************************************
1490 * URLCache_DeleteEntryFromHash (Internal)
1492 * Searches all the hash tables in the index for the given URL and
1493 * then if found deletes the entry.
1496 * TRUE if the entry was found
1497 * FALSE if the entry could not be found
1500 static BOOL URLCache_DeleteEntryFromHash(struct _HASH_ENTRY * pHashEntry)
1502 pHashEntry->dwHashKey = HASHTABLE_DEL;
1506 /***********************************************************************
1507 * URLCache_AddEntryToHash (Internal)
1509 * Searches all the hash tables for a free slot based on the offset
1510 * generated from the hash key. If a free slot is found, the offset and
1511 * key are entered into the hash table.
1514 * ERROR_SUCCESS if the entry was added
1515 * Any other Win32 error code if the entry could not be added
1518 static DWORD URLCache_AddEntryToHash(LPURLCACHE_HEADER pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry, DWORD dwFieldType)
1520 /* see URLCache_FindEntryInHash for structure of hash tables */
1522 DWORD key = URLCache_HashKey(lpszUrl);
1523 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1524 HASH_CACHEFILE_ENTRY * pHashEntry, *pHashPrev = NULL;
1525 DWORD dwHashTableNumber = 0;
1528 key = ((key >> HASHTABLE_FLAG_BITS) << HASHTABLE_FLAG_BITS) + dwFieldType;
1530 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1531 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1532 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->dwAddressNext))
1535 pHashPrev = pHashEntry;
1537 if (pHashEntry->dwHashTableNumber != dwHashTableNumber++)
1539 ERR("not right hash table number (%d) expected %d\n", pHashEntry->dwHashTableNumber, dwHashTableNumber);
1542 /* make sure that it is in fact a hash entry */
1543 if (pHashEntry->CacheFileEntry.signature != HASH_SIGNATURE)
1545 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->CacheFileEntry.signature);
1549 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1551 struct _HASH_ENTRY * pHashElement = &pHashEntry->HashTable[offset + i];
1552 if (pHashElement->dwHashKey==HASHTABLE_FREE || pHashElement->dwHashKey==HASHTABLE_DEL) /* if the slot is free */
1554 pHashElement->dwHashKey = key;
1555 pHashElement->dwOffsetEntry = dwOffsetEntry;
1556 return ERROR_SUCCESS;
1560 error = URLCache_CreateHashTable(pHeader, pHashPrev, &pHashEntry);
1561 if (error != ERROR_SUCCESS)
1564 pHashEntry->HashTable[offset].dwHashKey = key;
1565 pHashEntry->HashTable[offset].dwOffsetEntry = dwOffsetEntry;
1566 return ERROR_SUCCESS;
1569 /***********************************************************************
1570 * URLCache_CreateHashTable (Internal)
1572 * Creates a new hash table in free space and adds it to the chain of existing
1576 * ERROR_SUCCESS if the hash table was created
1577 * ERROR_DISK_FULL if the hash table could not be created
1580 static DWORD URLCache_CreateHashTable(LPURLCACHE_HEADER pHeader, HASH_CACHEFILE_ENTRY *pPrevHash, HASH_CACHEFILE_ENTRY **ppHash)
1582 DWORD dwOffset, error;
1585 if ((error = URLCache_FindFirstFreeEntry(pHeader, 0x20, (entry_header**)ppHash)) != ERROR_SUCCESS)
1588 dwOffset = (BYTE *)*ppHash - (BYTE *)pHeader;
1591 pPrevHash->dwAddressNext = dwOffset;
1593 pHeader->dwOffsetFirstHashTable = dwOffset;
1594 (*ppHash)->CacheFileEntry.signature = HASH_SIGNATURE;
1595 (*ppHash)->CacheFileEntry.blocks_used = 0x20;
1596 (*ppHash)->dwAddressNext = 0;
1597 (*ppHash)->dwHashTableNumber = pPrevHash ? pPrevHash->dwHashTableNumber + 1 : 0;
1598 for (i = 0; i < HASHTABLE_SIZE; i++)
1600 (*ppHash)->HashTable[i].dwOffsetEntry = HASHTABLE_FREE;
1601 (*ppHash)->HashTable[i].dwHashKey = HASHTABLE_FREE;
1603 return ERROR_SUCCESS;
1606 /***********************************************************************
1607 * URLCache_EnumHashTables (Internal)
1609 * Enumerates the hash tables in a container.
1612 * TRUE if an entry was found
1613 * FALSE if there are no more tables to enumerate.
1616 static BOOL URLCache_EnumHashTables(LPCURLCACHE_HEADER pHeader, DWORD *pdwHashTableNumber, HASH_CACHEFILE_ENTRY ** ppHashEntry)
1618 for (*ppHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->dwOffsetFirstHashTable);
1619 URLCache_IsHashEntryValid(pHeader, *ppHashEntry);
1620 *ppHashEntry = URLCache_HashEntryFromOffset(pHeader, (*ppHashEntry)->dwAddressNext))
1622 TRACE("looking at hash table number %d\n", (*ppHashEntry)->dwHashTableNumber);
1623 if ((*ppHashEntry)->dwHashTableNumber != *pdwHashTableNumber)
1625 /* make sure that it is in fact a hash entry */
1626 if ((*ppHashEntry)->CacheFileEntry.signature != HASH_SIGNATURE)
1628 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->CacheFileEntry.signature);
1629 (*pdwHashTableNumber)++;
1633 TRACE("hash table number %d found\n", *pdwHashTableNumber);
1639 /***********************************************************************
1640 * URLCache_EnumHashTableEntries (Internal)
1642 * Enumerates entries in a hash table and returns the next non-free entry.
1645 * TRUE if an entry was found
1646 * FALSE if the hash table is empty or there are no more entries to
1650 static BOOL URLCache_EnumHashTableEntries(LPCURLCACHE_HEADER pHeader, const HASH_CACHEFILE_ENTRY * pHashEntry,
1651 DWORD * index, const struct _HASH_ENTRY ** ppHashEntry)
1653 for (; *index < HASHTABLE_SIZE ; (*index)++)
1655 if (pHashEntry->HashTable[*index].dwHashKey==HASHTABLE_FREE || pHashEntry->HashTable[*index].dwHashKey==HASHTABLE_DEL)
1658 *ppHashEntry = &pHashEntry->HashTable[*index];
1659 TRACE("entry found %d\n", *index);
1662 TRACE("no more entries (%d)\n", *index);
1666 /***********************************************************************
1667 * URLCache_DeleteCacheDirectory (Internal)
1669 * Erase a directory containing an URL cache.
1672 * TRUE success, FALSE failure/aborted.
1675 static BOOL URLCache_DeleteCacheDirectory(LPCWSTR lpszPath)
1678 WCHAR path[MAX_PATH + 1];
1679 SHFILEOPSTRUCTW shfos;
1682 path_len = strlenW(lpszPath);
1683 if (path_len >= MAX_PATH)
1685 strcpyW(path, lpszPath);
1686 path[path_len + 1] = 0; /* double-NUL-terminate path */
1689 shfos.wFunc = FO_DELETE;
1692 shfos.fFlags = FOF_NOCONFIRMATION;
1693 shfos.fAnyOperationsAborted = FALSE;
1694 ret = SHFileOperationW(&shfos);
1696 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path), ret);
1697 return !(ret || shfos.fAnyOperationsAborted);
1700 /***********************************************************************
1701 * URLCache_IsLocked (Internal)
1703 * Checks if entry is locked. Unlocks it if possible.
1705 static BOOL URLCache_IsLocked(struct _HASH_ENTRY *hash_entry, entry_url *url_entry)
1708 ULARGE_INTEGER acc_time, time;
1710 if ((hash_entry->dwHashKey & ((1<<HASHTABLE_FLAG_BITS)-1)) != HASHTABLE_LOCK)
1713 GetSystemTimeAsFileTime(&cur_time);
1714 time.u.LowPart = cur_time.dwLowDateTime;
1715 time.u.HighPart = cur_time.dwHighDateTime;
1717 acc_time.u.LowPart = url_entry->access_time.dwLowDateTime;
1718 acc_time.u.HighPart = url_entry->access_time.dwHighDateTime;
1720 time.QuadPart -= acc_time.QuadPart;
1722 /* check if entry was locked for at least a day */
1723 if(time.QuadPart > (ULONGLONG)24*60*60*FILETIME_SECOND) {
1724 URLCache_HashEntrySetFlags(hash_entry, HASHTABLE_URL);
1725 url_entry->use_count = 0;
1732 /***********************************************************************
1733 * GetUrlCacheEntryInfoExA (WININET.@)
1736 BOOL WINAPI GetUrlCacheEntryInfoExA(
1738 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1739 LPDWORD lpdwCacheEntryInfoBufSize,
1741 LPDWORD lpdwReserved,
1745 LPURLCACHE_HEADER pHeader;
1746 struct _HASH_ENTRY * pHashEntry;
1747 const entry_header *pEntry;
1748 const entry_url * pUrlEntry;
1749 URLCACHECONTAINER * pContainer;
1752 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1753 debugstr_a(lpszUrl),
1755 lpdwCacheEntryInfoBufSize,
1761 if ((lpszReserved != NULL) ||
1762 (lpdwReserved != NULL) ||
1763 (lpReserved != NULL))
1765 ERR("Reserved value was not 0\n");
1766 SetLastError(ERROR_INVALID_PARAMETER);
1769 if (dwFlags & ~GET_INSTALLED_ENTRY)
1770 FIXME("ignoring unsupported flags: %x\n", dwFlags);
1772 error = URLCacheContainers_FindContainerA(lpszUrl, &pContainer);
1773 if (error != ERROR_SUCCESS)
1775 SetLastError(error);
1779 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
1780 if (error != ERROR_SUCCESS)
1782 SetLastError(error);
1786 if (!(pHeader = cache_container_lock_index(pContainer)))
1789 if (!URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1791 cache_container_unlock_index(pContainer, pHeader);
1792 WARN("entry %s not found!\n", debugstr_a(lpszUrl));
1793 SetLastError(ERROR_FILE_NOT_FOUND);
1797 pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1798 if (pEntry->signature != URL_SIGNATURE)
1800 cache_container_unlock_index(pContainer, pHeader);
1801 FIXME("Trying to retrieve entry of unknown format %s\n",
1802 debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
1803 SetLastError(ERROR_FILE_NOT_FOUND);
1807 pUrlEntry = (const entry_url *)pEntry;
1808 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->url_off));
1809 TRACE("Header info: %s\n", debugstr_an((LPCSTR)pUrlEntry +
1810 pUrlEntry->header_info_off, pUrlEntry->header_info_size));
1812 if((dwFlags & GET_INSTALLED_ENTRY) && !(pUrlEntry->cache_entry_type & INSTALLED_CACHE_ENTRY))
1814 cache_container_unlock_index(pContainer, pHeader);
1815 SetLastError(ERROR_FILE_NOT_FOUND);
1819 if (lpdwCacheEntryInfoBufSize)
1821 if (!lpCacheEntryInfo)
1822 *lpdwCacheEntryInfoBufSize = 0;
1824 error = URLCache_CopyEntry(
1828 lpdwCacheEntryInfoBufSize,
1831 if (error != ERROR_SUCCESS)
1833 cache_container_unlock_index(pContainer, pHeader);
1834 SetLastError(error);
1837 if(pUrlEntry->local_name_off)
1838 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->local_name_off));
1841 cache_container_unlock_index(pContainer, pHeader);
1846 /***********************************************************************
1847 * GetUrlCacheEntryInfoA (WININET.@)
1850 BOOL WINAPI GetUrlCacheEntryInfoA(
1851 IN LPCSTR lpszUrlName,
1852 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1853 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1856 return GetUrlCacheEntryInfoExA(lpszUrlName, lpCacheEntryInfo,
1857 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
1860 /***********************************************************************
1861 * GetUrlCacheEntryInfoW (WININET.@)
1864 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1865 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1866 LPDWORD lpdwCacheEntryInfoBufferSize)
1868 return GetUrlCacheEntryInfoExW(lpszUrl, lpCacheEntryInfo,
1869 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
1872 /***********************************************************************
1873 * GetUrlCacheEntryInfoExW (WININET.@)
1876 BOOL WINAPI GetUrlCacheEntryInfoExW(
1878 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1879 LPDWORD lpdwCacheEntryInfoBufSize,
1880 LPWSTR lpszReserved,
1881 LPDWORD lpdwReserved,
1885 LPURLCACHE_HEADER pHeader;
1886 struct _HASH_ENTRY * pHashEntry;
1887 const entry_header *pEntry;
1888 const entry_url * pUrlEntry;
1889 URLCACHECONTAINER * pContainer;
1892 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1893 debugstr_w(lpszUrl),
1895 lpdwCacheEntryInfoBufSize,
1901 /* Ignore GET_INSTALLED_ENTRY flag in unicode version of function */
1902 dwFlags &= ~GET_INSTALLED_ENTRY;
1904 if ((lpszReserved != NULL) ||
1905 (lpdwReserved != NULL) ||
1906 (lpReserved != NULL))
1908 ERR("Reserved value was not 0\n");
1909 SetLastError(ERROR_INVALID_PARAMETER);
1913 FIXME("ignoring unsupported flags: %x\n", dwFlags);
1915 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1916 if (error != ERROR_SUCCESS)
1918 SetLastError(error);
1922 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
1923 if (error != ERROR_SUCCESS)
1925 SetLastError(error);
1929 if (!(pHeader = cache_container_lock_index(pContainer)))
1932 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1934 cache_container_unlock_index(pContainer, pHeader);
1935 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1936 SetLastError(ERROR_FILE_NOT_FOUND);
1940 pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
1941 if (pEntry->signature != URL_SIGNATURE)
1943 cache_container_unlock_index(pContainer, pHeader);
1944 FIXME("Trying to retrieve entry of unknown format %s\n",
1945 debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
1946 SetLastError(ERROR_FILE_NOT_FOUND);
1950 pUrlEntry = (const entry_url *)pEntry;
1951 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->url_off));
1952 TRACE("Header info: %s\n", debugstr_an((LPCSTR)pUrlEntry +
1953 pUrlEntry->header_info_off, pUrlEntry->header_info_size));
1955 if (lpdwCacheEntryInfoBufSize)
1957 if (!lpCacheEntryInfo)
1958 *lpdwCacheEntryInfoBufSize = 0;
1960 error = URLCache_CopyEntry(
1963 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1964 lpdwCacheEntryInfoBufSize,
1966 TRUE /* UNICODE */);
1967 if (error != ERROR_SUCCESS)
1969 cache_container_unlock_index(pContainer, pHeader);
1970 SetLastError(error);
1973 if(pUrlEntry->local_name_off)
1974 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->local_name_off));
1977 cache_container_unlock_index(pContainer, pHeader);
1982 /***********************************************************************
1983 * SetUrlCacheEntryInfoA (WININET.@)
1985 BOOL WINAPI SetUrlCacheEntryInfoA(
1987 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1988 DWORD dwFieldControl)
1990 LPURLCACHE_HEADER pHeader;
1991 struct _HASH_ENTRY * pHashEntry;
1992 entry_header *pEntry;
1993 URLCACHECONTAINER * pContainer;
1996 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1998 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
1999 if (error != ERROR_SUCCESS)
2001 SetLastError(error);
2005 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2006 if (error != ERROR_SUCCESS)
2008 SetLastError(error);
2012 if (!(pHeader = cache_container_lock_index(pContainer)))
2015 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2017 cache_container_unlock_index(pContainer, pHeader);
2018 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
2019 SetLastError(ERROR_FILE_NOT_FOUND);
2023 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2024 if (pEntry->signature != URL_SIGNATURE)
2026 cache_container_unlock_index(pContainer, pHeader);
2027 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2028 SetLastError(ERROR_FILE_NOT_FOUND);
2032 URLCache_SetEntryInfo(
2033 (entry_url *)pEntry,
2034 (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
2037 cache_container_unlock_index(pContainer, pHeader);
2042 /***********************************************************************
2043 * SetUrlCacheEntryInfoW (WININET.@)
2045 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
2047 LPURLCACHE_HEADER pHeader;
2048 struct _HASH_ENTRY * pHashEntry;
2049 entry_header *pEntry;
2050 URLCACHECONTAINER * pContainer;
2053 TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
2055 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
2056 if (error != ERROR_SUCCESS)
2058 SetLastError(error);
2062 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2063 if (error != ERROR_SUCCESS)
2065 SetLastError(error);
2069 if (!(pHeader = cache_container_lock_index(pContainer)))
2072 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
2074 cache_container_unlock_index(pContainer, pHeader);
2075 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
2076 SetLastError(ERROR_FILE_NOT_FOUND);
2080 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2081 if (pEntry->signature != URL_SIGNATURE)
2083 cache_container_unlock_index(pContainer, pHeader);
2084 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2085 SetLastError(ERROR_FILE_NOT_FOUND);
2089 URLCache_SetEntryInfo(
2090 (entry_url *)pEntry,
2094 cache_container_unlock_index(pContainer, pHeader);
2099 /***********************************************************************
2100 * RetrieveUrlCacheEntryFileA (WININET.@)
2103 BOOL WINAPI RetrieveUrlCacheEntryFileA(
2104 IN LPCSTR lpszUrlName,
2105 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2106 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2110 LPURLCACHE_HEADER pHeader;
2111 struct _HASH_ENTRY * pHashEntry;
2112 entry_header *pEntry;
2113 entry_url * pUrlEntry;
2114 URLCACHECONTAINER * pContainer;
2117 TRACE("(%s, %p, %p, 0x%08x)\n",
2118 debugstr_a(lpszUrlName),
2120 lpdwCacheEntryInfoBufferSize,
2123 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
2124 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
2126 SetLastError(ERROR_INVALID_PARAMETER);
2130 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2131 if (error != ERROR_SUCCESS)
2133 SetLastError(error);
2137 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2138 if (error != ERROR_SUCCESS)
2140 SetLastError(error);
2144 if (!(pHeader = cache_container_lock_index(pContainer)))
2147 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2149 cache_container_unlock_index(pContainer, pHeader);
2150 TRACE("entry %s not found!\n", lpszUrlName);
2151 SetLastError(ERROR_FILE_NOT_FOUND);
2155 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2156 if (pEntry->signature != URL_SIGNATURE)
2158 cache_container_unlock_index(pContainer, pHeader);
2159 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2160 SetLastError(ERROR_FILE_NOT_FOUND);
2164 pUrlEntry = (entry_url *)pEntry;
2165 if (!pUrlEntry->local_name_off)
2167 cache_container_unlock_index(pContainer, pHeader);
2168 SetLastError(ERROR_INVALID_DATA);
2172 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->url_off));
2173 TRACE("Header info: %s\n", debugstr_an((LPCSTR)pUrlEntry + pUrlEntry->header_info_off,
2174 pUrlEntry->header_info_size));
2176 error = URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo,
2177 lpdwCacheEntryInfoBufferSize, pUrlEntry,
2179 if (error != ERROR_SUCCESS)
2181 cache_container_unlock_index(pContainer, pHeader);
2182 SetLastError(error);
2185 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->local_name_off));
2187 pUrlEntry->hit_rate++;
2188 pUrlEntry->use_count++;
2189 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_LOCK);
2190 GetSystemTimeAsFileTime(&pUrlEntry->access_time);
2192 cache_container_unlock_index(pContainer, pHeader);
2197 /***********************************************************************
2198 * RetrieveUrlCacheEntryFileW (WININET.@)
2201 BOOL WINAPI RetrieveUrlCacheEntryFileW(
2202 IN LPCWSTR lpszUrlName,
2203 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2204 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2208 LPURLCACHE_HEADER pHeader;
2209 struct _HASH_ENTRY * pHashEntry;
2210 entry_header *pEntry;
2211 entry_url * pUrlEntry;
2212 URLCACHECONTAINER * pContainer;
2215 TRACE("(%s, %p, %p, 0x%08x)\n",
2216 debugstr_w(lpszUrlName),
2218 lpdwCacheEntryInfoBufferSize,
2221 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
2222 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
2224 SetLastError(ERROR_INVALID_PARAMETER);
2228 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2229 if (error != ERROR_SUCCESS)
2231 SetLastError(error);
2235 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2236 if (error != ERROR_SUCCESS)
2238 SetLastError(error);
2242 if (!(pHeader = cache_container_lock_index(pContainer)))
2245 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2247 cache_container_unlock_index(pContainer, pHeader);
2248 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2249 SetLastError(ERROR_FILE_NOT_FOUND);
2253 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2254 if (pEntry->signature != URL_SIGNATURE)
2256 cache_container_unlock_index(pContainer, pHeader);
2257 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2258 SetLastError(ERROR_FILE_NOT_FOUND);
2262 pUrlEntry = (entry_url *)pEntry;
2263 if (!pUrlEntry->local_name_off)
2265 cache_container_unlock_index(pContainer, pHeader);
2266 SetLastError(ERROR_INVALID_DATA);
2270 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->url_off));
2271 TRACE("Header info: %s\n", debugstr_an((LPCSTR)pUrlEntry + pUrlEntry->header_info_off,
2272 pUrlEntry->header_info_size));
2274 error = URLCache_CopyEntry(
2277 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
2278 lpdwCacheEntryInfoBufferSize,
2280 TRUE /* UNICODE */);
2281 if (error != ERROR_SUCCESS)
2283 cache_container_unlock_index(pContainer, pHeader);
2284 SetLastError(error);
2287 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->local_name_off));
2289 pUrlEntry->hit_rate++;
2290 pUrlEntry->use_count++;
2291 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_LOCK);
2292 GetSystemTimeAsFileTime(&pUrlEntry->access_time);
2294 cache_container_unlock_index(pContainer, pHeader);
2299 static BOOL DeleteUrlCacheEntryInternal(const URLCACHECONTAINER * pContainer,
2300 LPURLCACHE_HEADER pHeader, struct _HASH_ENTRY *pHashEntry)
2302 entry_header *pEntry;
2303 entry_url * pUrlEntry;
2305 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2306 if (pEntry->signature != URL_SIGNATURE)
2308 FIXME("Trying to delete entry of unknown format %s\n",
2309 debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
2310 SetLastError(ERROR_FILE_NOT_FOUND);
2314 pUrlEntry = (entry_url *)pEntry;
2315 if(URLCache_IsLocked(pHashEntry, pUrlEntry))
2317 TRACE("Trying to delete locked entry\n");
2318 pUrlEntry->cache_entry_type |= PENDING_DELETE_CACHE_ENTRY;
2319 SetLastError(ERROR_SHARING_VIOLATION);
2323 if(!URLCache_DeleteFile(pContainer, pHeader, pUrlEntry))
2325 URLCache_DeleteEntry(pHeader, pEntry);
2329 /* Add entry to leaked files list */
2330 pUrlEntry->header.signature = LEAK_SIGNATURE;
2331 pUrlEntry->exempt_delta = pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
2332 pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET] = pHashEntry->dwOffsetEntry;
2335 URLCache_DeleteEntryFromHash(pHashEntry);
2339 static HANDLE free_cache_running;
2340 static HANDLE dll_unload_event;
2341 static DWORD WINAPI handle_full_cache_worker(void *param)
2343 FreeUrlCacheSpaceW(NULL, 20, 0);
2344 ReleaseSemaphore(free_cache_running, 1, NULL);
2348 static void handle_full_cache(void)
2350 if(WaitForSingleObject(free_cache_running, 0) == WAIT_OBJECT_0) {
2351 if(!QueueUserWorkItem(handle_full_cache_worker, NULL, 0))
2352 ReleaseSemaphore(free_cache_running, 1, NULL);
2356 /* Enumerates entries in cache, allows cache unlocking between calls. */
2357 static BOOL urlcache_next_entry(URLCACHE_HEADER *header, DWORD *hash_table_off, DWORD *hash_table_entry,
2358 struct _HASH_ENTRY **hash_entry, entry_header **entry)
2360 HASH_CACHEFILE_ENTRY *hashtable_entry;
2365 if(!*hash_table_off) {
2366 *hash_table_off = header->dwOffsetFirstHashTable;
2367 *hash_table_entry = 0;
2369 hashtable_entry = URLCache_HashEntryFromOffset(header, *hash_table_off);
2371 if(*hash_table_off >= header->dwFileSize) {
2372 *hash_table_off = 0;
2376 hashtable_entry = URLCache_HashEntryFromOffset(header, *hash_table_off);
2379 if(hashtable_entry->CacheFileEntry.signature != HASH_SIGNATURE) {
2380 *hash_table_off = 0;
2385 if(*hash_table_entry >= HASHTABLE_SIZE) {
2386 *hash_table_off = hashtable_entry->dwAddressNext;
2387 if(!*hash_table_off) {
2388 *hash_table_off = 0;
2392 hashtable_entry = URLCache_HashEntryFromOffset(header, *hash_table_off);
2393 *hash_table_entry = 0;
2396 if(hashtable_entry->HashTable[*hash_table_entry].dwHashKey != HASHTABLE_DEL &&
2397 hashtable_entry->HashTable[*hash_table_entry].dwHashKey != HASHTABLE_FREE) {
2398 *hash_entry = &hashtable_entry->HashTable[*hash_table_entry];
2399 *entry = (entry_header*)((LPBYTE)header + hashtable_entry->HashTable[*hash_table_entry].dwOffsetEntry);
2400 (*hash_table_entry)++;
2404 (*hash_table_entry)++;
2407 *hash_table_off = 0;
2411 /* Rates an urlcache entry to determine if it can be deleted.
2413 * Score 0 means that entry can safely be removed, the bigger rating
2414 * the smaller chance of entry being removed.
2415 * DWORD_MAX means that entry can't be deleted at all.
2417 * Rating system is currently not fully compatible with native implementation.
2419 static DWORD urlcache_rate_entry(entry_url *url_entry, FILETIME *cur_time)
2421 ULARGE_INTEGER time, access_time;
2424 access_time.u.LowPart = url_entry->access_time.dwLowDateTime;
2425 access_time.u.HighPart = url_entry->access_time.dwHighDateTime;
2427 time.u.LowPart = cur_time->dwLowDateTime;
2428 time.u.HighPart = cur_time->dwHighDateTime;
2430 /* Don't touch entries that were added less than 10 minutes ago */
2431 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)10*60*FILETIME_SECOND)
2434 if(url_entry->cache_entry_type & STICKY_CACHE_ENTRY)
2435 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)url_entry->exempt_delta*FILETIME_SECOND)
2438 time.QuadPart = (time.QuadPart-access_time.QuadPart)/FILETIME_SECOND;
2439 rating = 400*60*60*24/(60*60*24+time.QuadPart);
2441 if(url_entry->hit_rate > 100)
2444 rating += url_entry->hit_rate;
2449 static int dword_cmp(const void *p1, const void *p2)
2451 return *(const DWORD*)p1 - *(const DWORD*)p2;
2454 /***********************************************************************
2455 * FreeUrlCacheSpaceW (WININET.@)
2457 * Frees up some cache.
2460 * cache_path [I] Which volume to free up from, or NULL if you don't care.
2461 * size [I] Percentage of the cache that should be free.
2462 * filter [I] Which entries can't be deleted (CacheEntryType)
2465 * TRUE success. FALSE failure.
2468 * This implementation just retrieves the path of the cache directory, and
2469 * deletes its contents from the filesystem. The correct approach would
2470 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
2472 BOOL WINAPI FreeUrlCacheSpaceW(LPCWSTR cache_path, DWORD size, DWORD filter)
2474 URLCACHECONTAINER *container;
2475 DWORD path_len, err;
2477 TRACE("(%s, %x, %x)\n", debugstr_w(cache_path), size, filter);
2479 if(size<1 || size>100) {
2480 SetLastError(ERROR_INVALID_PARAMETER);
2485 path_len = strlenW(cache_path);
2486 if(cache_path[path_len-1] == '\\')
2492 if(size==100 && !filter) {
2493 LIST_FOR_EACH_ENTRY(container, &UrlContainers, URLCACHECONTAINER, entry)
2495 /* When cache_path==NULL only clean Temporary Internet Files */
2496 if((!path_len && container->cache_prefix[0]==0) ||
2497 (path_len && !strncmpiW(container->path, cache_path, path_len) &&
2498 (container->path[path_len]=='\0' || container->path[path_len]=='\\')))
2502 WaitForSingleObject(container->hMutex, INFINITE);
2504 /* unlock, delete, recreate and lock cache */
2505 cache_container_close_index(container);
2506 ret_del = URLCache_DeleteCacheDirectory(container->path);
2507 err = cache_container_open_index(container, MIN_BLOCK_NO);
2509 ReleaseMutex(container->hMutex);
2510 if(!ret_del || (err != ERROR_SUCCESS))
2518 LIST_FOR_EACH_ENTRY(container, &UrlContainers, URLCACHECONTAINER, entry)
2520 URLCACHE_HEADER *header;
2521 struct _HASH_ENTRY *hash_entry;
2522 entry_header *entry;
2523 entry_url *url_entry;
2524 ULONGLONG desired_size, cur_size;
2525 DWORD delete_factor, hash_table_off, hash_table_entry;
2526 DWORD rate[100], rate_no;
2529 if((path_len || container->cache_prefix[0]!=0) &&
2530 (!path_len || strncmpiW(container->path, cache_path, path_len) ||
2531 (container->path[path_len]!='\0' && container->path[path_len]!='\\')))
2534 err = cache_container_open_index(container, MIN_BLOCK_NO);
2535 if(err != ERROR_SUCCESS)
2538 header = cache_container_lock_index(container);
2542 urlcache_clean_leaked_entries(container, header);
2544 desired_size = header->CacheLimit.QuadPart*(100-size)/100;
2545 cur_size = header->CacheUsage.QuadPart+header->ExemptUsage.QuadPart;
2546 if(cur_size <= desired_size)
2549 delete_factor = (cur_size-desired_size)*100/cur_size;
2551 if(!delete_factor) {
2552 cache_container_unlock_index(container, header);
2557 hash_table_entry = 0;
2559 GetSystemTimeAsFileTime(&cur_time);
2560 while(rate_no<sizeof(rate)/sizeof(*rate) &&
2561 urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2562 if(entry->signature != URL_SIGNATURE) {
2563 WARN("only url entries are currently supported\n");
2567 url_entry = (entry_url*)entry;
2568 if(url_entry->cache_entry_type & filter)
2571 rate[rate_no] = urlcache_rate_entry(url_entry, &cur_time);
2572 if(rate[rate_no] != -1)
2577 TRACE("nothing to delete\n");
2578 cache_container_unlock_index(container, header);
2582 qsort(rate, rate_no, sizeof(DWORD), dword_cmp);
2584 delete_factor = delete_factor*rate_no/100;
2585 delete_factor = rate[delete_factor];
2586 TRACE("deleting files with rating %d or less\n", delete_factor);
2589 while(urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2590 if(entry->signature != URL_SIGNATURE)
2593 url_entry = (entry_url*)entry;
2594 if(url_entry->cache_entry_type & filter)
2597 if(urlcache_rate_entry(url_entry, &cur_time) <= delete_factor) {
2598 TRACE("deleting file: %s\n", debugstr_a((char*)url_entry+url_entry->local_name_off));
2599 DeleteUrlCacheEntryInternal(container, header, hash_entry);
2601 if(header->CacheUsage.QuadPart+header->ExemptUsage.QuadPart <= desired_size)
2604 /* Allow other threads to use cache while cleaning */
2605 cache_container_unlock_index(container, header);
2606 if(WaitForSingleObject(dll_unload_event, 0) == WAIT_OBJECT_0) {
2607 TRACE("got dll_unload_event - finishing\n");
2611 header = cache_container_lock_index(container);
2615 TRACE("cache size after cleaning 0x%s/0x%s\n",
2616 wine_dbgstr_longlong(header->CacheUsage.QuadPart+header->ExemptUsage.QuadPart),
2617 wine_dbgstr_longlong(header->CacheLimit.QuadPart));
2618 cache_container_unlock_index(container, header);
2624 /***********************************************************************
2625 * FreeUrlCacheSpaceA (WININET.@)
2627 * See FreeUrlCacheSpaceW.
2629 BOOL WINAPI FreeUrlCacheSpaceA(LPCSTR lpszCachePath, DWORD dwSize, DWORD dwFilter)
2632 LPWSTR path = heap_strdupAtoW(lpszCachePath);
2633 if (lpszCachePath == NULL || path != NULL)
2634 ret = FreeUrlCacheSpaceW(path, dwSize, dwFilter);
2639 /***********************************************************************
2640 * UnlockUrlCacheEntryFileA (WININET.@)
2643 BOOL WINAPI UnlockUrlCacheEntryFileA(
2644 IN LPCSTR lpszUrlName,
2648 LPURLCACHE_HEADER pHeader;
2649 struct _HASH_ENTRY * pHashEntry;
2650 entry_header *pEntry;
2651 entry_url * pUrlEntry;
2652 URLCACHECONTAINER * pContainer;
2655 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2659 ERR("dwReserved != 0\n");
2660 SetLastError(ERROR_INVALID_PARAMETER);
2664 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2665 if (error != ERROR_SUCCESS)
2667 SetLastError(error);
2671 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2672 if (error != ERROR_SUCCESS)
2674 SetLastError(error);
2678 if (!(pHeader = cache_container_lock_index(pContainer)))
2681 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2683 cache_container_unlock_index(pContainer, pHeader);
2684 TRACE("entry %s not found!\n", lpszUrlName);
2685 SetLastError(ERROR_FILE_NOT_FOUND);
2689 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2690 if (pEntry->signature != URL_SIGNATURE)
2692 cache_container_unlock_index(pContainer, pHeader);
2693 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2694 SetLastError(ERROR_FILE_NOT_FOUND);
2698 pUrlEntry = (entry_url *)pEntry;
2700 if (pUrlEntry->use_count == 0)
2702 cache_container_unlock_index(pContainer, pHeader);
2705 pUrlEntry->use_count--;
2706 if (!pUrlEntry->use_count)
2708 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_URL);
2709 if (pUrlEntry->cache_entry_type & PENDING_DELETE_CACHE_ENTRY)
2710 DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
2713 cache_container_unlock_index(pContainer, pHeader);
2718 /***********************************************************************
2719 * UnlockUrlCacheEntryFileW (WININET.@)
2722 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
2724 LPURLCACHE_HEADER pHeader;
2725 struct _HASH_ENTRY * pHashEntry;
2726 entry_header *pEntry;
2727 entry_url * pUrlEntry;
2728 URLCACHECONTAINER * pContainer;
2731 TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
2735 ERR("dwReserved != 0\n");
2736 SetLastError(ERROR_INVALID_PARAMETER);
2740 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2741 if (error != ERROR_SUCCESS)
2743 SetLastError(error);
2747 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2748 if (error != ERROR_SUCCESS)
2750 SetLastError(error);
2754 if (!(pHeader = cache_container_lock_index(pContainer)))
2757 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2759 cache_container_unlock_index(pContainer, pHeader);
2760 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2761 SetLastError(ERROR_FILE_NOT_FOUND);
2765 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
2766 if (pEntry->signature != URL_SIGNATURE)
2768 cache_container_unlock_index(pContainer, pHeader);
2769 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2770 SetLastError(ERROR_FILE_NOT_FOUND);
2774 pUrlEntry = (entry_url *)pEntry;
2776 if (pUrlEntry->use_count == 0)
2778 cache_container_unlock_index(pContainer, pHeader);
2781 pUrlEntry->use_count--;
2782 if (!pUrlEntry->use_count)
2783 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_URL);
2785 cache_container_unlock_index(pContainer, pHeader);
2790 /***********************************************************************
2791 * CreateUrlCacheEntryA (WININET.@)
2794 BOOL WINAPI CreateUrlCacheEntryA(
2795 IN LPCSTR lpszUrlName,
2796 IN DWORD dwExpectedFileSize,
2797 IN LPCSTR lpszFileExtension,
2798 OUT LPSTR lpszFileName,
2803 WCHAR *file_extension = NULL;
2804 WCHAR file_name[MAX_PATH];
2805 BOOL bSuccess = FALSE;
2808 TRACE("(%s %d %s %p %d)\n", debugstr_a(lpszUrlName), dwExpectedFileSize,
2809 debugstr_a(lpszFileExtension), lpszFileName, dwReserved);
2811 if (lpszUrlName && (url_name = heap_strdupAtoW(lpszUrlName)))
2813 if (!lpszFileExtension || (file_extension = heap_strdupAtoW(lpszFileExtension)))
2815 if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
2817 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
2823 dwError = GetLastError();
2828 dwError = GetLastError();
2830 heap_free(file_extension);
2834 dwError = GetLastError();
2836 heap_free(url_name);
2837 if (!bSuccess) SetLastError(dwError);
2841 /***********************************************************************
2842 * CreateUrlCacheEntryW (WININET.@)
2845 BOOL WINAPI CreateUrlCacheEntryW(
2846 IN LPCWSTR lpszUrlName,
2847 IN DWORD dwExpectedFileSize,
2848 IN LPCWSTR lpszFileExtension,
2849 OUT LPWSTR lpszFileName,
2853 URLCACHECONTAINER * pContainer;
2854 LPURLCACHE_HEADER pHeader;
2855 CHAR szFile[MAX_PATH];
2856 WCHAR szExtension[MAX_PATH];
2857 LPCWSTR lpszUrlPart;
2859 LPCWSTR lpszFileNameExtension;
2860 LPWSTR lpszFileNameNoPath;
2862 int countnoextension;
2865 BOOL bFound = FALSE;
2866 BOOL generate_name = FALSE;
2872 static const WCHAR szWWW[] = {'w','w','w',0};
2874 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2875 debugstr_w(lpszUrlName),
2877 debugstr_w(lpszFileExtension),
2882 FIXME("dwReserved 0x%08x\n", dwReserved);
2884 lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2886 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2889 lpszUrlPart = memchrW(lpszUrlName, '?', lpszUrlEnd - lpszUrlName);
2891 lpszUrlPart = memchrW(lpszUrlName, '#', lpszUrlEnd - lpszUrlName);
2893 lpszUrlEnd = lpszUrlPart;
2895 for (lpszUrlPart = lpszUrlEnd;
2896 (lpszUrlPart >= lpszUrlName);
2899 if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2909 if (!lstrcmpW(lpszUrlPart, szWWW))
2911 lpszUrlPart += lstrlenW(szWWW);
2914 count = lpszUrlEnd - lpszUrlPart;
2916 if (bFound && (count < MAX_PATH))
2918 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2922 while(len && szFile[--len] == '/') szFile[len] = '\0';
2924 /* FIXME: get rid of illegal characters like \, / and : */
2925 TRACE("File name: %s\n", debugstr_a(szFile));
2929 generate_name = TRUE;
2933 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2934 if (error != ERROR_SUCCESS)
2936 SetLastError(error);
2940 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2941 if (error != ERROR_SUCCESS)
2943 SetLastError(error);
2947 if (!(pHeader = cache_container_lock_index(pContainer)))
2950 if(pHeader->DirectoryCount)
2951 CacheDir = (BYTE)(rand() % pHeader->DirectoryCount);
2953 CacheDir = CACHE_CONTAINER_NO_SUBDIR;
2955 lBufferSize = MAX_PATH * sizeof(WCHAR);
2956 if (!URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize))
2958 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2959 debugstr_a(szFile), lBufferSize);
2960 cache_container_unlock_index(pContainer, pHeader);
2964 cache_container_unlock_index(pContainer, pHeader);
2966 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2967 lpszFileNameNoPath >= lpszFileName;
2968 --lpszFileNameNoPath)
2970 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2974 countnoextension = lstrlenW(lpszFileNameNoPath);
2975 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2976 if (lpszFileNameExtension)
2977 countnoextension -= lstrlenW(lpszFileNameExtension);
2978 *szExtension = '\0';
2980 if (lpszFileExtension)
2982 szExtension[0] = '.';
2983 lstrcpyW(szExtension+1, lpszFileExtension);
2986 for (i = 0; i<255 && !generate_name; i++)
2988 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2991 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2992 for (p = lpszFileNameNoPath + 1; *p; p++)
2998 case '/': case '\\':
3005 if (p[-1] == ' ' || p[-1] == '.') p[-1] = '_';
3007 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
3008 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
3009 if (hFile != INVALID_HANDLE_VALUE)
3016 /* Try to generate random name */
3017 GetSystemTimeAsFileTime(&ft);
3018 strcpyW(lpszFileNameNoPath+countnoextension+8, szExtension);
3020 for(i=0; i<255; i++)
3023 ULONGLONG n = ft.dwHighDateTime;
3025 n += ft.dwLowDateTime;
3026 n ^= (ULONGLONG)i<<48;
3032 lpszFileNameNoPath[countnoextension+j] = (r < 10 ? '0' + r : 'A' + r - 10);
3035 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
3036 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
3037 if (hFile != INVALID_HANDLE_VALUE)
3044 WARN("Could not find a unique filename\n");
3048 /***********************************************************************
3049 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
3051 * The bug we are compensating for is that some drongo at Microsoft
3052 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
3053 * As a consequence, CommitUrlCacheEntryA has been effectively
3054 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
3055 * is still defined as LPCWSTR. The result (other than madness) is
3056 * that we always need to store lpHeaderInfo in CP_ACP rather than
3057 * in UTF16, and we need to avoid converting lpHeaderInfo in
3058 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
3059 * result will lose data for arbitrary binary data.
3062 static BOOL CommitUrlCacheEntryInternal(
3063 IN LPCWSTR lpszUrlName,
3064 IN LPCWSTR lpszLocalFileName,
3065 IN FILETIME ExpireTime,
3066 IN FILETIME LastModifiedTime,
3067 IN DWORD CacheEntryType,
3068 IN LPBYTE lpHeaderInfo,
3069 IN DWORD dwHeaderSize,
3070 IN LPCWSTR lpszFileExtension,
3071 IN LPCWSTR lpszOriginalUrl
3074 URLCACHECONTAINER * pContainer;
3075 LPURLCACHE_HEADER pHeader;
3076 struct _HASH_ENTRY * pHashEntry;
3077 entry_header *pEntry;
3078 entry_url * pUrlEntry;
3079 DWORD url_entry_offset;
3080 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
3081 DWORD dwOffsetLocalFileName = 0;
3082 DWORD dwOffsetHeader = 0;
3083 DWORD dwOffsetFileExtension = 0;
3084 WIN32_FILE_ATTRIBUTE_DATA file_attr;
3085 LARGE_INTEGER file_size;
3087 char achFile[MAX_PATH];
3088 LPSTR lpszUrlNameA = NULL;
3089 LPSTR lpszFileExtensionA = NULL;
3090 char *pchLocalFileName = 0;
3092 DWORD exempt_delta = 0;
3095 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3096 debugstr_w(lpszUrlName),
3097 debugstr_w(lpszLocalFileName),
3101 debugstr_w(lpszFileExtension),
3102 debugstr_w(lpszOriginalUrl));
3104 if (CacheEntryType & STICKY_CACHE_ENTRY && !lpszLocalFileName)
3106 SetLastError(ERROR_INVALID_PARAMETER);
3109 if (lpszOriginalUrl)
3110 WARN(": lpszOriginalUrl ignored\n");
3112 memset(&file_attr, 0, sizeof(file_attr));
3113 if (lpszLocalFileName)
3115 if(!GetFileAttributesExW(lpszLocalFileName, GetFileExInfoStandard, &file_attr))
3118 file_size.u.LowPart = file_attr.nFileSizeLow;
3119 file_size.u.HighPart = file_attr.nFileSizeHigh;
3121 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3122 if (error != ERROR_SUCCESS)
3124 SetLastError(error);
3128 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3129 if (error != ERROR_SUCCESS)
3131 SetLastError(error);
3135 if (!(pHeader = cache_container_lock_index(pContainer)))
3138 lpszUrlNameA = heap_strdupWtoA(lpszUrlName);
3141 error = GetLastError();
3145 if (lpszFileExtension && !(lpszFileExtensionA = heap_strdupWtoA(lpszFileExtension)))
3147 error = GetLastError();
3151 if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
3153 entry_url *pUrlEntry = (entry_url*)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3154 if (URLCache_IsLocked(pHashEntry, pUrlEntry))
3156 TRACE("Trying to overwrite locked entry\n");
3157 error = ERROR_SHARING_VIOLATION;
3161 hit_rate = pUrlEntry->hit_rate;
3162 exempt_delta = pUrlEntry->exempt_delta;
3163 DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
3166 if (pHeader->DirectoryCount)
3169 cDirectory = CACHE_CONTAINER_NO_SUBDIR;
3171 if (lpszLocalFileName)
3173 BOOL bFound = FALSE;
3175 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
3177 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
3178 error = ERROR_INVALID_PARAMETER;
3182 /* skip container path prefix */
3183 lpszLocalFileName += lstrlenW(pContainer->path);
3185 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, MAX_PATH, NULL, NULL);
3186 pchLocalFileName = achFile;
3188 if(pHeader->DirectoryCount)
3190 for (cDirectory = 0; cDirectory < pHeader->DirectoryCount; cDirectory++)
3192 if (!strncmp(pHeader->directory_data[cDirectory].filename, pchLocalFileName, DIR_LENGTH))
3201 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
3202 error = ERROR_INVALID_PARAMETER;
3206 lpszLocalFileName += DIR_LENGTH + 1;
3207 pchLocalFileName += DIR_LENGTH + 1;
3211 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
3212 if (lpszLocalFileName)
3214 dwOffsetLocalFileName = dwBytesNeeded;
3215 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
3219 dwOffsetHeader = dwBytesNeeded;
3220 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
3222 if (lpszFileExtensionA)
3224 dwOffsetFileExtension = dwBytesNeeded;
3225 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
3228 /* round up to next block */
3229 if (dwBytesNeeded % BLOCKSIZE)
3231 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
3232 dwBytesNeeded += BLOCKSIZE;
3235 error = URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry);
3236 while (error == ERROR_HANDLE_DISK_FULL)
3238 error = cache_container_clean_index(pContainer, &pHeader);
3239 if (error == ERROR_SUCCESS)
3240 error = URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry);
3242 if (error != ERROR_SUCCESS)
3245 /* FindFirstFreeEntry fills in blocks used */
3246 pUrlEntry = (entry_url *)pEntry;
3247 url_entry_offset = (LPBYTE)pUrlEntry - (LPBYTE)pHeader;
3248 pUrlEntry->header.signature = URL_SIGNATURE;
3249 pUrlEntry->cache_dir = cDirectory;
3250 pUrlEntry->cache_entry_type = CacheEntryType | pContainer->default_entry_type;
3251 pUrlEntry->header_info_size = dwHeaderSize;
3252 if ((CacheEntryType & STICKY_CACHE_ENTRY) && !exempt_delta)
3254 /* Sticky entries have a default exempt time of one day */
3255 exempt_delta = 86400;
3257 pUrlEntry->exempt_delta = exempt_delta;
3258 pUrlEntry->hit_rate = hit_rate+1;
3259 pUrlEntry->file_extension_off = dwOffsetFileExtension;
3260 pUrlEntry->header_info_off = dwOffsetHeader;
3261 pUrlEntry->local_name_off = dwOffsetLocalFileName;
3262 pUrlEntry->url_off = DWORD_ALIGN(sizeof(*pUrlEntry));
3263 pUrlEntry->size.QuadPart = file_size.QuadPart;
3264 pUrlEntry->use_count = 0;
3265 GetSystemTimeAsFileTime(&pUrlEntry->access_time);
3266 pUrlEntry->modification_time = LastModifiedTime;
3267 URLCache_FileTimeToDosDateTime(&pUrlEntry->access_time, &pUrlEntry->sync_date, &pUrlEntry->sync_time);
3268 URLCache_FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->expire_date, &pUrlEntry->expire_time);
3269 URLCache_FileTimeToDosDateTime(&file_attr.ftLastWriteTime, &pUrlEntry->write_date, &pUrlEntry->write_time);
3272 pUrlEntry->unk1 = 0;
3273 pUrlEntry->unk2 = 0;
3274 pUrlEntry->unk3 = 0x60;
3275 pUrlEntry->unk4 = 0;
3276 pUrlEntry->unk5 = 0x1010;
3277 pUrlEntry->unk7 = 0;
3278 pUrlEntry->unk8 = 0;
3281 strcpy((LPSTR)pUrlEntry + pUrlEntry->url_off, lpszUrlNameA);
3282 if (dwOffsetLocalFileName)
3283 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName);
3285 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
3286 if (dwOffsetFileExtension)
3287 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
3289 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA, url_entry_offset, HASHTABLE_URL);
3290 while (error == ERROR_HANDLE_DISK_FULL)
3292 error = cache_container_clean_index(pContainer, &pHeader);
3293 if (error == ERROR_SUCCESS)
3295 pUrlEntry = (entry_url *)((LPBYTE)pHeader + url_entry_offset);
3296 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA,
3297 url_entry_offset, HASHTABLE_URL);
3300 if (error != ERROR_SUCCESS)
3301 URLCache_DeleteEntry(pHeader, &pUrlEntry->header);
3304 if (pUrlEntry->cache_dir < pHeader->DirectoryCount)
3305 pHeader->directory_data[pUrlEntry->cache_dir].dwNumFiles++;
3306 if (CacheEntryType & STICKY_CACHE_ENTRY)
3307 pHeader->ExemptUsage.QuadPart += file_size.QuadPart;
3309 pHeader->CacheUsage.QuadPart += file_size.QuadPart;
3310 if (pHeader->CacheUsage.QuadPart + pHeader->ExemptUsage.QuadPart >
3311 pHeader->CacheLimit.QuadPart)
3312 handle_full_cache();
3316 cache_container_unlock_index(pContainer, pHeader);
3317 heap_free(lpszUrlNameA);
3318 heap_free(lpszFileExtensionA);
3320 if (error == ERROR_SUCCESS)
3324 SetLastError(error);
3329 /***********************************************************************
3330 * CommitUrlCacheEntryA (WININET.@)
3333 BOOL WINAPI CommitUrlCacheEntryA(
3334 IN LPCSTR lpszUrlName,
3335 IN LPCSTR lpszLocalFileName,
3336 IN FILETIME ExpireTime,
3337 IN FILETIME LastModifiedTime,
3338 IN DWORD CacheEntryType,
3339 IN LPBYTE lpHeaderInfo,
3340 IN DWORD dwHeaderSize,
3341 IN LPCSTR lpszFileExtension,
3342 IN LPCSTR lpszOriginalUrl
3345 WCHAR *url_name = NULL;
3346 WCHAR *local_file_name = NULL;
3347 WCHAR *original_url = NULL;
3348 WCHAR *file_extension = NULL;
3349 BOOL bSuccess = FALSE;
3351 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3352 debugstr_a(lpszUrlName),
3353 debugstr_a(lpszLocalFileName),
3357 debugstr_a(lpszFileExtension),
3358 debugstr_a(lpszOriginalUrl));
3360 url_name = heap_strdupAtoW(lpszUrlName);
3364 if (lpszLocalFileName)
3366 local_file_name = heap_strdupAtoW(lpszLocalFileName);
3367 if (!local_file_name)
3370 if (lpszFileExtension)
3372 file_extension = heap_strdupAtoW(lpszFileExtension);
3373 if (!file_extension)
3376 if (lpszOriginalUrl)
3378 original_url = heap_strdupAtoW(lpszOriginalUrl);
3383 bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
3384 CacheEntryType, lpHeaderInfo, dwHeaderSize,
3385 file_extension, original_url);
3388 heap_free(original_url);
3389 heap_free(file_extension);
3390 heap_free(local_file_name);
3391 heap_free(url_name);
3395 /***********************************************************************
3396 * CommitUrlCacheEntryW (WININET.@)
3399 BOOL WINAPI CommitUrlCacheEntryW(
3400 IN LPCWSTR lpszUrlName,
3401 IN LPCWSTR lpszLocalFileName,
3402 IN FILETIME ExpireTime,
3403 IN FILETIME LastModifiedTime,
3404 IN DWORD CacheEntryType,
3405 IN LPWSTR lpHeaderInfo,
3406 IN DWORD dwHeaderSize,
3407 IN LPCWSTR lpszFileExtension,
3408 IN LPCWSTR lpszOriginalUrl
3412 BOOL bSuccess = FALSE;
3414 CHAR *header_info = NULL;
3416 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3417 debugstr_w(lpszUrlName),
3418 debugstr_w(lpszLocalFileName),
3422 debugstr_w(lpszFileExtension),
3423 debugstr_w(lpszOriginalUrl));
3425 if (!lpHeaderInfo || (header_info = heap_strdupWtoA(lpHeaderInfo)))
3428 len = strlen(header_info);
3429 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
3430 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
3436 dwError = GetLastError();
3440 heap_free(header_info);
3442 SetLastError(dwError);
3448 /***********************************************************************
3449 * ReadUrlCacheEntryStream (WININET.@)
3452 BOOL WINAPI ReadUrlCacheEntryStream(
3453 IN HANDLE hUrlCacheStream,
3454 IN DWORD dwLocation,
3455 IN OUT LPVOID lpBuffer,
3456 IN OUT LPDWORD lpdwLen,
3460 /* Get handle to file from 'stream' */
3461 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3463 if (dwReserved != 0)
3465 ERR("dwReserved != 0\n");
3466 SetLastError(ERROR_INVALID_PARAMETER);
3470 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3472 SetLastError(ERROR_INVALID_HANDLE);
3476 if (SetFilePointer(pStream->hFile, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
3478 return ReadFile(pStream->hFile, lpBuffer, *lpdwLen, lpdwLen, NULL);
3481 /***********************************************************************
3482 * RetrieveUrlCacheEntryStreamA (WININET.@)
3485 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
3486 IN LPCSTR lpszUrlName,
3487 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
3488 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3489 IN BOOL fRandomRead,
3493 /* NOTE: this is not the same as the way that the native
3494 * version allocates 'stream' handles. I did it this way
3495 * as it is much easier and no applications should depend
3496 * on this behaviour. (Native version appears to allocate
3497 * indices into a table)
3499 STREAM_HANDLE * pStream;
3502 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
3503 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3505 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
3507 lpdwCacheEntryInfoBufferSize,
3513 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
3518 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3520 if (hFile == INVALID_HANDLE_VALUE)
3523 /* allocate handle storage space */
3524 pStream = heap_alloc(sizeof(STREAM_HANDLE) + strlen(lpszUrlName) * sizeof(CHAR));
3528 SetLastError(ERROR_OUTOFMEMORY);
3532 pStream->hFile = hFile;
3533 strcpy(pStream->lpszUrl, lpszUrlName);
3537 /***********************************************************************
3538 * RetrieveUrlCacheEntryStreamW (WININET.@)
3541 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
3542 IN LPCWSTR lpszUrlName,
3543 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3544 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3545 IN BOOL fRandomRead,
3551 /* NOTE: this is not the same as the way that the native
3552 * version allocates 'stream' handles. I did it this way
3553 * as it is much easier and no applications should depend
3554 * on this behaviour. (Native version appears to allocate
3555 * indices into a table)
3557 STREAM_HANDLE * pStream;
3560 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3561 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3563 if (!RetrieveUrlCacheEntryFileW(lpszUrlName,
3565 lpdwCacheEntryInfoBufferSize,
3571 hFile = CreateFileW(lpCacheEntryInfo->lpszLocalFileName,
3576 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3578 if (hFile == INVALID_HANDLE_VALUE)
3581 /* allocate handle storage space */
3582 size = sizeof(STREAM_HANDLE);
3583 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
3585 pStream = heap_alloc(size);
3589 SetLastError(ERROR_OUTOFMEMORY);
3593 pStream->hFile = hFile;
3594 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, pStream->lpszUrl, url_len, NULL, NULL);
3598 /***********************************************************************
3599 * UnlockUrlCacheEntryStream (WININET.@)
3602 BOOL WINAPI UnlockUrlCacheEntryStream(
3603 IN HANDLE hUrlCacheStream,
3607 STREAM_HANDLE * pStream = (STREAM_HANDLE *)hUrlCacheStream;
3609 if (dwReserved != 0)
3611 ERR("dwReserved != 0\n");
3612 SetLastError(ERROR_INVALID_PARAMETER);
3616 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->lpszUrl, INTERNET_MAX_URL_LENGTH))
3618 SetLastError(ERROR_INVALID_HANDLE);
3622 if (!UnlockUrlCacheEntryFileA(pStream->lpszUrl, 0))
3625 CloseHandle(pStream->hFile);
3631 /***********************************************************************
3632 * DeleteUrlCacheEntryA (WININET.@)
3635 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3637 URLCACHECONTAINER * pContainer;
3638 LPURLCACHE_HEADER pHeader;
3639 struct _HASH_ENTRY * pHashEntry;
3643 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3645 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
3646 if (error != ERROR_SUCCESS)
3648 SetLastError(error);
3652 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3653 if (error != ERROR_SUCCESS)
3655 SetLastError(error);
3659 if (!(pHeader = cache_container_lock_index(pContainer)))
3662 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
3664 cache_container_unlock_index(pContainer, pHeader);
3665 TRACE("entry %s not found!\n", lpszUrlName);
3666 SetLastError(ERROR_FILE_NOT_FOUND);
3670 ret = DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
3672 cache_container_unlock_index(pContainer, pHeader);
3677 /***********************************************************************
3678 * DeleteUrlCacheEntryW (WININET.@)
3681 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3683 URLCACHECONTAINER * pContainer;
3684 LPURLCACHE_HEADER pHeader;
3685 struct _HASH_ENTRY * pHashEntry;
3690 TRACE("(%s)\n", debugstr_w(lpszUrlName));
3692 urlA = heap_strdupWtoA(lpszUrlName);
3695 SetLastError(ERROR_OUTOFMEMORY);
3699 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3700 if (error != ERROR_SUCCESS)
3703 SetLastError(error);
3707 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3708 if (error != ERROR_SUCCESS)
3711 SetLastError(error);
3715 if (!(pHeader = cache_container_lock_index(pContainer)))
3721 if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
3723 cache_container_unlock_index(pContainer, pHeader);
3724 TRACE("entry %s not found!\n", debugstr_a(urlA));
3726 SetLastError(ERROR_FILE_NOT_FOUND);
3730 ret = DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
3732 cache_container_unlock_index(pContainer, pHeader);
3737 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3739 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3743 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3745 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3749 /***********************************************************************
3750 * CreateCacheContainerA (WININET.@)
3752 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3753 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3755 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3756 d1, d2, d3, d4, d5, d6, d7, d8);
3760 /***********************************************************************
3761 * CreateCacheContainerW (WININET.@)
3763 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3764 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3766 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3767 d1, d2, d3, d4, d5, d6, d7, d8);
3771 /***********************************************************************
3772 * FindFirstUrlCacheContainerA (WININET.@)
3774 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3776 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3780 /***********************************************************************
3781 * FindFirstUrlCacheContainerW (WININET.@)
3783 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3785 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3789 /***********************************************************************
3790 * FindNextUrlCacheContainerA (WININET.@)
3792 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3794 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3798 /***********************************************************************
3799 * FindNextUrlCacheContainerW (WININET.@)
3801 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3803 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3807 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3808 LPCSTR lpszUrlSearchPattern,
3812 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3813 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3815 LPDWORD pcbReserved2,
3819 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3820 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3821 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3822 SetLastError(ERROR_FILE_NOT_FOUND);
3826 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3827 LPCWSTR lpszUrlSearchPattern,
3831 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3832 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3834 LPDWORD pcbReserved2,
3838 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3839 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3840 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3841 SetLastError(ERROR_FILE_NOT_FOUND);
3845 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
3847 typedef struct URLCacheFindEntryHandle
3850 LPWSTR lpszUrlSearchPattern;
3851 DWORD dwContainerIndex;
3852 DWORD dwHashTableIndex;
3853 DWORD dwHashEntryIndex;
3854 } URLCacheFindEntryHandle;
3856 /***********************************************************************
3857 * FindFirstUrlCacheEntryA (WININET.@)
3860 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3861 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3863 URLCacheFindEntryHandle *pEntryHandle;
3865 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3867 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3871 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3872 if (lpszUrlSearchPattern)
3874 pEntryHandle->lpszUrlSearchPattern = heap_strdupAtoW(lpszUrlSearchPattern);
3875 if (!pEntryHandle->lpszUrlSearchPattern)
3877 heap_free(pEntryHandle);
3882 pEntryHandle->lpszUrlSearchPattern = NULL;
3883 pEntryHandle->dwContainerIndex = 0;
3884 pEntryHandle->dwHashTableIndex = 0;
3885 pEntryHandle->dwHashEntryIndex = 0;
3887 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3889 heap_free(pEntryHandle);
3892 return pEntryHandle;
3895 /***********************************************************************
3896 * FindFirstUrlCacheEntryW (WININET.@)
3899 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3900 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3902 URLCacheFindEntryHandle *pEntryHandle;
3904 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3906 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3910 pEntryHandle->dwMagic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3911 if (lpszUrlSearchPattern)
3913 pEntryHandle->lpszUrlSearchPattern = heap_strdupW(lpszUrlSearchPattern);
3914 if (!pEntryHandle->lpszUrlSearchPattern)
3916 heap_free(pEntryHandle);
3921 pEntryHandle->lpszUrlSearchPattern = NULL;
3922 pEntryHandle->dwContainerIndex = 0;
3923 pEntryHandle->dwHashTableIndex = 0;
3924 pEntryHandle->dwHashEntryIndex = 0;
3926 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3928 heap_free(pEntryHandle);
3931 return pEntryHandle;
3934 static BOOL FindNextUrlCacheEntryInternal(
3936 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3937 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3940 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
3941 URLCACHECONTAINER * pContainer;
3943 if (pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3945 SetLastError(ERROR_INVALID_HANDLE);
3949 for (; URLCacheContainers_Enum(pEntryHandle->lpszUrlSearchPattern, pEntryHandle->dwContainerIndex, &pContainer);
3950 pEntryHandle->dwContainerIndex++, pEntryHandle->dwHashTableIndex = 0)
3952 LPURLCACHE_HEADER pHeader;
3953 HASH_CACHEFILE_ENTRY *pHashTableEntry;
3956 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3957 if (error != ERROR_SUCCESS)
3959 SetLastError(error);
3963 if (!(pHeader = cache_container_lock_index(pContainer)))
3966 for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->dwHashTableIndex, &pHashTableEntry);
3967 pEntryHandle->dwHashTableIndex++, pEntryHandle->dwHashEntryIndex = 0)
3969 const struct _HASH_ENTRY *pHashEntry = NULL;
3970 for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->dwHashEntryIndex, &pHashEntry);
3971 pEntryHandle->dwHashEntryIndex++)
3973 const entry_url *pUrlEntry;
3974 const entry_header *pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
3976 if (pEntry->signature != URL_SIGNATURE)
3979 pUrlEntry = (const entry_url *)pEntry;
3980 TRACE("Found URL: %s\n",
3981 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->url_off));
3982 TRACE("Header info: %s\n",
3983 debugstr_an((LPCSTR)pUrlEntry + pUrlEntry->header_info_off,
3984 pUrlEntry->header_info_size));
3986 error = URLCache_CopyEntry(
3989 lpNextCacheEntryInfo,
3990 lpdwNextCacheEntryInfoBufferSize,
3993 if (error != ERROR_SUCCESS)
3995 cache_container_unlock_index(pContainer, pHeader);
3996 SetLastError(error);
3999 if(pUrlEntry->local_name_off)
4000 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->local_name_off));
4002 /* increment the current index so that next time the function
4003 * is called the next entry is returned */
4004 pEntryHandle->dwHashEntryIndex++;
4005 cache_container_unlock_index(pContainer, pHeader);
4010 cache_container_unlock_index(pContainer, pHeader);
4013 SetLastError(ERROR_NO_MORE_ITEMS);
4017 /***********************************************************************
4018 * FindNextUrlCacheEntryA (WININET.@)
4020 BOOL WINAPI FindNextUrlCacheEntryA(
4022 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
4023 LPDWORD lpdwNextCacheEntryInfoBufferSize)
4025 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
4027 return FindNextUrlCacheEntryInternal(hEnumHandle, lpNextCacheEntryInfo,
4028 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
4031 /***********************************************************************
4032 * FindNextUrlCacheEntryW (WININET.@)
4034 BOOL WINAPI FindNextUrlCacheEntryW(
4036 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
4037 LPDWORD lpdwNextCacheEntryInfoBufferSize
4040 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
4042 return FindNextUrlCacheEntryInternal(hEnumHandle,
4043 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
4044 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
4047 /***********************************************************************
4048 * FindCloseUrlCache (WININET.@)
4050 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
4052 URLCacheFindEntryHandle *pEntryHandle = (URLCacheFindEntryHandle *)hEnumHandle;
4054 TRACE("(%p)\n", hEnumHandle);
4056 if (!pEntryHandle || pEntryHandle->dwMagic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
4058 SetLastError(ERROR_INVALID_HANDLE);
4062 pEntryHandle->dwMagic = 0;
4063 heap_free(pEntryHandle->lpszUrlSearchPattern);
4064 heap_free(pEntryHandle);
4068 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
4069 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
4071 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
4072 dwSearchCondition, lpGroupId, lpReserved);
4076 BOOL WINAPI FindNextUrlCacheEntryExA(
4078 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
4079 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
4081 LPDWORD pcbReserved2,
4085 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
4086 lpReserved, pcbReserved2, lpReserved3);
4090 BOOL WINAPI FindNextUrlCacheEntryExW(
4092 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
4093 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
4095 LPDWORD pcbReserved2,
4099 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
4100 lpReserved, pcbReserved2, lpReserved3);
4104 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
4106 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
4110 /***********************************************************************
4111 * CreateUrlCacheGroup (WININET.@)
4114 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
4116 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
4120 /***********************************************************************
4121 * DeleteUrlCacheGroup (WININET.@)
4124 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
4126 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
4127 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
4131 /***********************************************************************
4132 * DeleteWpadCacheForNetworks (WININET.@)
4133 * Undocumented, added in IE8
4135 BOOL WINAPI DeleteWpadCacheForNetworks(DWORD unk1)
4137 FIXME("(%d) stub\n", unk1);
4141 /***********************************************************************
4142 * SetUrlCacheEntryGroupA (WININET.@)
4145 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
4146 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
4149 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
4150 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
4151 pbGroupAttributes, cbGroupAttributes, lpReserved);
4152 SetLastError(ERROR_FILE_NOT_FOUND);
4156 /***********************************************************************
4157 * SetUrlCacheEntryGroupW (WININET.@)
4160 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
4161 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
4164 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
4165 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
4166 pbGroupAttributes, cbGroupAttributes, lpReserved);
4167 SetLastError(ERROR_FILE_NOT_FOUND);
4171 /***********************************************************************
4172 * GetUrlCacheConfigInfoW (WININET.@)
4174 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
4176 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
4177 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4181 /***********************************************************************
4182 * GetUrlCacheConfigInfoA (WININET.@)
4184 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
4186 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
4187 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4191 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4192 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
4193 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
4195 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
4196 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
4197 lpdwGroupInfo, lpReserved);
4201 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4202 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
4203 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
4205 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
4206 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
4207 lpdwGroupInfo, lpReserved);
4211 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4212 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
4214 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
4215 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
4219 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4220 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
4222 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
4223 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
4227 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
4229 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
4233 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
4235 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
4239 /***********************************************************************
4240 * DeleteIE3Cache (WININET.@)
4242 * Deletes the files used by the IE3 URL caching system.
4245 * hWnd [I] A dummy window.
4246 * hInst [I] Instance of process calling the function.
4247 * lpszCmdLine [I] Options used by function.
4248 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
4250 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
4252 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
4256 static BOOL IsUrlCacheEntryExpiredInternal(const entry_url *pUrlEntry,
4257 FILETIME *pftLastModified)
4260 FILETIME now, expired;
4262 *pftLastModified = pUrlEntry->modification_time;
4263 GetSystemTimeAsFileTime(&now);
4264 URLCache_DosDateTimeToFileTime(pUrlEntry->expire_date,
4265 pUrlEntry->expire_time, &expired);
4266 /* If the expired time is 0, it's interpreted as not expired */
4267 if (!expired.dwLowDateTime && !expired.dwHighDateTime)
4270 ret = CompareFileTime(&expired, &now) < 0;
4274 /***********************************************************************
4275 * IsUrlCacheEntryExpiredA (WININET.@)
4279 * dwFlags [I] Unknown
4280 * pftLastModified [O] Last modified time
4282 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
4284 LPURLCACHE_HEADER pHeader;
4285 struct _HASH_ENTRY * pHashEntry;
4286 const entry_header *pEntry;
4287 const entry_url * pUrlEntry;
4288 URLCACHECONTAINER * pContainer;
4291 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
4293 if (!url || !pftLastModified)
4296 FIXME("unknown flags 0x%08x\n", dwFlags);
4298 /* Any error implies that the URL is expired, i.e. not in the cache */
4299 if (URLCacheContainers_FindContainerA(url, &pContainer))
4301 memset(pftLastModified, 0, sizeof(*pftLastModified));
4305 if (cache_container_open_index(pContainer, MIN_BLOCK_NO))
4307 memset(pftLastModified, 0, sizeof(*pftLastModified));
4311 if (!(pHeader = cache_container_lock_index(pContainer)))
4313 memset(pftLastModified, 0, sizeof(*pftLastModified));
4317 if (!URLCache_FindHash(pHeader, url, &pHashEntry))
4319 cache_container_unlock_index(pContainer, pHeader);
4320 memset(pftLastModified, 0, sizeof(*pftLastModified));
4321 TRACE("entry %s not found!\n", url);
4325 pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
4326 if (pEntry->signature != URL_SIGNATURE)
4328 cache_container_unlock_index(pContainer, pHeader);
4329 memset(pftLastModified, 0, sizeof(*pftLastModified));
4330 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
4334 pUrlEntry = (const entry_url *)pEntry;
4335 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
4337 cache_container_unlock_index(pContainer, pHeader);
4342 /***********************************************************************
4343 * IsUrlCacheEntryExpiredW (WININET.@)
4347 * dwFlags [I] Unknown
4348 * pftLastModified [O] Last modified time
4350 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
4352 LPURLCACHE_HEADER pHeader;
4353 struct _HASH_ENTRY * pHashEntry;
4354 const entry_header *pEntry;
4355 const entry_url * pUrlEntry;
4356 URLCACHECONTAINER * pContainer;
4359 TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
4361 if (!url || !pftLastModified)
4364 FIXME("unknown flags 0x%08x\n", dwFlags);
4366 /* Any error implies that the URL is expired, i.e. not in the cache */
4367 if (URLCacheContainers_FindContainerW(url, &pContainer))
4369 memset(pftLastModified, 0, sizeof(*pftLastModified));
4373 if (cache_container_open_index(pContainer, MIN_BLOCK_NO))
4375 memset(pftLastModified, 0, sizeof(*pftLastModified));
4379 if (!(pHeader = cache_container_lock_index(pContainer)))
4381 memset(pftLastModified, 0, sizeof(*pftLastModified));
4385 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
4387 cache_container_unlock_index(pContainer, pHeader);
4388 memset(pftLastModified, 0, sizeof(*pftLastModified));
4389 TRACE("entry %s not found!\n", debugstr_w(url));
4393 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
4395 cache_container_unlock_index(pContainer, pHeader);
4396 memset(pftLastModified, 0, sizeof(*pftLastModified));
4397 TRACE("entry %s not found!\n", debugstr_w(url));
4401 pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->dwOffsetEntry);
4402 if (pEntry->signature != URL_SIGNATURE)
4404 cache_container_unlock_index(pContainer, pHeader);
4405 memset(pftLastModified, 0, sizeof(*pftLastModified));
4406 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
4410 pUrlEntry = (const entry_url *)pEntry;
4411 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
4413 cache_container_unlock_index(pContainer, pHeader);
4418 /***********************************************************************
4419 * GetDiskInfoA (WININET.@)
4421 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
4424 ULARGE_INTEGER bytes_free, bytes_total;
4426 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
4430 SetLastError(ERROR_INVALID_PARAMETER);
4434 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
4436 if (cluster_size) *cluster_size = 1;
4437 if (free) *free = bytes_free.QuadPart;
4438 if (total) *total = bytes_total.QuadPart;
4443 /***********************************************************************
4444 * RegisterUrlCacheNotification (WININET.@)
4446 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
4448 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
4452 /***********************************************************************
4453 * IncrementUrlCacheHeaderData (WININET.@)
4455 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
4457 FIXME("(%u, %p)\n", index, data);
4461 /***********************************************************************
4462 * RunOnceUrlCache (WININET.@)
4465 DWORD WINAPI RunOnceUrlCache(HWND hwnd, HINSTANCE hinst, LPSTR cmd, int cmdshow)
4467 FIXME("(%p, %p, %s, %d): stub\n", hwnd, hinst, debugstr_a(cmd), cmdshow);
4471 BOOL init_urlcache(void)
4473 dll_unload_event = CreateEventW(NULL, FALSE, FALSE, NULL);
4474 if(!dll_unload_event)
4477 free_cache_running = CreateSemaphoreW(NULL, 1, 1, NULL);
4478 if(!free_cache_running) {
4479 CloseHandle(dll_unload_event);
4483 URLCacheContainers_CreateDefaults();
4487 void free_urlcache(void)
4489 SetEvent(dll_unload_event);
4490 WaitForSingleObject(free_cache_running, INFINITE);
4491 ReleaseSemaphore(free_cache_running, 1, NULL);
4492 CloseHandle(free_cache_running);
4493 CloseHandle(dll_unload_event);
4495 URLCacheContainers_DeleteAll();
4498 /***********************************************************************
4499 * LoadUrlCacheContent (WININET.@)
4501 BOOL WINAPI LoadUrlCacheContent(void)