wininet: Rename URLCacheFindEntryHandle to find_handle.
[wine.git] / dlls / wininet / urlcache.c
blob1c54a7f01001c7caf4c4f00be031404e534ca6d2
1 /*
2 * Wininet - Url Cache functions
4 * Copyright 2001,2002 CodeWeavers
5 * Copyright 2003-2008 Robert Shearman
7 * Eric Kohl
8 * Aric Stewart
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
25 #include "config.h"
26 #include "wine/port.h"
28 #define NONAMELESSUNION
29 #define NONAMELESSSTRUCT
31 #if defined(__MINGW32__) || defined (_MSC_VER)
32 #include <ws2tcpip.h>
33 #endif
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sys/types.h>
40 #ifdef HAVE_SYS_SOCKET_H
41 # include <sys/socket.h>
42 #endif
43 #include <time.h>
45 #include "windef.h"
46 #include "winbase.h"
47 #include "winuser.h"
48 #include "wininet.h"
49 #include "winineti.h"
50 #include "winerror.h"
51 #include "winreg.h"
52 #include "shlwapi.h"
53 #include "shlobj.h"
54 #include "shellapi.h"
56 #include "internet.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
67 #define DIR_LENGTH 8
68 #define MAX_DIR_NO 0x20
69 #define BLOCKSIZE 128
70 #define HASHTABLE_SIZE 448
71 #define HASHTABLE_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
72 #define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
73 #define ALLOCATION_TABLE_OFFSET 0x250
74 #define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
75 #define MIN_BLOCK_NO 0x80
76 #define MAX_BLOCK_NO (ALLOCATION_TABLE_SIZE * 8)
77 #define FILE_SIZE(blocks) ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
79 #define HASHTABLE_URL 0
80 #define HASHTABLE_DEL 1
81 #define HASHTABLE_LOCK 2
82 #define HASHTABLE_FREE 3
83 #define HASHTABLE_REDR 5
84 #define HASHTABLE_FLAG_BITS 6
86 #define PENDING_DELETE_CACHE_ENTRY 0x00400000
87 #define INSTALLED_CACHE_ENTRY 0x10000000
88 #define GET_INSTALLED_ENTRY 0x200
89 #define CACHE_CONTAINER_NO_SUBDIR 0xFE
91 #define CACHE_HEADER_DATA_ROOT_LEAK_OFFSET 0x16
93 #define FILETIME_SECOND 10000000
95 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
96 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
97 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
98 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
99 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
101 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
103 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
105 typedef struct
107 DWORD signature;
108 DWORD blocks_used; /* number of 128byte blocks used by this entry */
109 } entry_header;
111 typedef struct
113 entry_header header;
114 FILETIME modification_time;
115 FILETIME access_time;
116 WORD expire_date; /* expire date in dos format */
117 WORD expire_time; /* expire time in dos format */
118 DWORD unk1; /* usually zero */
119 ULARGE_INTEGER size; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
120 DWORD unk2; /* usually zero */
121 DWORD exempt_delta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
122 DWORD unk3; /* usually 0x60 */
123 DWORD url_off; /* offset of start of url from start of entry */
124 BYTE cache_dir; /* index of cache directory this url is stored in */
125 BYTE unk4; /* usually zero */
126 WORD unk5; /* usually 0x1010 */
127 DWORD local_name_off; /* offset of start of local filename from start of entry */
128 DWORD cache_entry_type; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
129 DWORD header_info_off; /* offset of start of header info from start of entry */
130 DWORD header_info_size;
131 DWORD file_extension_off; /* offset of start of file extension from start of entry */
132 WORD sync_date; /* last sync date in dos format */
133 WORD sync_time; /* last sync time in dos format */
134 DWORD hit_rate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
135 DWORD use_count; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
136 WORD write_date;
137 WORD write_time;
138 DWORD unk7; /* usually zero */
139 DWORD unk8; /* usually zero */
140 /* packing to dword align start of next field */
141 /* CHAR szSourceUrlName[]; (url) */
142 /* packing to dword align start of next field */
143 /* CHAR szLocalFileName[]; (local file name excluding path) */
144 /* packing to dword align start of next field */
145 /* CHAR szHeaderInfo[]; (header info) */
146 } entry_url;
148 struct hash_entry
150 DWORD key;
151 DWORD offset;
154 typedef struct
156 entry_header header;
157 DWORD next;
158 DWORD id;
159 struct hash_entry hash_table[HASHTABLE_SIZE];
160 } entry_hash_table;
162 typedef struct
164 char signature[28];
165 DWORD size;
166 DWORD hash_table_off;
167 DWORD capacity_in_blocks;
168 DWORD blocks_in_use;
169 DWORD unk1;
170 ULARGE_INTEGER cache_limit;
171 ULARGE_INTEGER cache_usage;
172 ULARGE_INTEGER exempt_usage;
173 DWORD dirs_no;
174 struct _directory_data
176 DWORD files_no;
177 char name[DIR_LENGTH];
178 } directory_data[MAX_DIR_NO];
179 DWORD options[0x21];
180 BYTE allocation_table[ALLOCATION_TABLE_SIZE];
181 } urlcache_header;
183 typedef struct
185 HANDLE file;
186 CHAR url[1];
187 } stream_handle;
189 typedef struct
191 struct list entry; /* part of a list */
192 LPWSTR cache_prefix; /* string that has to be prefixed for this container to be used */
193 LPWSTR path; /* path to url container directory */
194 HANDLE mapping; /* handle of file mapping */
195 DWORD file_size; /* size of file when mapping was opened */
196 HANDLE mutex; /* handle of mutex */
197 DWORD default_entry_type;
198 } cache_container;
200 typedef struct
202 DWORD magic;
203 LPWSTR url_search_pattern;
204 DWORD container_idx;
205 DWORD hash_table_idx;
206 DWORD hash_entry_idx;
207 } find_handle;
209 /* List of all containers available */
210 static struct list UrlContainers = LIST_INIT(UrlContainers);
212 static DWORD URLCache_CreateHashTable(urlcache_header *pHeader, entry_hash_table *pPrevHash, entry_hash_table **ppHash);
214 /***********************************************************************
215 * URLCache_PathToObjectName (Internal)
217 * Converts a path to a name suitable for use as a Win32 object name.
218 * Replaces '\\' characters in-place with the specified character
219 * (usually '_' or '!')
221 * RETURNS
222 * nothing
225 static void URLCache_PathToObjectName(LPWSTR lpszPath, WCHAR replace)
227 for (; *lpszPath; lpszPath++)
229 if (*lpszPath == '\\')
230 *lpszPath = replace;
234 /* Caller must hold container lock */
235 static HANDLE cache_container_map_index(HANDLE file, const WCHAR *path, DWORD size, BOOL *validate)
237 static const WCHAR mapping_name_format[]
238 = {'%','s','i','n','d','e','x','.','d','a','t','_','%','l','u',0};
239 WCHAR mapping_name[MAX_PATH];
240 HANDLE mapping;
242 wsprintfW(mapping_name, mapping_name_format, path, size);
243 URLCache_PathToObjectName(mapping_name, '_');
245 mapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, mapping_name);
246 if(mapping) {
247 if(validate) *validate = FALSE;
248 return mapping;
251 if(validate) *validate = TRUE;
252 return CreateFileMappingW(file, NULL, PAGE_READWRITE, 0, 0, mapping_name);
255 /* Caller must hold container lock */
256 static DWORD cache_container_set_size(cache_container *container, HANDLE file, DWORD blocks_no)
258 static const WCHAR cache_content_key[] = {'S','o','f','t','w','a','r','e','\\',
259 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
260 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
261 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s','\\',
262 'C','a','c','h','e','\\','C','o','n','t','e','n','t',0};
263 static const WCHAR cache_limit[] = {'C','a','c','h','e','L','i','m','i','t',0};
265 DWORD file_size = FILE_SIZE(blocks_no);
266 WCHAR dir_path[MAX_PATH], *dir_name;
267 entry_hash_table *hashtable_entry;
268 urlcache_header *header;
269 HANDLE mapping;
270 FILETIME ft;
271 HKEY key;
272 int i, j;
274 if(SetFilePointer(file, file_size, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
275 return GetLastError();
277 if(!SetEndOfFile(file))
278 return GetLastError();
280 mapping = cache_container_map_index(file, container->path, file_size, NULL);
281 if(!mapping)
282 return GetLastError();
284 header = MapViewOfFile(mapping, FILE_MAP_WRITE, 0, 0, 0);
285 if(!header) {
286 CloseHandle(mapping);
287 return GetLastError();
290 if(blocks_no != MIN_BLOCK_NO) {
291 if(file_size > header->size)
292 memset((char*)header+header->size, 0, file_size-header->size);
293 header->size = file_size;
294 header->capacity_in_blocks = blocks_no;
296 UnmapViewOfFile(header);
297 CloseHandle(container->mapping);
298 container->mapping = mapping;
299 container->file_size = file_size;
300 return ERROR_SUCCESS;
303 memset(header, 0, file_size);
304 /* First set some constants and defaults in the header */
305 memcpy(header->signature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1);
306 memcpy(header->signature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1);
307 header->size = file_size;
308 header->capacity_in_blocks = blocks_no;
309 /* 127MB - taken from default for Windows 2000 */
310 header->cache_limit.QuadPart = 0x07ff5400;
311 /* Copied from a Windows 2000 cache index */
312 header->dirs_no = container->default_entry_type==NORMAL_CACHE_ENTRY ? 4 : 0;
314 /* If the registry has a cache size set, use the registry value */
315 if(RegOpenKeyW(HKEY_CURRENT_USER, cache_content_key, &key) == ERROR_SUCCESS) {
316 DWORD dw, len = sizeof(dw), keytype;
318 if(RegQueryValueExW(key, cache_limit, NULL, &keytype, (BYTE*)&dw, &len) == ERROR_SUCCESS &&
319 keytype == REG_DWORD)
320 header->cache_limit.QuadPart = (ULONGLONG)dw * 1024;
321 RegCloseKey(key);
324 URLCache_CreateHashTable(header, NULL, &hashtable_entry);
326 /* Last step - create the directories */
327 strcpyW(dir_path, container->path);
328 dir_name = dir_path + strlenW(dir_path);
329 dir_name[8] = 0;
331 GetSystemTimeAsFileTime(&ft);
333 for(i=0; i<header->dirs_no; ++i) {
334 header->directory_data[i].files_no = 0;
335 for(j=0;; ++j) {
336 ULONGLONG n = ft.dwHighDateTime;
337 int k;
339 /* Generate a file name to attempt to create.
340 * This algorithm will create what will appear
341 * to be random and unrelated directory names
342 * of up to 9 characters in length.
344 n <<= 32;
345 n += ft.dwLowDateTime;
346 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
348 for(k = 0; k < 8; ++k) {
349 int r = (n % 36);
351 /* Dividing by a prime greater than 36 helps
352 * with the appearance of randomness
354 n /= 37;
356 if(r < 10)
357 dir_name[k] = '0' + r;
358 else
359 dir_name[k] = 'A' + (r - 10);
362 if(CreateDirectoryW(dir_path, 0)) {
363 /* The following is OK because we generated an
364 * 8 character directory name made from characters
365 * [A-Z0-9], which are equivalent for all code
366 * pages and for UTF-16
368 for (k = 0; k < 8; ++k)
369 header->directory_data[i].name[k] = dir_name[k];
370 break;
371 }else if(j >= 255) {
372 /* Give up. The most likely cause of this
373 * is a full disk, but whatever the cause
374 * is, it should be more than apparent that
375 * we won't succeed.
377 UnmapViewOfFile(header);
378 CloseHandle(mapping);
379 return GetLastError();
384 UnmapViewOfFile(header);
385 CloseHandle(container->mapping);
386 container->mapping = mapping;
387 container->file_size = file_size;
388 return ERROR_SUCCESS;
391 static BOOL cache_container_is_valid(urlcache_header *header, DWORD file_size)
393 DWORD allocation_size, count_bits, i;
395 if(file_size < FILE_SIZE(MIN_BLOCK_NO))
396 return FALSE;
398 if(file_size != header->size)
399 return FALSE;
401 if (!memcmp(header->signature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1) &&
402 memcmp(header->signature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1))
403 return FALSE;
405 if(FILE_SIZE(header->capacity_in_blocks) != file_size)
406 return FALSE;
408 allocation_size = 0;
409 for(i=0; i<header->capacity_in_blocks/8; i++) {
410 for(count_bits = header->allocation_table[i]; count_bits!=0; count_bits>>=1) {
411 if(count_bits & 1)
412 allocation_size++;
415 if(allocation_size != header->blocks_in_use)
416 return FALSE;
418 for(; i<ALLOCATION_TABLE_SIZE; i++) {
419 if(header->allocation_table[i])
420 return FALSE;
423 return TRUE;
426 /***********************************************************************
427 * cache_container_open_index (Internal)
429 * Opens the index file and saves mapping handle
431 * RETURNS
432 * ERROR_SUCCESS if succeeded
433 * Any other Win32 error code if failed
436 static DWORD cache_container_open_index(cache_container *container, DWORD blocks_no)
438 static const WCHAR index_dat[] = {'i','n','d','e','x','.','d','a','t',0};
440 HANDLE file;
441 WCHAR index_path[MAX_PATH];
442 DWORD file_size;
443 BOOL validate;
445 WaitForSingleObject(container->mutex, INFINITE);
447 if(container->mapping) {
448 ReleaseMutex(container->mutex);
449 return ERROR_SUCCESS;
452 strcpyW(index_path, container->path);
453 strcatW(index_path, index_dat);
455 file = CreateFileW(index_path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
456 if(file == INVALID_HANDLE_VALUE) {
457 /* Maybe the directory wasn't there? Try to create it */
458 if(CreateDirectoryW(container->path, 0))
459 file = CreateFileW(index_path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
461 if(file == INVALID_HANDLE_VALUE) {
462 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(index_path));
463 ReleaseMutex(container->mutex);
464 return GetLastError();
467 file_size = GetFileSize(file, NULL);
468 if(file_size == INVALID_FILE_SIZE) {
469 CloseHandle(file);
470 ReleaseMutex(container->mutex);
471 return GetLastError();
474 if(blocks_no < MIN_BLOCK_NO)
475 blocks_no = MIN_BLOCK_NO;
476 else if(blocks_no > MAX_BLOCK_NO)
477 blocks_no = MAX_BLOCK_NO;
479 if(file_size < FILE_SIZE(blocks_no)) {
480 DWORD ret = cache_container_set_size(container, file, blocks_no);
481 CloseHandle(file);
482 ReleaseMutex(container->mutex);
483 return ret;
486 container->file_size = file_size;
487 container->mapping = cache_container_map_index(file, container->path, file_size, &validate);
488 CloseHandle(file);
489 if(container->mapping && validate) {
490 urlcache_header *header = MapViewOfFile(container->mapping, FILE_MAP_WRITE, 0, 0, 0);
492 if(header && !cache_container_is_valid(header, file_size)) {
493 WARN("detected old or broken index.dat file\n");
494 UnmapViewOfFile(header);
495 FreeUrlCacheSpaceW(container->path, 100, 0);
496 }else if(header) {
497 UnmapViewOfFile(header);
498 }else {
499 CloseHandle(container->mapping);
500 container->mapping = NULL;
504 if(!container->mapping)
506 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
507 ReleaseMutex(container->mutex);
508 return GetLastError();
511 ReleaseMutex(container->mutex);
512 return ERROR_SUCCESS;
515 /***********************************************************************
516 * cache_container_close_index (Internal)
518 * Closes the index
520 * RETURNS
521 * nothing
524 static void cache_container_close_index(cache_container *pContainer)
526 CloseHandle(pContainer->mapping);
527 pContainer->mapping = NULL;
530 static BOOL URLCacheContainers_AddContainer(LPCWSTR cache_prefix,
531 LPCWSTR path, DWORD default_entry_type, LPWSTR mutex_name)
533 cache_container *pContainer = heap_alloc(sizeof(cache_container));
534 int cache_prefix_len = strlenW(cache_prefix);
536 if (!pContainer)
538 return FALSE;
541 pContainer->mapping = NULL;
542 pContainer->file_size = 0;
543 pContainer->default_entry_type = default_entry_type;
545 pContainer->path = heap_strdupW(path);
546 if (!pContainer->path)
548 heap_free(pContainer);
549 return FALSE;
552 pContainer->cache_prefix = heap_alloc((cache_prefix_len + 1) * sizeof(WCHAR));
553 if (!pContainer->cache_prefix)
555 heap_free(pContainer->path);
556 heap_free(pContainer);
557 return FALSE;
560 memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
562 CharLowerW(mutex_name);
563 URLCache_PathToObjectName(mutex_name, '!');
565 if ((pContainer->mutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
567 ERR("couldn't create mutex (error is %d)\n", GetLastError());
568 heap_free(pContainer->path);
569 heap_free(pContainer);
570 return FALSE;
573 list_add_head(&UrlContainers, &pContainer->entry);
575 return TRUE;
578 static void cache_container_delete_container(cache_container *pContainer)
580 list_remove(&pContainer->entry);
582 cache_container_close_index(pContainer);
583 CloseHandle(pContainer->mutex);
584 heap_free(pContainer->path);
585 heap_free(pContainer->cache_prefix);
586 heap_free(pContainer);
589 static void URLCacheContainers_CreateDefaults(void)
591 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
592 static const WCHAR UrlPrefix[] = {0};
593 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
594 static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
595 static const WCHAR CookieSuffix[] = {0};
596 static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
597 static const struct
599 int nFolder; /* CSIDL_* constant */
600 const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
601 const WCHAR * cache_prefix; /* prefix used to reference the container */
602 DWORD default_entry_type;
603 } DefaultContainerData[] =
605 { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix, NORMAL_CACHE_ENTRY },
606 { CSIDL_HISTORY, HistorySuffix, HistoryPrefix, URLHISTORY_CACHE_ENTRY },
607 { CSIDL_COOKIES, CookieSuffix, CookiePrefix, COOKIE_CACHE_ENTRY },
609 DWORD i;
611 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
613 WCHAR wszCachePath[MAX_PATH];
614 WCHAR wszMutexName[MAX_PATH];
615 int path_len, suffix_len;
617 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
619 ERR("Couldn't get path for default container %u\n", i);
620 continue;
622 path_len = strlenW(wszCachePath);
623 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
625 if (path_len + suffix_len + 2 > MAX_PATH)
627 ERR("Path too long\n");
628 continue;
631 wszCachePath[path_len] = '\\';
632 wszCachePath[path_len+1] = 0;
634 strcpyW(wszMutexName, wszCachePath);
636 if (suffix_len)
638 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
639 wszCachePath[path_len + suffix_len + 1] = '\\';
640 wszCachePath[path_len + suffix_len + 2] = '\0';
643 URLCacheContainers_AddContainer(DefaultContainerData[i].cache_prefix, wszCachePath,
644 DefaultContainerData[i].default_entry_type, wszMutexName);
648 static void URLCacheContainers_DeleteAll(void)
650 while(!list_empty(&UrlContainers))
651 cache_container_delete_container(
652 LIST_ENTRY(list_head(&UrlContainers), cache_container, entry)
656 static DWORD URLCacheContainers_FindContainerW(LPCWSTR lpwszUrl, cache_container **ppContainer)
658 cache_container *pContainer;
660 TRACE("searching for prefix for URL: %s\n", debugstr_w(lpwszUrl));
662 if(!lpwszUrl)
663 return ERROR_INVALID_PARAMETER;
665 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, cache_container, entry)
667 int prefix_len = strlenW(pContainer->cache_prefix);
668 if (!strncmpW(pContainer->cache_prefix, lpwszUrl, prefix_len))
670 TRACE("found container with prefix %s for URL %s\n", debugstr_w(pContainer->cache_prefix), debugstr_w(lpwszUrl));
671 *ppContainer = pContainer;
672 return ERROR_SUCCESS;
675 ERR("no container found\n");
676 return ERROR_FILE_NOT_FOUND;
679 static DWORD URLCacheContainers_FindContainerA(LPCSTR lpszUrl, cache_container **ppContainer)
681 LPWSTR url = NULL;
682 DWORD ret;
684 if (lpszUrl && !(url = heap_strdupAtoW(lpszUrl)))
685 return ERROR_OUTOFMEMORY;
687 ret = URLCacheContainers_FindContainerW(url, ppContainer);
688 heap_free(url);
689 return ret;
692 static BOOL URLCacheContainers_Enum(LPCWSTR lpwszSearchPattern, DWORD dwIndex, cache_container **ppContainer)
694 DWORD i = 0;
695 cache_container *pContainer;
697 TRACE("searching for prefix: %s\n", debugstr_w(lpwszSearchPattern));
699 /* non-NULL search pattern only returns one container ever */
700 if (lpwszSearchPattern && dwIndex > 0)
701 return FALSE;
703 LIST_FOR_EACH_ENTRY(pContainer, &UrlContainers, cache_container, entry)
705 if (lpwszSearchPattern)
707 if (!strcmpW(pContainer->cache_prefix, lpwszSearchPattern))
709 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
710 *ppContainer = pContainer;
711 return TRUE;
714 else
716 if (i == dwIndex)
718 TRACE("found container with prefix %s\n", debugstr_w(pContainer->cache_prefix));
719 *ppContainer = pContainer;
720 return TRUE;
723 i++;
725 return FALSE;
728 /***********************************************************************
729 * cache_container_lock_index (Internal)
731 * Locks the index for system-wide exclusive access.
733 * RETURNS
734 * Cache file header if successful
735 * NULL if failed and calls SetLastError.
737 static urlcache_header* cache_container_lock_index(cache_container *pContainer)
739 BYTE index;
740 LPVOID pIndexData;
741 urlcache_header* pHeader;
742 DWORD error;
744 /* acquire mutex */
745 WaitForSingleObject(pContainer->mutex, INFINITE);
747 pIndexData = MapViewOfFile(pContainer->mapping, FILE_MAP_WRITE, 0, 0, 0);
749 if (!pIndexData)
751 ReleaseMutex(pContainer->mutex);
752 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
753 return NULL;
755 pHeader = (urlcache_header*)pIndexData;
757 /* file has grown - we need to remap to prevent us getting
758 * access violations when we try and access beyond the end
759 * of the memory mapped file */
760 if (pHeader->size != pContainer->file_size)
762 UnmapViewOfFile( pHeader );
763 cache_container_close_index(pContainer);
764 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
765 if (error != ERROR_SUCCESS)
767 ReleaseMutex(pContainer->mutex);
768 SetLastError(error);
769 return NULL;
771 pIndexData = MapViewOfFile(pContainer->mapping, FILE_MAP_WRITE, 0, 0, 0);
773 if (!pIndexData)
775 ReleaseMutex(pContainer->mutex);
776 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
777 return NULL;
779 pHeader = (urlcache_header*)pIndexData;
782 TRACE("Signature: %s, file size: %d bytes\n", pHeader->signature, pHeader->size);
784 for (index = 0; index < pHeader->dirs_no; index++)
786 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].name);
789 return pHeader;
792 /***********************************************************************
793 * cache_container_unlock_index (Internal)
796 static BOOL cache_container_unlock_index(cache_container *pContainer, urlcache_header *pHeader)
798 /* release mutex */
799 ReleaseMutex(pContainer->mutex);
800 return UnmapViewOfFile(pHeader);
803 #ifndef CHAR_BIT
804 #define CHAR_BIT (8 * sizeof(CHAR))
805 #endif
807 /***********************************************************************
808 * URLCache_Allocation_BlockIsFree (Internal)
810 * Is the specified block number free?
812 * RETURNS
813 * zero if free
814 * non-zero otherwise
817 static inline BYTE URLCache_Allocation_BlockIsFree(BYTE * AllocationTable, DWORD dwBlockNumber)
819 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
820 return (AllocationTable[dwBlockNumber / CHAR_BIT] & mask) == 0;
823 /***********************************************************************
824 * URLCache_Allocation_BlockFree (Internal)
826 * Marks the specified block as free
828 * CAUTION
829 * this function is not updating used blocks count
831 * RETURNS
832 * nothing
835 static inline void URLCache_Allocation_BlockFree(BYTE * AllocationTable, DWORD dwBlockNumber)
837 BYTE mask = ~(1 << (dwBlockNumber % CHAR_BIT));
838 AllocationTable[dwBlockNumber / CHAR_BIT] &= mask;
841 /***********************************************************************
842 * URLCache_Allocation_BlockAllocate (Internal)
844 * Marks the specified block as allocated
846 * CAUTION
847 * this function is not updating used blocks count
849 * RETURNS
850 * nothing
853 static inline void URLCache_Allocation_BlockAllocate(BYTE * AllocationTable, DWORD dwBlockNumber)
855 BYTE mask = 1 << (dwBlockNumber % CHAR_BIT);
856 AllocationTable[dwBlockNumber / CHAR_BIT] |= mask;
859 /***********************************************************************
860 * URLCache_FindFirstFreeEntry (Internal)
862 * Finds and allocates the first block of free space big enough and
863 * sets ppEntry to point to it.
865 * RETURNS
866 * ERROR_SUCCESS when free memory block was found
867 * Any other Win32 error code if the entry could not be added
870 static DWORD URLCache_FindFirstFreeEntry(urlcache_header *pHeader, DWORD dwBlocksNeeded, entry_header **ppEntry)
872 DWORD dwBlockNumber;
873 DWORD dwFreeCounter;
874 for (dwBlockNumber = 0; dwBlockNumber < pHeader->capacity_in_blocks; dwBlockNumber++)
876 for (dwFreeCounter = 0;
877 dwFreeCounter < dwBlocksNeeded &&
878 dwFreeCounter + dwBlockNumber < pHeader->capacity_in_blocks &&
879 URLCache_Allocation_BlockIsFree(pHeader->allocation_table, dwBlockNumber + dwFreeCounter);
880 dwFreeCounter++)
881 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
883 if (dwFreeCounter == dwBlocksNeeded)
885 DWORD index;
886 TRACE("Found free blocks starting at no. %d (0x%x)\n", dwBlockNumber, ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
887 for (index = 0; index < dwBlocksNeeded; index++)
888 URLCache_Allocation_BlockAllocate(pHeader->allocation_table, dwBlockNumber + index);
889 *ppEntry = (entry_header*)((LPBYTE)pHeader + ENTRY_START_OFFSET + dwBlockNumber * BLOCKSIZE);
890 for (index = 0; index < dwBlocksNeeded * BLOCKSIZE / sizeof(DWORD); index++)
891 ((DWORD*)*ppEntry)[index] = 0xdeadbeef;
892 (*ppEntry)->blocks_used = dwBlocksNeeded;
893 pHeader->blocks_in_use += dwBlocksNeeded;
894 return ERROR_SUCCESS;
898 return ERROR_HANDLE_DISK_FULL;
901 /***********************************************************************
902 * URLCache_DeleteEntry (Internal)
904 * Deletes the specified entry and frees the space allocated to it
906 * RETURNS
907 * TRUE if it succeeded
908 * FALSE if it failed
911 static BOOL URLCache_DeleteEntry(urlcache_header *pHeader, entry_header *pEntry)
913 DWORD dwStartBlock;
914 DWORD dwBlock;
916 /* update allocation table */
917 dwStartBlock = ((DWORD)((BYTE *)pEntry - (BYTE *)pHeader) - ENTRY_START_OFFSET) / BLOCKSIZE;
918 for (dwBlock = dwStartBlock; dwBlock < dwStartBlock + pEntry->blocks_used; dwBlock++)
919 URLCache_Allocation_BlockFree(pHeader->allocation_table, dwBlock);
921 pHeader->blocks_in_use -= pEntry->blocks_used;
922 return TRUE;
925 /***********************************************************************
926 * URLCache_LocalFileNameToPathW (Internal)
928 * Copies the full path to the specified buffer given the local file
929 * name and the index of the directory it is in. Always sets value in
930 * lpBufferSize to the required buffer size (in bytes).
932 * RETURNS
933 * TRUE if the buffer was big enough
934 * FALSE if the buffer was too small
937 static BOOL URLCache_LocalFileNameToPathW(
938 const cache_container *pContainer,
939 const urlcache_header *pHeader,
940 LPCSTR szLocalFileName,
941 BYTE Directory,
942 LPWSTR wszPath,
943 LPLONG lpBufferSize)
945 LONG nRequired;
946 int path_len = strlenW(pContainer->path);
947 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
948 if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->dirs_no)
950 *lpBufferSize = 0;
951 return FALSE;
954 nRequired = (path_len + file_name_len) * sizeof(WCHAR);
955 if(Directory != CACHE_CONTAINER_NO_SUBDIR)
956 nRequired += (DIR_LENGTH + 1) * sizeof(WCHAR);
957 if (nRequired <= *lpBufferSize)
959 int dir_len;
961 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
962 if (Directory != CACHE_CONTAINER_NO_SUBDIR)
964 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].name, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
965 wszPath[dir_len + path_len] = '\\';
966 dir_len++;
968 else
970 dir_len = 0;
972 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len, file_name_len);
973 *lpBufferSize = nRequired;
974 return TRUE;
976 *lpBufferSize = nRequired;
977 return FALSE;
980 /***********************************************************************
981 * URLCache_LocalFileNameToPathA (Internal)
983 * Copies the full path to the specified buffer given the local file
984 * name and the index of the directory it is in. Always sets value in
985 * lpBufferSize to the required buffer size.
987 * RETURNS
988 * TRUE if the buffer was big enough
989 * FALSE if the buffer was too small
992 static BOOL URLCache_LocalFileNameToPathA(
993 const cache_container *pContainer,
994 const urlcache_header *pHeader,
995 LPCSTR szLocalFileName,
996 BYTE Directory,
997 LPSTR szPath,
998 LPLONG lpBufferSize)
1000 LONG nRequired;
1001 int path_len, file_name_len, dir_len;
1003 if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->dirs_no)
1005 *lpBufferSize = 0;
1006 return FALSE;
1009 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
1010 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
1011 if (Directory!=CACHE_CONTAINER_NO_SUBDIR)
1012 dir_len = DIR_LENGTH+1;
1013 else
1014 dir_len = 0;
1016 nRequired = (path_len + dir_len + file_name_len) * sizeof(char);
1017 if (nRequired < *lpBufferSize)
1019 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
1020 if(dir_len) {
1021 memcpy(szPath+path_len, pHeader->directory_data[Directory].name, dir_len-1);
1022 szPath[path_len + dir_len-1] = '\\';
1024 memcpy(szPath + path_len + dir_len, szLocalFileName, file_name_len);
1025 *lpBufferSize = nRequired;
1026 return TRUE;
1028 *lpBufferSize = nRequired;
1029 return FALSE;
1032 /* Just like FileTimeToDosDateTime, except that it also maps the special
1033 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1035 static void URLCache_FileTimeToDosDateTime(const FILETIME *ft, WORD *fatdate,
1036 WORD *fattime)
1038 if (!ft->dwLowDateTime && !ft->dwHighDateTime)
1039 *fatdate = *fattime = 0;
1040 else
1041 FileTimeToDosDateTime(ft, fatdate, fattime);
1044 /***********************************************************************
1045 * URLCache_DeleteFile (Internal)
1047 static DWORD URLCache_DeleteFile(const cache_container *container,
1048 urlcache_header *header, entry_url *url_entry)
1050 WIN32_FILE_ATTRIBUTE_DATA attr;
1051 WCHAR path[MAX_PATH];
1052 LONG path_size = sizeof(path);
1053 DWORD err;
1054 WORD date, time;
1056 if(!url_entry->local_name_off)
1057 goto succ;
1059 if(!URLCache_LocalFileNameToPathW(container, header,
1060 (LPCSTR)url_entry+url_entry->local_name_off,
1061 url_entry->cache_dir, path, &path_size))
1062 goto succ;
1064 if(!GetFileAttributesExW(path, GetFileExInfoStandard, &attr))
1065 goto succ;
1066 URLCache_FileTimeToDosDateTime(&attr.ftLastWriteTime, &date, &time);
1067 if(date != url_entry->write_date || time != url_entry->write_time)
1068 goto succ;
1070 err = (DeleteFileW(path) ? ERROR_SUCCESS : GetLastError());
1071 if(err == ERROR_ACCESS_DENIED || err == ERROR_SHARING_VIOLATION)
1072 return err;
1074 succ:
1075 if (url_entry->cache_dir < header->dirs_no)
1077 if (header->directory_data[url_entry->cache_dir].files_no)
1078 header->directory_data[url_entry->cache_dir].files_no--;
1080 if (url_entry->cache_entry_type & STICKY_CACHE_ENTRY)
1082 if (url_entry->size.QuadPart < header->exempt_usage.QuadPart)
1083 header->exempt_usage.QuadPart -= url_entry->size.QuadPart;
1084 else
1085 header->exempt_usage.QuadPart = 0;
1087 else
1089 if (url_entry->size.QuadPart < header->cache_usage.QuadPart)
1090 header->cache_usage.QuadPart -= url_entry->size.QuadPart;
1091 else
1092 header->cache_usage.QuadPart = 0;
1095 return ERROR_SUCCESS;
1098 static BOOL urlcache_clean_leaked_entries(cache_container *container, urlcache_header *header)
1100 DWORD *leak_off;
1101 BOOL freed = FALSE;
1103 leak_off = &header->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
1104 while(*leak_off) {
1105 entry_url *url_entry = (entry_url*)((LPBYTE)header + *leak_off);
1107 if(SUCCEEDED(URLCache_DeleteFile(container, header, url_entry))) {
1108 *leak_off = url_entry->exempt_delta;
1109 URLCache_DeleteEntry(header, &url_entry->header);
1110 freed = TRUE;
1111 }else {
1112 leak_off = &url_entry->exempt_delta;
1116 return freed;
1119 /***********************************************************************
1120 * cache_container_clean_index (Internal)
1122 * This function is meant to make place in index file by removing leaked
1123 * files entries and resizing the file.
1125 * CAUTION: file view may get mapped to new memory
1127 * RETURNS
1128 * ERROR_SUCCESS when new memory is available
1129 * error code otherwise
1131 static DWORD cache_container_clean_index(cache_container *container, urlcache_header **file_view)
1133 urlcache_header *header = *file_view;
1134 DWORD ret;
1136 TRACE("(%s %s)\n", debugstr_w(container->cache_prefix), debugstr_w(container->path));
1138 if(urlcache_clean_leaked_entries(container, header))
1139 return ERROR_SUCCESS;
1141 if(header->size >= ALLOCATION_TABLE_SIZE*8*BLOCKSIZE + ENTRY_START_OFFSET) {
1142 WARN("index file has maximal size\n");
1143 return ERROR_NOT_ENOUGH_MEMORY;
1146 cache_container_close_index(container);
1147 ret = cache_container_open_index(container, header->capacity_in_blocks*2);
1148 if(ret != ERROR_SUCCESS)
1149 return ret;
1150 header = MapViewOfFile(container->mapping, FILE_MAP_WRITE, 0, 0, 0);
1151 if(!header)
1152 return GetLastError();
1154 UnmapViewOfFile(*file_view);
1155 *file_view = header;
1156 return ERROR_SUCCESS;
1159 /* Just like DosDateTimeToFileTime, except that it also maps the special
1160 * case of a DOS date/time of (0,0) to a filetime of (0,0).
1162 static void URLCache_DosDateTimeToFileTime(WORD fatdate, WORD fattime,
1163 FILETIME *ft)
1165 if (!fatdate && !fattime)
1166 ft->dwLowDateTime = ft->dwHighDateTime = 0;
1167 else
1168 DosDateTimeToFileTime(fatdate, fattime, ft);
1171 /***********************************************************************
1172 * URLCache_CopyEntry (Internal)
1174 * Copies an entry from the cache index file to the Win32 structure
1176 * RETURNS
1177 * ERROR_SUCCESS if the buffer was big enough
1178 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1181 static DWORD URLCache_CopyEntry(
1182 cache_container *pContainer,
1183 const urlcache_header *pHeader,
1184 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1185 LPDWORD lpdwBufferSize,
1186 const entry_url * pUrlEntry,
1187 BOOL bUnicode)
1189 int lenUrl;
1190 DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
1192 if (*lpdwBufferSize >= dwRequiredSize)
1194 lpCacheEntryInfo->lpHeaderInfo = NULL;
1195 lpCacheEntryInfo->lpszFileExtension = NULL;
1196 lpCacheEntryInfo->lpszLocalFileName = NULL;
1197 lpCacheEntryInfo->lpszSourceUrlName = NULL;
1198 lpCacheEntryInfo->CacheEntryType = pUrlEntry->cache_entry_type;
1199 lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->exempt_delta;
1200 lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->header_info_size;
1201 lpCacheEntryInfo->dwHitRate = pUrlEntry->hit_rate;
1202 lpCacheEntryInfo->dwSizeHigh = pUrlEntry->size.u.HighPart;
1203 lpCacheEntryInfo->dwSizeLow = pUrlEntry->size.u.LowPart;
1204 lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
1205 lpCacheEntryInfo->dwUseCount = pUrlEntry->use_count;
1206 URLCache_DosDateTimeToFileTime(pUrlEntry->expire_date, pUrlEntry->expire_time, &lpCacheEntryInfo->ExpireTime);
1207 lpCacheEntryInfo->LastAccessTime = pUrlEntry->access_time;
1208 lpCacheEntryInfo->LastModifiedTime = pUrlEntry->modification_time;
1209 URLCache_DosDateTimeToFileTime(pUrlEntry->sync_date, pUrlEntry->sync_time, &lpCacheEntryInfo->LastSyncTime);
1212 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1213 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1214 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1215 if (bUnicode)
1216 lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->url_off, -1, NULL, 0);
1217 else
1218 lenUrl = strlen((LPCSTR)pUrlEntry + pUrlEntry->url_off);
1219 dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1221 /* FIXME: is source url optional? */
1222 if (*lpdwBufferSize >= dwRequiredSize)
1224 DWORD lenUrlBytes = (lenUrl+1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1226 lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrlBytes;
1227 if (bUnicode)
1228 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->url_off, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
1229 else
1230 memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPCSTR)pUrlEntry + pUrlEntry->url_off, lenUrlBytes);
1233 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1234 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1235 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1237 if (pUrlEntry->local_name_off)
1239 LONG nLocalFilePathSize;
1240 LPSTR lpszLocalFileName;
1241 lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
1242 nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
1243 if ((bUnicode && URLCache_LocalFileNameToPathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->local_name_off, pUrlEntry->cache_dir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
1244 (!bUnicode && URLCache_LocalFileNameToPathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->local_name_off, pUrlEntry->cache_dir, lpszLocalFileName, &nLocalFilePathSize)))
1246 lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
1248 dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
1250 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1251 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1252 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1254 dwRequiredSize += pUrlEntry->header_info_size + 1;
1256 if (*lpdwBufferSize >= dwRequiredSize)
1258 lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->header_info_size - 1;
1259 memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPCSTR)pUrlEntry + pUrlEntry->header_info_off, pUrlEntry->header_info_size);
1260 ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
1262 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1263 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1264 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1266 if (pUrlEntry->file_extension_off)
1268 int lenExtension;
1270 if (bUnicode)
1271 lenExtension = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->file_extension_off, -1, NULL, 0);
1272 else
1273 lenExtension = strlen((LPCSTR)pUrlEntry + pUrlEntry->file_extension_off) + 1;
1274 dwRequiredSize += lenExtension * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1276 if (*lpdwBufferSize >= dwRequiredSize)
1278 lpCacheEntryInfo->lpszFileExtension = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenExtension;
1279 if (bUnicode)
1280 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->file_extension_off, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenExtension);
1281 else
1282 memcpy(lpCacheEntryInfo->lpszFileExtension, (LPCSTR)pUrlEntry + pUrlEntry->file_extension_off, lenExtension * sizeof(CHAR));
1285 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1286 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1287 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1290 if (dwRequiredSize > *lpdwBufferSize)
1292 *lpdwBufferSize = dwRequiredSize;
1293 return ERROR_INSUFFICIENT_BUFFER;
1295 *lpdwBufferSize = dwRequiredSize;
1296 return ERROR_SUCCESS;
1299 /***********************************************************************
1300 * URLCache_SetEntryInfo (Internal)
1302 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1303 * according to the flags set by dwFieldControl.
1305 * RETURNS
1306 * ERROR_SUCCESS if the buffer was big enough
1307 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1310 static DWORD URLCache_SetEntryInfo(entry_url * pUrlEntry, const INTERNET_CACHE_ENTRY_INFOW * lpCacheEntryInfo, DWORD dwFieldControl)
1312 if (dwFieldControl & CACHE_ENTRY_ACCTIME_FC)
1313 pUrlEntry->access_time = lpCacheEntryInfo->LastAccessTime;
1314 if (dwFieldControl & CACHE_ENTRY_ATTRIBUTE_FC)
1315 pUrlEntry->cache_entry_type = lpCacheEntryInfo->CacheEntryType;
1316 if (dwFieldControl & CACHE_ENTRY_EXEMPT_DELTA_FC)
1317 pUrlEntry->exempt_delta = lpCacheEntryInfo->u.dwExemptDelta;
1318 if (dwFieldControl & CACHE_ENTRY_EXPTIME_FC)
1319 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->ExpireTime, &pUrlEntry->expire_date, &pUrlEntry->expire_time);
1320 if (dwFieldControl & CACHE_ENTRY_HEADERINFO_FC)
1321 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1322 if (dwFieldControl & CACHE_ENTRY_HITRATE_FC)
1323 pUrlEntry->hit_rate = lpCacheEntryInfo->dwHitRate;
1324 if (dwFieldControl & CACHE_ENTRY_MODTIME_FC)
1325 pUrlEntry->modification_time = lpCacheEntryInfo->LastModifiedTime;
1326 if (dwFieldControl & CACHE_ENTRY_SYNCTIME_FC)
1327 URLCache_FileTimeToDosDateTime(&lpCacheEntryInfo->LastAccessTime, &pUrlEntry->sync_date, &pUrlEntry->sync_time);
1329 return ERROR_SUCCESS;
1332 /***********************************************************************
1333 * URLCache_HashKey (Internal)
1335 * Returns the hash key for a given string
1337 * RETURNS
1338 * hash key for the string
1341 static DWORD URLCache_HashKey(LPCSTR lpszKey)
1343 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1344 * but the algorithm and result are not the same!
1346 static const unsigned char lookupTable[256] =
1348 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1349 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1350 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1351 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1352 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1353 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1354 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1355 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1356 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1357 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1358 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1359 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1360 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1361 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1362 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1363 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1364 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1365 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1366 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1367 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1368 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1369 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1370 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1371 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1372 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1373 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1374 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1375 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1376 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1377 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1378 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1379 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1381 BYTE key[4];
1382 DWORD i;
1384 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1385 key[i] = lookupTable[(*lpszKey + i) & 0xFF];
1387 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1389 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1390 key[i] = lookupTable[*lpszKey ^ key[i]];
1393 return *(DWORD *)key;
1396 static inline entry_hash_table* URLCache_HashEntryFromOffset(const urlcache_header *pHeader, DWORD dwOffset)
1398 return (entry_hash_table*)((LPBYTE)pHeader + dwOffset);
1401 static inline BOOL URLCache_IsHashEntryValid(const urlcache_header *pHeader, const entry_hash_table *pHashEntry)
1403 /* check pHashEntry located within acceptable bounds in the URL cache mapping */
1404 return ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) >= ENTRY_START_OFFSET) &&
1405 ((DWORD)((const BYTE*)pHashEntry - (const BYTE*)pHeader) < pHeader->size);
1408 static BOOL URLCache_FindHash(const urlcache_header *pHeader, LPCSTR lpszUrl, struct hash_entry **ppHashEntry)
1410 /* structure of hash table:
1411 * 448 entries divided into 64 blocks
1412 * each block therefore contains a chain of 7 key/offset pairs
1413 * how position in table is calculated:
1414 * 1. the url is hashed in helper function
1415 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1416 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1418 * note:
1419 * there can be multiple hash tables in the file and the offset to
1420 * the next one is stored in the header of the hash table
1422 DWORD key = URLCache_HashKey(lpszUrl);
1423 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1424 entry_hash_table* pHashEntry;
1425 DWORD id = 0;
1427 key >>= HASHTABLE_FLAG_BITS;
1429 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->hash_table_off);
1430 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1431 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->next))
1433 int i;
1434 if (pHashEntry->id != id++)
1436 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->id, id);
1437 continue;
1439 /* make sure that it is in fact a hash entry */
1440 if (pHashEntry->header.signature != HASH_SIGNATURE)
1442 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->header.signature);
1443 continue;
1446 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1448 struct hash_entry *pHashElement = &pHashEntry->hash_table[offset + i];
1449 if (key == pHashElement->key>>HASHTABLE_FLAG_BITS)
1451 /* FIXME: we should make sure that this is the right element
1452 * before returning and claiming that it is. We can do this
1453 * by doing a simple compare between the URL we were given
1454 * and the URL stored in the entry. However, this assumes
1455 * we know the format of all the entries stored in the
1456 * hash table */
1457 *ppHashEntry = pHashElement;
1458 return TRUE;
1462 return FALSE;
1465 static BOOL URLCache_FindHashW(const urlcache_header *pHeader, LPCWSTR lpszUrl, struct hash_entry **ppHashEntry)
1467 LPSTR urlA;
1468 BOOL ret;
1470 urlA = heap_strdupWtoA(lpszUrl);
1471 if (!urlA)
1473 SetLastError(ERROR_OUTOFMEMORY);
1474 return FALSE;
1477 ret = URLCache_FindHash(pHeader, urlA, ppHashEntry);
1478 heap_free(urlA);
1479 return ret;
1482 /***********************************************************************
1483 * URLCache_HashEntrySetFlags (Internal)
1485 * Sets special bits in hash key
1487 * RETURNS
1488 * nothing
1491 static void URLCache_HashEntrySetFlags(struct hash_entry *pHashEntry, DWORD dwFlag)
1493 pHashEntry->key = (pHashEntry->key >> HASHTABLE_FLAG_BITS << HASHTABLE_FLAG_BITS) | dwFlag;
1496 /***********************************************************************
1497 * URLCache_DeleteEntryFromHash (Internal)
1499 * Searches all the hash tables in the index for the given URL and
1500 * then if found deletes the entry.
1502 * RETURNS
1503 * TRUE if the entry was found
1504 * FALSE if the entry could not be found
1507 static BOOL URLCache_DeleteEntryFromHash(struct hash_entry *pHashEntry)
1509 pHashEntry->key = HASHTABLE_DEL;
1510 return TRUE;
1513 /***********************************************************************
1514 * URLCache_AddEntryToHash (Internal)
1516 * Searches all the hash tables for a free slot based on the offset
1517 * generated from the hash key. If a free slot is found, the offset and
1518 * key are entered into the hash table.
1520 * RETURNS
1521 * ERROR_SUCCESS if the entry was added
1522 * Any other Win32 error code if the entry could not be added
1525 static DWORD URLCache_AddEntryToHash(urlcache_header *pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry, DWORD dwFieldType)
1527 /* see URLCache_FindEntryInHash for structure of hash tables */
1529 DWORD key = URLCache_HashKey(lpszUrl);
1530 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1531 entry_hash_table* pHashEntry, *pHashPrev = NULL;
1532 DWORD id = 0;
1533 DWORD error;
1535 key = ((key >> HASHTABLE_FLAG_BITS) << HASHTABLE_FLAG_BITS) + dwFieldType;
1537 for (pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->hash_table_off);
1538 URLCache_IsHashEntryValid(pHeader, pHashEntry);
1539 pHashEntry = URLCache_HashEntryFromOffset(pHeader, pHashEntry->next))
1541 int i;
1542 pHashPrev = pHashEntry;
1544 if (pHashEntry->id != id++)
1546 ERR("not right hash table number (%d) expected %d\n", pHashEntry->id, id);
1547 break;
1549 /* make sure that it is in fact a hash entry */
1550 if (pHashEntry->header.signature != HASH_SIGNATURE)
1552 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->header.signature);
1553 break;
1556 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1558 struct hash_entry *pHashElement = &pHashEntry->hash_table[offset + i];
1559 if (pHashElement->key==HASHTABLE_FREE || pHashElement->key==HASHTABLE_DEL) /* if the slot is free */
1561 pHashElement->key = key;
1562 pHashElement->offset = dwOffsetEntry;
1563 return ERROR_SUCCESS;
1567 error = URLCache_CreateHashTable(pHeader, pHashPrev, &pHashEntry);
1568 if (error != ERROR_SUCCESS)
1569 return error;
1571 pHashEntry->hash_table[offset].key = key;
1572 pHashEntry->hash_table[offset].offset = dwOffsetEntry;
1573 return ERROR_SUCCESS;
1576 /***********************************************************************
1577 * URLCache_CreateHashTable (Internal)
1579 * Creates a new hash table in free space and adds it to the chain of existing
1580 * hash tables.
1582 * RETURNS
1583 * ERROR_SUCCESS if the hash table was created
1584 * ERROR_DISK_FULL if the hash table could not be created
1587 static DWORD URLCache_CreateHashTable(urlcache_header *pHeader, entry_hash_table *pPrevHash, entry_hash_table **ppHash)
1589 DWORD dwOffset, error;
1590 int i;
1592 if ((error = URLCache_FindFirstFreeEntry(pHeader, 0x20, (entry_header**)ppHash)) != ERROR_SUCCESS)
1593 return error;
1595 dwOffset = (BYTE *)*ppHash - (BYTE *)pHeader;
1597 if (pPrevHash)
1598 pPrevHash->next = dwOffset;
1599 else
1600 pHeader->hash_table_off = dwOffset;
1601 (*ppHash)->header.signature = HASH_SIGNATURE;
1602 (*ppHash)->header.blocks_used = 0x20;
1603 (*ppHash)->next = 0;
1604 (*ppHash)->id = pPrevHash ? pPrevHash->id + 1 : 0;
1605 for (i = 0; i < HASHTABLE_SIZE; i++)
1607 (*ppHash)->hash_table[i].offset = HASHTABLE_FREE;
1608 (*ppHash)->hash_table[i].key = HASHTABLE_FREE;
1610 return ERROR_SUCCESS;
1613 /***********************************************************************
1614 * URLCache_EnumHashTables (Internal)
1616 * Enumerates the hash tables in a container.
1618 * RETURNS
1619 * TRUE if an entry was found
1620 * FALSE if there are no more tables to enumerate.
1623 static BOOL URLCache_EnumHashTables(const urlcache_header *pHeader, DWORD *id, entry_hash_table **ppHashEntry)
1625 for (*ppHashEntry = URLCache_HashEntryFromOffset(pHeader, pHeader->hash_table_off);
1626 URLCache_IsHashEntryValid(pHeader, *ppHashEntry);
1627 *ppHashEntry = URLCache_HashEntryFromOffset(pHeader, (*ppHashEntry)->next))
1629 TRACE("looking at hash table number %d\n", (*ppHashEntry)->id);
1630 if ((*ppHashEntry)->id != *id)
1631 continue;
1632 /* make sure that it is in fact a hash entry */
1633 if ((*ppHashEntry)->header.signature != HASH_SIGNATURE)
1635 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->header.signature);
1636 (*id)++;
1637 continue;
1640 TRACE("hash table number %d found\n", *id);
1641 return TRUE;
1643 return FALSE;
1646 /***********************************************************************
1647 * URLCache_EnumHashTableEntries (Internal)
1649 * Enumerates entries in a hash table and returns the next non-free entry.
1651 * RETURNS
1652 * TRUE if an entry was found
1653 * FALSE if the hash table is empty or there are no more entries to
1654 * enumerate.
1657 static BOOL URLCache_EnumHashTableEntries(const urlcache_header *pHeader, const entry_hash_table *pHashEntry,
1658 DWORD * index, const struct hash_entry **ppHashEntry)
1660 for (; *index < HASHTABLE_SIZE ; (*index)++)
1662 if (pHashEntry->hash_table[*index].key==HASHTABLE_FREE || pHashEntry->hash_table[*index].key==HASHTABLE_DEL)
1663 continue;
1665 *ppHashEntry = &pHashEntry->hash_table[*index];
1666 TRACE("entry found %d\n", *index);
1667 return TRUE;
1669 TRACE("no more entries (%d)\n", *index);
1670 return FALSE;
1673 /***********************************************************************
1674 * URLCache_DeleteCacheDirectory (Internal)
1676 * Erase a directory containing an URL cache.
1678 * RETURNS
1679 * TRUE success, FALSE failure/aborted.
1682 static BOOL URLCache_DeleteCacheDirectory(LPCWSTR lpszPath)
1684 DWORD path_len;
1685 WCHAR path[MAX_PATH + 1];
1686 SHFILEOPSTRUCTW shfos;
1687 int ret;
1689 path_len = strlenW(lpszPath);
1690 if (path_len >= MAX_PATH)
1691 return FALSE;
1692 strcpyW(path, lpszPath);
1693 path[path_len + 1] = 0; /* double-NUL-terminate path */
1695 shfos.hwnd = NULL;
1696 shfos.wFunc = FO_DELETE;
1697 shfos.pFrom = path;
1698 shfos.pTo = NULL;
1699 shfos.fFlags = FOF_NOCONFIRMATION;
1700 shfos.fAnyOperationsAborted = FALSE;
1701 ret = SHFileOperationW(&shfos);
1702 if (ret)
1703 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path), ret);
1704 return !(ret || shfos.fAnyOperationsAborted);
1707 /***********************************************************************
1708 * URLCache_IsLocked (Internal)
1710 * Checks if entry is locked. Unlocks it if possible.
1712 static BOOL URLCache_IsLocked(struct hash_entry *hash_entry, entry_url *url_entry)
1714 FILETIME cur_time;
1715 ULARGE_INTEGER acc_time, time;
1717 if ((hash_entry->key & ((1<<HASHTABLE_FLAG_BITS)-1)) != HASHTABLE_LOCK)
1718 return FALSE;
1720 GetSystemTimeAsFileTime(&cur_time);
1721 time.u.LowPart = cur_time.dwLowDateTime;
1722 time.u.HighPart = cur_time.dwHighDateTime;
1724 acc_time.u.LowPart = url_entry->access_time.dwLowDateTime;
1725 acc_time.u.HighPart = url_entry->access_time.dwHighDateTime;
1727 time.QuadPart -= acc_time.QuadPart;
1729 /* check if entry was locked for at least a day */
1730 if(time.QuadPart > (ULONGLONG)24*60*60*FILETIME_SECOND) {
1731 URLCache_HashEntrySetFlags(hash_entry, HASHTABLE_URL);
1732 url_entry->use_count = 0;
1733 return FALSE;
1736 return TRUE;
1739 /***********************************************************************
1740 * GetUrlCacheEntryInfoExA (WININET.@)
1743 BOOL WINAPI GetUrlCacheEntryInfoExA(
1744 LPCSTR lpszUrl,
1745 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1746 LPDWORD lpdwCacheEntryInfoBufSize,
1747 LPSTR lpszReserved,
1748 LPDWORD lpdwReserved,
1749 LPVOID lpReserved,
1750 DWORD dwFlags)
1752 urlcache_header *pHeader;
1753 struct hash_entry *pHashEntry;
1754 const entry_header *pEntry;
1755 const entry_url * pUrlEntry;
1756 cache_container *pContainer;
1757 DWORD error;
1759 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1760 debugstr_a(lpszUrl),
1761 lpCacheEntryInfo,
1762 lpdwCacheEntryInfoBufSize,
1763 lpszReserved,
1764 lpdwReserved,
1765 lpReserved,
1766 dwFlags);
1768 if ((lpszReserved != NULL) ||
1769 (lpdwReserved != NULL) ||
1770 (lpReserved != NULL))
1772 ERR("Reserved value was not 0\n");
1773 SetLastError(ERROR_INVALID_PARAMETER);
1774 return FALSE;
1776 if (dwFlags & ~GET_INSTALLED_ENTRY)
1777 FIXME("ignoring unsupported flags: %x\n", dwFlags);
1779 error = URLCacheContainers_FindContainerA(lpszUrl, &pContainer);
1780 if (error != ERROR_SUCCESS)
1782 SetLastError(error);
1783 return FALSE;
1786 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
1787 if (error != ERROR_SUCCESS)
1789 SetLastError(error);
1790 return FALSE;
1793 if (!(pHeader = cache_container_lock_index(pContainer)))
1794 return FALSE;
1796 if (!URLCache_FindHash(pHeader, lpszUrl, &pHashEntry))
1798 cache_container_unlock_index(pContainer, pHeader);
1799 WARN("entry %s not found!\n", debugstr_a(lpszUrl));
1800 SetLastError(ERROR_FILE_NOT_FOUND);
1801 return FALSE;
1804 pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
1805 if (pEntry->signature != URL_SIGNATURE)
1807 cache_container_unlock_index(pContainer, pHeader);
1808 FIXME("Trying to retrieve entry of unknown format %s\n",
1809 debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
1810 SetLastError(ERROR_FILE_NOT_FOUND);
1811 return FALSE;
1814 pUrlEntry = (const entry_url *)pEntry;
1815 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->url_off));
1816 TRACE("Header info: %s\n", debugstr_an((LPCSTR)pUrlEntry +
1817 pUrlEntry->header_info_off, pUrlEntry->header_info_size));
1819 if((dwFlags & GET_INSTALLED_ENTRY) && !(pUrlEntry->cache_entry_type & INSTALLED_CACHE_ENTRY))
1821 cache_container_unlock_index(pContainer, pHeader);
1822 SetLastError(ERROR_FILE_NOT_FOUND);
1823 return FALSE;
1826 if (lpdwCacheEntryInfoBufSize)
1828 if (!lpCacheEntryInfo)
1829 *lpdwCacheEntryInfoBufSize = 0;
1831 error = URLCache_CopyEntry(
1832 pContainer,
1833 pHeader,
1834 lpCacheEntryInfo,
1835 lpdwCacheEntryInfoBufSize,
1836 pUrlEntry,
1837 FALSE /* ANSI */);
1838 if (error != ERROR_SUCCESS)
1840 cache_container_unlock_index(pContainer, pHeader);
1841 SetLastError(error);
1842 return FALSE;
1844 if(pUrlEntry->local_name_off)
1845 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->local_name_off));
1848 cache_container_unlock_index(pContainer, pHeader);
1850 return TRUE;
1853 /***********************************************************************
1854 * GetUrlCacheEntryInfoA (WININET.@)
1857 BOOL WINAPI GetUrlCacheEntryInfoA(
1858 IN LPCSTR lpszUrlName,
1859 IN LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1860 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
1863 return GetUrlCacheEntryInfoExA(lpszUrlName, lpCacheEntryInfo,
1864 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
1867 /***********************************************************************
1868 * GetUrlCacheEntryInfoW (WININET.@)
1871 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1872 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1873 LPDWORD lpdwCacheEntryInfoBufferSize)
1875 return GetUrlCacheEntryInfoExW(lpszUrl, lpCacheEntryInfo,
1876 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
1879 /***********************************************************************
1880 * GetUrlCacheEntryInfoExW (WININET.@)
1883 BOOL WINAPI GetUrlCacheEntryInfoExW(
1884 LPCWSTR lpszUrl,
1885 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1886 LPDWORD lpdwCacheEntryInfoBufSize,
1887 LPWSTR lpszReserved,
1888 LPDWORD lpdwReserved,
1889 LPVOID lpReserved,
1890 DWORD dwFlags)
1892 urlcache_header *pHeader;
1893 struct hash_entry *pHashEntry;
1894 const entry_header *pEntry;
1895 const entry_url * pUrlEntry;
1896 cache_container *pContainer;
1897 DWORD error;
1899 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1900 debugstr_w(lpszUrl),
1901 lpCacheEntryInfo,
1902 lpdwCacheEntryInfoBufSize,
1903 lpszReserved,
1904 lpdwReserved,
1905 lpReserved,
1906 dwFlags);
1908 /* Ignore GET_INSTALLED_ENTRY flag in unicode version of function */
1909 dwFlags &= ~GET_INSTALLED_ENTRY;
1911 if ((lpszReserved != NULL) ||
1912 (lpdwReserved != NULL) ||
1913 (lpReserved != NULL))
1915 ERR("Reserved value was not 0\n");
1916 SetLastError(ERROR_INVALID_PARAMETER);
1917 return FALSE;
1919 if (dwFlags)
1920 FIXME("ignoring unsupported flags: %x\n", dwFlags);
1922 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
1923 if (error != ERROR_SUCCESS)
1925 SetLastError(error);
1926 return FALSE;
1929 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
1930 if (error != ERROR_SUCCESS)
1932 SetLastError(error);
1933 return FALSE;
1936 if (!(pHeader = cache_container_lock_index(pContainer)))
1937 return FALSE;
1939 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
1941 cache_container_unlock_index(pContainer, pHeader);
1942 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
1943 SetLastError(ERROR_FILE_NOT_FOUND);
1944 return FALSE;
1947 pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
1948 if (pEntry->signature != URL_SIGNATURE)
1950 cache_container_unlock_index(pContainer, pHeader);
1951 FIXME("Trying to retrieve entry of unknown format %s\n",
1952 debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
1953 SetLastError(ERROR_FILE_NOT_FOUND);
1954 return FALSE;
1957 pUrlEntry = (const entry_url *)pEntry;
1958 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->url_off));
1959 TRACE("Header info: %s\n", debugstr_an((LPCSTR)pUrlEntry +
1960 pUrlEntry->header_info_off, pUrlEntry->header_info_size));
1962 if (lpdwCacheEntryInfoBufSize)
1964 if (!lpCacheEntryInfo)
1965 *lpdwCacheEntryInfoBufSize = 0;
1967 error = URLCache_CopyEntry(
1968 pContainer,
1969 pHeader,
1970 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
1971 lpdwCacheEntryInfoBufSize,
1972 pUrlEntry,
1973 TRUE /* UNICODE */);
1974 if (error != ERROR_SUCCESS)
1976 cache_container_unlock_index(pContainer, pHeader);
1977 SetLastError(error);
1978 return FALSE;
1980 if(pUrlEntry->local_name_off)
1981 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->local_name_off));
1984 cache_container_unlock_index(pContainer, pHeader);
1986 return TRUE;
1989 /***********************************************************************
1990 * SetUrlCacheEntryInfoA (WININET.@)
1992 BOOL WINAPI SetUrlCacheEntryInfoA(
1993 LPCSTR lpszUrlName,
1994 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1995 DWORD dwFieldControl)
1997 urlcache_header *pHeader;
1998 struct hash_entry *pHashEntry;
1999 entry_header *pEntry;
2000 cache_container *pContainer;
2001 DWORD error;
2003 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
2005 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2006 if (error != ERROR_SUCCESS)
2008 SetLastError(error);
2009 return FALSE;
2012 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2013 if (error != ERROR_SUCCESS)
2015 SetLastError(error);
2016 return FALSE;
2019 if (!(pHeader = cache_container_lock_index(pContainer)))
2020 return FALSE;
2022 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2024 cache_container_unlock_index(pContainer, pHeader);
2025 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
2026 SetLastError(ERROR_FILE_NOT_FOUND);
2027 return FALSE;
2030 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2031 if (pEntry->signature != URL_SIGNATURE)
2033 cache_container_unlock_index(pContainer, pHeader);
2034 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2035 SetLastError(ERROR_FILE_NOT_FOUND);
2036 return FALSE;
2039 URLCache_SetEntryInfo(
2040 (entry_url *)pEntry,
2041 (const INTERNET_CACHE_ENTRY_INFOW *)lpCacheEntryInfo,
2042 dwFieldControl);
2044 cache_container_unlock_index(pContainer, pHeader);
2046 return TRUE;
2049 /***********************************************************************
2050 * SetUrlCacheEntryInfoW (WININET.@)
2052 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl, LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo, DWORD dwFieldControl)
2054 urlcache_header *pHeader;
2055 struct hash_entry *pHashEntry;
2056 entry_header *pEntry;
2057 cache_container *pContainer;
2058 DWORD error;
2060 TRACE("(%s, %p, 0x%08x)\n", debugstr_w(lpszUrl), lpCacheEntryInfo, dwFieldControl);
2062 error = URLCacheContainers_FindContainerW(lpszUrl, &pContainer);
2063 if (error != ERROR_SUCCESS)
2065 SetLastError(error);
2066 return FALSE;
2069 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2070 if (error != ERROR_SUCCESS)
2072 SetLastError(error);
2073 return FALSE;
2076 if (!(pHeader = cache_container_lock_index(pContainer)))
2077 return FALSE;
2079 if (!URLCache_FindHashW(pHeader, lpszUrl, &pHashEntry))
2081 cache_container_unlock_index(pContainer, pHeader);
2082 WARN("entry %s not found!\n", debugstr_w(lpszUrl));
2083 SetLastError(ERROR_FILE_NOT_FOUND);
2084 return FALSE;
2087 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2088 if (pEntry->signature != URL_SIGNATURE)
2090 cache_container_unlock_index(pContainer, pHeader);
2091 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2092 SetLastError(ERROR_FILE_NOT_FOUND);
2093 return FALSE;
2096 URLCache_SetEntryInfo(
2097 (entry_url *)pEntry,
2098 lpCacheEntryInfo,
2099 dwFieldControl);
2101 cache_container_unlock_index(pContainer, pHeader);
2103 return TRUE;
2106 /***********************************************************************
2107 * RetrieveUrlCacheEntryFileA (WININET.@)
2110 BOOL WINAPI RetrieveUrlCacheEntryFileA(
2111 IN LPCSTR lpszUrlName,
2112 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2113 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2114 IN DWORD dwReserved
2117 urlcache_header *pHeader;
2118 struct hash_entry *pHashEntry;
2119 entry_header *pEntry;
2120 entry_url * pUrlEntry;
2121 cache_container *pContainer;
2122 DWORD error;
2124 TRACE("(%s, %p, %p, 0x%08x)\n",
2125 debugstr_a(lpszUrlName),
2126 lpCacheEntryInfo,
2127 lpdwCacheEntryInfoBufferSize,
2128 dwReserved);
2130 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
2131 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
2133 SetLastError(ERROR_INVALID_PARAMETER);
2134 return FALSE;
2137 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2138 if (error != ERROR_SUCCESS)
2140 SetLastError(error);
2141 return FALSE;
2144 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2145 if (error != ERROR_SUCCESS)
2147 SetLastError(error);
2148 return FALSE;
2151 if (!(pHeader = cache_container_lock_index(pContainer)))
2152 return FALSE;
2154 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2156 cache_container_unlock_index(pContainer, pHeader);
2157 TRACE("entry %s not found!\n", lpszUrlName);
2158 SetLastError(ERROR_FILE_NOT_FOUND);
2159 return FALSE;
2162 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2163 if (pEntry->signature != URL_SIGNATURE)
2165 cache_container_unlock_index(pContainer, pHeader);
2166 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2167 SetLastError(ERROR_FILE_NOT_FOUND);
2168 return FALSE;
2171 pUrlEntry = (entry_url *)pEntry;
2172 if (!pUrlEntry->local_name_off)
2174 cache_container_unlock_index(pContainer, pHeader);
2175 SetLastError(ERROR_INVALID_DATA);
2176 return FALSE;
2179 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->url_off));
2180 TRACE("Header info: %s\n", debugstr_an((LPCSTR)pUrlEntry + pUrlEntry->header_info_off,
2181 pUrlEntry->header_info_size));
2183 error = URLCache_CopyEntry(pContainer, pHeader, lpCacheEntryInfo,
2184 lpdwCacheEntryInfoBufferSize, pUrlEntry,
2185 FALSE);
2186 if (error != ERROR_SUCCESS)
2188 cache_container_unlock_index(pContainer, pHeader);
2189 SetLastError(error);
2190 return FALSE;
2192 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->local_name_off));
2194 pUrlEntry->hit_rate++;
2195 pUrlEntry->use_count++;
2196 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_LOCK);
2197 GetSystemTimeAsFileTime(&pUrlEntry->access_time);
2199 cache_container_unlock_index(pContainer, pHeader);
2201 return TRUE;
2204 /***********************************************************************
2205 * RetrieveUrlCacheEntryFileW (WININET.@)
2208 BOOL WINAPI RetrieveUrlCacheEntryFileW(
2209 IN LPCWSTR lpszUrlName,
2210 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2211 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
2212 IN DWORD dwReserved
2215 urlcache_header *pHeader;
2216 struct hash_entry *pHashEntry;
2217 entry_header *pEntry;
2218 entry_url * pUrlEntry;
2219 cache_container *pContainer;
2220 DWORD error;
2222 TRACE("(%s, %p, %p, 0x%08x)\n",
2223 debugstr_w(lpszUrlName),
2224 lpCacheEntryInfo,
2225 lpdwCacheEntryInfoBufferSize,
2226 dwReserved);
2228 if (!lpszUrlName || !lpdwCacheEntryInfoBufferSize ||
2229 (!lpCacheEntryInfo && *lpdwCacheEntryInfoBufferSize))
2231 SetLastError(ERROR_INVALID_PARAMETER);
2232 return FALSE;
2235 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2236 if (error != ERROR_SUCCESS)
2238 SetLastError(error);
2239 return FALSE;
2242 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2243 if (error != ERROR_SUCCESS)
2245 SetLastError(error);
2246 return FALSE;
2249 if (!(pHeader = cache_container_lock_index(pContainer)))
2250 return FALSE;
2252 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2254 cache_container_unlock_index(pContainer, pHeader);
2255 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2256 SetLastError(ERROR_FILE_NOT_FOUND);
2257 return FALSE;
2260 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2261 if (pEntry->signature != URL_SIGNATURE)
2263 cache_container_unlock_index(pContainer, pHeader);
2264 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2265 SetLastError(ERROR_FILE_NOT_FOUND);
2266 return FALSE;
2269 pUrlEntry = (entry_url *)pEntry;
2270 if (!pUrlEntry->local_name_off)
2272 cache_container_unlock_index(pContainer, pHeader);
2273 SetLastError(ERROR_INVALID_DATA);
2274 return FALSE;
2277 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->url_off));
2278 TRACE("Header info: %s\n", debugstr_an((LPCSTR)pUrlEntry + pUrlEntry->header_info_off,
2279 pUrlEntry->header_info_size));
2281 error = URLCache_CopyEntry(
2282 pContainer,
2283 pHeader,
2284 (LPINTERNET_CACHE_ENTRY_INFOA)lpCacheEntryInfo,
2285 lpdwCacheEntryInfoBufferSize,
2286 pUrlEntry,
2287 TRUE /* UNICODE */);
2288 if (error != ERROR_SUCCESS)
2290 cache_container_unlock_index(pContainer, pHeader);
2291 SetLastError(error);
2292 return FALSE;
2294 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->local_name_off));
2296 pUrlEntry->hit_rate++;
2297 pUrlEntry->use_count++;
2298 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_LOCK);
2299 GetSystemTimeAsFileTime(&pUrlEntry->access_time);
2301 cache_container_unlock_index(pContainer, pHeader);
2303 return TRUE;
2306 static BOOL DeleteUrlCacheEntryInternal(const cache_container *pContainer,
2307 urlcache_header *pHeader, struct hash_entry *pHashEntry)
2309 entry_header *pEntry;
2310 entry_url * pUrlEntry;
2312 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2313 if (pEntry->signature != URL_SIGNATURE)
2315 FIXME("Trying to delete entry of unknown format %s\n",
2316 debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
2317 SetLastError(ERROR_FILE_NOT_FOUND);
2318 return FALSE;
2321 pUrlEntry = (entry_url *)pEntry;
2322 if(URLCache_IsLocked(pHashEntry, pUrlEntry))
2324 TRACE("Trying to delete locked entry\n");
2325 pUrlEntry->cache_entry_type |= PENDING_DELETE_CACHE_ENTRY;
2326 SetLastError(ERROR_SHARING_VIOLATION);
2327 return FALSE;
2330 if(!URLCache_DeleteFile(pContainer, pHeader, pUrlEntry))
2332 URLCache_DeleteEntry(pHeader, pEntry);
2334 else
2336 /* Add entry to leaked files list */
2337 pUrlEntry->header.signature = LEAK_SIGNATURE;
2338 pUrlEntry->exempt_delta = pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
2339 pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET] = pHashEntry->offset;
2342 URLCache_DeleteEntryFromHash(pHashEntry);
2343 return TRUE;
2346 static HANDLE free_cache_running;
2347 static HANDLE dll_unload_event;
2348 static DWORD WINAPI handle_full_cache_worker(void *param)
2350 FreeUrlCacheSpaceW(NULL, 20, 0);
2351 ReleaseSemaphore(free_cache_running, 1, NULL);
2352 return 0;
2355 static void handle_full_cache(void)
2357 if(WaitForSingleObject(free_cache_running, 0) == WAIT_OBJECT_0) {
2358 if(!QueueUserWorkItem(handle_full_cache_worker, NULL, 0))
2359 ReleaseSemaphore(free_cache_running, 1, NULL);
2363 /* Enumerates entries in cache, allows cache unlocking between calls. */
2364 static BOOL urlcache_next_entry(urlcache_header *header, DWORD *hash_table_off, DWORD *hash_table_entry,
2365 struct hash_entry **hash_entry, entry_header **entry)
2367 entry_hash_table *hashtable_entry;
2369 *hash_entry = NULL;
2370 *entry = NULL;
2372 if(!*hash_table_off) {
2373 *hash_table_off = header->hash_table_off;
2374 *hash_table_entry = 0;
2376 hashtable_entry = URLCache_HashEntryFromOffset(header, *hash_table_off);
2377 }else {
2378 if(*hash_table_off >= header->size) {
2379 *hash_table_off = 0;
2380 return FALSE;
2383 hashtable_entry = URLCache_HashEntryFromOffset(header, *hash_table_off);
2386 if(hashtable_entry->header.signature != HASH_SIGNATURE) {
2387 *hash_table_off = 0;
2388 return FALSE;
2391 while(1) {
2392 if(*hash_table_entry >= HASHTABLE_SIZE) {
2393 *hash_table_off = hashtable_entry->next;
2394 if(!*hash_table_off) {
2395 *hash_table_off = 0;
2396 return FALSE;
2399 hashtable_entry = URLCache_HashEntryFromOffset(header, *hash_table_off);
2400 *hash_table_entry = 0;
2403 if(hashtable_entry->hash_table[*hash_table_entry].key != HASHTABLE_DEL &&
2404 hashtable_entry->hash_table[*hash_table_entry].key != HASHTABLE_FREE) {
2405 *hash_entry = &hashtable_entry->hash_table[*hash_table_entry];
2406 *entry = (entry_header*)((LPBYTE)header + hashtable_entry->hash_table[*hash_table_entry].offset);
2407 (*hash_table_entry)++;
2408 return TRUE;
2411 (*hash_table_entry)++;
2414 *hash_table_off = 0;
2415 return FALSE;
2418 /* Rates an urlcache entry to determine if it can be deleted.
2420 * Score 0 means that entry can safely be removed, the bigger rating
2421 * the smaller chance of entry being removed.
2422 * DWORD_MAX means that entry can't be deleted at all.
2424 * Rating system is currently not fully compatible with native implementation.
2426 static DWORD urlcache_rate_entry(entry_url *url_entry, FILETIME *cur_time)
2428 ULARGE_INTEGER time, access_time;
2429 DWORD rating;
2431 access_time.u.LowPart = url_entry->access_time.dwLowDateTime;
2432 access_time.u.HighPart = url_entry->access_time.dwHighDateTime;
2434 time.u.LowPart = cur_time->dwLowDateTime;
2435 time.u.HighPart = cur_time->dwHighDateTime;
2437 /* Don't touch entries that were added less than 10 minutes ago */
2438 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)10*60*FILETIME_SECOND)
2439 return -1;
2441 if(url_entry->cache_entry_type & STICKY_CACHE_ENTRY)
2442 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)url_entry->exempt_delta*FILETIME_SECOND)
2443 return -1;
2445 time.QuadPart = (time.QuadPart-access_time.QuadPart)/FILETIME_SECOND;
2446 rating = 400*60*60*24/(60*60*24+time.QuadPart);
2448 if(url_entry->hit_rate > 100)
2449 rating += 100;
2450 else
2451 rating += url_entry->hit_rate;
2453 return rating;
2456 static int dword_cmp(const void *p1, const void *p2)
2458 return *(const DWORD*)p1 - *(const DWORD*)p2;
2461 /***********************************************************************
2462 * FreeUrlCacheSpaceW (WININET.@)
2464 * Frees up some cache.
2466 * PARAMETERS
2467 * cache_path [I] Which volume to free up from, or NULL if you don't care.
2468 * size [I] Percentage of the cache that should be free.
2469 * filter [I] Which entries can't be deleted (CacheEntryType)
2471 * RETURNS
2472 * TRUE success. FALSE failure.
2474 * IMPLEMENTATION
2475 * This implementation just retrieves the path of the cache directory, and
2476 * deletes its contents from the filesystem. The correct approach would
2477 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
2479 BOOL WINAPI FreeUrlCacheSpaceW(LPCWSTR cache_path, DWORD size, DWORD filter)
2481 cache_container *container;
2482 DWORD path_len, err;
2484 TRACE("(%s, %x, %x)\n", debugstr_w(cache_path), size, filter);
2486 if(size<1 || size>100) {
2487 SetLastError(ERROR_INVALID_PARAMETER);
2488 return FALSE;
2491 if(cache_path) {
2492 path_len = strlenW(cache_path);
2493 if(cache_path[path_len-1] == '\\')
2494 path_len--;
2495 }else {
2496 path_len = 0;
2499 if(size==100 && !filter) {
2500 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
2502 /* When cache_path==NULL only clean Temporary Internet Files */
2503 if((!path_len && container->cache_prefix[0]==0) ||
2504 (path_len && !strncmpiW(container->path, cache_path, path_len) &&
2505 (container->path[path_len]=='\0' || container->path[path_len]=='\\')))
2507 BOOL ret_del;
2509 WaitForSingleObject(container->mutex, INFINITE);
2511 /* unlock, delete, recreate and lock cache */
2512 cache_container_close_index(container);
2513 ret_del = URLCache_DeleteCacheDirectory(container->path);
2514 err = cache_container_open_index(container, MIN_BLOCK_NO);
2516 ReleaseMutex(container->mutex);
2517 if(!ret_del || (err != ERROR_SUCCESS))
2518 return FALSE;
2522 return TRUE;
2525 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
2527 urlcache_header *header;
2528 struct hash_entry *hash_entry;
2529 entry_header *entry;
2530 entry_url *url_entry;
2531 ULONGLONG desired_size, cur_size;
2532 DWORD delete_factor, hash_table_off, hash_table_entry;
2533 DWORD rate[100], rate_no;
2534 FILETIME cur_time;
2536 if((path_len || container->cache_prefix[0]!=0) &&
2537 (!path_len || strncmpiW(container->path, cache_path, path_len) ||
2538 (container->path[path_len]!='\0' && container->path[path_len]!='\\')))
2539 continue;
2541 err = cache_container_open_index(container, MIN_BLOCK_NO);
2542 if(err != ERROR_SUCCESS)
2543 continue;
2545 header = cache_container_lock_index(container);
2546 if(!header)
2547 continue;
2549 urlcache_clean_leaked_entries(container, header);
2551 desired_size = header->cache_limit.QuadPart*(100-size)/100;
2552 cur_size = header->cache_usage.QuadPart+header->exempt_usage.QuadPart;
2553 if(cur_size <= desired_size)
2554 delete_factor = 0;
2555 else
2556 delete_factor = (cur_size-desired_size)*100/cur_size;
2558 if(!delete_factor) {
2559 cache_container_unlock_index(container, header);
2560 continue;
2563 hash_table_off = 0;
2564 hash_table_entry = 0;
2565 rate_no = 0;
2566 GetSystemTimeAsFileTime(&cur_time);
2567 while(rate_no<sizeof(rate)/sizeof(*rate) &&
2568 urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2569 if(entry->signature != URL_SIGNATURE) {
2570 WARN("only url entries are currently supported\n");
2571 continue;
2574 url_entry = (entry_url*)entry;
2575 if(url_entry->cache_entry_type & filter)
2576 continue;
2578 rate[rate_no] = urlcache_rate_entry(url_entry, &cur_time);
2579 if(rate[rate_no] != -1)
2580 rate_no++;
2583 if(!rate_no) {
2584 TRACE("nothing to delete\n");
2585 cache_container_unlock_index(container, header);
2586 continue;
2589 qsort(rate, rate_no, sizeof(DWORD), dword_cmp);
2591 delete_factor = delete_factor*rate_no/100;
2592 delete_factor = rate[delete_factor];
2593 TRACE("deleting files with rating %d or less\n", delete_factor);
2595 hash_table_off = 0;
2596 while(urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2597 if(entry->signature != URL_SIGNATURE)
2598 continue;
2600 url_entry = (entry_url*)entry;
2601 if(url_entry->cache_entry_type & filter)
2602 continue;
2604 if(urlcache_rate_entry(url_entry, &cur_time) <= delete_factor) {
2605 TRACE("deleting file: %s\n", debugstr_a((char*)url_entry+url_entry->local_name_off));
2606 DeleteUrlCacheEntryInternal(container, header, hash_entry);
2608 if(header->cache_usage.QuadPart+header->exempt_usage.QuadPart <= desired_size)
2609 break;
2611 /* Allow other threads to use cache while cleaning */
2612 cache_container_unlock_index(container, header);
2613 if(WaitForSingleObject(dll_unload_event, 0) == WAIT_OBJECT_0) {
2614 TRACE("got dll_unload_event - finishing\n");
2615 return TRUE;
2617 Sleep(0);
2618 header = cache_container_lock_index(container);
2622 TRACE("cache size after cleaning 0x%s/0x%s\n",
2623 wine_dbgstr_longlong(header->cache_usage.QuadPart+header->exempt_usage.QuadPart),
2624 wine_dbgstr_longlong(header->cache_limit.QuadPart));
2625 cache_container_unlock_index(container, header);
2628 return TRUE;
2631 /***********************************************************************
2632 * FreeUrlCacheSpaceA (WININET.@)
2634 * See FreeUrlCacheSpaceW.
2636 BOOL WINAPI FreeUrlCacheSpaceA(LPCSTR lpszCachePath, DWORD dwSize, DWORD dwFilter)
2638 BOOL ret = FALSE;
2639 LPWSTR path = heap_strdupAtoW(lpszCachePath);
2640 if (lpszCachePath == NULL || path != NULL)
2641 ret = FreeUrlCacheSpaceW(path, dwSize, dwFilter);
2642 heap_free(path);
2643 return ret;
2646 /***********************************************************************
2647 * UnlockUrlCacheEntryFileA (WININET.@)
2650 BOOL WINAPI UnlockUrlCacheEntryFileA(
2651 IN LPCSTR lpszUrlName,
2652 IN DWORD dwReserved
2655 urlcache_header *pHeader;
2656 struct hash_entry *pHashEntry;
2657 entry_header *pEntry;
2658 entry_url * pUrlEntry;
2659 cache_container *pContainer;
2660 DWORD error;
2662 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2664 if (dwReserved)
2666 ERR("dwReserved != 0\n");
2667 SetLastError(ERROR_INVALID_PARAMETER);
2668 return FALSE;
2671 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
2672 if (error != ERROR_SUCCESS)
2674 SetLastError(error);
2675 return FALSE;
2678 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2679 if (error != ERROR_SUCCESS)
2681 SetLastError(error);
2682 return FALSE;
2685 if (!(pHeader = cache_container_lock_index(pContainer)))
2686 return FALSE;
2688 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
2690 cache_container_unlock_index(pContainer, pHeader);
2691 TRACE("entry %s not found!\n", lpszUrlName);
2692 SetLastError(ERROR_FILE_NOT_FOUND);
2693 return FALSE;
2696 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2697 if (pEntry->signature != URL_SIGNATURE)
2699 cache_container_unlock_index(pContainer, pHeader);
2700 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2701 SetLastError(ERROR_FILE_NOT_FOUND);
2702 return FALSE;
2705 pUrlEntry = (entry_url *)pEntry;
2707 if (pUrlEntry->use_count == 0)
2709 cache_container_unlock_index(pContainer, pHeader);
2710 return FALSE;
2712 pUrlEntry->use_count--;
2713 if (!pUrlEntry->use_count)
2715 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_URL);
2716 if (pUrlEntry->cache_entry_type & PENDING_DELETE_CACHE_ENTRY)
2717 DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
2720 cache_container_unlock_index(pContainer, pHeader);
2722 return TRUE;
2725 /***********************************************************************
2726 * UnlockUrlCacheEntryFileW (WININET.@)
2729 BOOL WINAPI UnlockUrlCacheEntryFileW( LPCWSTR lpszUrlName, DWORD dwReserved )
2731 urlcache_header *pHeader;
2732 struct hash_entry *pHashEntry;
2733 entry_header *pEntry;
2734 entry_url * pUrlEntry;
2735 cache_container *pContainer;
2736 DWORD error;
2738 TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName), dwReserved);
2740 if (dwReserved)
2742 ERR("dwReserved != 0\n");
2743 SetLastError(ERROR_INVALID_PARAMETER);
2744 return FALSE;
2747 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2748 if (error != ERROR_SUCCESS)
2750 SetLastError(error);
2751 return FALSE;
2754 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2755 if (error != ERROR_SUCCESS)
2757 SetLastError(error);
2758 return FALSE;
2761 if (!(pHeader = cache_container_lock_index(pContainer)))
2762 return FALSE;
2764 if (!URLCache_FindHashW(pHeader, lpszUrlName, &pHashEntry))
2766 cache_container_unlock_index(pContainer, pHeader);
2767 TRACE("entry %s not found!\n", debugstr_w(lpszUrlName));
2768 SetLastError(ERROR_FILE_NOT_FOUND);
2769 return FALSE;
2772 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2773 if (pEntry->signature != URL_SIGNATURE)
2775 cache_container_unlock_index(pContainer, pHeader);
2776 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2777 SetLastError(ERROR_FILE_NOT_FOUND);
2778 return FALSE;
2781 pUrlEntry = (entry_url *)pEntry;
2783 if (pUrlEntry->use_count == 0)
2785 cache_container_unlock_index(pContainer, pHeader);
2786 return FALSE;
2788 pUrlEntry->use_count--;
2789 if (!pUrlEntry->use_count)
2790 URLCache_HashEntrySetFlags(pHashEntry, HASHTABLE_URL);
2792 cache_container_unlock_index(pContainer, pHeader);
2794 return TRUE;
2797 /***********************************************************************
2798 * CreateUrlCacheEntryA (WININET.@)
2801 BOOL WINAPI CreateUrlCacheEntryA(
2802 IN LPCSTR lpszUrlName,
2803 IN DWORD dwExpectedFileSize,
2804 IN LPCSTR lpszFileExtension,
2805 OUT LPSTR lpszFileName,
2806 IN DWORD dwReserved
2809 WCHAR *url_name;
2810 WCHAR *file_extension = NULL;
2811 WCHAR file_name[MAX_PATH];
2812 BOOL bSuccess = FALSE;
2813 DWORD dwError = 0;
2815 TRACE("(%s %d %s %p %d)\n", debugstr_a(lpszUrlName), dwExpectedFileSize,
2816 debugstr_a(lpszFileExtension), lpszFileName, dwReserved);
2818 if (lpszUrlName && (url_name = heap_strdupAtoW(lpszUrlName)))
2820 if (!lpszFileExtension || (file_extension = heap_strdupAtoW(lpszFileExtension)))
2822 if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
2824 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
2826 bSuccess = TRUE;
2828 else
2830 dwError = GetLastError();
2833 else
2835 dwError = GetLastError();
2837 heap_free(file_extension);
2839 else
2841 dwError = GetLastError();
2843 heap_free(url_name);
2844 if (!bSuccess) SetLastError(dwError);
2846 return bSuccess;
2848 /***********************************************************************
2849 * CreateUrlCacheEntryW (WININET.@)
2852 BOOL WINAPI CreateUrlCacheEntryW(
2853 IN LPCWSTR lpszUrlName,
2854 IN DWORD dwExpectedFileSize,
2855 IN LPCWSTR lpszFileExtension,
2856 OUT LPWSTR lpszFileName,
2857 IN DWORD dwReserved
2860 cache_container *pContainer;
2861 urlcache_header *pHeader;
2862 CHAR szFile[MAX_PATH];
2863 WCHAR szExtension[MAX_PATH];
2864 LPCWSTR lpszUrlPart;
2865 LPCWSTR lpszUrlEnd;
2866 LPCWSTR lpszFileNameExtension;
2867 LPWSTR lpszFileNameNoPath;
2868 int i;
2869 int countnoextension;
2870 BYTE CacheDir;
2871 LONG lBufferSize;
2872 BOOL bFound = FALSE;
2873 BOOL generate_name = FALSE;
2874 int count;
2875 DWORD error;
2876 HANDLE hFile;
2877 FILETIME ft;
2879 static const WCHAR szWWW[] = {'w','w','w',0};
2881 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2882 debugstr_w(lpszUrlName),
2883 dwExpectedFileSize,
2884 debugstr_w(lpszFileExtension),
2885 lpszFileName,
2886 dwReserved);
2888 if (dwReserved)
2889 FIXME("dwReserved 0x%08x\n", dwReserved);
2891 lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2893 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2894 lpszUrlEnd--;
2896 lpszUrlPart = memchrW(lpszUrlName, '?', lpszUrlEnd - lpszUrlName);
2897 if (!lpszUrlPart)
2898 lpszUrlPart = memchrW(lpszUrlName, '#', lpszUrlEnd - lpszUrlName);
2899 if (lpszUrlPart)
2900 lpszUrlEnd = lpszUrlPart;
2902 for (lpszUrlPart = lpszUrlEnd;
2903 (lpszUrlPart >= lpszUrlName);
2904 lpszUrlPart--)
2906 if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2908 bFound = TRUE;
2909 lpszUrlPart++;
2910 break;
2913 if(!bFound)
2914 lpszUrlPart++;
2916 if (!lstrcmpW(lpszUrlPart, szWWW))
2918 lpszUrlPart += lstrlenW(szWWW);
2921 count = lpszUrlEnd - lpszUrlPart;
2923 if (bFound && (count < MAX_PATH))
2925 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2926 if (!len)
2927 return FALSE;
2928 szFile[len] = '\0';
2929 while(len && szFile[--len] == '/') szFile[len] = '\0';
2931 /* FIXME: get rid of illegal characters like \, / and : */
2932 TRACE("File name: %s\n", debugstr_a(szFile));
2934 else
2936 generate_name = TRUE;
2937 szFile[0] = 0;
2940 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
2941 if (error != ERROR_SUCCESS)
2943 SetLastError(error);
2944 return FALSE;
2947 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2948 if (error != ERROR_SUCCESS)
2950 SetLastError(error);
2951 return FALSE;
2954 if (!(pHeader = cache_container_lock_index(pContainer)))
2955 return FALSE;
2957 if(pHeader->dirs_no)
2958 CacheDir = (BYTE)(rand() % pHeader->dirs_no);
2959 else
2960 CacheDir = CACHE_CONTAINER_NO_SUBDIR;
2962 lBufferSize = MAX_PATH * sizeof(WCHAR);
2963 if (!URLCache_LocalFileNameToPathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize))
2965 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2966 debugstr_a(szFile), lBufferSize);
2967 cache_container_unlock_index(pContainer, pHeader);
2968 return FALSE;
2971 cache_container_unlock_index(pContainer, pHeader);
2973 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2974 lpszFileNameNoPath >= lpszFileName;
2975 --lpszFileNameNoPath)
2977 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2978 break;
2981 countnoextension = lstrlenW(lpszFileNameNoPath);
2982 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2983 if (lpszFileNameExtension)
2984 countnoextension -= lstrlenW(lpszFileNameExtension);
2985 *szExtension = '\0';
2987 if (lpszFileExtension)
2989 szExtension[0] = '.';
2990 lstrcpyW(szExtension+1, lpszFileExtension);
2993 for (i = 0; i<255 && !generate_name; i++)
2995 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2996 WCHAR *p;
2998 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2999 for (p = lpszFileNameNoPath + 1; *p; p++)
3001 switch (*p)
3003 case '<': case '>':
3004 case ':': case '"':
3005 case '/': case '\\':
3006 case '|': case '?':
3007 case '*':
3008 *p = '_'; break;
3009 default: break;
3012 if (p[-1] == ' ' || p[-1] == '.') p[-1] = '_';
3014 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
3015 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
3016 if (hFile != INVALID_HANDLE_VALUE)
3018 CloseHandle(hFile);
3019 return TRUE;
3023 /* Try to generate random name */
3024 GetSystemTimeAsFileTime(&ft);
3025 strcpyW(lpszFileNameNoPath+countnoextension+8, szExtension);
3027 for(i=0; i<255; i++)
3029 int j;
3030 ULONGLONG n = ft.dwHighDateTime;
3031 n <<= 32;
3032 n += ft.dwLowDateTime;
3033 n ^= (ULONGLONG)i<<48;
3035 for(j=0; j<8; j++)
3037 int r = (n % 36);
3038 n /= 37;
3039 lpszFileNameNoPath[countnoextension+j] = (r < 10 ? '0' + r : 'A' + r - 10);
3042 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
3043 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
3044 if (hFile != INVALID_HANDLE_VALUE)
3046 CloseHandle(hFile);
3047 return TRUE;
3051 WARN("Could not find a unique filename\n");
3052 return FALSE;
3055 /***********************************************************************
3056 * CommitUrlCacheEntryInternal (Compensates for an MS bug)
3058 * The bug we are compensating for is that some drongo at Microsoft
3059 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
3060 * As a consequence, CommitUrlCacheEntryA has been effectively
3061 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
3062 * is still defined as LPCWSTR. The result (other than madness) is
3063 * that we always need to store lpHeaderInfo in CP_ACP rather than
3064 * in UTF16, and we need to avoid converting lpHeaderInfo in
3065 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
3066 * result will lose data for arbitrary binary data.
3069 static BOOL CommitUrlCacheEntryInternal(
3070 IN LPCWSTR lpszUrlName,
3071 IN LPCWSTR lpszLocalFileName,
3072 IN FILETIME ExpireTime,
3073 IN FILETIME LastModifiedTime,
3074 IN DWORD CacheEntryType,
3075 IN LPBYTE lpHeaderInfo,
3076 IN DWORD dwHeaderSize,
3077 IN LPCWSTR lpszFileExtension,
3078 IN LPCWSTR lpszOriginalUrl
3081 cache_container *pContainer;
3082 urlcache_header *pHeader;
3083 struct hash_entry *pHashEntry;
3084 entry_header *pEntry;
3085 entry_url * pUrlEntry;
3086 DWORD url_entry_offset;
3087 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
3088 DWORD dwOffsetLocalFileName = 0;
3089 DWORD dwOffsetHeader = 0;
3090 DWORD dwOffsetFileExtension = 0;
3091 WIN32_FILE_ATTRIBUTE_DATA file_attr;
3092 LARGE_INTEGER file_size;
3093 BYTE cDirectory;
3094 char achFile[MAX_PATH];
3095 LPSTR lpszUrlNameA = NULL;
3096 LPSTR lpszFileExtensionA = NULL;
3097 char *pchLocalFileName = 0;
3098 DWORD hit_rate = 0;
3099 DWORD exempt_delta = 0;
3100 DWORD error;
3102 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3103 debugstr_w(lpszUrlName),
3104 debugstr_w(lpszLocalFileName),
3105 CacheEntryType,
3106 lpHeaderInfo,
3107 dwHeaderSize,
3108 debugstr_w(lpszFileExtension),
3109 debugstr_w(lpszOriginalUrl));
3111 if (CacheEntryType & STICKY_CACHE_ENTRY && !lpszLocalFileName)
3113 SetLastError(ERROR_INVALID_PARAMETER);
3114 return FALSE;
3116 if (lpszOriginalUrl)
3117 WARN(": lpszOriginalUrl ignored\n");
3119 memset(&file_attr, 0, sizeof(file_attr));
3120 if (lpszLocalFileName)
3122 if(!GetFileAttributesExW(lpszLocalFileName, GetFileExInfoStandard, &file_attr))
3123 return FALSE;
3125 file_size.u.LowPart = file_attr.nFileSizeLow;
3126 file_size.u.HighPart = file_attr.nFileSizeHigh;
3128 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3129 if (error != ERROR_SUCCESS)
3131 SetLastError(error);
3132 return FALSE;
3135 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3136 if (error != ERROR_SUCCESS)
3138 SetLastError(error);
3139 return FALSE;
3142 if (!(pHeader = cache_container_lock_index(pContainer)))
3143 return FALSE;
3145 lpszUrlNameA = heap_strdupWtoA(lpszUrlName);
3146 if (!lpszUrlNameA)
3148 error = GetLastError();
3149 goto cleanup;
3152 if (lpszFileExtension && !(lpszFileExtensionA = heap_strdupWtoA(lpszFileExtension)))
3154 error = GetLastError();
3155 goto cleanup;
3158 if (URLCache_FindHash(pHeader, lpszUrlNameA, &pHashEntry))
3160 entry_url *pUrlEntry = (entry_url*)((LPBYTE)pHeader + pHashEntry->offset);
3161 if (URLCache_IsLocked(pHashEntry, pUrlEntry))
3163 TRACE("Trying to overwrite locked entry\n");
3164 error = ERROR_SHARING_VIOLATION;
3165 goto cleanup;
3168 hit_rate = pUrlEntry->hit_rate;
3169 exempt_delta = pUrlEntry->exempt_delta;
3170 DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
3173 if (pHeader->dirs_no)
3174 cDirectory = 0;
3175 else
3176 cDirectory = CACHE_CONTAINER_NO_SUBDIR;
3178 if (lpszLocalFileName)
3180 BOOL bFound = FALSE;
3182 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
3184 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
3185 error = ERROR_INVALID_PARAMETER;
3186 goto cleanup;
3189 /* skip container path prefix */
3190 lpszLocalFileName += lstrlenW(pContainer->path);
3192 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, MAX_PATH, NULL, NULL);
3193 pchLocalFileName = achFile;
3195 if(pHeader->dirs_no)
3197 for (cDirectory = 0; cDirectory < pHeader->dirs_no; cDirectory++)
3199 if (!strncmp(pHeader->directory_data[cDirectory].name, pchLocalFileName, DIR_LENGTH))
3201 bFound = TRUE;
3202 break;
3206 if (!bFound)
3208 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
3209 error = ERROR_INVALID_PARAMETER;
3210 goto cleanup;
3213 lpszLocalFileName += DIR_LENGTH + 1;
3214 pchLocalFileName += DIR_LENGTH + 1;
3218 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
3219 if (lpszLocalFileName)
3221 dwOffsetLocalFileName = dwBytesNeeded;
3222 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
3224 if (lpHeaderInfo)
3226 dwOffsetHeader = dwBytesNeeded;
3227 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
3229 if (lpszFileExtensionA)
3231 dwOffsetFileExtension = dwBytesNeeded;
3232 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
3235 /* round up to next block */
3236 if (dwBytesNeeded % BLOCKSIZE)
3238 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
3239 dwBytesNeeded += BLOCKSIZE;
3242 error = URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry);
3243 while (error == ERROR_HANDLE_DISK_FULL)
3245 error = cache_container_clean_index(pContainer, &pHeader);
3246 if (error == ERROR_SUCCESS)
3247 error = URLCache_FindFirstFreeEntry(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry);
3249 if (error != ERROR_SUCCESS)
3250 goto cleanup;
3252 /* FindFirstFreeEntry fills in blocks used */
3253 pUrlEntry = (entry_url *)pEntry;
3254 url_entry_offset = (LPBYTE)pUrlEntry - (LPBYTE)pHeader;
3255 pUrlEntry->header.signature = URL_SIGNATURE;
3256 pUrlEntry->cache_dir = cDirectory;
3257 pUrlEntry->cache_entry_type = CacheEntryType | pContainer->default_entry_type;
3258 pUrlEntry->header_info_size = dwHeaderSize;
3259 if ((CacheEntryType & STICKY_CACHE_ENTRY) && !exempt_delta)
3261 /* Sticky entries have a default exempt time of one day */
3262 exempt_delta = 86400;
3264 pUrlEntry->exempt_delta = exempt_delta;
3265 pUrlEntry->hit_rate = hit_rate+1;
3266 pUrlEntry->file_extension_off = dwOffsetFileExtension;
3267 pUrlEntry->header_info_off = dwOffsetHeader;
3268 pUrlEntry->local_name_off = dwOffsetLocalFileName;
3269 pUrlEntry->url_off = DWORD_ALIGN(sizeof(*pUrlEntry));
3270 pUrlEntry->size.QuadPart = file_size.QuadPart;
3271 pUrlEntry->use_count = 0;
3272 GetSystemTimeAsFileTime(&pUrlEntry->access_time);
3273 pUrlEntry->modification_time = LastModifiedTime;
3274 URLCache_FileTimeToDosDateTime(&pUrlEntry->access_time, &pUrlEntry->sync_date, &pUrlEntry->sync_time);
3275 URLCache_FileTimeToDosDateTime(&ExpireTime, &pUrlEntry->expire_date, &pUrlEntry->expire_time);
3276 URLCache_FileTimeToDosDateTime(&file_attr.ftLastWriteTime, &pUrlEntry->write_date, &pUrlEntry->write_time);
3278 /*** Unknowns ***/
3279 pUrlEntry->unk1 = 0;
3280 pUrlEntry->unk2 = 0;
3281 pUrlEntry->unk3 = 0x60;
3282 pUrlEntry->unk4 = 0;
3283 pUrlEntry->unk5 = 0x1010;
3284 pUrlEntry->unk7 = 0;
3285 pUrlEntry->unk8 = 0;
3288 strcpy((LPSTR)pUrlEntry + pUrlEntry->url_off, lpszUrlNameA);
3289 if (dwOffsetLocalFileName)
3290 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName);
3291 if (dwOffsetHeader)
3292 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
3293 if (dwOffsetFileExtension)
3294 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
3296 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA, url_entry_offset, HASHTABLE_URL);
3297 while (error == ERROR_HANDLE_DISK_FULL)
3299 error = cache_container_clean_index(pContainer, &pHeader);
3300 if (error == ERROR_SUCCESS)
3302 pUrlEntry = (entry_url *)((LPBYTE)pHeader + url_entry_offset);
3303 error = URLCache_AddEntryToHash(pHeader, lpszUrlNameA,
3304 url_entry_offset, HASHTABLE_URL);
3307 if (error != ERROR_SUCCESS)
3308 URLCache_DeleteEntry(pHeader, &pUrlEntry->header);
3309 else
3311 if (pUrlEntry->cache_dir < pHeader->dirs_no)
3312 pHeader->directory_data[pUrlEntry->cache_dir].files_no++;
3313 if (CacheEntryType & STICKY_CACHE_ENTRY)
3314 pHeader->exempt_usage.QuadPart += file_size.QuadPart;
3315 else
3316 pHeader->cache_usage.QuadPart += file_size.QuadPart;
3317 if (pHeader->cache_usage.QuadPart + pHeader->exempt_usage.QuadPart >
3318 pHeader->cache_limit.QuadPart)
3319 handle_full_cache();
3322 cleanup:
3323 cache_container_unlock_index(pContainer, pHeader);
3324 heap_free(lpszUrlNameA);
3325 heap_free(lpszFileExtensionA);
3327 if (error == ERROR_SUCCESS)
3328 return TRUE;
3329 else
3331 SetLastError(error);
3332 return FALSE;
3336 /***********************************************************************
3337 * CommitUrlCacheEntryA (WININET.@)
3340 BOOL WINAPI CommitUrlCacheEntryA(
3341 IN LPCSTR lpszUrlName,
3342 IN LPCSTR lpszLocalFileName,
3343 IN FILETIME ExpireTime,
3344 IN FILETIME LastModifiedTime,
3345 IN DWORD CacheEntryType,
3346 IN LPBYTE lpHeaderInfo,
3347 IN DWORD dwHeaderSize,
3348 IN LPCSTR lpszFileExtension,
3349 IN LPCSTR lpszOriginalUrl
3352 WCHAR *url_name = NULL;
3353 WCHAR *local_file_name = NULL;
3354 WCHAR *original_url = NULL;
3355 WCHAR *file_extension = NULL;
3356 BOOL bSuccess = FALSE;
3358 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3359 debugstr_a(lpszUrlName),
3360 debugstr_a(lpszLocalFileName),
3361 CacheEntryType,
3362 lpHeaderInfo,
3363 dwHeaderSize,
3364 debugstr_a(lpszFileExtension),
3365 debugstr_a(lpszOriginalUrl));
3367 url_name = heap_strdupAtoW(lpszUrlName);
3368 if (!url_name)
3369 goto cleanup;
3371 if (lpszLocalFileName)
3373 local_file_name = heap_strdupAtoW(lpszLocalFileName);
3374 if (!local_file_name)
3375 goto cleanup;
3377 if (lpszFileExtension)
3379 file_extension = heap_strdupAtoW(lpszFileExtension);
3380 if (!file_extension)
3381 goto cleanup;
3383 if (lpszOriginalUrl)
3385 original_url = heap_strdupAtoW(lpszOriginalUrl);
3386 if (!original_url)
3387 goto cleanup;
3390 bSuccess = CommitUrlCacheEntryInternal(url_name, local_file_name, ExpireTime, LastModifiedTime,
3391 CacheEntryType, lpHeaderInfo, dwHeaderSize,
3392 file_extension, original_url);
3394 cleanup:
3395 heap_free(original_url);
3396 heap_free(file_extension);
3397 heap_free(local_file_name);
3398 heap_free(url_name);
3399 return bSuccess;
3402 /***********************************************************************
3403 * CommitUrlCacheEntryW (WININET.@)
3406 BOOL WINAPI CommitUrlCacheEntryW(
3407 IN LPCWSTR lpszUrlName,
3408 IN LPCWSTR lpszLocalFileName,
3409 IN FILETIME ExpireTime,
3410 IN FILETIME LastModifiedTime,
3411 IN DWORD CacheEntryType,
3412 IN LPWSTR lpHeaderInfo,
3413 IN DWORD dwHeaderSize,
3414 IN LPCWSTR lpszFileExtension,
3415 IN LPCWSTR lpszOriginalUrl
3418 DWORD dwError = 0;
3419 BOOL bSuccess = FALSE;
3420 DWORD len = 0;
3421 CHAR *header_info = NULL;
3423 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3424 debugstr_w(lpszUrlName),
3425 debugstr_w(lpszLocalFileName),
3426 CacheEntryType,
3427 lpHeaderInfo,
3428 dwHeaderSize,
3429 debugstr_w(lpszFileExtension),
3430 debugstr_w(lpszOriginalUrl));
3432 if (!lpHeaderInfo || (header_info = heap_strdupWtoA(lpHeaderInfo)))
3434 if(header_info)
3435 len = strlen(header_info);
3436 if (CommitUrlCacheEntryInternal(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
3437 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
3439 bSuccess = TRUE;
3441 else
3443 dwError = GetLastError();
3445 if (header_info)
3447 heap_free(header_info);
3448 if (!bSuccess)
3449 SetLastError(dwError);
3452 return bSuccess;
3455 /***********************************************************************
3456 * ReadUrlCacheEntryStream (WININET.@)
3459 BOOL WINAPI ReadUrlCacheEntryStream(
3460 IN HANDLE hUrlCacheStream,
3461 IN DWORD dwLocation,
3462 IN OUT LPVOID lpBuffer,
3463 IN OUT LPDWORD lpdwLen,
3464 IN DWORD dwReserved
3467 /* Get handle to file from 'stream' */
3468 stream_handle *pStream = (stream_handle*)hUrlCacheStream;
3470 if (dwReserved != 0)
3472 ERR("dwReserved != 0\n");
3473 SetLastError(ERROR_INVALID_PARAMETER);
3474 return FALSE;
3477 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->url, INTERNET_MAX_URL_LENGTH))
3479 SetLastError(ERROR_INVALID_HANDLE);
3480 return FALSE;
3483 if (SetFilePointer(pStream->file, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
3484 return FALSE;
3485 return ReadFile(pStream->file, lpBuffer, *lpdwLen, lpdwLen, NULL);
3488 /***********************************************************************
3489 * RetrieveUrlCacheEntryStreamA (WININET.@)
3492 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
3493 IN LPCSTR lpszUrlName,
3494 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
3495 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3496 IN BOOL fRandomRead,
3497 IN DWORD dwReserved
3500 /* NOTE: this is not the same as the way that the native
3501 * version allocates 'stream' handles. I did it this way
3502 * as it is much easier and no applications should depend
3503 * on this behaviour. (Native version appears to allocate
3504 * indices into a table)
3506 stream_handle *pStream;
3507 HANDLE hFile;
3509 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
3510 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3512 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
3513 lpCacheEntryInfo,
3514 lpdwCacheEntryInfoBufferSize,
3515 dwReserved))
3517 return NULL;
3520 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
3521 GENERIC_READ,
3522 FILE_SHARE_READ,
3523 NULL,
3524 OPEN_EXISTING,
3525 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3526 NULL);
3527 if (hFile == INVALID_HANDLE_VALUE)
3528 return FALSE;
3530 /* allocate handle storage space */
3531 pStream = heap_alloc(sizeof(stream_handle) + strlen(lpszUrlName) * sizeof(CHAR));
3532 if (!pStream)
3534 CloseHandle(hFile);
3535 SetLastError(ERROR_OUTOFMEMORY);
3536 return FALSE;
3539 pStream->file = hFile;
3540 strcpy(pStream->url, lpszUrlName);
3541 return pStream;
3544 /***********************************************************************
3545 * RetrieveUrlCacheEntryStreamW (WININET.@)
3548 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
3549 IN LPCWSTR lpszUrlName,
3550 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3551 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3552 IN BOOL fRandomRead,
3553 IN DWORD dwReserved
3556 DWORD size;
3557 int url_len;
3558 /* NOTE: this is not the same as the way that the native
3559 * version allocates 'stream' handles. I did it this way
3560 * as it is much easier and no applications should depend
3561 * on this behaviour. (Native version appears to allocate
3562 * indices into a table)
3564 stream_handle *pStream;
3565 HANDLE hFile;
3567 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3568 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3570 if (!RetrieveUrlCacheEntryFileW(lpszUrlName,
3571 lpCacheEntryInfo,
3572 lpdwCacheEntryInfoBufferSize,
3573 dwReserved))
3575 return NULL;
3578 hFile = CreateFileW(lpCacheEntryInfo->lpszLocalFileName,
3579 GENERIC_READ,
3580 FILE_SHARE_READ,
3581 NULL,
3582 OPEN_EXISTING,
3583 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3584 NULL);
3585 if (hFile == INVALID_HANDLE_VALUE)
3586 return FALSE;
3588 /* allocate handle storage space */
3589 size = sizeof(stream_handle);
3590 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
3591 size += url_len;
3592 pStream = heap_alloc(size);
3593 if (!pStream)
3595 CloseHandle(hFile);
3596 SetLastError(ERROR_OUTOFMEMORY);
3597 return FALSE;
3600 pStream->file = hFile;
3601 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, pStream->url, url_len, NULL, NULL);
3602 return pStream;
3605 /***********************************************************************
3606 * UnlockUrlCacheEntryStream (WININET.@)
3609 BOOL WINAPI UnlockUrlCacheEntryStream(
3610 IN HANDLE hUrlCacheStream,
3611 IN DWORD dwReserved
3614 stream_handle *pStream = (stream_handle*)hUrlCacheStream;
3616 if (dwReserved != 0)
3618 ERR("dwReserved != 0\n");
3619 SetLastError(ERROR_INVALID_PARAMETER);
3620 return FALSE;
3623 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->url, INTERNET_MAX_URL_LENGTH))
3625 SetLastError(ERROR_INVALID_HANDLE);
3626 return FALSE;
3629 if (!UnlockUrlCacheEntryFileA(pStream->url, 0))
3630 return FALSE;
3632 CloseHandle(pStream->file);
3633 heap_free(pStream);
3634 return TRUE;
3638 /***********************************************************************
3639 * DeleteUrlCacheEntryA (WININET.@)
3642 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3644 cache_container *pContainer;
3645 urlcache_header *pHeader;
3646 struct hash_entry *pHashEntry;
3647 DWORD error;
3648 BOOL ret;
3650 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3652 error = URLCacheContainers_FindContainerA(lpszUrlName, &pContainer);
3653 if (error != ERROR_SUCCESS)
3655 SetLastError(error);
3656 return FALSE;
3659 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3660 if (error != ERROR_SUCCESS)
3662 SetLastError(error);
3663 return FALSE;
3666 if (!(pHeader = cache_container_lock_index(pContainer)))
3667 return FALSE;
3669 if (!URLCache_FindHash(pHeader, lpszUrlName, &pHashEntry))
3671 cache_container_unlock_index(pContainer, pHeader);
3672 TRACE("entry %s not found!\n", lpszUrlName);
3673 SetLastError(ERROR_FILE_NOT_FOUND);
3674 return FALSE;
3677 ret = DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
3679 cache_container_unlock_index(pContainer, pHeader);
3681 return ret;
3684 /***********************************************************************
3685 * DeleteUrlCacheEntryW (WININET.@)
3688 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3690 cache_container *pContainer;
3691 urlcache_header *pHeader;
3692 struct hash_entry *pHashEntry;
3693 LPSTR urlA;
3694 DWORD error;
3695 BOOL ret;
3697 TRACE("(%s)\n", debugstr_w(lpszUrlName));
3699 urlA = heap_strdupWtoA(lpszUrlName);
3700 if (!urlA)
3702 SetLastError(ERROR_OUTOFMEMORY);
3703 return FALSE;
3706 error = URLCacheContainers_FindContainerW(lpszUrlName, &pContainer);
3707 if (error != ERROR_SUCCESS)
3709 heap_free(urlA);
3710 SetLastError(error);
3711 return FALSE;
3714 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3715 if (error != ERROR_SUCCESS)
3717 heap_free(urlA);
3718 SetLastError(error);
3719 return FALSE;
3722 if (!(pHeader = cache_container_lock_index(pContainer)))
3724 heap_free(urlA);
3725 return FALSE;
3728 if (!URLCache_FindHash(pHeader, urlA, &pHashEntry))
3730 cache_container_unlock_index(pContainer, pHeader);
3731 TRACE("entry %s not found!\n", debugstr_a(urlA));
3732 heap_free(urlA);
3733 SetLastError(ERROR_FILE_NOT_FOUND);
3734 return FALSE;
3737 ret = DeleteUrlCacheEntryInternal(pContainer, pHeader, pHashEntry);
3739 cache_container_unlock_index(pContainer, pHeader);
3740 heap_free(urlA);
3741 return ret;
3744 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3746 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3747 return TRUE;
3750 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3752 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3753 return TRUE;
3756 /***********************************************************************
3757 * CreateCacheContainerA (WININET.@)
3759 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3760 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3762 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3763 d1, d2, d3, d4, d5, d6, d7, d8);
3764 return TRUE;
3767 /***********************************************************************
3768 * CreateCacheContainerW (WININET.@)
3770 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3771 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3773 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3774 d1, d2, d3, d4, d5, d6, d7, d8);
3775 return TRUE;
3778 /***********************************************************************
3779 * FindFirstUrlCacheContainerA (WININET.@)
3781 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3783 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3784 return NULL;
3787 /***********************************************************************
3788 * FindFirstUrlCacheContainerW (WININET.@)
3790 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3792 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3793 return NULL;
3796 /***********************************************************************
3797 * FindNextUrlCacheContainerA (WININET.@)
3799 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3801 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3802 return FALSE;
3805 /***********************************************************************
3806 * FindNextUrlCacheContainerW (WININET.@)
3808 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3810 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3811 return FALSE;
3814 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3815 LPCSTR lpszUrlSearchPattern,
3816 DWORD dwFlags,
3817 DWORD dwFilter,
3818 GROUPID GroupId,
3819 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3820 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3821 LPVOID lpReserved,
3822 LPDWORD pcbReserved2,
3823 LPVOID lpReserved3
3826 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3827 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3828 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3829 SetLastError(ERROR_FILE_NOT_FOUND);
3830 return NULL;
3833 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3834 LPCWSTR lpszUrlSearchPattern,
3835 DWORD dwFlags,
3836 DWORD dwFilter,
3837 GROUPID GroupId,
3838 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3839 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3840 LPVOID lpReserved,
3841 LPDWORD pcbReserved2,
3842 LPVOID lpReserved3
3845 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3846 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3847 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3848 SetLastError(ERROR_FILE_NOT_FOUND);
3849 return NULL;
3852 /***********************************************************************
3853 * FindFirstUrlCacheEntryA (WININET.@)
3856 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3857 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3859 find_handle *pEntryHandle;
3861 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3863 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3864 if (!pEntryHandle)
3865 return NULL;
3867 pEntryHandle->magic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3868 if (lpszUrlSearchPattern)
3870 pEntryHandle->url_search_pattern = heap_strdupAtoW(lpszUrlSearchPattern);
3871 if (!pEntryHandle->url_search_pattern)
3873 heap_free(pEntryHandle);
3874 return NULL;
3877 else
3878 pEntryHandle->url_search_pattern = NULL;
3879 pEntryHandle->container_idx = 0;
3880 pEntryHandle->hash_table_idx = 0;
3881 pEntryHandle->hash_entry_idx = 0;
3883 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3885 heap_free(pEntryHandle);
3886 return NULL;
3888 return pEntryHandle;
3891 /***********************************************************************
3892 * FindFirstUrlCacheEntryW (WININET.@)
3895 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3896 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3898 find_handle *pEntryHandle;
3900 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3902 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3903 if (!pEntryHandle)
3904 return NULL;
3906 pEntryHandle->magic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3907 if (lpszUrlSearchPattern)
3909 pEntryHandle->url_search_pattern = heap_strdupW(lpszUrlSearchPattern);
3910 if (!pEntryHandle->url_search_pattern)
3912 heap_free(pEntryHandle);
3913 return NULL;
3916 else
3917 pEntryHandle->url_search_pattern = NULL;
3918 pEntryHandle->container_idx = 0;
3919 pEntryHandle->hash_table_idx = 0;
3920 pEntryHandle->hash_entry_idx = 0;
3922 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3924 heap_free(pEntryHandle);
3925 return NULL;
3927 return pEntryHandle;
3930 static BOOL FindNextUrlCacheEntryInternal(
3931 HANDLE hEnumHandle,
3932 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3933 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3934 BOOL unicode)
3936 find_handle *pEntryHandle = (find_handle*)hEnumHandle;
3937 cache_container *pContainer;
3939 if (pEntryHandle->magic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3941 SetLastError(ERROR_INVALID_HANDLE);
3942 return FALSE;
3945 for (; URLCacheContainers_Enum(pEntryHandle->url_search_pattern, pEntryHandle->container_idx, &pContainer);
3946 pEntryHandle->container_idx++, pEntryHandle->hash_table_idx = 0)
3948 urlcache_header *pHeader;
3949 entry_hash_table *pHashTableEntry;
3950 DWORD error;
3952 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3953 if (error != ERROR_SUCCESS)
3955 SetLastError(error);
3956 return FALSE;
3959 if (!(pHeader = cache_container_lock_index(pContainer)))
3960 return FALSE;
3962 for (; URLCache_EnumHashTables(pHeader, &pEntryHandle->hash_table_idx, &pHashTableEntry);
3963 pEntryHandle->hash_table_idx++, pEntryHandle->hash_entry_idx = 0)
3965 const struct hash_entry *pHashEntry = NULL;
3966 for (; URLCache_EnumHashTableEntries(pHeader, pHashTableEntry, &pEntryHandle->hash_entry_idx, &pHashEntry);
3967 pEntryHandle->hash_entry_idx++)
3969 const entry_url *pUrlEntry;
3970 const entry_header *pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
3972 if (pEntry->signature != URL_SIGNATURE)
3973 continue;
3975 pUrlEntry = (const entry_url *)pEntry;
3976 TRACE("Found URL: %s\n",
3977 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->url_off));
3978 TRACE("Header info: %s\n",
3979 debugstr_an((LPCSTR)pUrlEntry + pUrlEntry->header_info_off,
3980 pUrlEntry->header_info_size));
3982 error = URLCache_CopyEntry(
3983 pContainer,
3984 pHeader,
3985 lpNextCacheEntryInfo,
3986 lpdwNextCacheEntryInfoBufferSize,
3987 pUrlEntry,
3988 unicode);
3989 if (error != ERROR_SUCCESS)
3991 cache_container_unlock_index(pContainer, pHeader);
3992 SetLastError(error);
3993 return FALSE;
3995 if(pUrlEntry->local_name_off)
3996 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->local_name_off));
3998 /* increment the current index so that next time the function
3999 * is called the next entry is returned */
4000 pEntryHandle->hash_entry_idx++;
4001 cache_container_unlock_index(pContainer, pHeader);
4002 return TRUE;
4006 cache_container_unlock_index(pContainer, pHeader);
4009 SetLastError(ERROR_NO_MORE_ITEMS);
4010 return FALSE;
4013 /***********************************************************************
4014 * FindNextUrlCacheEntryA (WININET.@)
4016 BOOL WINAPI FindNextUrlCacheEntryA(
4017 HANDLE hEnumHandle,
4018 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
4019 LPDWORD lpdwNextCacheEntryInfoBufferSize)
4021 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
4023 return FindNextUrlCacheEntryInternal(hEnumHandle, lpNextCacheEntryInfo,
4024 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
4027 /***********************************************************************
4028 * FindNextUrlCacheEntryW (WININET.@)
4030 BOOL WINAPI FindNextUrlCacheEntryW(
4031 HANDLE hEnumHandle,
4032 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
4033 LPDWORD lpdwNextCacheEntryInfoBufferSize
4036 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
4038 return FindNextUrlCacheEntryInternal(hEnumHandle,
4039 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
4040 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
4043 /***********************************************************************
4044 * FindCloseUrlCache (WININET.@)
4046 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
4048 find_handle *pEntryHandle = (find_handle*)hEnumHandle;
4050 TRACE("(%p)\n", hEnumHandle);
4052 if (!pEntryHandle || pEntryHandle->magic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
4054 SetLastError(ERROR_INVALID_HANDLE);
4055 return FALSE;
4058 pEntryHandle->magic = 0;
4059 heap_free(pEntryHandle->url_search_pattern);
4060 heap_free(pEntryHandle);
4061 return TRUE;
4064 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
4065 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
4067 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
4068 dwSearchCondition, lpGroupId, lpReserved);
4069 return NULL;
4072 BOOL WINAPI FindNextUrlCacheEntryExA(
4073 HANDLE hEnumHandle,
4074 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
4075 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
4076 LPVOID lpReserved,
4077 LPDWORD pcbReserved2,
4078 LPVOID lpReserved3
4081 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
4082 lpReserved, pcbReserved2, lpReserved3);
4083 return FALSE;
4086 BOOL WINAPI FindNextUrlCacheEntryExW(
4087 HANDLE hEnumHandle,
4088 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
4089 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
4090 LPVOID lpReserved,
4091 LPDWORD pcbReserved2,
4092 LPVOID lpReserved3
4095 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
4096 lpReserved, pcbReserved2, lpReserved3);
4097 return FALSE;
4100 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
4102 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
4103 return FALSE;
4106 /***********************************************************************
4107 * CreateUrlCacheGroup (WININET.@)
4110 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
4112 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
4113 return FALSE;
4116 /***********************************************************************
4117 * DeleteUrlCacheGroup (WININET.@)
4120 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
4122 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
4123 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
4124 return FALSE;
4127 /***********************************************************************
4128 * DeleteWpadCacheForNetworks (WININET.@)
4129 * Undocumented, added in IE8
4131 BOOL WINAPI DeleteWpadCacheForNetworks(DWORD unk1)
4133 FIXME("(%d) stub\n", unk1);
4134 return FALSE;
4137 /***********************************************************************
4138 * SetUrlCacheEntryGroupA (WININET.@)
4141 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
4142 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
4143 LPVOID lpReserved)
4145 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
4146 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
4147 pbGroupAttributes, cbGroupAttributes, lpReserved);
4148 SetLastError(ERROR_FILE_NOT_FOUND);
4149 return FALSE;
4152 /***********************************************************************
4153 * SetUrlCacheEntryGroupW (WININET.@)
4156 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
4157 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
4158 LPVOID lpReserved)
4160 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
4161 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
4162 pbGroupAttributes, cbGroupAttributes, lpReserved);
4163 SetLastError(ERROR_FILE_NOT_FOUND);
4164 return FALSE;
4167 /***********************************************************************
4168 * GetUrlCacheConfigInfoW (WININET.@)
4170 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
4172 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
4173 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4174 return FALSE;
4177 /***********************************************************************
4178 * GetUrlCacheConfigInfoA (WININET.@)
4180 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
4182 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
4183 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
4184 return FALSE;
4187 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4188 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
4189 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
4191 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
4192 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
4193 lpdwGroupInfo, lpReserved);
4194 return FALSE;
4197 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4198 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
4199 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
4201 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
4202 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
4203 lpdwGroupInfo, lpReserved);
4204 return FALSE;
4207 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4208 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
4210 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
4211 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
4212 return TRUE;
4215 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
4216 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
4218 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
4219 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
4220 return TRUE;
4223 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
4225 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
4226 return TRUE;
4229 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
4231 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
4232 return TRUE;
4235 /***********************************************************************
4236 * DeleteIE3Cache (WININET.@)
4238 * Deletes the files used by the IE3 URL caching system.
4240 * PARAMS
4241 * hWnd [I] A dummy window.
4242 * hInst [I] Instance of process calling the function.
4243 * lpszCmdLine [I] Options used by function.
4244 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
4246 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
4248 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
4249 return 0;
4252 static BOOL IsUrlCacheEntryExpiredInternal(const entry_url *pUrlEntry,
4253 FILETIME *pftLastModified)
4255 BOOL ret;
4256 FILETIME now, expired;
4258 *pftLastModified = pUrlEntry->modification_time;
4259 GetSystemTimeAsFileTime(&now);
4260 URLCache_DosDateTimeToFileTime(pUrlEntry->expire_date,
4261 pUrlEntry->expire_time, &expired);
4262 /* If the expired time is 0, it's interpreted as not expired */
4263 if (!expired.dwLowDateTime && !expired.dwHighDateTime)
4264 ret = FALSE;
4265 else
4266 ret = CompareFileTime(&expired, &now) < 0;
4267 return ret;
4270 /***********************************************************************
4271 * IsUrlCacheEntryExpiredA (WININET.@)
4273 * PARAMS
4274 * url [I] Url
4275 * dwFlags [I] Unknown
4276 * pftLastModified [O] Last modified time
4278 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
4280 urlcache_header *pHeader;
4281 struct hash_entry *pHashEntry;
4282 const entry_header *pEntry;
4283 const entry_url * pUrlEntry;
4284 cache_container *pContainer;
4285 BOOL expired;
4287 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
4289 if (!url || !pftLastModified)
4290 return TRUE;
4291 if (dwFlags)
4292 FIXME("unknown flags 0x%08x\n", dwFlags);
4294 /* Any error implies that the URL is expired, i.e. not in the cache */
4295 if (URLCacheContainers_FindContainerA(url, &pContainer))
4297 memset(pftLastModified, 0, sizeof(*pftLastModified));
4298 return TRUE;
4301 if (cache_container_open_index(pContainer, MIN_BLOCK_NO))
4303 memset(pftLastModified, 0, sizeof(*pftLastModified));
4304 return TRUE;
4307 if (!(pHeader = cache_container_lock_index(pContainer)))
4309 memset(pftLastModified, 0, sizeof(*pftLastModified));
4310 return TRUE;
4313 if (!URLCache_FindHash(pHeader, url, &pHashEntry))
4315 cache_container_unlock_index(pContainer, pHeader);
4316 memset(pftLastModified, 0, sizeof(*pftLastModified));
4317 TRACE("entry %s not found!\n", url);
4318 return TRUE;
4321 pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
4322 if (pEntry->signature != URL_SIGNATURE)
4324 cache_container_unlock_index(pContainer, pHeader);
4325 memset(pftLastModified, 0, sizeof(*pftLastModified));
4326 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
4327 return TRUE;
4330 pUrlEntry = (const entry_url *)pEntry;
4331 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
4333 cache_container_unlock_index(pContainer, pHeader);
4335 return expired;
4338 /***********************************************************************
4339 * IsUrlCacheEntryExpiredW (WININET.@)
4341 * PARAMS
4342 * url [I] Url
4343 * dwFlags [I] Unknown
4344 * pftLastModified [O] Last modified time
4346 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
4348 urlcache_header *pHeader;
4349 struct hash_entry *pHashEntry;
4350 const entry_header *pEntry;
4351 const entry_url * pUrlEntry;
4352 cache_container *pContainer;
4353 BOOL expired;
4355 TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
4357 if (!url || !pftLastModified)
4358 return TRUE;
4359 if (dwFlags)
4360 FIXME("unknown flags 0x%08x\n", dwFlags);
4362 /* Any error implies that the URL is expired, i.e. not in the cache */
4363 if (URLCacheContainers_FindContainerW(url, &pContainer))
4365 memset(pftLastModified, 0, sizeof(*pftLastModified));
4366 return TRUE;
4369 if (cache_container_open_index(pContainer, MIN_BLOCK_NO))
4371 memset(pftLastModified, 0, sizeof(*pftLastModified));
4372 return TRUE;
4375 if (!(pHeader = cache_container_lock_index(pContainer)))
4377 memset(pftLastModified, 0, sizeof(*pftLastModified));
4378 return TRUE;
4381 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
4383 cache_container_unlock_index(pContainer, pHeader);
4384 memset(pftLastModified, 0, sizeof(*pftLastModified));
4385 TRACE("entry %s not found!\n", debugstr_w(url));
4386 return TRUE;
4389 if (!URLCache_FindHashW(pHeader, url, &pHashEntry))
4391 cache_container_unlock_index(pContainer, pHeader);
4392 memset(pftLastModified, 0, sizeof(*pftLastModified));
4393 TRACE("entry %s not found!\n", debugstr_w(url));
4394 return TRUE;
4397 pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
4398 if (pEntry->signature != URL_SIGNATURE)
4400 cache_container_unlock_index(pContainer, pHeader);
4401 memset(pftLastModified, 0, sizeof(*pftLastModified));
4402 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
4403 return TRUE;
4406 pUrlEntry = (const entry_url *)pEntry;
4407 expired = IsUrlCacheEntryExpiredInternal(pUrlEntry, pftLastModified);
4409 cache_container_unlock_index(pContainer, pHeader);
4411 return expired;
4414 /***********************************************************************
4415 * GetDiskInfoA (WININET.@)
4417 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
4419 BOOL ret;
4420 ULARGE_INTEGER bytes_free, bytes_total;
4422 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
4424 if (!path)
4426 SetLastError(ERROR_INVALID_PARAMETER);
4427 return FALSE;
4430 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
4432 if (cluster_size) *cluster_size = 1;
4433 if (free) *free = bytes_free.QuadPart;
4434 if (total) *total = bytes_total.QuadPart;
4436 return ret;
4439 /***********************************************************************
4440 * RegisterUrlCacheNotification (WININET.@)
4442 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
4444 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
4445 return 0;
4448 /***********************************************************************
4449 * IncrementUrlCacheHeaderData (WININET.@)
4451 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
4453 FIXME("(%u, %p)\n", index, data);
4454 return FALSE;
4457 /***********************************************************************
4458 * RunOnceUrlCache (WININET.@)
4461 DWORD WINAPI RunOnceUrlCache(HWND hwnd, HINSTANCE hinst, LPSTR cmd, int cmdshow)
4463 FIXME("(%p, %p, %s, %d): stub\n", hwnd, hinst, debugstr_a(cmd), cmdshow);
4464 return 0;
4467 BOOL init_urlcache(void)
4469 dll_unload_event = CreateEventW(NULL, FALSE, FALSE, NULL);
4470 if(!dll_unload_event)
4471 return FALSE;
4473 free_cache_running = CreateSemaphoreW(NULL, 1, 1, NULL);
4474 if(!free_cache_running) {
4475 CloseHandle(dll_unload_event);
4476 return FALSE;
4479 URLCacheContainers_CreateDefaults();
4480 return TRUE;
4483 void free_urlcache(void)
4485 SetEvent(dll_unload_event);
4486 WaitForSingleObject(free_cache_running, INFINITE);
4487 ReleaseSemaphore(free_cache_running, 1, NULL);
4488 CloseHandle(free_cache_running);
4489 CloseHandle(dll_unload_event);
4491 URLCacheContainers_DeleteAll();
4494 /***********************************************************************
4495 * LoadUrlCacheContent (WININET.@)
4497 BOOL WINAPI LoadUrlCacheContent(void)
4499 FIXME("stub!\n");
4500 return FALSE;