2 * Wininet - Url Cache functions
4 * Copyright 2001,2002 CodeWeavers
5 * Copyright 2003-2008 Robert Shearman
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include "wine/port.h"
28 #define NONAMELESSUNION
29 #define NONAMELESSSTRUCT
31 #if defined(__MINGW32__) || defined (_MSC_VER)
39 #include <sys/types.h>
40 #ifdef HAVE_SYS_SOCKET_H
41 # include <sys/socket.h>
58 #include "wine/unicode.h"
59 #include "wine/debug.h"
61 WINE_DEFAULT_DEBUG_CHANNEL(wininet
);
63 static const char urlcache_ver_prefix
[] = "WINE URLCache Ver ";
64 static const char urlcache_ver
[] = "0.2012001";
66 #define ENTRY_START_OFFSET 0x4000
68 #define MAX_DIR_NO 0x20
70 #define HASHTABLE_SIZE 448
71 #define HASHTABLE_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
72 #define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
73 #define ALLOCATION_TABLE_OFFSET 0x250
74 #define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
75 #define MIN_BLOCK_NO 0x80
76 #define MAX_BLOCK_NO (ALLOCATION_TABLE_SIZE * 8)
77 #define FILE_SIZE(blocks) ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
79 #define HASHTABLE_URL 0
80 #define HASHTABLE_DEL 1
81 #define HASHTABLE_LOCK 2
82 #define HASHTABLE_FREE 3
83 #define HASHTABLE_REDR 5
84 #define HASHTABLE_FLAG_BITS 6
86 #define PENDING_DELETE_CACHE_ENTRY 0x00400000
87 #define INSTALLED_CACHE_ENTRY 0x10000000
88 #define GET_INSTALLED_ENTRY 0x200
89 #define CACHE_CONTAINER_NO_SUBDIR 0xFE
91 #define CACHE_HEADER_DATA_ROOT_LEAK_OFFSET 0x16
93 #define FILETIME_SECOND 10000000
95 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
96 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
97 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
98 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
99 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
101 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
103 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
108 DWORD blocks_used
; /* number of 128byte blocks used by this entry */
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 */
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) */
159 struct hash_entry hash_table
[HASHTABLE_SIZE
];
166 DWORD hash_table_off
;
167 DWORD capacity_in_blocks
;
170 ULARGE_INTEGER cache_limit
;
171 ULARGE_INTEGER cache_usage
;
172 ULARGE_INTEGER exempt_usage
;
174 struct _directory_data
177 char name
[DIR_LENGTH
];
178 } directory_data
[MAX_DIR_NO
];
180 BYTE allocation_table
[ALLOCATION_TABLE_SIZE
];
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
;
203 LPWSTR url_search_pattern
;
205 DWORD hash_table_idx
;
206 DWORD hash_entry_idx
;
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 '!')
225 static void URLCache_PathToObjectName(LPWSTR lpszPath
, WCHAR replace
)
227 for (; *lpszPath
; lpszPath
++)
229 if (*lpszPath
== '\\')
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
];
242 wsprintfW(mapping_name
, mapping_name_format
, path
, size
);
243 URLCache_PathToObjectName(mapping_name
, '_');
245 mapping
= OpenFileMappingW(FILE_MAP_WRITE
, FALSE
, mapping_name
);
247 if(validate
) *validate
= FALSE
;
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
;
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
);
282 return GetLastError();
284 header
= MapViewOfFile(mapping
, FILE_MAP_WRITE
, 0, 0, 0);
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;
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
);
331 GetSystemTimeAsFileTime(&ft
);
333 for(i
=0; i
<header
->dirs_no
; ++i
) {
334 header
->directory_data
[i
].files_no
= 0;
336 ULONGLONG n
= ft
.dwHighDateTime
;
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.
345 n
+= ft
.dwLowDateTime
;
346 n
^= ((ULONGLONG
) i
<< 56) | ((ULONGLONG
) j
<< 48);
348 for(k
= 0; k
< 8; ++k
) {
351 /* Dividing by a prime greater than 36 helps
352 * with the appearance of randomness
357 dir_name
[k
] = '0' + r
;
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
];
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
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
))
398 if(file_size
!= header
->size
)
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))
405 if(FILE_SIZE(header
->capacity_in_blocks
) != file_size
)
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) {
415 if(allocation_size
!= header
->blocks_in_use
)
418 for(; i
<ALLOCATION_TABLE_SIZE
; i
++) {
419 if(header
->allocation_table
[i
])
426 /***********************************************************************
427 * cache_container_open_index (Internal)
429 * Opens the index file and saves mapping handle
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};
441 WCHAR index_path
[MAX_PATH
];
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
) {
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
);
482 ReleaseMutex(container
->mutex
);
486 container
->file_size
= file_size
;
487 container
->mapping
= cache_container_map_index(file
, container
->path
, file_size
, &validate
);
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);
497 UnmapViewOfFile(header
);
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)
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
);
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
);
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
);
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
);
573 list_add_head(&UrlContainers
, &pContainer
->entry
);
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};
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
},
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
);
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");
631 wszCachePath
[path_len
] = '\\';
632 wszCachePath
[path_len
+1] = 0;
634 strcpyW(wszMutexName
, wszCachePath
);
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
));
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
)
684 if (lpszUrl
&& !(url
= heap_strdupAtoW(lpszUrl
)))
685 return ERROR_OUTOFMEMORY
;
687 ret
= URLCacheContainers_FindContainerW(url
, ppContainer
);
692 static BOOL
URLCacheContainers_Enum(LPCWSTR lpwszSearchPattern
, DWORD dwIndex
, cache_container
**ppContainer
)
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)
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
;
718 TRACE("found container with prefix %s\n", debugstr_w(pContainer
->cache_prefix
));
719 *ppContainer
= pContainer
;
728 /***********************************************************************
729 * cache_container_lock_index (Internal)
731 * Locks the index for system-wide exclusive access.
734 * Cache file header if successful
735 * NULL if failed and calls SetLastError.
737 static urlcache_header
* cache_container_lock_index(cache_container
*pContainer
)
741 urlcache_header
* pHeader
;
745 WaitForSingleObject(pContainer
->mutex
, INFINITE
);
747 pIndexData
= MapViewOfFile(pContainer
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
751 ReleaseMutex(pContainer
->mutex
);
752 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
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
);
771 pIndexData
= MapViewOfFile(pContainer
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
775 ReleaseMutex(pContainer
->mutex
);
776 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
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
);
792 /***********************************************************************
793 * cache_container_unlock_index (Internal)
796 static BOOL
cache_container_unlock_index(cache_container
*pContainer
, urlcache_header
*pHeader
)
799 ReleaseMutex(pContainer
->mutex
);
800 return UnmapViewOfFile(pHeader
);
804 #define CHAR_BIT (8 * sizeof(CHAR))
807 /***********************************************************************
808 * URLCache_Allocation_BlockIsFree (Internal)
810 * Is the specified block number free?
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
829 * this function is not updating used blocks count
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
847 * this function is not updating used blocks count
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.
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
)
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
);
881 TRACE("Found free block at no. %d (0x%x)\n", dwBlockNumber
, ENTRY_START_OFFSET
+ dwBlockNumber
* BLOCKSIZE
);
883 if (dwFreeCounter
== dwBlocksNeeded
)
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
907 * TRUE if it succeeded
911 static BOOL
URLCache_DeleteEntry(urlcache_header
*pHeader
, entry_header
*pEntry
)
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
;
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).
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
,
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
)
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
)
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
] = '\\';
972 MultiByteToWideChar(CP_ACP
, 0, szLocalFileName
, -1, wszPath
+ dir_len
+ path_len
, file_name_len
);
973 *lpBufferSize
= nRequired
;
976 *lpBufferSize
= nRequired
;
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.
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
,
1001 int path_len
, file_name_len
, dir_len
;
1003 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
&& Directory
>=pHeader
->dirs_no
)
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;
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
);
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
;
1028 *lpBufferSize
= nRequired
;
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
,
1038 if (!ft
->dwLowDateTime
&& !ft
->dwHighDateTime
)
1039 *fatdate
= *fattime
= 0;
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
);
1056 if(!url_entry
->local_name_off
)
1059 if(!URLCache_LocalFileNameToPathW(container
, header
,
1060 (LPCSTR
)url_entry
+url_entry
->local_name_off
,
1061 url_entry
->cache_dir
, path
, &path_size
))
1064 if(!GetFileAttributesExW(path
, GetFileExInfoStandard
, &attr
))
1066 URLCache_FileTimeToDosDateTime(&attr
.ftLastWriteTime
, &date
, &time
);
1067 if(date
!= url_entry
->write_date
|| time
!= url_entry
->write_time
)
1070 err
= (DeleteFileW(path
) ? ERROR_SUCCESS
: GetLastError());
1071 if(err
== ERROR_ACCESS_DENIED
|| err
== ERROR_SHARING_VIOLATION
)
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
;
1085 header
->exempt_usage
.QuadPart
= 0;
1089 if (url_entry
->size
.QuadPart
< header
->cache_usage
.QuadPart
)
1090 header
->cache_usage
.QuadPart
-= url_entry
->size
.QuadPart
;
1092 header
->cache_usage
.QuadPart
= 0;
1095 return ERROR_SUCCESS
;
1098 static BOOL
urlcache_clean_leaked_entries(cache_container
*container
, urlcache_header
*header
)
1103 leak_off
= &header
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
];
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
);
1112 leak_off
= &url_entry
->exempt_delta
;
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
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
;
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
)
1150 header
= MapViewOfFile(container
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
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
,
1165 if (!fatdate
&& !fattime
)
1166 ft
->dwLowDateTime
= ft
->dwHighDateTime
= 0;
1168 DosDateTimeToFileTime(fatdate
, fattime
, ft
);
1171 /***********************************************************************
1172 * URLCache_CopyEntry (Internal)
1174 * Copies an entry from the cache index file to the Win32 structure
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
,
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
);
1216 lenUrl
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pUrlEntry
+ pUrlEntry
->url_off
, -1, NULL
, 0);
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
;
1228 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pUrlEntry
+ pUrlEntry
->url_off
, -1, (LPWSTR
)lpCacheEntryInfo
->lpszSourceUrlName
, lenUrl
+ 1);
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
)
1271 lenExtension
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pUrlEntry
+ pUrlEntry
->file_extension_off
, -1, NULL
, 0);
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
;
1280 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pUrlEntry
+ pUrlEntry
->file_extension_off
, -1, (LPWSTR
)lpCacheEntryInfo
->lpszSourceUrlName
, lenExtension
);
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.
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
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
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
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
;
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
))
1434 if (pHashEntry
->id
!= id
++)
1436 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry
->id
, id
);
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
);
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
1457 *ppHashEntry
= pHashElement
;
1465 static BOOL
URLCache_FindHashW(const urlcache_header
*pHeader
, LPCWSTR lpszUrl
, struct hash_entry
**ppHashEntry
)
1470 urlA
= heap_strdupWtoA(lpszUrl
);
1473 SetLastError(ERROR_OUTOFMEMORY
);
1477 ret
= URLCache_FindHash(pHeader
, urlA
, ppHashEntry
);
1482 /***********************************************************************
1483 * URLCache_HashEntrySetFlags (Internal)
1485 * Sets special bits in hash key
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.
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
;
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.
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
;
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
))
1542 pHashPrev
= pHashEntry
;
1544 if (pHashEntry
->id
!= id
++)
1546 ERR("not right hash table number (%d) expected %d\n", pHashEntry
->id
, id
);
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
);
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
)
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
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
;
1592 if ((error
= URLCache_FindFirstFreeEntry(pHeader
, 0x20, (entry_header
**)ppHash
)) != ERROR_SUCCESS
)
1595 dwOffset
= (BYTE
*)*ppHash
- (BYTE
*)pHeader
;
1598 pPrevHash
->next
= dwOffset
;
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.
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
)
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
);
1640 TRACE("hash table number %d found\n", *id
);
1646 /***********************************************************************
1647 * URLCache_EnumHashTableEntries (Internal)
1649 * Enumerates entries in a hash table and returns the next non-free entry.
1652 * TRUE if an entry was found
1653 * FALSE if the hash table is empty or there are no more entries to
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
)
1665 *ppHashEntry
= &pHashEntry
->hash_table
[*index
];
1666 TRACE("entry found %d\n", *index
);
1669 TRACE("no more entries (%d)\n", *index
);
1673 /***********************************************************************
1674 * URLCache_DeleteCacheDirectory (Internal)
1676 * Erase a directory containing an URL cache.
1679 * TRUE success, FALSE failure/aborted.
1682 static BOOL
URLCache_DeleteCacheDirectory(LPCWSTR lpszPath
)
1685 WCHAR path
[MAX_PATH
+ 1];
1686 SHFILEOPSTRUCTW shfos
;
1689 path_len
= strlenW(lpszPath
);
1690 if (path_len
>= MAX_PATH
)
1692 strcpyW(path
, lpszPath
);
1693 path
[path_len
+ 1] = 0; /* double-NUL-terminate path */
1696 shfos
.wFunc
= FO_DELETE
;
1699 shfos
.fFlags
= FOF_NOCONFIRMATION
;
1700 shfos
.fAnyOperationsAborted
= FALSE
;
1701 ret
= SHFileOperationW(&shfos
);
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
)
1715 ULARGE_INTEGER acc_time
, time
;
1717 if ((hash_entry
->key
& ((1<<HASHTABLE_FLAG_BITS
)-1)) != HASHTABLE_LOCK
)
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;
1739 /***********************************************************************
1740 * GetUrlCacheEntryInfoExA (WININET.@)
1743 BOOL WINAPI
GetUrlCacheEntryInfoExA(
1745 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1746 LPDWORD lpdwCacheEntryInfoBufSize
,
1748 LPDWORD lpdwReserved
,
1752 urlcache_header
*pHeader
;
1753 struct hash_entry
*pHashEntry
;
1754 const entry_header
*pEntry
;
1755 const entry_url
* pUrlEntry
;
1756 cache_container
*pContainer
;
1759 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1760 debugstr_a(lpszUrl
),
1762 lpdwCacheEntryInfoBufSize
,
1768 if ((lpszReserved
!= NULL
) ||
1769 (lpdwReserved
!= NULL
) ||
1770 (lpReserved
!= NULL
))
1772 ERR("Reserved value was not 0\n");
1773 SetLastError(ERROR_INVALID_PARAMETER
);
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
);
1786 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
1787 if (error
!= ERROR_SUCCESS
)
1789 SetLastError(error
);
1793 if (!(pHeader
= cache_container_lock_index(pContainer
)))
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
);
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
);
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
);
1826 if (lpdwCacheEntryInfoBufSize
)
1828 if (!lpCacheEntryInfo
)
1829 *lpdwCacheEntryInfoBufSize
= 0;
1831 error
= URLCache_CopyEntry(
1835 lpdwCacheEntryInfoBufSize
,
1838 if (error
!= ERROR_SUCCESS
)
1840 cache_container_unlock_index(pContainer
, pHeader
);
1841 SetLastError(error
);
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
);
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(
1885 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
1886 LPDWORD lpdwCacheEntryInfoBufSize
,
1887 LPWSTR lpszReserved
,
1888 LPDWORD lpdwReserved
,
1892 urlcache_header
*pHeader
;
1893 struct hash_entry
*pHashEntry
;
1894 const entry_header
*pEntry
;
1895 const entry_url
* pUrlEntry
;
1896 cache_container
*pContainer
;
1899 TRACE("(%s, %p, %p, %p, %p, %p, %x)\n",
1900 debugstr_w(lpszUrl
),
1902 lpdwCacheEntryInfoBufSize
,
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
);
1920 FIXME("ignoring unsupported flags: %x\n", dwFlags
);
1922 error
= URLCacheContainers_FindContainerW(lpszUrl
, &pContainer
);
1923 if (error
!= ERROR_SUCCESS
)
1925 SetLastError(error
);
1929 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
1930 if (error
!= ERROR_SUCCESS
)
1932 SetLastError(error
);
1936 if (!(pHeader
= cache_container_lock_index(pContainer
)))
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
);
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
);
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(
1970 (LPINTERNET_CACHE_ENTRY_INFOA
)lpCacheEntryInfo
,
1971 lpdwCacheEntryInfoBufSize
,
1973 TRUE
/* UNICODE */);
1974 if (error
!= ERROR_SUCCESS
)
1976 cache_container_unlock_index(pContainer
, pHeader
);
1977 SetLastError(error
);
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
);
1989 /***********************************************************************
1990 * SetUrlCacheEntryInfoA (WININET.@)
1992 BOOL WINAPI
SetUrlCacheEntryInfoA(
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
;
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
);
2012 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
2013 if (error
!= ERROR_SUCCESS
)
2015 SetLastError(error
);
2019 if (!(pHeader
= cache_container_lock_index(pContainer
)))
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
);
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
);
2039 URLCache_SetEntryInfo(
2040 (entry_url
*)pEntry
,
2041 (const INTERNET_CACHE_ENTRY_INFOW
*)lpCacheEntryInfo
,
2044 cache_container_unlock_index(pContainer
, pHeader
);
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
;
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
);
2069 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
2070 if (error
!= ERROR_SUCCESS
)
2072 SetLastError(error
);
2076 if (!(pHeader
= cache_container_lock_index(pContainer
)))
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
);
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
);
2096 URLCache_SetEntryInfo(
2097 (entry_url
*)pEntry
,
2101 cache_container_unlock_index(pContainer
, pHeader
);
2106 /***********************************************************************
2107 * RetrieveUrlCacheEntryFileA (WININET.@)
2110 BOOL WINAPI
RetrieveUrlCacheEntryFileA(
2111 IN LPCSTR lpszUrlName
,
2112 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
2113 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
,
2117 urlcache_header
*pHeader
;
2118 struct hash_entry
*pHashEntry
;
2119 entry_header
*pEntry
;
2120 entry_url
* pUrlEntry
;
2121 cache_container
*pContainer
;
2124 TRACE("(%s, %p, %p, 0x%08x)\n",
2125 debugstr_a(lpszUrlName
),
2127 lpdwCacheEntryInfoBufferSize
,
2130 if (!lpszUrlName
|| !lpdwCacheEntryInfoBufferSize
||
2131 (!lpCacheEntryInfo
&& *lpdwCacheEntryInfoBufferSize
))
2133 SetLastError(ERROR_INVALID_PARAMETER
);
2137 error
= URLCacheContainers_FindContainerA(lpszUrlName
, &pContainer
);
2138 if (error
!= ERROR_SUCCESS
)
2140 SetLastError(error
);
2144 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
2145 if (error
!= ERROR_SUCCESS
)
2147 SetLastError(error
);
2151 if (!(pHeader
= cache_container_lock_index(pContainer
)))
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
);
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
);
2171 pUrlEntry
= (entry_url
*)pEntry
;
2172 if (!pUrlEntry
->local_name_off
)
2174 cache_container_unlock_index(pContainer
, pHeader
);
2175 SetLastError(ERROR_INVALID_DATA
);
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
,
2186 if (error
!= ERROR_SUCCESS
)
2188 cache_container_unlock_index(pContainer
, pHeader
);
2189 SetLastError(error
);
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
);
2204 /***********************************************************************
2205 * RetrieveUrlCacheEntryFileW (WININET.@)
2208 BOOL WINAPI
RetrieveUrlCacheEntryFileW(
2209 IN LPCWSTR lpszUrlName
,
2210 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
2211 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
,
2215 urlcache_header
*pHeader
;
2216 struct hash_entry
*pHashEntry
;
2217 entry_header
*pEntry
;
2218 entry_url
* pUrlEntry
;
2219 cache_container
*pContainer
;
2222 TRACE("(%s, %p, %p, 0x%08x)\n",
2223 debugstr_w(lpszUrlName
),
2225 lpdwCacheEntryInfoBufferSize
,
2228 if (!lpszUrlName
|| !lpdwCacheEntryInfoBufferSize
||
2229 (!lpCacheEntryInfo
&& *lpdwCacheEntryInfoBufferSize
))
2231 SetLastError(ERROR_INVALID_PARAMETER
);
2235 error
= URLCacheContainers_FindContainerW(lpszUrlName
, &pContainer
);
2236 if (error
!= ERROR_SUCCESS
)
2238 SetLastError(error
);
2242 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
2243 if (error
!= ERROR_SUCCESS
)
2245 SetLastError(error
);
2249 if (!(pHeader
= cache_container_lock_index(pContainer
)))
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
);
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
);
2269 pUrlEntry
= (entry_url
*)pEntry
;
2270 if (!pUrlEntry
->local_name_off
)
2272 cache_container_unlock_index(pContainer
, pHeader
);
2273 SetLastError(ERROR_INVALID_DATA
);
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(
2284 (LPINTERNET_CACHE_ENTRY_INFOA
)lpCacheEntryInfo
,
2285 lpdwCacheEntryInfoBufferSize
,
2287 TRUE
/* UNICODE */);
2288 if (error
!= ERROR_SUCCESS
)
2290 cache_container_unlock_index(pContainer
, pHeader
);
2291 SetLastError(error
);
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
);
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
);
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
);
2330 if(!URLCache_DeleteFile(pContainer
, pHeader
, pUrlEntry
))
2332 URLCache_DeleteEntry(pHeader
, pEntry
);
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
);
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
);
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
;
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
);
2378 if(*hash_table_off
>= header
->size
) {
2379 *hash_table_off
= 0;
2383 hashtable_entry
= URLCache_HashEntryFromOffset(header
, *hash_table_off
);
2386 if(hashtable_entry
->header
.signature
!= HASH_SIGNATURE
) {
2387 *hash_table_off
= 0;
2392 if(*hash_table_entry
>= HASHTABLE_SIZE
) {
2393 *hash_table_off
= hashtable_entry
->next
;
2394 if(!*hash_table_off
) {
2395 *hash_table_off
= 0;
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
)++;
2411 (*hash_table_entry
)++;
2414 *hash_table_off
= 0;
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
;
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
)
2441 if(url_entry
->cache_entry_type
& STICKY_CACHE_ENTRY
)
2442 if(time
.QuadPart
< access_time
.QuadPart
+ (ULONGLONG
)url_entry
->exempt_delta
*FILETIME_SECOND
)
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)
2451 rating
+= url_entry
->hit_rate
;
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.
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)
2472 * TRUE success. FALSE failure.
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
);
2492 path_len
= strlenW(cache_path
);
2493 if(cache_path
[path_len
-1] == '\\')
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
]=='\\')))
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
))
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
;
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
]!='\\')))
2541 err
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2542 if(err
!= ERROR_SUCCESS
)
2545 header
= cache_container_lock_index(container
);
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
)
2556 delete_factor
= (cur_size
-desired_size
)*100/cur_size
;
2558 if(!delete_factor
) {
2559 cache_container_unlock_index(container
, header
);
2564 hash_table_entry
= 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");
2574 url_entry
= (entry_url
*)entry
;
2575 if(url_entry
->cache_entry_type
& filter
)
2578 rate
[rate_no
] = urlcache_rate_entry(url_entry
, &cur_time
);
2579 if(rate
[rate_no
] != -1)
2584 TRACE("nothing to delete\n");
2585 cache_container_unlock_index(container
, header
);
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
);
2596 while(urlcache_next_entry(header
, &hash_table_off
, &hash_table_entry
, &hash_entry
, &entry
)) {
2597 if(entry
->signature
!= URL_SIGNATURE
)
2600 url_entry
= (entry_url
*)entry
;
2601 if(url_entry
->cache_entry_type
& filter
)
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
)
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");
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
);
2631 /***********************************************************************
2632 * FreeUrlCacheSpaceA (WININET.@)
2634 * See FreeUrlCacheSpaceW.
2636 BOOL WINAPI
FreeUrlCacheSpaceA(LPCSTR lpszCachePath
, DWORD dwSize
, DWORD dwFilter
)
2639 LPWSTR path
= heap_strdupAtoW(lpszCachePath
);
2640 if (lpszCachePath
== NULL
|| path
!= NULL
)
2641 ret
= FreeUrlCacheSpaceW(path
, dwSize
, dwFilter
);
2646 /***********************************************************************
2647 * UnlockUrlCacheEntryFileA (WININET.@)
2650 BOOL WINAPI
UnlockUrlCacheEntryFileA(
2651 IN LPCSTR lpszUrlName
,
2655 urlcache_header
*pHeader
;
2656 struct hash_entry
*pHashEntry
;
2657 entry_header
*pEntry
;
2658 entry_url
* pUrlEntry
;
2659 cache_container
*pContainer
;
2662 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName
), dwReserved
);
2666 ERR("dwReserved != 0\n");
2667 SetLastError(ERROR_INVALID_PARAMETER
);
2671 error
= URLCacheContainers_FindContainerA(lpszUrlName
, &pContainer
);
2672 if (error
!= ERROR_SUCCESS
)
2674 SetLastError(error
);
2678 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
2679 if (error
!= ERROR_SUCCESS
)
2681 SetLastError(error
);
2685 if (!(pHeader
= cache_container_lock_index(pContainer
)))
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
);
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
);
2705 pUrlEntry
= (entry_url
*)pEntry
;
2707 if (pUrlEntry
->use_count
== 0)
2709 cache_container_unlock_index(pContainer
, pHeader
);
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
);
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
;
2738 TRACE("(%s, 0x%08x)\n", debugstr_w(lpszUrlName
), dwReserved
);
2742 ERR("dwReserved != 0\n");
2743 SetLastError(ERROR_INVALID_PARAMETER
);
2747 error
= URLCacheContainers_FindContainerW(lpszUrlName
, &pContainer
);
2748 if (error
!= ERROR_SUCCESS
)
2750 SetLastError(error
);
2754 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
2755 if (error
!= ERROR_SUCCESS
)
2757 SetLastError(error
);
2761 if (!(pHeader
= cache_container_lock_index(pContainer
)))
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
);
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
);
2781 pUrlEntry
= (entry_url
*)pEntry
;
2783 if (pUrlEntry
->use_count
== 0)
2785 cache_container_unlock_index(pContainer
, pHeader
);
2788 pUrlEntry
->use_count
--;
2789 if (!pUrlEntry
->use_count
)
2790 URLCache_HashEntrySetFlags(pHashEntry
, HASHTABLE_URL
);
2792 cache_container_unlock_index(pContainer
, pHeader
);
2797 /***********************************************************************
2798 * CreateUrlCacheEntryA (WININET.@)
2801 BOOL WINAPI
CreateUrlCacheEntryA(
2802 IN LPCSTR lpszUrlName
,
2803 IN DWORD dwExpectedFileSize
,
2804 IN LPCSTR lpszFileExtension
,
2805 OUT LPSTR lpszFileName
,
2810 WCHAR
*file_extension
= NULL
;
2811 WCHAR file_name
[MAX_PATH
];
2812 BOOL bSuccess
= FALSE
;
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
)
2830 dwError
= GetLastError();
2835 dwError
= GetLastError();
2837 heap_free(file_extension
);
2841 dwError
= GetLastError();
2843 heap_free(url_name
);
2844 if (!bSuccess
) SetLastError(dwError
);
2848 /***********************************************************************
2849 * CreateUrlCacheEntryW (WININET.@)
2852 BOOL WINAPI
CreateUrlCacheEntryW(
2853 IN LPCWSTR lpszUrlName
,
2854 IN DWORD dwExpectedFileSize
,
2855 IN LPCWSTR lpszFileExtension
,
2856 OUT LPWSTR lpszFileName
,
2860 cache_container
*pContainer
;
2861 urlcache_header
*pHeader
;
2862 CHAR szFile
[MAX_PATH
];
2863 WCHAR szExtension
[MAX_PATH
];
2864 LPCWSTR lpszUrlPart
;
2866 LPCWSTR lpszFileNameExtension
;
2867 LPWSTR lpszFileNameNoPath
;
2869 int countnoextension
;
2872 BOOL bFound
= FALSE
;
2873 BOOL generate_name
= FALSE
;
2879 static const WCHAR szWWW
[] = {'w','w','w',0};
2881 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2882 debugstr_w(lpszUrlName
),
2884 debugstr_w(lpszFileExtension
),
2889 FIXME("dwReserved 0x%08x\n", dwReserved
);
2891 lpszUrlEnd
= lpszUrlName
+ strlenW(lpszUrlName
);
2893 if (((lpszUrlEnd
- lpszUrlName
) > 1) && (*(lpszUrlEnd
- 1) == '/' || *(lpszUrlEnd
- 1) == '\\'))
2896 lpszUrlPart
= memchrW(lpszUrlName
, '?', lpszUrlEnd
- lpszUrlName
);
2898 lpszUrlPart
= memchrW(lpszUrlName
, '#', lpszUrlEnd
- lpszUrlName
);
2900 lpszUrlEnd
= lpszUrlPart
;
2902 for (lpszUrlPart
= lpszUrlEnd
;
2903 (lpszUrlPart
>= lpszUrlName
);
2906 if ((*lpszUrlPart
== '/' || *lpszUrlPart
== '\\') && ((lpszUrlEnd
- lpszUrlPart
) > 1))
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
);
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
));
2936 generate_name
= TRUE
;
2940 error
= URLCacheContainers_FindContainerW(lpszUrlName
, &pContainer
);
2941 if (error
!= ERROR_SUCCESS
)
2943 SetLastError(error
);
2947 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
2948 if (error
!= ERROR_SUCCESS
)
2950 SetLastError(error
);
2954 if (!(pHeader
= cache_container_lock_index(pContainer
)))
2957 if(pHeader
->dirs_no
)
2958 CacheDir
= (BYTE
)(rand() % pHeader
->dirs_no
);
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
);
2971 cache_container_unlock_index(pContainer
, pHeader
);
2973 for (lpszFileNameNoPath
= lpszFileName
+ lBufferSize
/ sizeof(WCHAR
) - 2;
2974 lpszFileNameNoPath
>= lpszFileName
;
2975 --lpszFileNameNoPath
)
2977 if (*lpszFileNameNoPath
== '/' || *lpszFileNameNoPath
== '\\')
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};
2998 wsprintfW(lpszFileNameNoPath
+ countnoextension
, szFormat
, i
, szExtension
);
2999 for (p
= lpszFileNameNoPath
+ 1; *p
; p
++)
3005 case '/': case '\\':
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
)
3023 /* Try to generate random name */
3024 GetSystemTimeAsFileTime(&ft
);
3025 strcpyW(lpszFileNameNoPath
+countnoextension
+8, szExtension
);
3027 for(i
=0; i
<255; i
++)
3030 ULONGLONG n
= ft
.dwHighDateTime
;
3032 n
+= ft
.dwLowDateTime
;
3033 n
^= (ULONGLONG
)i
<<48;
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
)
3051 WARN("Could not find a unique filename\n");
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
;
3094 char achFile
[MAX_PATH
];
3095 LPSTR lpszUrlNameA
= NULL
;
3096 LPSTR lpszFileExtensionA
= NULL
;
3097 char *pchLocalFileName
= 0;
3099 DWORD exempt_delta
= 0;
3102 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3103 debugstr_w(lpszUrlName
),
3104 debugstr_w(lpszLocalFileName
),
3108 debugstr_w(lpszFileExtension
),
3109 debugstr_w(lpszOriginalUrl
));
3111 if (CacheEntryType
& STICKY_CACHE_ENTRY
&& !lpszLocalFileName
)
3113 SetLastError(ERROR_INVALID_PARAMETER
);
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
))
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
);
3135 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
3136 if (error
!= ERROR_SUCCESS
)
3138 SetLastError(error
);
3142 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3145 lpszUrlNameA
= heap_strdupWtoA(lpszUrlName
);
3148 error
= GetLastError();
3152 if (lpszFileExtension
&& !(lpszFileExtensionA
= heap_strdupWtoA(lpszFileExtension
)))
3154 error
= GetLastError();
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
;
3168 hit_rate
= pUrlEntry
->hit_rate
;
3169 exempt_delta
= pUrlEntry
->exempt_delta
;
3170 DeleteUrlCacheEntryInternal(pContainer
, pHeader
, pHashEntry
);
3173 if (pHeader
->dirs_no
)
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
;
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
))
3208 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName
));
3209 error
= ERROR_INVALID_PARAMETER
;
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);
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
)
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
);
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
);
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
);
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
;
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();
3323 cache_container_unlock_index(pContainer
, pHeader
);
3324 heap_free(lpszUrlNameA
);
3325 heap_free(lpszFileExtensionA
);
3327 if (error
== ERROR_SUCCESS
)
3331 SetLastError(error
);
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
),
3364 debugstr_a(lpszFileExtension
),
3365 debugstr_a(lpszOriginalUrl
));
3367 url_name
= heap_strdupAtoW(lpszUrlName
);
3371 if (lpszLocalFileName
)
3373 local_file_name
= heap_strdupAtoW(lpszLocalFileName
);
3374 if (!local_file_name
)
3377 if (lpszFileExtension
)
3379 file_extension
= heap_strdupAtoW(lpszFileExtension
);
3380 if (!file_extension
)
3383 if (lpszOriginalUrl
)
3385 original_url
= heap_strdupAtoW(lpszOriginalUrl
);
3390 bSuccess
= CommitUrlCacheEntryInternal(url_name
, local_file_name
, ExpireTime
, LastModifiedTime
,
3391 CacheEntryType
, lpHeaderInfo
, dwHeaderSize
,
3392 file_extension
, original_url
);
3395 heap_free(original_url
);
3396 heap_free(file_extension
);
3397 heap_free(local_file_name
);
3398 heap_free(url_name
);
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
3419 BOOL bSuccess
= FALSE
;
3421 CHAR
*header_info
= NULL
;
3423 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3424 debugstr_w(lpszUrlName
),
3425 debugstr_w(lpszLocalFileName
),
3429 debugstr_w(lpszFileExtension
),
3430 debugstr_w(lpszOriginalUrl
));
3432 if (!lpHeaderInfo
|| (header_info
= heap_strdupWtoA(lpHeaderInfo
)))
3435 len
= strlen(header_info
);
3436 if (CommitUrlCacheEntryInternal(lpszUrlName
, lpszLocalFileName
, ExpireTime
, LastModifiedTime
,
3437 CacheEntryType
, (LPBYTE
)header_info
, len
, lpszFileExtension
, lpszOriginalUrl
))
3443 dwError
= GetLastError();
3447 heap_free(header_info
);
3449 SetLastError(dwError
);
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
,
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
);
3477 if (IsBadReadPtr(pStream
, sizeof(*pStream
)) || IsBadStringPtrA(pStream
->url
, INTERNET_MAX_URL_LENGTH
))
3479 SetLastError(ERROR_INVALID_HANDLE
);
3483 if (SetFilePointer(pStream
->file
, dwLocation
, NULL
, FILE_CURRENT
) == INVALID_SET_FILE_POINTER
)
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
,
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
;
3509 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName
), lpCacheEntryInfo
,
3510 lpdwCacheEntryInfoBufferSize
, fRandomRead
, dwReserved
);
3512 if (!RetrieveUrlCacheEntryFileA(lpszUrlName
,
3514 lpdwCacheEntryInfoBufferSize
,
3520 hFile
= CreateFileA(lpCacheEntryInfo
->lpszLocalFileName
,
3525 fRandomRead
? FILE_FLAG_RANDOM_ACCESS
: 0,
3527 if (hFile
== INVALID_HANDLE_VALUE
)
3530 /* allocate handle storage space */
3531 pStream
= heap_alloc(sizeof(stream_handle
) + strlen(lpszUrlName
) * sizeof(CHAR
));
3535 SetLastError(ERROR_OUTOFMEMORY
);
3539 pStream
->file
= hFile
;
3540 strcpy(pStream
->url
, lpszUrlName
);
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
,
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
;
3567 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName
), lpCacheEntryInfo
,
3568 lpdwCacheEntryInfoBufferSize
, fRandomRead
, dwReserved
);
3570 if (!RetrieveUrlCacheEntryFileW(lpszUrlName
,
3572 lpdwCacheEntryInfoBufferSize
,
3578 hFile
= CreateFileW(lpCacheEntryInfo
->lpszLocalFileName
,
3583 fRandomRead
? FILE_FLAG_RANDOM_ACCESS
: 0,
3585 if (hFile
== INVALID_HANDLE_VALUE
)
3588 /* allocate handle storage space */
3589 size
= sizeof(stream_handle
);
3590 url_len
= WideCharToMultiByte(CP_ACP
, 0, lpszUrlName
, -1, NULL
, 0, NULL
, NULL
);
3592 pStream
= heap_alloc(size
);
3596 SetLastError(ERROR_OUTOFMEMORY
);
3600 pStream
->file
= hFile
;
3601 WideCharToMultiByte(CP_ACP
, 0, lpszUrlName
, -1, pStream
->url
, url_len
, NULL
, NULL
);
3605 /***********************************************************************
3606 * UnlockUrlCacheEntryStream (WININET.@)
3609 BOOL WINAPI
UnlockUrlCacheEntryStream(
3610 IN HANDLE hUrlCacheStream
,
3614 stream_handle
*pStream
= (stream_handle
*)hUrlCacheStream
;
3616 if (dwReserved
!= 0)
3618 ERR("dwReserved != 0\n");
3619 SetLastError(ERROR_INVALID_PARAMETER
);
3623 if (IsBadReadPtr(pStream
, sizeof(*pStream
)) || IsBadStringPtrA(pStream
->url
, INTERNET_MAX_URL_LENGTH
))
3625 SetLastError(ERROR_INVALID_HANDLE
);
3629 if (!UnlockUrlCacheEntryFileA(pStream
->url
, 0))
3632 CloseHandle(pStream
->file
);
3638 /***********************************************************************
3639 * DeleteUrlCacheEntryA (WININET.@)
3642 BOOL WINAPI
DeleteUrlCacheEntryA(LPCSTR lpszUrlName
)
3644 cache_container
*pContainer
;
3645 urlcache_header
*pHeader
;
3646 struct hash_entry
*pHashEntry
;
3650 TRACE("(%s)\n", debugstr_a(lpszUrlName
));
3652 error
= URLCacheContainers_FindContainerA(lpszUrlName
, &pContainer
);
3653 if (error
!= ERROR_SUCCESS
)
3655 SetLastError(error
);
3659 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
3660 if (error
!= ERROR_SUCCESS
)
3662 SetLastError(error
);
3666 if (!(pHeader
= cache_container_lock_index(pContainer
)))
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
);
3677 ret
= DeleteUrlCacheEntryInternal(pContainer
, pHeader
, pHashEntry
);
3679 cache_container_unlock_index(pContainer
, pHeader
);
3684 /***********************************************************************
3685 * DeleteUrlCacheEntryW (WININET.@)
3688 BOOL WINAPI
DeleteUrlCacheEntryW(LPCWSTR lpszUrlName
)
3690 cache_container
*pContainer
;
3691 urlcache_header
*pHeader
;
3692 struct hash_entry
*pHashEntry
;
3697 TRACE("(%s)\n", debugstr_w(lpszUrlName
));
3699 urlA
= heap_strdupWtoA(lpszUrlName
);
3702 SetLastError(ERROR_OUTOFMEMORY
);
3706 error
= URLCacheContainers_FindContainerW(lpszUrlName
, &pContainer
);
3707 if (error
!= ERROR_SUCCESS
)
3710 SetLastError(error
);
3714 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
3715 if (error
!= ERROR_SUCCESS
)
3718 SetLastError(error
);
3722 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3728 if (!URLCache_FindHash(pHeader
, urlA
, &pHashEntry
))
3730 cache_container_unlock_index(pContainer
, pHeader
);
3731 TRACE("entry %s not found!\n", debugstr_a(urlA
));
3733 SetLastError(ERROR_FILE_NOT_FOUND
);
3737 ret
= DeleteUrlCacheEntryInternal(pContainer
, pHeader
, pHashEntry
);
3739 cache_container_unlock_index(pContainer
, pHeader
);
3744 BOOL WINAPI
DeleteUrlCacheContainerA(DWORD d1
, DWORD d2
)
3746 FIXME("(0x%08x, 0x%08x) stub\n", d1
, d2
);
3750 BOOL WINAPI
DeleteUrlCacheContainerW(DWORD d1
, DWORD d2
)
3752 FIXME("(0x%08x, 0x%08x) stub\n", d1
, d2
);
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
);
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
);
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
);
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
);
3796 /***********************************************************************
3797 * FindNextUrlCacheContainerA (WININET.@)
3799 BOOL WINAPI
FindNextUrlCacheContainerA( HANDLE handle
, LPVOID p1
, LPVOID p2
)
3801 FIXME("(%p, %p, %p) stub\n", handle
, p1
, p2
);
3805 /***********************************************************************
3806 * FindNextUrlCacheContainerW (WININET.@)
3808 BOOL WINAPI
FindNextUrlCacheContainerW( HANDLE handle
, LPVOID p1
, LPVOID p2
)
3810 FIXME("(%p, %p, %p) stub\n", handle
, p1
, p2
);
3814 HANDLE WINAPI
FindFirstUrlCacheEntryExA(
3815 LPCSTR lpszUrlSearchPattern
,
3819 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
,
3820 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3822 LPDWORD pcbReserved2
,
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
);
3833 HANDLE WINAPI
FindFirstUrlCacheEntryExW(
3834 LPCWSTR lpszUrlSearchPattern
,
3838 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
,
3839 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3841 LPDWORD pcbReserved2
,
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
);
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
));
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
);
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
);
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
));
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
);
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
);
3927 return pEntryHandle
;
3930 static BOOL
FindNextUrlCacheEntryInternal(
3932 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo
,
3933 LPDWORD lpdwNextCacheEntryInfoBufferSize
,
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
);
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
;
3952 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
3953 if (error
!= ERROR_SUCCESS
)
3955 SetLastError(error
);
3959 if (!(pHeader
= cache_container_lock_index(pContainer
)))
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
)
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(
3985 lpNextCacheEntryInfo
,
3986 lpdwNextCacheEntryInfoBufferSize
,
3989 if (error
!= ERROR_SUCCESS
)
3991 cache_container_unlock_index(pContainer
, pHeader
);
3992 SetLastError(error
);
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
);
4006 cache_container_unlock_index(pContainer
, pHeader
);
4009 SetLastError(ERROR_NO_MORE_ITEMS
);
4013 /***********************************************************************
4014 * FindNextUrlCacheEntryA (WININET.@)
4016 BOOL WINAPI
FindNextUrlCacheEntryA(
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(
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
);
4058 pEntryHandle
->magic
= 0;
4059 heap_free(pEntryHandle
->url_search_pattern
);
4060 heap_free(pEntryHandle
);
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
);
4072 BOOL WINAPI
FindNextUrlCacheEntryExA(
4074 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
,
4075 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
4077 LPDWORD pcbReserved2
,
4081 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
,
4082 lpReserved
, pcbReserved2
, lpReserved3
);
4086 BOOL WINAPI
FindNextUrlCacheEntryExW(
4088 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
,
4089 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
4091 LPDWORD pcbReserved2
,
4095 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
,
4096 lpReserved
, pcbReserved2
, lpReserved3
);
4100 BOOL WINAPI
FindNextUrlCacheGroup( HANDLE hFind
, GROUPID
* lpGroupId
, LPVOID lpReserved
)
4102 FIXME("(%p, %p, %p) stub\n", hFind
, lpGroupId
, lpReserved
);
4106 /***********************************************************************
4107 * CreateUrlCacheGroup (WININET.@)
4110 INTERNETAPI GROUPID WINAPI
CreateUrlCacheGroup(DWORD dwFlags
, LPVOID lpReserved
)
4112 FIXME("(0x%08x, %p): stub\n", dwFlags
, lpReserved
);
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
);
4127 /***********************************************************************
4128 * DeleteWpadCacheForNetworks (WININET.@)
4129 * Undocumented, added in IE8
4131 BOOL WINAPI
DeleteWpadCacheForNetworks(DWORD unk1
)
4133 FIXME("(%d) stub\n", unk1
);
4137 /***********************************************************************
4138 * SetUrlCacheEntryGroupA (WININET.@)
4141 BOOL WINAPI
SetUrlCacheEntryGroupA(LPCSTR lpszUrlName
, DWORD dwFlags
,
4142 GROUPID GroupId
, LPBYTE pbGroupAttributes
, DWORD cbGroupAttributes
,
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
);
4152 /***********************************************************************
4153 * SetUrlCacheEntryGroupW (WININET.@)
4156 BOOL WINAPI
SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName
, DWORD dwFlags
,
4157 GROUPID GroupId
, LPBYTE pbGroupAttributes
, DWORD cbGroupAttributes
,
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
);
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
);
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
);
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
);
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
);
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
);
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
);
4223 BOOL WINAPI
SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo
, DWORD dwFieldControl
)
4225 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo
, dwFieldControl
);
4229 BOOL WINAPI
SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo
, DWORD dwFieldControl
)
4231 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo
, dwFieldControl
);
4235 /***********************************************************************
4236 * DeleteIE3Cache (WININET.@)
4238 * Deletes the files used by the IE3 URL caching system.
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
);
4252 static BOOL
IsUrlCacheEntryExpiredInternal(const entry_url
*pUrlEntry
,
4253 FILETIME
*pftLastModified
)
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
)
4266 ret
= CompareFileTime(&expired
, &now
) < 0;
4270 /***********************************************************************
4271 * IsUrlCacheEntryExpiredA (WININET.@)
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
;
4287 TRACE("(%s, %08x, %p)\n", debugstr_a(url
), dwFlags
, pftLastModified
);
4289 if (!url
|| !pftLastModified
)
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
));
4301 if (cache_container_open_index(pContainer
, MIN_BLOCK_NO
))
4303 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
4307 if (!(pHeader
= cache_container_lock_index(pContainer
)))
4309 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
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
);
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
)));
4330 pUrlEntry
= (const entry_url
*)pEntry
;
4331 expired
= IsUrlCacheEntryExpiredInternal(pUrlEntry
, pftLastModified
);
4333 cache_container_unlock_index(pContainer
, pHeader
);
4338 /***********************************************************************
4339 * IsUrlCacheEntryExpiredW (WININET.@)
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
;
4355 TRACE("(%s, %08x, %p)\n", debugstr_w(url
), dwFlags
, pftLastModified
);
4357 if (!url
|| !pftLastModified
)
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
));
4369 if (cache_container_open_index(pContainer
, MIN_BLOCK_NO
))
4371 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
4375 if (!(pHeader
= cache_container_lock_index(pContainer
)))
4377 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
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
));
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
));
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
)));
4406 pUrlEntry
= (const entry_url
*)pEntry
;
4407 expired
= IsUrlCacheEntryExpiredInternal(pUrlEntry
, pftLastModified
);
4409 cache_container_unlock_index(pContainer
, pHeader
);
4414 /***********************************************************************
4415 * GetDiskInfoA (WININET.@)
4417 BOOL WINAPI
GetDiskInfoA(PCSTR path
, PDWORD cluster_size
, PDWORDLONG free
, PDWORDLONG total
)
4420 ULARGE_INTEGER bytes_free
, bytes_total
;
4422 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path
), cluster_size
, free
, total
);
4426 SetLastError(ERROR_INVALID_PARAMETER
);
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
;
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
);
4448 /***********************************************************************
4449 * IncrementUrlCacheHeaderData (WININET.@)
4451 BOOL WINAPI
IncrementUrlCacheHeaderData(DWORD index
, LPDWORD data
)
4453 FIXME("(%u, %p)\n", index
, data
);
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
);
4467 BOOL
init_urlcache(void)
4469 dll_unload_event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
4470 if(!dll_unload_event
)
4473 free_cache_running
= CreateSemaphoreW(NULL
, 1, 1, NULL
);
4474 if(!free_cache_running
) {
4475 CloseHandle(dll_unload_event
);
4479 URLCacheContainers_CreateDefaults();
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)