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";
67 #define CHAR_BIT (8 * sizeof(CHAR))
70 #define ENTRY_START_OFFSET 0x4000
72 #define MAX_DIR_NO 0x20
74 #define HASHTABLE_SIZE 448
75 #define HASHTABLE_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
76 #define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
77 #define ALLOCATION_TABLE_OFFSET 0x250
78 #define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
79 #define MIN_BLOCK_NO 0x80
80 #define MAX_BLOCK_NO (ALLOCATION_TABLE_SIZE * CHAR_BIT)
81 #define FILE_SIZE(blocks) ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
83 #define HASHTABLE_URL 0
84 #define HASHTABLE_DEL 1
85 #define HASHTABLE_LOCK 2
86 #define HASHTABLE_FREE 3
87 #define HASHTABLE_REDR 5
88 #define HASHTABLE_FLAG_BITS 6
90 #define PENDING_DELETE_CACHE_ENTRY 0x00400000
91 #define INSTALLED_CACHE_ENTRY 0x10000000
92 #define GET_INSTALLED_ENTRY 0x200
93 #define CACHE_CONTAINER_NO_SUBDIR 0xFE
95 #define CACHE_HEADER_DATA_ROOT_LEAK_OFFSET 0x16
97 #define FILETIME_SECOND 10000000
99 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
100 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
101 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
102 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
103 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
105 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
107 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
112 DWORD blocks_used
; /* number of 128byte blocks used by this entry */
118 FILETIME modification_time
;
119 FILETIME access_time
;
120 WORD expire_date
; /* expire date in dos format */
121 WORD expire_time
; /* expire time in dos format */
122 DWORD unk1
; /* usually zero */
123 ULARGE_INTEGER size
; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
124 DWORD unk2
; /* usually zero */
125 DWORD exempt_delta
; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
126 DWORD unk3
; /* usually 0x60 */
127 DWORD url_off
; /* offset of start of url from start of entry */
128 BYTE cache_dir
; /* index of cache directory this url is stored in */
129 BYTE unk4
; /* usually zero */
130 WORD unk5
; /* usually 0x1010 */
131 DWORD local_name_off
; /* offset of start of local filename from start of entry */
132 DWORD cache_entry_type
; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
133 DWORD header_info_off
; /* offset of start of header info from start of entry */
134 DWORD header_info_size
;
135 DWORD file_extension_off
; /* offset of start of file extension from start of entry */
136 WORD sync_date
; /* last sync date in dos format */
137 WORD sync_time
; /* last sync time in dos format */
138 DWORD hit_rate
; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
139 DWORD use_count
; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
142 DWORD unk7
; /* usually zero */
143 DWORD unk8
; /* usually zero */
144 /* packing to dword align start of next field */
145 /* CHAR szSourceUrlName[]; (url) */
146 /* packing to dword align start of next field */
147 /* CHAR szLocalFileName[]; (local file name excluding path) */
148 /* packing to dword align start of next field */
149 /* CHAR szHeaderInfo[]; (header info) */
163 struct hash_entry hash_table
[HASHTABLE_SIZE
];
170 DWORD hash_table_off
;
171 DWORD capacity_in_blocks
;
174 ULARGE_INTEGER cache_limit
;
175 ULARGE_INTEGER cache_usage
;
176 ULARGE_INTEGER exempt_usage
;
178 struct _directory_data
181 char name
[DIR_LENGTH
];
182 } directory_data
[MAX_DIR_NO
];
184 BYTE allocation_table
[ALLOCATION_TABLE_SIZE
];
195 struct list entry
; /* part of a list */
196 char *cache_prefix
; /* string that has to be prefixed for this container to be used */
197 LPWSTR path
; /* path to url container directory */
198 HANDLE mapping
; /* handle of file mapping */
199 DWORD file_size
; /* size of file when mapping was opened */
200 HANDLE mutex
; /* handle of mutex */
201 DWORD default_entry_type
;
207 char *url_search_pattern
;
209 DWORD hash_table_idx
;
210 DWORD hash_entry_idx
;
213 /* List of all containers available */
214 static struct list UrlContainers
= LIST_INIT(UrlContainers
);
216 static inline char *heap_strdupWtoUTF8(LPCWSTR str
)
221 DWORD size
= WideCharToMultiByte(CP_UTF8
, 0, str
, -1, NULL
, 0, NULL
, NULL
);
222 ret
= heap_alloc(size
);
224 WideCharToMultiByte(CP_UTF8
, 0, str
, -1, ret
, size
, NULL
, NULL
);
230 /***********************************************************************
231 * urlcache_block_is_free (Internal)
233 * Is the specified block number free?
240 static inline BYTE
urlcache_block_is_free(BYTE
*allocation_table
, DWORD block_number
)
242 BYTE mask
= 1 << (block_number
%CHAR_BIT
);
243 return (allocation_table
[block_number
/CHAR_BIT
] & mask
) == 0;
246 /***********************************************************************
247 * urlcache_block_free (Internal)
249 * Marks the specified block as free
252 * this function is not updating used blocks count
258 static inline void urlcache_block_free(BYTE
*allocation_table
, DWORD block_number
)
260 BYTE mask
= ~(1 << (block_number
%CHAR_BIT
));
261 allocation_table
[block_number
/CHAR_BIT
] &= mask
;
264 /***********************************************************************
265 * urlcache_block_alloc (Internal)
267 * Marks the specified block as allocated
270 * this function is not updating used blocks count
276 static inline void urlcache_block_alloc(BYTE
*allocation_table
, DWORD block_number
)
278 BYTE mask
= 1 << (block_number
%CHAR_BIT
);
279 allocation_table
[block_number
/CHAR_BIT
] |= mask
;
282 /***********************************************************************
283 * urlcache_entry_alloc (Internal)
285 * Finds and allocates the first block of free space big enough and
286 * sets entry to point to it.
289 * ERROR_SUCCESS when free memory block was found
290 * Any other Win32 error code if the entry could not be added
293 static DWORD
urlcache_entry_alloc(urlcache_header
*header
, DWORD blocks_needed
, entry_header
**entry
)
295 DWORD block
, block_size
;
297 for(block
=0; block
<header
->capacity_in_blocks
; block
+=block_size
+1)
300 while(block_size
<blocks_needed
&& block_size
+block
<header
->capacity_in_blocks
301 && urlcache_block_is_free(header
->allocation_table
, block
+block_size
))
304 if(block_size
== blocks_needed
)
308 TRACE("Found free blocks starting at no. %d (0x%x)\n", block
, ENTRY_START_OFFSET
+block
*BLOCKSIZE
);
310 for(index
=0; index
<blocks_needed
; index
++)
311 urlcache_block_alloc(header
->allocation_table
, block
+index
);
313 *entry
= (entry_header
*)((BYTE
*)header
+ENTRY_START_OFFSET
+block
*BLOCKSIZE
);
314 for(index
=0; index
<blocks_needed
*BLOCKSIZE
/sizeof(DWORD
); index
++)
315 ((DWORD
*)*entry
)[index
] = 0xdeadbeef;
316 (*entry
)->blocks_used
= blocks_needed
;
318 header
->blocks_in_use
+= blocks_needed
;
319 return ERROR_SUCCESS
;
323 return ERROR_HANDLE_DISK_FULL
;
326 /***********************************************************************
327 * urlcache_entry_free (Internal)
329 * Deletes the specified entry and frees the space allocated to it
332 * TRUE if it succeeded
336 static BOOL
urlcache_entry_free(urlcache_header
*header
, entry_header
*entry
)
338 DWORD start_block
, block
;
340 /* update allocation table */
341 start_block
= ((DWORD
)((BYTE
*)entry
- (BYTE
*)header
) - ENTRY_START_OFFSET
) / BLOCKSIZE
;
342 for(block
= start_block
; block
< start_block
+entry
->blocks_used
; block
++)
343 urlcache_block_free(header
->allocation_table
, block
);
345 header
->blocks_in_use
-= entry
->blocks_used
;
349 /***********************************************************************
350 * urlcache_create_hash_table (Internal)
352 * Creates a new hash table in free space and adds it to the chain of existing
356 * ERROR_SUCCESS if the hash table was created
357 * ERROR_DISK_FULL if the hash table could not be created
360 static DWORD
urlcache_create_hash_table(urlcache_header
*header
, entry_hash_table
*hash_table_prev
, entry_hash_table
**hash_table
)
362 DWORD dwOffset
, error
;
365 if((error
= urlcache_entry_alloc(header
, 0x20, (entry_header
**)hash_table
)) != ERROR_SUCCESS
)
368 dwOffset
= (BYTE
*)*hash_table
-(BYTE
*)header
;
371 hash_table_prev
->next
= dwOffset
;
373 header
->hash_table_off
= dwOffset
;
375 (*hash_table
)->header
.signature
= HASH_SIGNATURE
;
376 (*hash_table
)->next
= 0;
377 (*hash_table
)->id
= hash_table_prev
? hash_table_prev
->id
+1 : 0;
378 for(i
= 0; i
< HASHTABLE_SIZE
; i
++) {
379 (*hash_table
)->hash_table
[i
].offset
= HASHTABLE_FREE
;
380 (*hash_table
)->hash_table
[i
].key
= HASHTABLE_FREE
;
382 return ERROR_SUCCESS
;
385 /***********************************************************************
386 * cache_container_create_object_name (Internal)
388 * Converts a path to a name suitable for use as a Win32 object name.
389 * Replaces '\\' characters in-place with the specified character
390 * (usually '_' or '!')
396 static void cache_container_create_object_name(LPWSTR lpszPath
, WCHAR replace
)
398 for (; *lpszPath
; lpszPath
++)
400 if (*lpszPath
== '\\')
405 /* Caller must hold container lock */
406 static HANDLE
cache_container_map_index(HANDLE file
, const WCHAR
*path
, DWORD size
, BOOL
*validate
)
408 static const WCHAR mapping_name_format
[]
409 = {'%','s','i','n','d','e','x','.','d','a','t','_','%','l','u',0};
410 WCHAR mapping_name
[MAX_PATH
];
413 wsprintfW(mapping_name
, mapping_name_format
, path
, size
);
414 cache_container_create_object_name(mapping_name
, '_');
416 mapping
= OpenFileMappingW(FILE_MAP_WRITE
, FALSE
, mapping_name
);
418 if(validate
) *validate
= FALSE
;
422 if(validate
) *validate
= TRUE
;
423 return CreateFileMappingW(file
, NULL
, PAGE_READWRITE
, 0, 0, mapping_name
);
426 /* Caller must hold container lock */
427 static DWORD
cache_container_set_size(cache_container
*container
, HANDLE file
, DWORD blocks_no
)
429 static const WCHAR cache_content_key
[] = {'S','o','f','t','w','a','r','e','\\',
430 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
431 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
432 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s','\\',
433 'C','a','c','h','e','\\','C','o','n','t','e','n','t',0};
434 static const WCHAR cache_limit
[] = {'C','a','c','h','e','L','i','m','i','t',0};
436 DWORD file_size
= FILE_SIZE(blocks_no
);
437 WCHAR dir_path
[MAX_PATH
], *dir_name
;
438 entry_hash_table
*hashtable_entry
;
439 urlcache_header
*header
;
445 if(SetFilePointer(file
, file_size
, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
446 return GetLastError();
448 if(!SetEndOfFile(file
))
449 return GetLastError();
451 mapping
= cache_container_map_index(file
, container
->path
, file_size
, NULL
);
453 return GetLastError();
455 header
= MapViewOfFile(mapping
, FILE_MAP_WRITE
, 0, 0, 0);
457 CloseHandle(mapping
);
458 return GetLastError();
461 if(blocks_no
!= MIN_BLOCK_NO
) {
462 if(file_size
> header
->size
)
463 memset((char*)header
+header
->size
, 0, file_size
-header
->size
);
464 header
->size
= file_size
;
465 header
->capacity_in_blocks
= blocks_no
;
467 UnmapViewOfFile(header
);
468 CloseHandle(container
->mapping
);
469 container
->mapping
= mapping
;
470 container
->file_size
= file_size
;
471 return ERROR_SUCCESS
;
474 memset(header
, 0, file_size
);
475 /* First set some constants and defaults in the header */
476 memcpy(header
->signature
, urlcache_ver_prefix
, sizeof(urlcache_ver_prefix
)-1);
477 memcpy(header
->signature
+sizeof(urlcache_ver_prefix
)-1, urlcache_ver
, sizeof(urlcache_ver
)-1);
478 header
->size
= file_size
;
479 header
->capacity_in_blocks
= blocks_no
;
480 /* 127MB - taken from default for Windows 2000 */
481 header
->cache_limit
.QuadPart
= 0x07ff5400;
482 /* Copied from a Windows 2000 cache index */
483 header
->dirs_no
= container
->default_entry_type
==NORMAL_CACHE_ENTRY
? 4 : 0;
485 /* If the registry has a cache size set, use the registry value */
486 if(RegOpenKeyW(HKEY_CURRENT_USER
, cache_content_key
, &key
) == ERROR_SUCCESS
) {
487 DWORD dw
, len
= sizeof(dw
), keytype
;
489 if(RegQueryValueExW(key
, cache_limit
, NULL
, &keytype
, (BYTE
*)&dw
, &len
) == ERROR_SUCCESS
&&
490 keytype
== REG_DWORD
)
491 header
->cache_limit
.QuadPart
= (ULONGLONG
)dw
* 1024;
495 urlcache_create_hash_table(header
, NULL
, &hashtable_entry
);
497 /* Last step - create the directories */
498 strcpyW(dir_path
, container
->path
);
499 dir_name
= dir_path
+ strlenW(dir_path
);
502 GetSystemTimeAsFileTime(&ft
);
504 for(i
=0; i
<header
->dirs_no
; ++i
) {
505 header
->directory_data
[i
].files_no
= 0;
507 ULONGLONG n
= ft
.dwHighDateTime
;
510 /* Generate a file name to attempt to create.
511 * This algorithm will create what will appear
512 * to be random and unrelated directory names
513 * of up to 9 characters in length.
516 n
+= ft
.dwLowDateTime
;
517 n
^= ((ULONGLONG
) i
<< 56) | ((ULONGLONG
) j
<< 48);
519 for(k
= 0; k
< 8; ++k
) {
522 /* Dividing by a prime greater than 36 helps
523 * with the appearance of randomness
528 dir_name
[k
] = '0' + r
;
530 dir_name
[k
] = 'A' + (r
- 10);
533 if(CreateDirectoryW(dir_path
, 0)) {
534 /* The following is OK because we generated an
535 * 8 character directory name made from characters
536 * [A-Z0-9], which are equivalent for all code
537 * pages and for UTF-16
539 for (k
= 0; k
< 8; ++k
)
540 header
->directory_data
[i
].name
[k
] = dir_name
[k
];
543 /* Give up. The most likely cause of this
544 * is a full disk, but whatever the cause
545 * is, it should be more than apparent that
548 UnmapViewOfFile(header
);
549 CloseHandle(mapping
);
550 return GetLastError();
555 UnmapViewOfFile(header
);
556 CloseHandle(container
->mapping
);
557 container
->mapping
= mapping
;
558 container
->file_size
= file_size
;
559 return ERROR_SUCCESS
;
562 static BOOL
cache_container_is_valid(urlcache_header
*header
, DWORD file_size
)
564 DWORD allocation_size
, count_bits
, i
;
566 if(file_size
< FILE_SIZE(MIN_BLOCK_NO
))
569 if(file_size
!= header
->size
)
572 if (!memcmp(header
->signature
, urlcache_ver_prefix
, sizeof(urlcache_ver_prefix
)-1) &&
573 memcmp(header
->signature
+sizeof(urlcache_ver_prefix
)-1, urlcache_ver
, sizeof(urlcache_ver
)-1))
576 if(FILE_SIZE(header
->capacity_in_blocks
) != file_size
)
580 for(i
=0; i
<header
->capacity_in_blocks
/8; i
++) {
581 for(count_bits
= header
->allocation_table
[i
]; count_bits
!=0; count_bits
>>=1) {
586 if(allocation_size
!= header
->blocks_in_use
)
589 for(; i
<ALLOCATION_TABLE_SIZE
; i
++) {
590 if(header
->allocation_table
[i
])
597 /***********************************************************************
598 * cache_container_open_index (Internal)
600 * Opens the index file and saves mapping handle
603 * ERROR_SUCCESS if succeeded
604 * Any other Win32 error code if failed
607 static DWORD
cache_container_open_index(cache_container
*container
, DWORD blocks_no
)
609 static const WCHAR index_dat
[] = {'i','n','d','e','x','.','d','a','t',0};
612 WCHAR index_path
[MAX_PATH
];
616 WaitForSingleObject(container
->mutex
, INFINITE
);
618 if(container
->mapping
) {
619 ReleaseMutex(container
->mutex
);
620 return ERROR_SUCCESS
;
623 strcpyW(index_path
, container
->path
);
624 strcatW(index_path
, index_dat
);
626 file
= CreateFileW(index_path
, GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_ALWAYS
, 0, NULL
);
627 if(file
== INVALID_HANDLE_VALUE
) {
628 /* Maybe the directory wasn't there? Try to create it */
629 if(CreateDirectoryW(container
->path
, 0))
630 file
= CreateFileW(index_path
, GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_ALWAYS
, 0, NULL
);
632 if(file
== INVALID_HANDLE_VALUE
) {
633 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(index_path
));
634 ReleaseMutex(container
->mutex
);
635 return GetLastError();
638 file_size
= GetFileSize(file
, NULL
);
639 if(file_size
== INVALID_FILE_SIZE
) {
641 ReleaseMutex(container
->mutex
);
642 return GetLastError();
645 if(blocks_no
< MIN_BLOCK_NO
)
646 blocks_no
= MIN_BLOCK_NO
;
647 else if(blocks_no
> MAX_BLOCK_NO
)
648 blocks_no
= MAX_BLOCK_NO
;
650 if(file_size
< FILE_SIZE(blocks_no
)) {
651 DWORD ret
= cache_container_set_size(container
, file
, blocks_no
);
653 ReleaseMutex(container
->mutex
);
657 container
->file_size
= file_size
;
658 container
->mapping
= cache_container_map_index(file
, container
->path
, file_size
, &validate
);
660 if(container
->mapping
&& validate
) {
661 urlcache_header
*header
= MapViewOfFile(container
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
663 if(header
&& !cache_container_is_valid(header
, file_size
)) {
664 WARN("detected old or broken index.dat file\n");
665 UnmapViewOfFile(header
);
666 FreeUrlCacheSpaceW(container
->path
, 100, 0);
668 UnmapViewOfFile(header
);
670 CloseHandle(container
->mapping
);
671 container
->mapping
= NULL
;
675 if(!container
->mapping
)
677 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
678 ReleaseMutex(container
->mutex
);
679 return GetLastError();
682 ReleaseMutex(container
->mutex
);
683 return ERROR_SUCCESS
;
686 /***********************************************************************
687 * cache_container_close_index (Internal)
695 static void cache_container_close_index(cache_container
*pContainer
)
697 CloseHandle(pContainer
->mapping
);
698 pContainer
->mapping
= NULL
;
701 static BOOL
cache_containers_add(const char *cache_prefix
, LPCWSTR path
,
702 DWORD default_entry_type
, LPWSTR mutex_name
)
704 cache_container
*pContainer
= heap_alloc(sizeof(cache_container
));
705 int cache_prefix_len
= strlen(cache_prefix
);
712 pContainer
->mapping
= NULL
;
713 pContainer
->file_size
= 0;
714 pContainer
->default_entry_type
= default_entry_type
;
716 pContainer
->path
= heap_strdupW(path
);
717 if (!pContainer
->path
)
719 heap_free(pContainer
);
723 pContainer
->cache_prefix
= heap_alloc(cache_prefix_len
+1);
724 if (!pContainer
->cache_prefix
)
726 heap_free(pContainer
->path
);
727 heap_free(pContainer
);
731 memcpy(pContainer
->cache_prefix
, cache_prefix
, cache_prefix_len
+1);
733 CharLowerW(mutex_name
);
734 cache_container_create_object_name(mutex_name
, '!');
736 if ((pContainer
->mutex
= CreateMutexW(NULL
, FALSE
, mutex_name
)) == NULL
)
738 ERR("couldn't create mutex (error is %d)\n", GetLastError());
739 heap_free(pContainer
->path
);
740 heap_free(pContainer
);
744 list_add_head(&UrlContainers
, &pContainer
->entry
);
749 static void cache_container_delete_container(cache_container
*pContainer
)
751 list_remove(&pContainer
->entry
);
753 cache_container_close_index(pContainer
);
754 CloseHandle(pContainer
->mutex
);
755 heap_free(pContainer
->path
);
756 heap_free(pContainer
->cache_prefix
);
757 heap_free(pContainer
);
760 static void cache_containers_init(void)
762 static const WCHAR UrlSuffix
[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
763 static const WCHAR HistorySuffix
[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
764 static const WCHAR CookieSuffix
[] = {0};
767 int nFolder
; /* CSIDL_* constant */
768 const WCHAR
*shpath_suffix
; /* suffix on path returned by SHGetSpecialFolderPath */
769 const char *cache_prefix
; /* prefix used to reference the container */
770 DWORD default_entry_type
;
771 } DefaultContainerData
[] =
773 { CSIDL_INTERNET_CACHE
, UrlSuffix
, "", NORMAL_CACHE_ENTRY
},
774 { CSIDL_HISTORY
, HistorySuffix
, "Visited:", URLHISTORY_CACHE_ENTRY
},
775 { CSIDL_COOKIES
, CookieSuffix
, "Cookie:", COOKIE_CACHE_ENTRY
},
779 for (i
= 0; i
< sizeof(DefaultContainerData
) / sizeof(DefaultContainerData
[0]); i
++)
781 WCHAR wszCachePath
[MAX_PATH
];
782 WCHAR wszMutexName
[MAX_PATH
];
783 int path_len
, suffix_len
;
786 if (!SHGetSpecialFolderPathW(NULL
, wszCachePath
, DefaultContainerData
[i
].nFolder
, TRUE
))
788 ERR("Couldn't get path for default container %u\n", i
);
791 path_len
= strlenW(wszCachePath
);
792 suffix_len
= strlenW(DefaultContainerData
[i
].shpath_suffix
);
794 if (path_len
+ suffix_len
+ 2 > MAX_PATH
)
796 ERR("Path too long\n");
800 wszCachePath
[path_len
] = '\\';
801 wszCachePath
[path_len
+1] = 0;
803 strcpyW(wszMutexName
, wszCachePath
);
807 memcpy(wszCachePath
+ path_len
+ 1, DefaultContainerData
[i
].shpath_suffix
, (suffix_len
+ 1) * sizeof(WCHAR
));
808 wszCachePath
[path_len
+ suffix_len
+ 1] = '\\';
809 wszCachePath
[path_len
+ suffix_len
+ 2] = '\0';
812 if (!WideCharToMultiByte(CP_ACP
, WC_NO_BEST_FIT_CHARS
, wszCachePath
, path_len
,
813 NULL
, 0, NULL
, &def_char
) || def_char
)
817 /* cannot convert path to ANSI code page */
818 if (!(path_len
= GetShortPathNameW(wszCachePath
, tmp
, MAX_PATH
)) ||
819 !WideCharToMultiByte(CP_ACP
, WC_NO_BEST_FIT_CHARS
, tmp
, path_len
,
820 NULL
, 0, NULL
, &def_char
) || def_char
)
821 ERR("Can't create container path accessible by ANSI functions\n");
823 memcpy(wszCachePath
, tmp
, (path_len
+1)*sizeof(WCHAR
));
826 cache_containers_add(DefaultContainerData
[i
].cache_prefix
, wszCachePath
,
827 DefaultContainerData
[i
].default_entry_type
, wszMutexName
);
831 static void cache_containers_free(void)
833 while(!list_empty(&UrlContainers
))
834 cache_container_delete_container(
835 LIST_ENTRY(list_head(&UrlContainers
), cache_container
, entry
)
839 static DWORD
cache_containers_find(const char *url
, cache_container
**ret
)
841 cache_container
*container
;
843 TRACE("searching for prefix for URL: %s\n", debugstr_a(url
));
846 return ERROR_INVALID_PARAMETER
;
848 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
850 int prefix_len
= strlen(container
->cache_prefix
);
852 if(!strncmp(container
->cache_prefix
, url
, prefix_len
)) {
853 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
855 return ERROR_SUCCESS
;
859 ERR("no container found\n");
860 return ERROR_FILE_NOT_FOUND
;
863 static BOOL
cache_containers_enum(char *search_pattern
, DWORD index
, cache_container
**ret
)
866 cache_container
*container
;
868 TRACE("searching for prefix: %s\n", debugstr_a(search_pattern
));
870 /* non-NULL search pattern only returns one container ever */
871 if (search_pattern
&& index
> 0)
874 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
878 if (!strcmp(container
->cache_prefix
, search_pattern
))
880 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
889 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
899 /***********************************************************************
900 * cache_container_lock_index (Internal)
902 * Locks the index for system-wide exclusive access.
905 * Cache file header if successful
906 * NULL if failed and calls SetLastError.
908 static urlcache_header
* cache_container_lock_index(cache_container
*pContainer
)
912 urlcache_header
* pHeader
;
916 WaitForSingleObject(pContainer
->mutex
, INFINITE
);
918 pIndexData
= MapViewOfFile(pContainer
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
922 ReleaseMutex(pContainer
->mutex
);
923 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
926 pHeader
= (urlcache_header
*)pIndexData
;
928 /* file has grown - we need to remap to prevent us getting
929 * access violations when we try and access beyond the end
930 * of the memory mapped file */
931 if (pHeader
->size
!= pContainer
->file_size
)
933 UnmapViewOfFile( pHeader
);
934 cache_container_close_index(pContainer
);
935 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
936 if (error
!= ERROR_SUCCESS
)
938 ReleaseMutex(pContainer
->mutex
);
942 pIndexData
= MapViewOfFile(pContainer
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
946 ReleaseMutex(pContainer
->mutex
);
947 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
950 pHeader
= (urlcache_header
*)pIndexData
;
953 TRACE("Signature: %s, file size: %d bytes\n", pHeader
->signature
, pHeader
->size
);
955 for (index
= 0; index
< pHeader
->dirs_no
; index
++)
957 TRACE("Directory[%d] = \"%.8s\"\n", index
, pHeader
->directory_data
[index
].name
);
963 /***********************************************************************
964 * cache_container_unlock_index (Internal)
967 static BOOL
cache_container_unlock_index(cache_container
*pContainer
, urlcache_header
*pHeader
)
970 ReleaseMutex(pContainer
->mutex
);
971 return UnmapViewOfFile(pHeader
);
974 /***********************************************************************
975 * urlcache_create_file_pathW (Internal)
977 * Copies the full path to the specified buffer given the local file
978 * name and the index of the directory it is in. Always sets value in
979 * lpBufferSize to the required buffer size (in bytes).
982 * TRUE if the buffer was big enough
983 * FALSE if the buffer was too small
986 static BOOL
urlcache_create_file_pathW(
987 const cache_container
*pContainer
,
988 const urlcache_header
*pHeader
,
989 LPCSTR szLocalFileName
,
995 int path_len
= strlenW(pContainer
->path
);
996 int file_name_len
= MultiByteToWideChar(CP_ACP
, 0, szLocalFileName
, -1, NULL
, 0);
997 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
&& Directory
>=pHeader
->dirs_no
)
1003 nRequired
= (path_len
+ file_name_len
) * sizeof(WCHAR
);
1004 if(Directory
!= CACHE_CONTAINER_NO_SUBDIR
)
1005 nRequired
+= (DIR_LENGTH
+ 1) * sizeof(WCHAR
);
1006 if (nRequired
<= *lpBufferSize
)
1010 memcpy(wszPath
, pContainer
->path
, path_len
* sizeof(WCHAR
));
1011 if (Directory
!= CACHE_CONTAINER_NO_SUBDIR
)
1013 dir_len
= MultiByteToWideChar(CP_ACP
, 0, pHeader
->directory_data
[Directory
].name
, DIR_LENGTH
, wszPath
+ path_len
, DIR_LENGTH
);
1014 wszPath
[dir_len
+ path_len
] = '\\';
1021 MultiByteToWideChar(CP_ACP
, 0, szLocalFileName
, -1, wszPath
+ dir_len
+ path_len
, file_name_len
);
1022 *lpBufferSize
= nRequired
;
1025 *lpBufferSize
= nRequired
;
1029 /***********************************************************************
1030 * urlcache_create_file_pathA (Internal)
1032 * Copies the full path to the specified buffer given the local file
1033 * name and the index of the directory it is in. Always sets value in
1034 * lpBufferSize to the required buffer size.
1037 * TRUE if the buffer was big enough
1038 * FALSE if the buffer was too small
1041 static BOOL
urlcache_create_file_pathA(
1042 const cache_container
*pContainer
,
1043 const urlcache_header
*pHeader
,
1044 LPCSTR szLocalFileName
,
1047 LPLONG lpBufferSize
)
1050 int path_len
, file_name_len
, dir_len
;
1052 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
&& Directory
>=pHeader
->dirs_no
)
1058 path_len
= WideCharToMultiByte(CP_ACP
, 0, pContainer
->path
, -1, NULL
, 0, NULL
, NULL
) - 1;
1059 file_name_len
= strlen(szLocalFileName
) + 1 /* for nul-terminator */;
1060 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
)
1061 dir_len
= DIR_LENGTH
+1;
1065 nRequired
= (path_len
+ dir_len
+ file_name_len
) * sizeof(char);
1066 if (nRequired
<= *lpBufferSize
)
1068 WideCharToMultiByte(CP_ACP
, 0, pContainer
->path
, -1, szPath
, path_len
, NULL
, NULL
);
1070 memcpy(szPath
+path_len
, pHeader
->directory_data
[Directory
].name
, dir_len
-1);
1071 szPath
[path_len
+ dir_len
-1] = '\\';
1073 memcpy(szPath
+ path_len
+ dir_len
, szLocalFileName
, file_name_len
);
1074 *lpBufferSize
= nRequired
;
1077 *lpBufferSize
= nRequired
;
1081 /* Just like FileTimeToDosDateTime, except that it also maps the special
1082 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1084 static void file_time_to_dos_date_time(const FILETIME
*ft
, WORD
*fatdate
,
1087 if (!ft
->dwLowDateTime
&& !ft
->dwHighDateTime
)
1088 *fatdate
= *fattime
= 0;
1090 FileTimeToDosDateTime(ft
, fatdate
, fattime
);
1093 /***********************************************************************
1094 * urlcache_delete_file (Internal)
1096 static DWORD
urlcache_delete_file(const cache_container
*container
,
1097 urlcache_header
*header
, entry_url
*url_entry
)
1099 WIN32_FILE_ATTRIBUTE_DATA attr
;
1100 WCHAR path
[MAX_PATH
];
1101 LONG path_size
= sizeof(path
);
1105 if(!url_entry
->local_name_off
)
1108 if(!urlcache_create_file_pathW(container
, header
,
1109 (LPCSTR
)url_entry
+url_entry
->local_name_off
,
1110 url_entry
->cache_dir
, path
, &path_size
))
1113 if(!GetFileAttributesExW(path
, GetFileExInfoStandard
, &attr
))
1115 file_time_to_dos_date_time(&attr
.ftLastWriteTime
, &date
, &time
);
1116 if(date
!= url_entry
->write_date
|| time
!= url_entry
->write_time
)
1119 err
= (DeleteFileW(path
) ? ERROR_SUCCESS
: GetLastError());
1120 if(err
== ERROR_ACCESS_DENIED
|| err
== ERROR_SHARING_VIOLATION
)
1124 if (url_entry
->cache_dir
< header
->dirs_no
)
1126 if (header
->directory_data
[url_entry
->cache_dir
].files_no
)
1127 header
->directory_data
[url_entry
->cache_dir
].files_no
--;
1129 if (url_entry
->cache_entry_type
& STICKY_CACHE_ENTRY
)
1131 if (url_entry
->size
.QuadPart
< header
->exempt_usage
.QuadPart
)
1132 header
->exempt_usage
.QuadPart
-= url_entry
->size
.QuadPart
;
1134 header
->exempt_usage
.QuadPart
= 0;
1138 if (url_entry
->size
.QuadPart
< header
->cache_usage
.QuadPart
)
1139 header
->cache_usage
.QuadPart
-= url_entry
->size
.QuadPart
;
1141 header
->cache_usage
.QuadPart
= 0;
1144 return ERROR_SUCCESS
;
1147 static BOOL
urlcache_clean_leaked_entries(cache_container
*container
, urlcache_header
*header
)
1152 leak_off
= &header
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
];
1154 entry_url
*url_entry
= (entry_url
*)((LPBYTE
)header
+ *leak_off
);
1156 if(SUCCEEDED(urlcache_delete_file(container
, header
, url_entry
))) {
1157 *leak_off
= url_entry
->exempt_delta
;
1158 urlcache_entry_free(header
, &url_entry
->header
);
1161 leak_off
= &url_entry
->exempt_delta
;
1168 /***********************************************************************
1169 * cache_container_clean_index (Internal)
1171 * This function is meant to make place in index file by removing leaked
1172 * files entries and resizing the file.
1174 * CAUTION: file view may get mapped to new memory
1177 * ERROR_SUCCESS when new memory is available
1178 * error code otherwise
1180 static DWORD
cache_container_clean_index(cache_container
*container
, urlcache_header
**file_view
)
1182 urlcache_header
*header
= *file_view
;
1185 TRACE("(%s %s)\n", debugstr_a(container
->cache_prefix
), debugstr_w(container
->path
));
1187 if(urlcache_clean_leaked_entries(container
, header
))
1188 return ERROR_SUCCESS
;
1190 if(header
->size
>= ALLOCATION_TABLE_SIZE
*8*BLOCKSIZE
+ ENTRY_START_OFFSET
) {
1191 WARN("index file has maximal size\n");
1192 return ERROR_NOT_ENOUGH_MEMORY
;
1195 cache_container_close_index(container
);
1196 ret
= cache_container_open_index(container
, header
->capacity_in_blocks
*2);
1197 if(ret
!= ERROR_SUCCESS
)
1199 header
= MapViewOfFile(container
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
1201 return GetLastError();
1203 UnmapViewOfFile(*file_view
);
1204 *file_view
= header
;
1205 return ERROR_SUCCESS
;
1208 /* Just like DosDateTimeToFileTime, except that it also maps the special
1209 * case of a DOS date/time of (0,0) to a filetime of (0,0).
1211 static void dos_date_time_to_file_time(WORD fatdate
, WORD fattime
,
1214 if (!fatdate
&& !fattime
)
1215 ft
->dwLowDateTime
= ft
->dwHighDateTime
= 0;
1217 DosDateTimeToFileTime(fatdate
, fattime
, ft
);
1220 static int urlcache_decode_url(const char *url
, WCHAR
*decoded_url
, int decoded_len
)
1223 DWORD len
, part_len
;
1226 memset(&uc
, 0, sizeof(uc
));
1227 uc
.dwStructSize
= sizeof(uc
);
1228 uc
.dwHostNameLength
= 1;
1229 if(!InternetCrackUrlA(url
, 0, 0, &uc
))
1230 uc
.nScheme
= INTERNET_SCHEME_UNKNOWN
;
1232 if(uc
.nScheme
!=INTERNET_SCHEME_HTTP
&& uc
.nScheme
!=INTERNET_SCHEME_HTTPS
)
1233 return MultiByteToWideChar(CP_UTF8
, 0, url
, -1, decoded_url
, decoded_len
);
1238 len
= MultiByteToWideChar(CP_UTF8
, 0, url
, uc
.lpszHostName
-url
, decoded_url
, decoded_len
);
1244 host_name
= heap_alloc(uc
.dwHostNameLength
*sizeof(WCHAR
));
1247 if(!MultiByteToWideChar(CP_UTF8
, 0, uc
.lpszHostName
, uc
.dwHostNameLength
,
1248 host_name
, uc
.dwHostNameLength
)) {
1249 heap_free(host_name
);
1252 part_len
= IdnToUnicode(0, host_name
, uc
.dwHostNameLength
,
1253 decoded_url
? decoded_url
+len
: NULL
, decoded_len
);
1254 heap_free(host_name
);
1256 SetLastError(ERROR_INTERNET_INVALID_URL
);
1261 decoded_len
-= part_len
;
1263 part_len
= MultiByteToWideChar(CP_UTF8
, 0,
1264 uc
.lpszHostName
+uc
.dwHostNameLength
,
1265 -1, decoded_url
? decoded_url
+len
: NULL
, decoded_len
);
1273 /***********************************************************************
1274 * urlcache_copy_entry (Internal)
1276 * Copies an entry from the cache index file to the Win32 structure
1279 * ERROR_SUCCESS if the buffer was big enough
1280 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1283 static DWORD
urlcache_copy_entry(cache_container
*container
, const urlcache_header
*header
,
1284 INTERNET_CACHE_ENTRY_INFOA
*entry_info
, DWORD
*info_size
, const entry_url
*url_entry
, BOOL unicode
)
1287 DWORD size
= sizeof(*entry_info
);
1289 if(*info_size
>= size
) {
1290 entry_info
->lpHeaderInfo
= NULL
;
1291 entry_info
->lpszFileExtension
= NULL
;
1292 entry_info
->lpszLocalFileName
= NULL
;
1293 entry_info
->lpszSourceUrlName
= NULL
;
1294 entry_info
->CacheEntryType
= url_entry
->cache_entry_type
;
1295 entry_info
->u
.dwExemptDelta
= url_entry
->exempt_delta
;
1296 entry_info
->dwHeaderInfoSize
= url_entry
->header_info_size
;
1297 entry_info
->dwHitRate
= url_entry
->hit_rate
;
1298 entry_info
->dwSizeHigh
= url_entry
->size
.u
.HighPart
;
1299 entry_info
->dwSizeLow
= url_entry
->size
.u
.LowPart
;
1300 entry_info
->dwStructSize
= sizeof(*entry_info
);
1301 entry_info
->dwUseCount
= url_entry
->use_count
;
1302 dos_date_time_to_file_time(url_entry
->expire_date
, url_entry
->expire_time
, &entry_info
->ExpireTime
);
1303 entry_info
->LastAccessTime
= url_entry
->access_time
;
1304 entry_info
->LastModifiedTime
= url_entry
->modification_time
;
1305 dos_date_time_to_file_time(url_entry
->sync_date
, url_entry
->sync_time
, &entry_info
->LastSyncTime
);
1308 if(size
%4 && size
<*info_size
)
1309 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1310 size
= DWORD_ALIGN(size
);
1312 url_len
= urlcache_decode_url((const char*)url_entry
+url_entry
->url_off
, NULL
, 0);
1314 url_len
= strlen((LPCSTR
)url_entry
+url_entry
->url_off
) + 1;
1315 size
+= url_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1317 if(*info_size
>= size
) {
1318 DWORD url_size
= url_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1320 entry_info
->lpszSourceUrlName
= (LPSTR
)entry_info
+size
-url_size
;
1322 urlcache_decode_url((const char*)url_entry
+url_entry
->url_off
, (WCHAR
*)entry_info
->lpszSourceUrlName
, url_len
);
1324 memcpy(entry_info
->lpszSourceUrlName
, (LPCSTR
)url_entry
+url_entry
->url_off
, url_size
);
1327 if(size
%4 && size
<*info_size
)
1328 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1329 size
= DWORD_ALIGN(size
);
1331 if(url_entry
->local_name_off
) {
1332 LONG file_name_size
;
1334 file_name
= (LPSTR
)entry_info
+size
;
1335 file_name_size
= *info_size
-size
;
1336 if((unicode
&& urlcache_create_file_pathW(container
, header
, (LPCSTR
)url_entry
+url_entry
->local_name_off
, url_entry
->cache_dir
, (LPWSTR
)file_name
, &file_name_size
)) ||
1337 (!unicode
&& urlcache_create_file_pathA(container
, header
, (LPCSTR
)url_entry
+url_entry
->local_name_off
, url_entry
->cache_dir
, file_name
, &file_name_size
))) {
1338 entry_info
->lpszLocalFileName
= file_name
;
1340 size
+= file_name_size
;
1342 if(size
%4 && size
<*info_size
)
1343 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1344 size
= DWORD_ALIGN(size
);
1347 if(url_entry
->header_info_off
) {
1351 header_len
= MultiByteToWideChar(CP_UTF8
, 0, (const char*)url_entry
+url_entry
->header_info_off
,
1352 url_entry
->header_info_size
, NULL
, 0);
1354 header_len
= url_entry
->header_info_size
;
1355 size
+= header_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1357 if(*info_size
>= size
) {
1358 DWORD header_size
= header_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1359 entry_info
->lpHeaderInfo
= (LPBYTE
)entry_info
+size
-header_size
;
1361 MultiByteToWideChar(CP_UTF8
, 0, (const char*)url_entry
+url_entry
->header_info_off
,
1362 url_entry
->header_info_size
, (LPWSTR
)entry_info
->lpHeaderInfo
, header_len
);
1364 memcpy(entry_info
->lpHeaderInfo
, (LPCSTR
)url_entry
+url_entry
->header_info_off
, header_len
);
1366 if(size
%4 && size
<*info_size
)
1367 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1368 size
= DWORD_ALIGN(size
);
1371 if(url_entry
->file_extension_off
) {
1375 ext_len
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)url_entry
+url_entry
->file_extension_off
, -1, NULL
, 0);
1377 ext_len
= strlen((LPCSTR
)url_entry
+url_entry
->file_extension_off
) + 1;
1378 size
+= ext_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1380 if(*info_size
>= size
) {
1381 DWORD ext_size
= ext_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1382 entry_info
->lpszFileExtension
= (LPSTR
)entry_info
+size
-ext_size
;
1384 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)url_entry
+url_entry
->file_extension_off
, -1, (LPWSTR
)entry_info
->lpszFileExtension
, ext_len
);
1386 memcpy(entry_info
->lpszFileExtension
, (LPCSTR
)url_entry
+url_entry
->file_extension_off
, ext_len
*sizeof(CHAR
));
1389 if(size
%4 && size
<*info_size
)
1390 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1391 size
= DWORD_ALIGN(size
);
1394 if(size
> *info_size
) {
1396 return ERROR_INSUFFICIENT_BUFFER
;
1399 return ERROR_SUCCESS
;
1402 /***********************************************************************
1403 * urlcache_set_entry_info (Internal)
1405 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1406 * according to the flags set by field_control.
1409 * ERROR_SUCCESS if the buffer was big enough
1410 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1413 static DWORD
urlcache_set_entry_info(entry_url
*url_entry
, const INTERNET_CACHE_ENTRY_INFOA
*entry_info
, DWORD field_control
)
1415 if (field_control
& CACHE_ENTRY_ACCTIME_FC
)
1416 url_entry
->access_time
= entry_info
->LastAccessTime
;
1417 if (field_control
& CACHE_ENTRY_ATTRIBUTE_FC
)
1418 url_entry
->cache_entry_type
= entry_info
->CacheEntryType
;
1419 if (field_control
& CACHE_ENTRY_EXEMPT_DELTA_FC
)
1420 url_entry
->exempt_delta
= entry_info
->u
.dwExemptDelta
;
1421 if (field_control
& CACHE_ENTRY_EXPTIME_FC
)
1422 file_time_to_dos_date_time(&entry_info
->ExpireTime
, &url_entry
->expire_date
, &url_entry
->expire_time
);
1423 if (field_control
& CACHE_ENTRY_HEADERINFO_FC
)
1424 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1425 if (field_control
& CACHE_ENTRY_HITRATE_FC
)
1426 url_entry
->hit_rate
= entry_info
->dwHitRate
;
1427 if (field_control
& CACHE_ENTRY_MODTIME_FC
)
1428 url_entry
->modification_time
= entry_info
->LastModifiedTime
;
1429 if (field_control
& CACHE_ENTRY_SYNCTIME_FC
)
1430 file_time_to_dos_date_time(&entry_info
->LastAccessTime
, &url_entry
->sync_date
, &url_entry
->sync_time
);
1432 return ERROR_SUCCESS
;
1435 /***********************************************************************
1436 * urlcache_hash_key (Internal)
1438 * Returns the hash key for a given string
1441 * hash key for the string
1444 static DWORD
urlcache_hash_key(LPCSTR lpszKey
)
1446 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1447 * but the algorithm and result are not the same!
1449 static const unsigned char lookupTable
[256] =
1451 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1452 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1453 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1454 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1455 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1456 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1457 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1458 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1459 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1460 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1461 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1462 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1463 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1464 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1465 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1466 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1467 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1468 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1469 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1470 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1471 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1472 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1473 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1474 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1475 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1476 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1477 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1478 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1479 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1480 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1481 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1482 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1487 for (i
= 0; i
< sizeof(key
) / sizeof(key
[0]); i
++)
1488 key
[i
] = lookupTable
[(*lpszKey
+ i
) & 0xFF];
1490 for (lpszKey
++; *lpszKey
; lpszKey
++)
1492 for (i
= 0; i
< sizeof(key
) / sizeof(key
[0]); i
++)
1493 key
[i
] = lookupTable
[*lpszKey
^ key
[i
]];
1496 return *(DWORD
*)key
;
1499 static inline entry_hash_table
* urlcache_get_hash_table(const urlcache_header
*pHeader
, DWORD dwOffset
)
1503 return (entry_hash_table
*)((LPBYTE
)pHeader
+ dwOffset
);
1506 static BOOL
urlcache_find_hash_entry(const urlcache_header
*pHeader
, LPCSTR lpszUrl
, struct hash_entry
**ppHashEntry
)
1508 /* structure of hash table:
1509 * 448 entries divided into 64 blocks
1510 * each block therefore contains a chain of 7 key/offset pairs
1511 * how position in table is calculated:
1512 * 1. the url is hashed in helper function
1513 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1514 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1517 * there can be multiple hash tables in the file and the offset to
1518 * the next one is stored in the header of the hash table
1520 DWORD key
= urlcache_hash_key(lpszUrl
);
1521 DWORD offset
= (key
& (HASHTABLE_NUM_ENTRIES
-1)) * HASHTABLE_BLOCKSIZE
;
1522 entry_hash_table
* pHashEntry
;
1525 key
>>= HASHTABLE_FLAG_BITS
;
1527 for (pHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1528 pHashEntry
; pHashEntry
= urlcache_get_hash_table(pHeader
, pHashEntry
->next
))
1531 if (pHashEntry
->id
!= id
++)
1533 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry
->id
, id
);
1536 /* make sure that it is in fact a hash entry */
1537 if (pHashEntry
->header
.signature
!= HASH_SIGNATURE
)
1539 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&pHashEntry
->header
.signature
);
1543 for (i
= 0; i
< HASHTABLE_BLOCKSIZE
; i
++)
1545 struct hash_entry
*pHashElement
= &pHashEntry
->hash_table
[offset
+ i
];
1546 if (key
== pHashElement
->key
>>HASHTABLE_FLAG_BITS
)
1548 /* FIXME: we should make sure that this is the right element
1549 * before returning and claiming that it is. We can do this
1550 * by doing a simple compare between the URL we were given
1551 * and the URL stored in the entry. However, this assumes
1552 * we know the format of all the entries stored in the
1554 *ppHashEntry
= pHashElement
;
1562 /***********************************************************************
1563 * urlcache_hash_entry_set_flags (Internal)
1565 * Sets special bits in hash key
1571 static void urlcache_hash_entry_set_flags(struct hash_entry
*pHashEntry
, DWORD dwFlag
)
1573 pHashEntry
->key
= (pHashEntry
->key
>> HASHTABLE_FLAG_BITS
<< HASHTABLE_FLAG_BITS
) | dwFlag
;
1576 /***********************************************************************
1577 * urlcache_hash_entry_delete (Internal)
1579 * Searches all the hash tables in the index for the given URL and
1580 * then if found deletes the entry.
1583 * TRUE if the entry was found
1584 * FALSE if the entry could not be found
1587 static BOOL
urlcache_hash_entry_delete(struct hash_entry
*pHashEntry
)
1589 pHashEntry
->key
= HASHTABLE_DEL
;
1593 /***********************************************************************
1594 * urlcache_hash_entry_create (Internal)
1596 * Searches all the hash tables for a free slot based on the offset
1597 * generated from the hash key. If a free slot is found, the offset and
1598 * key are entered into the hash table.
1601 * ERROR_SUCCESS if the entry was added
1602 * Any other Win32 error code if the entry could not be added
1605 static DWORD
urlcache_hash_entry_create(urlcache_header
*pHeader
, LPCSTR lpszUrl
, DWORD dwOffsetEntry
, DWORD dwFieldType
)
1607 /* see urlcache_find_hash_entry for structure of hash tables */
1609 DWORD key
= urlcache_hash_key(lpszUrl
);
1610 DWORD offset
= (key
& (HASHTABLE_NUM_ENTRIES
-1)) * HASHTABLE_BLOCKSIZE
;
1611 entry_hash_table
* pHashEntry
, *pHashPrev
= NULL
;
1615 key
= ((key
>> HASHTABLE_FLAG_BITS
) << HASHTABLE_FLAG_BITS
) + dwFieldType
;
1617 for (pHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1618 pHashEntry
; pHashEntry
= urlcache_get_hash_table(pHeader
, pHashEntry
->next
))
1621 pHashPrev
= pHashEntry
;
1623 if (pHashEntry
->id
!= id
++)
1625 ERR("not right hash table number (%d) expected %d\n", pHashEntry
->id
, id
);
1628 /* make sure that it is in fact a hash entry */
1629 if (pHashEntry
->header
.signature
!= HASH_SIGNATURE
)
1631 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&pHashEntry
->header
.signature
);
1635 for (i
= 0; i
< HASHTABLE_BLOCKSIZE
; i
++)
1637 struct hash_entry
*pHashElement
= &pHashEntry
->hash_table
[offset
+ i
];
1638 if (pHashElement
->key
==HASHTABLE_FREE
|| pHashElement
->key
==HASHTABLE_DEL
) /* if the slot is free */
1640 pHashElement
->key
= key
;
1641 pHashElement
->offset
= dwOffsetEntry
;
1642 return ERROR_SUCCESS
;
1646 error
= urlcache_create_hash_table(pHeader
, pHashPrev
, &pHashEntry
);
1647 if (error
!= ERROR_SUCCESS
)
1650 pHashEntry
->hash_table
[offset
].key
= key
;
1651 pHashEntry
->hash_table
[offset
].offset
= dwOffsetEntry
;
1652 return ERROR_SUCCESS
;
1655 /***********************************************************************
1656 * urlcache_enum_hash_tables (Internal)
1658 * Enumerates the hash tables in a container.
1661 * TRUE if an entry was found
1662 * FALSE if there are no more tables to enumerate.
1665 static BOOL
urlcache_enum_hash_tables(const urlcache_header
*pHeader
, DWORD
*id
, entry_hash_table
**ppHashEntry
)
1667 for (*ppHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1668 *ppHashEntry
; *ppHashEntry
= urlcache_get_hash_table(pHeader
, (*ppHashEntry
)->next
))
1670 TRACE("looking at hash table number %d\n", (*ppHashEntry
)->id
);
1671 if ((*ppHashEntry
)->id
!= *id
)
1673 /* make sure that it is in fact a hash entry */
1674 if ((*ppHashEntry
)->header
.signature
!= HASH_SIGNATURE
)
1676 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&(*ppHashEntry
)->header
.signature
);
1681 TRACE("hash table number %d found\n", *id
);
1687 /***********************************************************************
1688 * urlcache_enum_hash_table_entries (Internal)
1690 * Enumerates entries in a hash table and returns the next non-free entry.
1693 * TRUE if an entry was found
1694 * FALSE if the hash table is empty or there are no more entries to
1698 static BOOL
urlcache_enum_hash_table_entries(const urlcache_header
*pHeader
, const entry_hash_table
*pHashEntry
,
1699 DWORD
* index
, const struct hash_entry
**ppHashEntry
)
1701 for (; *index
< HASHTABLE_SIZE
; (*index
)++)
1703 if (pHashEntry
->hash_table
[*index
].key
==HASHTABLE_FREE
|| pHashEntry
->hash_table
[*index
].key
==HASHTABLE_DEL
)
1706 *ppHashEntry
= &pHashEntry
->hash_table
[*index
];
1707 TRACE("entry found %d\n", *index
);
1710 TRACE("no more entries (%d)\n", *index
);
1714 /***********************************************************************
1715 * cache_container_delete_dir (Internal)
1717 * Erase a directory containing an URL cache.
1720 * TRUE success, FALSE failure/aborted.
1723 static BOOL
cache_container_delete_dir(LPCWSTR lpszPath
)
1726 WCHAR path
[MAX_PATH
+ 1];
1727 SHFILEOPSTRUCTW shfos
;
1730 path_len
= strlenW(lpszPath
);
1731 if (path_len
>= MAX_PATH
)
1733 strcpyW(path
, lpszPath
);
1734 path
[path_len
+ 1] = 0; /* double-NUL-terminate path */
1737 shfos
.wFunc
= FO_DELETE
;
1740 shfos
.fFlags
= FOF_NOCONFIRMATION
;
1741 shfos
.fAnyOperationsAborted
= FALSE
;
1742 ret
= SHFileOperationW(&shfos
);
1744 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path
), ret
);
1745 return !(ret
|| shfos
.fAnyOperationsAborted
);
1748 /***********************************************************************
1749 * urlcache_hash_entry_is_locked (Internal)
1751 * Checks if entry is locked. Unlocks it if possible.
1753 static BOOL
urlcache_hash_entry_is_locked(struct hash_entry
*hash_entry
, entry_url
*url_entry
)
1756 ULARGE_INTEGER acc_time
, time
;
1758 if ((hash_entry
->key
& ((1<<HASHTABLE_FLAG_BITS
)-1)) != HASHTABLE_LOCK
)
1761 GetSystemTimeAsFileTime(&cur_time
);
1762 time
.u
.LowPart
= cur_time
.dwLowDateTime
;
1763 time
.u
.HighPart
= cur_time
.dwHighDateTime
;
1765 acc_time
.u
.LowPart
= url_entry
->access_time
.dwLowDateTime
;
1766 acc_time
.u
.HighPart
= url_entry
->access_time
.dwHighDateTime
;
1768 time
.QuadPart
-= acc_time
.QuadPart
;
1770 /* check if entry was locked for at least a day */
1771 if(time
.QuadPart
> (ULONGLONG
)24*60*60*FILETIME_SECOND
) {
1772 urlcache_hash_entry_set_flags(hash_entry
, HASHTABLE_URL
);
1773 url_entry
->use_count
= 0;
1780 static BOOL
urlcache_get_entry_info(const char *url
, void *entry_info
,
1781 DWORD
*size
, DWORD flags
, BOOL unicode
)
1783 urlcache_header
*header
;
1784 struct hash_entry
*hash_entry
;
1785 const entry_url
*url_entry
;
1786 cache_container
*container
;
1789 TRACE("(%s, %p, %p, %x, %x)\n", debugstr_a(url
), entry_info
, size
, flags
, unicode
);
1791 if(flags
& ~GET_INSTALLED_ENTRY
)
1792 FIXME("ignoring unsupported flags: %x\n", flags
);
1794 error
= cache_containers_find(url
, &container
);
1795 if(error
!= ERROR_SUCCESS
) {
1796 SetLastError(error
);
1800 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
1801 if(error
!= ERROR_SUCCESS
) {
1802 SetLastError(error
);
1806 if(!(header
= cache_container_lock_index(container
)))
1809 if(!urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
1810 cache_container_unlock_index(container
, header
);
1811 WARN("entry %s not found!\n", debugstr_a(url
));
1812 SetLastError(ERROR_FILE_NOT_FOUND
);
1816 url_entry
= (const entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
1817 if(url_entry
->header
.signature
!= URL_SIGNATURE
) {
1818 cache_container_unlock_index(container
, header
);
1819 FIXME("Trying to retrieve entry of unknown format %s\n",
1820 debugstr_an((LPCSTR
)&url_entry
->header
.signature
, sizeof(DWORD
)));
1821 SetLastError(ERROR_FILE_NOT_FOUND
);
1825 TRACE("Found URL: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->url_off
));
1826 TRACE("Header info: %s\n", debugstr_an((LPCSTR
)url_entry
+
1827 url_entry
->header_info_off
, url_entry
->header_info_size
));
1829 if((flags
& GET_INSTALLED_ENTRY
) && !(url_entry
->cache_entry_type
& INSTALLED_CACHE_ENTRY
)) {
1830 cache_container_unlock_index(container
, header
);
1831 SetLastError(ERROR_FILE_NOT_FOUND
);
1839 error
= urlcache_copy_entry(container
, header
, entry_info
, size
, url_entry
, unicode
);
1840 if(error
!= ERROR_SUCCESS
) {
1841 cache_container_unlock_index(container
, header
);
1842 SetLastError(error
);
1845 if(url_entry
->local_name_off
)
1846 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->local_name_off
));
1849 cache_container_unlock_index(container
, header
);
1853 /***********************************************************************
1854 * GetUrlCacheEntryInfoExA (WININET.@)
1857 BOOL WINAPI
GetUrlCacheEntryInfoExA(LPCSTR lpszUrl
,
1858 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1859 LPDWORD lpdwCacheEntryInfoBufSize
, LPSTR lpszReserved
,
1860 LPDWORD lpdwReserved
, LPVOID lpReserved
, DWORD dwFlags
)
1862 if(lpszReserved
!=NULL
|| lpdwReserved
!=NULL
|| lpReserved
!=NULL
) {
1863 ERR("Reserved value was not 0\n");
1864 SetLastError(ERROR_INVALID_PARAMETER
);
1868 return urlcache_get_entry_info(lpszUrl
, lpCacheEntryInfo
,
1869 lpdwCacheEntryInfoBufSize
, dwFlags
, FALSE
);
1872 /***********************************************************************
1873 * GetUrlCacheEntryInfoA (WININET.@)
1876 BOOL WINAPI
GetUrlCacheEntryInfoA(LPCSTR lpszUrlName
,
1877 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1878 LPDWORD lpdwCacheEntryInfoBufferSize
)
1880 return GetUrlCacheEntryInfoExA(lpszUrlName
, lpCacheEntryInfo
,
1881 lpdwCacheEntryInfoBufferSize
, NULL
, NULL
, NULL
, 0);
1884 static int urlcache_encode_url(const WCHAR
*url
, char *encoded_url
, int encoded_len
)
1887 DWORD len
, part_len
;
1890 TRACE("%s\n", debugstr_w(url
));
1892 memset(&uc
, 0, sizeof(uc
));
1893 uc
.dwStructSize
= sizeof(uc
);
1894 uc
.dwHostNameLength
= 1;
1895 if(!InternetCrackUrlW(url
, 0, 0, &uc
))
1896 uc
.nScheme
= INTERNET_SCHEME_UNKNOWN
;
1898 if(uc
.nScheme
!=INTERNET_SCHEME_HTTP
&& uc
.nScheme
!=INTERNET_SCHEME_HTTPS
)
1899 return WideCharToMultiByte(CP_UTF8
, 0, url
, -1, encoded_url
, encoded_len
, NULL
, NULL
);
1901 len
= WideCharToMultiByte(CP_UTF8
, 0, url
, uc
.lpszHostName
-url
,
1902 encoded_url
, encoded_len
, NULL
, NULL
);
1908 part_len
= IdnToAscii(0, uc
.lpszHostName
, uc
.dwHostNameLength
, NULL
, 0);
1910 SetLastError(ERROR_INTERNET_INVALID_URL
);
1914 punycode
= heap_alloc(part_len
*sizeof(WCHAR
));
1918 part_len
= IdnToAscii(0, uc
.lpszHostName
, uc
.dwHostNameLength
, punycode
, part_len
);
1920 heap_free(punycode
);
1924 part_len
= WideCharToMultiByte(CP_UTF8
, 0, punycode
, part_len
,
1925 encoded_url
? encoded_url
+len
: NULL
, encoded_len
, NULL
, NULL
);
1926 heap_free(punycode
);
1930 encoded_len
-= part_len
;
1933 part_len
= WideCharToMultiByte(CP_UTF8
, 0, uc
.lpszHostName
+uc
.dwHostNameLength
,
1934 -1, encoded_url
? encoded_url
+len
: NULL
, encoded_len
, NULL
, NULL
);
1939 TRACE("got (%d)%s\n", len
, debugstr_a(encoded_url
));
1943 static BOOL
urlcache_encode_url_alloc(const WCHAR
*url
, char **encoded_url
)
1948 encoded_len
= urlcache_encode_url(url
, NULL
, 0);
1952 ret
= heap_alloc(encoded_len
*sizeof(WCHAR
));
1956 encoded_len
= urlcache_encode_url(url
, ret
, encoded_len
);
1966 /***********************************************************************
1967 * GetUrlCacheEntryInfoExW (WININET.@)
1970 BOOL WINAPI
GetUrlCacheEntryInfoExW(LPCWSTR lpszUrl
,
1971 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
1972 LPDWORD lpdwCacheEntryInfoBufSize
, LPWSTR lpszReserved
,
1973 LPDWORD lpdwReserved
, LPVOID lpReserved
, DWORD dwFlags
)
1978 if(lpszReserved
!=NULL
|| lpdwReserved
!=NULL
|| lpReserved
!=NULL
) {
1979 ERR("Reserved value was not 0\n");
1980 SetLastError(ERROR_INVALID_PARAMETER
);
1984 /* Ignore GET_INSTALLED_ENTRY flag in unicode version of function */
1985 dwFlags
&= ~GET_INSTALLED_ENTRY
;
1987 if(!urlcache_encode_url_alloc(lpszUrl
, &url
))
1990 ret
= urlcache_get_entry_info(url
, lpCacheEntryInfo
,
1991 lpdwCacheEntryInfoBufSize
, dwFlags
, TRUE
);
1996 /***********************************************************************
1997 * GetUrlCacheEntryInfoW (WININET.@)
2000 BOOL WINAPI
GetUrlCacheEntryInfoW(LPCWSTR lpszUrl
,
2001 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
2002 LPDWORD lpdwCacheEntryInfoBufferSize
)
2004 return GetUrlCacheEntryInfoExW(lpszUrl
, lpCacheEntryInfo
,
2005 lpdwCacheEntryInfoBufferSize
, NULL
, NULL
, NULL
, 0);
2008 /***********************************************************************
2009 * SetUrlCacheEntryInfoA (WININET.@)
2011 BOOL WINAPI
SetUrlCacheEntryInfoA(LPCSTR lpszUrlName
,
2012 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
2013 DWORD dwFieldControl
)
2015 urlcache_header
*pHeader
;
2016 struct hash_entry
*pHashEntry
;
2017 entry_header
*pEntry
;
2018 cache_container
*pContainer
;
2021 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName
), lpCacheEntryInfo
, dwFieldControl
);
2023 error
= cache_containers_find(lpszUrlName
, &pContainer
);
2024 if (error
!= ERROR_SUCCESS
)
2026 SetLastError(error
);
2030 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
2031 if (error
!= ERROR_SUCCESS
)
2033 SetLastError(error
);
2037 if (!(pHeader
= cache_container_lock_index(pContainer
)))
2040 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
2042 cache_container_unlock_index(pContainer
, pHeader
);
2043 WARN("entry %s not found!\n", debugstr_a(lpszUrlName
));
2044 SetLastError(ERROR_FILE_NOT_FOUND
);
2048 pEntry
= (entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2049 if (pEntry
->signature
!= URL_SIGNATURE
)
2051 cache_container_unlock_index(pContainer
, pHeader
);
2052 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR
)&pEntry
->signature
, sizeof(DWORD
)));
2053 SetLastError(ERROR_FILE_NOT_FOUND
);
2057 urlcache_set_entry_info((entry_url
*)pEntry
, lpCacheEntryInfo
, dwFieldControl
);
2059 cache_container_unlock_index(pContainer
, pHeader
);
2064 /***********************************************************************
2065 * SetUrlCacheEntryInfoW (WININET.@)
2067 BOOL WINAPI
SetUrlCacheEntryInfoW(LPCWSTR lpszUrl
,
2068 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
2069 DWORD dwFieldControl
)
2074 if(!urlcache_encode_url_alloc(lpszUrl
, &url
))
2077 ret
= SetUrlCacheEntryInfoA(url
, (INTERNET_CACHE_ENTRY_INFOA
*)lpCacheEntryInfo
, dwFieldControl
);
2082 static BOOL
urlcache_entry_get_file(const char *url
, void *entry_info
, DWORD
*size
, BOOL unicode
)
2084 urlcache_header
*header
;
2085 struct hash_entry
*hash_entry
;
2086 entry_url
*url_entry
;
2087 cache_container
*container
;
2090 TRACE("(%s, %p, %p, %x)\n", debugstr_a(url
), entry_info
, size
, unicode
);
2092 if(!url
|| !size
|| (!entry_info
&& *size
)) {
2093 SetLastError(ERROR_INVALID_PARAMETER
);
2097 error
= cache_containers_find(url
, &container
);
2098 if(error
!= ERROR_SUCCESS
) {
2099 SetLastError(error
);
2103 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2104 if (error
!= ERROR_SUCCESS
) {
2105 SetLastError(error
);
2109 if (!(header
= cache_container_lock_index(container
)))
2112 if (!urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
2113 cache_container_unlock_index(container
, header
);
2114 TRACE("entry %s not found!\n", url
);
2115 SetLastError(ERROR_FILE_NOT_FOUND
);
2119 url_entry
= (entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
2120 if(url_entry
->header
.signature
!= URL_SIGNATURE
) {
2121 cache_container_unlock_index(container
, header
);
2122 FIXME("Trying to retrieve entry of unknown format %s\n",
2123 debugstr_an((LPSTR
)&url_entry
->header
.signature
, sizeof(DWORD
)));
2124 SetLastError(ERROR_FILE_NOT_FOUND
);
2128 if(!url_entry
->local_name_off
) {
2129 cache_container_unlock_index(container
, header
);
2130 SetLastError(ERROR_INVALID_DATA
);
2134 TRACE("Found URL: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->url_off
));
2135 TRACE("Header info: %s\n", debugstr_an((LPCSTR
)url_entry
+ url_entry
->header_info_off
,
2136 url_entry
->header_info_size
));
2138 error
= urlcache_copy_entry(container
, header
, entry_info
,
2139 size
, url_entry
, unicode
);
2140 if(error
!= ERROR_SUCCESS
) {
2141 cache_container_unlock_index(container
, header
);
2142 SetLastError(error
);
2145 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->local_name_off
));
2147 url_entry
->hit_rate
++;
2148 url_entry
->use_count
++;
2149 urlcache_hash_entry_set_flags(hash_entry
, HASHTABLE_LOCK
);
2150 GetSystemTimeAsFileTime(&url_entry
->access_time
);
2152 cache_container_unlock_index(container
, header
);
2157 /***********************************************************************
2158 * RetrieveUrlCacheEntryFileA (WININET.@)
2161 BOOL WINAPI
RetrieveUrlCacheEntryFileA(LPCSTR lpszUrlName
,
2162 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
2163 LPDWORD lpdwCacheEntryInfoBufferSize
, DWORD dwReserved
)
2165 return urlcache_entry_get_file(lpszUrlName
, lpCacheEntryInfo
,
2166 lpdwCacheEntryInfoBufferSize
, FALSE
);
2169 /***********************************************************************
2170 * RetrieveUrlCacheEntryFileW (WININET.@)
2173 BOOL WINAPI
RetrieveUrlCacheEntryFileW(LPCWSTR lpszUrlName
,
2174 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
2175 LPDWORD lpdwCacheEntryInfoBufferSize
, DWORD dwReserved
)
2180 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
2183 ret
= urlcache_entry_get_file(url
, lpCacheEntryInfo
,
2184 lpdwCacheEntryInfoBufferSize
, TRUE
);
2189 static BOOL
urlcache_entry_delete(const cache_container
*pContainer
,
2190 urlcache_header
*pHeader
, struct hash_entry
*pHashEntry
)
2192 entry_header
*pEntry
;
2193 entry_url
* pUrlEntry
;
2195 pEntry
= (entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2196 if (pEntry
->signature
!= URL_SIGNATURE
)
2198 FIXME("Trying to delete entry of unknown format %s\n",
2199 debugstr_an((LPCSTR
)&pEntry
->signature
, sizeof(DWORD
)));
2200 SetLastError(ERROR_FILE_NOT_FOUND
);
2204 pUrlEntry
= (entry_url
*)pEntry
;
2205 if(urlcache_hash_entry_is_locked(pHashEntry
, pUrlEntry
))
2207 TRACE("Trying to delete locked entry\n");
2208 pUrlEntry
->cache_entry_type
|= PENDING_DELETE_CACHE_ENTRY
;
2209 SetLastError(ERROR_SHARING_VIOLATION
);
2213 if(!urlcache_delete_file(pContainer
, pHeader
, pUrlEntry
))
2215 urlcache_entry_free(pHeader
, pEntry
);
2219 /* Add entry to leaked files list */
2220 pUrlEntry
->header
.signature
= LEAK_SIGNATURE
;
2221 pUrlEntry
->exempt_delta
= pHeader
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
];
2222 pHeader
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
] = pHashEntry
->offset
;
2225 urlcache_hash_entry_delete(pHashEntry
);
2229 static HANDLE free_cache_running
;
2230 static HANDLE dll_unload_event
;
2231 static DWORD WINAPI
handle_full_cache_worker(void *param
)
2233 FreeUrlCacheSpaceW(NULL
, 20, 0);
2234 ReleaseSemaphore(free_cache_running
, 1, NULL
);
2238 static void handle_full_cache(void)
2240 if(WaitForSingleObject(free_cache_running
, 0) == WAIT_OBJECT_0
) {
2241 if(!QueueUserWorkItem(handle_full_cache_worker
, NULL
, 0))
2242 ReleaseSemaphore(free_cache_running
, 1, NULL
);
2246 /* Enumerates entries in cache, allows cache unlocking between calls. */
2247 static BOOL
urlcache_next_entry(urlcache_header
*header
, DWORD
*hash_table_off
, DWORD
*hash_table_entry
,
2248 struct hash_entry
**hash_entry
, entry_header
**entry
)
2250 entry_hash_table
*hashtable_entry
;
2255 if(!*hash_table_off
) {
2256 *hash_table_off
= header
->hash_table_off
;
2257 *hash_table_entry
= 0;
2259 hashtable_entry
= urlcache_get_hash_table(header
, *hash_table_off
);
2261 if(*hash_table_off
>= header
->size
) {
2262 *hash_table_off
= 0;
2266 hashtable_entry
= urlcache_get_hash_table(header
, *hash_table_off
);
2269 if(hashtable_entry
->header
.signature
!= HASH_SIGNATURE
) {
2270 *hash_table_off
= 0;
2275 if(*hash_table_entry
>= HASHTABLE_SIZE
) {
2276 *hash_table_off
= hashtable_entry
->next
;
2277 if(!*hash_table_off
) {
2278 *hash_table_off
= 0;
2282 hashtable_entry
= urlcache_get_hash_table(header
, *hash_table_off
);
2283 *hash_table_entry
= 0;
2286 if(hashtable_entry
->hash_table
[*hash_table_entry
].key
!= HASHTABLE_DEL
&&
2287 hashtable_entry
->hash_table
[*hash_table_entry
].key
!= HASHTABLE_FREE
) {
2288 *hash_entry
= &hashtable_entry
->hash_table
[*hash_table_entry
];
2289 *entry
= (entry_header
*)((LPBYTE
)header
+ hashtable_entry
->hash_table
[*hash_table_entry
].offset
);
2290 (*hash_table_entry
)++;
2294 (*hash_table_entry
)++;
2297 *hash_table_off
= 0;
2301 /* Rates an urlcache entry to determine if it can be deleted.
2303 * Score 0 means that entry can safely be removed, the bigger rating
2304 * the smaller chance of entry being removed.
2305 * DWORD_MAX means that entry can't be deleted at all.
2307 * Rating system is currently not fully compatible with native implementation.
2309 static DWORD
urlcache_rate_entry(entry_url
*url_entry
, FILETIME
*cur_time
)
2311 ULARGE_INTEGER time
, access_time
;
2314 access_time
.u
.LowPart
= url_entry
->access_time
.dwLowDateTime
;
2315 access_time
.u
.HighPart
= url_entry
->access_time
.dwHighDateTime
;
2317 time
.u
.LowPart
= cur_time
->dwLowDateTime
;
2318 time
.u
.HighPart
= cur_time
->dwHighDateTime
;
2320 /* Don't touch entries that were added less than 10 minutes ago */
2321 if(time
.QuadPart
< access_time
.QuadPart
+ (ULONGLONG
)10*60*FILETIME_SECOND
)
2324 if(url_entry
->cache_entry_type
& STICKY_CACHE_ENTRY
)
2325 if(time
.QuadPart
< access_time
.QuadPart
+ (ULONGLONG
)url_entry
->exempt_delta
*FILETIME_SECOND
)
2328 time
.QuadPart
= (time
.QuadPart
-access_time
.QuadPart
)/FILETIME_SECOND
;
2329 rating
= 400*60*60*24/(60*60*24+time
.QuadPart
);
2331 if(url_entry
->hit_rate
> 100)
2334 rating
+= url_entry
->hit_rate
;
2339 static int dword_cmp(const void *p1
, const void *p2
)
2341 return *(const DWORD
*)p1
- *(const DWORD
*)p2
;
2344 /***********************************************************************
2345 * FreeUrlCacheSpaceW (WININET.@)
2347 * Frees up some cache.
2350 * cache_path [I] Which volume to free up from, or NULL if you don't care.
2351 * size [I] Percentage of the cache that should be free.
2352 * filter [I] Which entries can't be deleted (CacheEntryType)
2355 * TRUE success. FALSE failure.
2358 * This implementation just retrieves the path of the cache directory, and
2359 * deletes its contents from the filesystem. The correct approach would
2360 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
2362 BOOL WINAPI
FreeUrlCacheSpaceW(LPCWSTR cache_path
, DWORD size
, DWORD filter
)
2364 cache_container
*container
;
2365 DWORD path_len
, err
;
2367 TRACE("(%s, %x, %x)\n", debugstr_w(cache_path
), size
, filter
);
2369 if(size
<1 || size
>100) {
2370 SetLastError(ERROR_INVALID_PARAMETER
);
2375 path_len
= strlenW(cache_path
);
2376 if(cache_path
[path_len
-1] == '\\')
2382 if(size
==100 && !filter
) {
2383 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
2385 /* When cache_path==NULL only clean Temporary Internet Files */
2386 if((!path_len
&& container
->cache_prefix
[0]==0) ||
2387 (path_len
&& !strncmpiW(container
->path
, cache_path
, path_len
) &&
2388 (container
->path
[path_len
]=='\0' || container
->path
[path_len
]=='\\')))
2392 WaitForSingleObject(container
->mutex
, INFINITE
);
2394 /* unlock, delete, recreate and lock cache */
2395 cache_container_close_index(container
);
2396 ret_del
= cache_container_delete_dir(container
->path
);
2397 err
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2399 ReleaseMutex(container
->mutex
);
2400 if(!ret_del
|| (err
!= ERROR_SUCCESS
))
2408 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
2410 urlcache_header
*header
;
2411 struct hash_entry
*hash_entry
;
2412 entry_header
*entry
;
2413 entry_url
*url_entry
;
2414 ULONGLONG desired_size
, cur_size
;
2415 DWORD delete_factor
, hash_table_off
, hash_table_entry
;
2416 DWORD rate
[100], rate_no
;
2419 if((path_len
|| container
->cache_prefix
[0]!=0) &&
2420 (!path_len
|| strncmpiW(container
->path
, cache_path
, path_len
) ||
2421 (container
->path
[path_len
]!='\0' && container
->path
[path_len
]!='\\')))
2424 err
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2425 if(err
!= ERROR_SUCCESS
)
2428 header
= cache_container_lock_index(container
);
2432 urlcache_clean_leaked_entries(container
, header
);
2434 desired_size
= header
->cache_limit
.QuadPart
*(100-size
)/100;
2435 cur_size
= header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
;
2436 if(cur_size
<= desired_size
)
2439 delete_factor
= (cur_size
-desired_size
)*100/cur_size
;
2441 if(!delete_factor
) {
2442 cache_container_unlock_index(container
, header
);
2447 hash_table_entry
= 0;
2449 GetSystemTimeAsFileTime(&cur_time
);
2450 while(rate_no
<sizeof(rate
)/sizeof(*rate
) &&
2451 urlcache_next_entry(header
, &hash_table_off
, &hash_table_entry
, &hash_entry
, &entry
)) {
2452 if(entry
->signature
!= URL_SIGNATURE
) {
2453 WARN("only url entries are currently supported\n");
2457 url_entry
= (entry_url
*)entry
;
2458 if(url_entry
->cache_entry_type
& filter
)
2461 rate
[rate_no
] = urlcache_rate_entry(url_entry
, &cur_time
);
2462 if(rate
[rate_no
] != -1)
2467 TRACE("nothing to delete\n");
2468 cache_container_unlock_index(container
, header
);
2472 qsort(rate
, rate_no
, sizeof(DWORD
), dword_cmp
);
2474 delete_factor
= delete_factor
*rate_no
/100;
2475 delete_factor
= rate
[delete_factor
];
2476 TRACE("deleting files with rating %d or less\n", delete_factor
);
2479 while(urlcache_next_entry(header
, &hash_table_off
, &hash_table_entry
, &hash_entry
, &entry
)) {
2480 if(entry
->signature
!= URL_SIGNATURE
)
2483 url_entry
= (entry_url
*)entry
;
2484 if(url_entry
->cache_entry_type
& filter
)
2487 if(urlcache_rate_entry(url_entry
, &cur_time
) <= delete_factor
) {
2488 TRACE("deleting file: %s\n", debugstr_a((char*)url_entry
+url_entry
->local_name_off
));
2489 urlcache_entry_delete(container
, header
, hash_entry
);
2491 if(header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
<= desired_size
)
2494 /* Allow other threads to use cache while cleaning */
2495 cache_container_unlock_index(container
, header
);
2496 if(WaitForSingleObject(dll_unload_event
, 0) == WAIT_OBJECT_0
) {
2497 TRACE("got dll_unload_event - finishing\n");
2501 header
= cache_container_lock_index(container
);
2505 TRACE("cache size after cleaning 0x%s/0x%s\n",
2506 wine_dbgstr_longlong(header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
),
2507 wine_dbgstr_longlong(header
->cache_limit
.QuadPart
));
2508 cache_container_unlock_index(container
, header
);
2514 /***********************************************************************
2515 * FreeUrlCacheSpaceA (WININET.@)
2517 * See FreeUrlCacheSpaceW.
2519 BOOL WINAPI
FreeUrlCacheSpaceA(LPCSTR lpszCachePath
, DWORD dwSize
, DWORD dwFilter
)
2522 LPWSTR path
= heap_strdupAtoW(lpszCachePath
);
2523 if (lpszCachePath
== NULL
|| path
!= NULL
)
2524 ret
= FreeUrlCacheSpaceW(path
, dwSize
, dwFilter
);
2529 /***********************************************************************
2530 * UnlockUrlCacheEntryFileA (WININET.@)
2533 BOOL WINAPI
UnlockUrlCacheEntryFileA(LPCSTR lpszUrlName
, DWORD dwReserved
)
2535 urlcache_header
*pHeader
;
2536 struct hash_entry
*pHashEntry
;
2537 entry_header
*pEntry
;
2538 entry_url
* pUrlEntry
;
2539 cache_container
*pContainer
;
2542 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName
), dwReserved
);
2546 ERR("dwReserved != 0\n");
2547 SetLastError(ERROR_INVALID_PARAMETER
);
2551 error
= cache_containers_find(lpszUrlName
, &pContainer
);
2552 if (error
!= ERROR_SUCCESS
)
2554 SetLastError(error
);
2558 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
2559 if (error
!= ERROR_SUCCESS
)
2561 SetLastError(error
);
2565 if (!(pHeader
= cache_container_lock_index(pContainer
)))
2568 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
2570 cache_container_unlock_index(pContainer
, pHeader
);
2571 TRACE("entry %s not found!\n", lpszUrlName
);
2572 SetLastError(ERROR_FILE_NOT_FOUND
);
2576 pEntry
= (entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2577 if (pEntry
->signature
!= URL_SIGNATURE
)
2579 cache_container_unlock_index(pContainer
, pHeader
);
2580 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR
)&pEntry
->signature
, sizeof(DWORD
)));
2581 SetLastError(ERROR_FILE_NOT_FOUND
);
2585 pUrlEntry
= (entry_url
*)pEntry
;
2587 if (pUrlEntry
->use_count
== 0)
2589 cache_container_unlock_index(pContainer
, pHeader
);
2592 pUrlEntry
->use_count
--;
2593 if (!pUrlEntry
->use_count
)
2595 urlcache_hash_entry_set_flags(pHashEntry
, HASHTABLE_URL
);
2596 if (pUrlEntry
->cache_entry_type
& PENDING_DELETE_CACHE_ENTRY
)
2597 urlcache_entry_delete(pContainer
, pHeader
, pHashEntry
);
2600 cache_container_unlock_index(pContainer
, pHeader
);
2605 /***********************************************************************
2606 * UnlockUrlCacheEntryFileW (WININET.@)
2609 BOOL WINAPI
UnlockUrlCacheEntryFileW(LPCWSTR lpszUrlName
, DWORD dwReserved
)
2614 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
2617 ret
= UnlockUrlCacheEntryFileA(url
, dwReserved
);
2622 static BOOL
urlcache_entry_create(const char *url
, const char *ext
, WCHAR
*full_path
)
2624 cache_container
*container
;
2625 urlcache_header
*header
;
2626 char file_name
[MAX_PATH
];
2627 WCHAR extW
[MAX_PATH
];
2630 BOOL generate_name
= FALSE
;
2637 TRACE("(%s, %s, %p)\n", debugstr_a(url
), debugstr_a(ext
), full_path
);
2639 memset(&uc
, 0, sizeof(uc
));
2640 uc
.dwStructSize
= sizeof(uc
);
2641 uc
.dwUrlPathLength
= 1;
2642 uc
.dwExtraInfoLength
= 1;
2643 if(!InternetCrackUrlA(url
, 0, 0, &uc
))
2644 uc
.dwUrlPathLength
= 0;
2646 if(!uc
.dwUrlPathLength
) {
2651 p
= e
= uc
.lpszUrlPath
+uc
.dwUrlPathLength
;
2652 while(p
>uc
.lpszUrlPath
&& *(p
-1)!='/' && *(p
-1)!='\\' && *(p
-1)!='.')
2654 if(p
>uc
.lpszUrlPath
&& *(p
-1)=='.') {
2656 while(p
>uc
.lpszUrlPath
&& *(p
-1)!='/' && *(p
-1)!='\\')
2660 memcpy(file_name
, p
, e
-p
);
2663 for(p
=file_name
; *p
; p
++) {
2676 generate_name
= TRUE
;
2678 error
= cache_containers_find(url
, &container
);
2679 if(error
!= ERROR_SUCCESS
) {
2680 SetLastError(error
);
2684 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2685 if(error
!= ERROR_SUCCESS
) {
2686 SetLastError(error
);
2690 if(!(header
= cache_container_lock_index(container
)))
2694 cache_dir
= (BYTE
)(rand() % header
->dirs_no
);
2696 cache_dir
= CACHE_CONTAINER_NO_SUBDIR
;
2698 full_path_len
= MAX_PATH
* sizeof(WCHAR
);
2699 if(!urlcache_create_file_pathW(container
, header
, file_name
, cache_dir
, full_path
, &full_path_len
)) {
2700 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2701 debugstr_a(file_name
), full_path_len
);
2702 cache_container_unlock_index(container
, header
);
2705 full_path_len
= full_path_len
/sizeof(WCHAR
) - 1;
2707 cache_container_unlock_index(container
, header
);
2713 MultiByteToWideChar(CP_ACP
, 0, ext
, -1, extW
+1, MAX_PATH
-1);
2715 for(p
=extW
; *p
; p
++) {
2725 if(p
[-1]==' ' || p
[-1]=='.')
2731 for(i
=0; i
<255 && !generate_name
; i
++) {
2732 static const WCHAR format
[] = {'[','%','u',']','%','s',0};
2734 wsprintfW(full_path
+full_path_len
, format
, i
, extW
);
2736 TRACE("Trying: %s\n", debugstr_w(full_path
));
2737 file
= CreateFileW(full_path
, GENERIC_READ
, 0, NULL
, CREATE_NEW
, 0, NULL
);
2738 if(file
!= INVALID_HANDLE_VALUE
) {
2744 /* Try to generate random name */
2745 GetSystemTimeAsFileTime(&ft
);
2746 strcpyW(full_path
+full_path_len
+8, extW
);
2748 for(i
=0; i
<255; i
++) {
2750 ULONGLONG n
= ft
.dwHighDateTime
;
2752 n
+= ft
.dwLowDateTime
;
2753 n
^= (ULONGLONG
)i
<<48;
2755 for(j
=0; j
<8; j
++) {
2758 full_path
[full_path_len
+j
] = (r
< 10 ? '0' + r
: 'A' + r
- 10);
2761 TRACE("Trying: %s\n", debugstr_w(full_path
));
2762 file
= CreateFileW(full_path
, GENERIC_READ
, 0, NULL
, CREATE_NEW
, 0, NULL
);
2763 if(file
!= INVALID_HANDLE_VALUE
) {
2769 WARN("Could not find a unique filename\n");
2773 /***********************************************************************
2774 * CreateUrlCacheEntryA (WININET.@)
2777 BOOL WINAPI
CreateUrlCacheEntryA(LPCSTR lpszUrlName
, DWORD dwExpectedFileSize
,
2778 LPCSTR lpszFileExtension
, LPSTR lpszFileName
, DWORD dwReserved
)
2780 WCHAR file_name
[MAX_PATH
];
2783 FIXME("dwReserved 0x%08x\n", dwReserved
);
2785 if(!urlcache_entry_create(lpszUrlName
, lpszFileExtension
, file_name
))
2788 if(!WideCharToMultiByte(CP_ACP
, 0, file_name
, -1, lpszFileName
, MAX_PATH
, NULL
, NULL
))
2792 /***********************************************************************
2793 * CreateUrlCacheEntryW (WININET.@)
2796 BOOL WINAPI
CreateUrlCacheEntryW(LPCWSTR lpszUrlName
, DWORD dwExpectedFileSize
,
2797 LPCWSTR lpszFileExtension
, LPWSTR lpszFileName
, DWORD dwReserved
)
2799 char *url
, *ext
= NULL
;
2803 FIXME("dwReserved 0x%08x\n", dwReserved
);
2805 if(lpszFileExtension
) {
2806 ext
= heap_strdupWtoUTF8(lpszFileExtension
);
2811 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
)) {
2816 ret
= urlcache_entry_create(url
, ext
, lpszFileName
);
2822 static BOOL
urlcache_entry_commit(const char *url
, const WCHAR
*file_name
,
2823 FILETIME expire_time
, FILETIME modify_time
, DWORD entry_type
,
2824 BYTE
*header_info
, DWORD header_size
, const char *file_ext
,
2825 const char *original_url
)
2827 cache_container
*container
;
2828 urlcache_header
*header
;
2829 struct hash_entry
*hash_entry
;
2830 entry_header
*entry
;
2831 entry_url
*url_entry
;
2832 DWORD url_entry_offset
;
2833 DWORD size
= DWORD_ALIGN(sizeof(*url_entry
));
2834 DWORD file_name_off
= 0;
2835 DWORD header_info_off
= 0;
2836 DWORD file_ext_off
= 0;
2837 WIN32_FILE_ATTRIBUTE_DATA file_attr
;
2838 LARGE_INTEGER file_size
;
2840 char file_name_no_container
[MAX_PATH
];
2841 char *local_file_name
= 0;
2843 DWORD exempt_delta
= 0;
2846 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n", debugstr_a(url
), debugstr_w(file_name
),
2847 entry_type
, header_info
, header_size
, debugstr_a(file_ext
), debugstr_a(original_url
));
2849 if(entry_type
& STICKY_CACHE_ENTRY
&& !file_name
) {
2850 SetLastError(ERROR_INVALID_PARAMETER
);
2854 WARN(": original_url ignored\n");
2856 memset(&file_attr
, 0, sizeof(file_attr
));
2858 if(!GetFileAttributesExW(file_name
, GetFileExInfoStandard
, &file_attr
))
2861 file_size
.u
.LowPart
= file_attr
.nFileSizeLow
;
2862 file_size
.u
.HighPart
= file_attr
.nFileSizeHigh
;
2864 error
= cache_containers_find(url
, &container
);
2865 if(error
!= ERROR_SUCCESS
) {
2866 SetLastError(error
);
2870 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2871 if(error
!= ERROR_SUCCESS
) {
2872 SetLastError(error
);
2876 if(!(header
= cache_container_lock_index(container
)))
2879 if(urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
2880 entry_url
*url_entry
= (entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
2882 if(urlcache_hash_entry_is_locked(hash_entry
, url_entry
)) {
2883 TRACE("Trying to overwrite locked entry\n");
2884 cache_container_unlock_index(container
, header
);
2885 SetLastError(ERROR_SHARING_VIOLATION
);
2889 hit_rate
= url_entry
->hit_rate
;
2890 exempt_delta
= url_entry
->exempt_delta
;
2891 urlcache_entry_delete(container
, header
, hash_entry
);
2897 dir_id
= CACHE_CONTAINER_NO_SUBDIR
;
2900 BOOL bFound
= FALSE
;
2902 if(strncmpW(file_name
, container
->path
, lstrlenW(container
->path
))) {
2903 ERR("path %s must begin with cache content path %s\n", debugstr_w(file_name
), debugstr_w(container
->path
));
2904 cache_container_unlock_index(container
, header
);
2905 SetLastError(ERROR_INVALID_PARAMETER
);
2909 /* skip container path prefix */
2910 file_name
+= lstrlenW(container
->path
);
2912 WideCharToMultiByte(CP_ACP
, 0, file_name
, -1, file_name_no_container
, MAX_PATH
, NULL
, NULL
);
2913 local_file_name
= file_name_no_container
;
2915 if(header
->dirs_no
) {
2916 for(dir_id
= 0; dir_id
< header
->dirs_no
; dir_id
++) {
2917 if(!strncmp(header
->directory_data
[dir_id
].name
, local_file_name
, DIR_LENGTH
)) {
2924 ERR("cache directory not found in path %s\n", debugstr_w(file_name
));
2925 cache_container_unlock_index(container
, header
);
2926 SetLastError(ERROR_INVALID_PARAMETER
);
2930 file_name
+= DIR_LENGTH
+ 1;
2931 local_file_name
+= DIR_LENGTH
+ 1;
2935 size
= DWORD_ALIGN(size
+ strlen(url
) + 1);
2937 file_name_off
= size
;
2938 size
= DWORD_ALIGN(size
+ strlen(local_file_name
) + 1);
2940 if(header_info
&& header_size
) {
2941 header_info_off
= size
;
2942 size
= DWORD_ALIGN(size
+ header_size
);
2944 if(file_ext
&& (file_ext_off
= strlen(file_ext
))) {
2945 DWORD len
= file_ext_off
;
2947 file_ext_off
= size
;
2948 size
= DWORD_ALIGN(size
+ len
+ 1);
2951 /* round up to next block */
2952 if(size
% BLOCKSIZE
) {
2953 size
-= size
% BLOCKSIZE
;
2957 error
= urlcache_entry_alloc(header
, size
/ BLOCKSIZE
, &entry
);
2958 while(error
== ERROR_HANDLE_DISK_FULL
) {
2959 error
= cache_container_clean_index(container
, &header
);
2960 if(error
== ERROR_SUCCESS
)
2961 error
= urlcache_entry_alloc(header
, size
/ BLOCKSIZE
, &entry
);
2963 if(error
!= ERROR_SUCCESS
) {
2964 cache_container_unlock_index(container
, header
);
2965 SetLastError(error
);
2969 /* FindFirstFreeEntry fills in blocks used */
2970 url_entry
= (entry_url
*)entry
;
2971 url_entry_offset
= (LPBYTE
)url_entry
- (LPBYTE
)header
;
2972 url_entry
->header
.signature
= URL_SIGNATURE
;
2973 url_entry
->cache_dir
= dir_id
;
2974 url_entry
->cache_entry_type
= entry_type
| container
->default_entry_type
;
2975 url_entry
->header_info_size
= header_size
;
2976 if((entry_type
& STICKY_CACHE_ENTRY
) && !exempt_delta
) {
2977 /* Sticky entries have a default exempt time of one day */
2978 exempt_delta
= 86400;
2980 url_entry
->exempt_delta
= exempt_delta
;
2981 url_entry
->hit_rate
= hit_rate
+1;
2982 url_entry
->file_extension_off
= file_ext_off
;
2983 url_entry
->header_info_off
= header_info_off
;
2984 url_entry
->local_name_off
= file_name_off
;
2985 url_entry
->url_off
= DWORD_ALIGN(sizeof(*url_entry
));
2986 url_entry
->size
.QuadPart
= file_size
.QuadPart
;
2987 url_entry
->use_count
= 0;
2988 GetSystemTimeAsFileTime(&url_entry
->access_time
);
2989 url_entry
->modification_time
= modify_time
;
2990 file_time_to_dos_date_time(&url_entry
->access_time
, &url_entry
->sync_date
, &url_entry
->sync_time
);
2991 file_time_to_dos_date_time(&expire_time
, &url_entry
->expire_date
, &url_entry
->expire_time
);
2992 file_time_to_dos_date_time(&file_attr
.ftLastWriteTime
, &url_entry
->write_date
, &url_entry
->write_time
);
2995 url_entry
->unk1
= 0;
2996 url_entry
->unk2
= 0;
2997 url_entry
->unk3
= 0x60;
2998 url_entry
->unk4
= 0;
2999 url_entry
->unk5
= 0x1010;
3000 url_entry
->unk7
= 0;
3001 url_entry
->unk8
= 0;
3004 strcpy((LPSTR
)url_entry
+ url_entry
->url_off
, url
);
3006 strcpy((LPSTR
)((LPBYTE
)url_entry
+ file_name_off
), local_file_name
);
3008 memcpy((LPBYTE
)url_entry
+ header_info_off
, header_info
, header_size
);
3010 strcpy((LPSTR
)((LPBYTE
)url_entry
+ file_ext_off
), file_ext
);
3012 error
= urlcache_hash_entry_create(header
, url
, url_entry_offset
, HASHTABLE_URL
);
3013 while(error
== ERROR_HANDLE_DISK_FULL
) {
3014 error
= cache_container_clean_index(container
, &header
);
3015 if(error
== ERROR_SUCCESS
) {
3016 url_entry
= (entry_url
*)((LPBYTE
)header
+ url_entry_offset
);
3017 error
= urlcache_hash_entry_create(header
, url
,
3018 url_entry_offset
, HASHTABLE_URL
);
3021 if(error
!= ERROR_SUCCESS
) {
3022 urlcache_entry_free(header
, &url_entry
->header
);
3023 cache_container_unlock_index(container
, header
);
3024 SetLastError(error
);
3028 if(url_entry
->cache_dir
< header
->dirs_no
)
3029 header
->directory_data
[url_entry
->cache_dir
].files_no
++;
3030 if(entry_type
& STICKY_CACHE_ENTRY
)
3031 header
->exempt_usage
.QuadPart
+= file_size
.QuadPart
;
3033 header
->cache_usage
.QuadPart
+= file_size
.QuadPart
;
3034 if(header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
> header
->cache_limit
.QuadPart
)
3035 handle_full_cache();
3037 cache_container_unlock_index(container
, header
);
3041 /***********************************************************************
3042 * CommitUrlCacheEntryA (WININET.@)
3044 BOOL WINAPI
CommitUrlCacheEntryA(LPCSTR lpszUrlName
, LPCSTR lpszLocalFileName
,
3045 FILETIME ExpireTime
, FILETIME LastModifiedTime
, DWORD CacheEntryType
,
3046 LPBYTE lpHeaderInfo
, DWORD dwHeaderSize
, LPCSTR lpszFileExtension
, LPCSTR lpszOriginalUrl
)
3048 WCHAR
*file_name
= NULL
;
3051 if(lpszLocalFileName
) {
3052 file_name
= heap_strdupAtoW(lpszLocalFileName
);
3057 ret
= urlcache_entry_commit(lpszUrlName
, file_name
, ExpireTime
, LastModifiedTime
,
3058 CacheEntryType
, lpHeaderInfo
, dwHeaderSize
, lpszFileExtension
, lpszOriginalUrl
);
3059 heap_free(file_name
);
3063 /***********************************************************************
3064 * CommitUrlCacheEntryW (WININET.@)
3066 BOOL WINAPI
CommitUrlCacheEntryW(LPCWSTR lpszUrlName
, LPCWSTR lpszLocalFileName
,
3067 FILETIME ExpireTime
, FILETIME LastModifiedTime
, DWORD CacheEntryType
,
3068 LPWSTR lpHeaderInfo
, DWORD dwHeaderSize
, LPCWSTR lpszFileExtension
, LPCWSTR lpszOriginalUrl
)
3070 char *url
, *original_url
=NULL
, *file_ext
=NULL
, *header_info
=NULL
;
3073 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
3077 header_info
= heap_strdupWtoUTF8(lpHeaderInfo
);
3082 dwHeaderSize
= strlen(header_info
);
3085 if(lpszFileExtension
) {
3086 file_ext
= heap_strdupWtoA(lpszFileExtension
);
3089 heap_free(header_info
);
3094 if(lpszOriginalUrl
&& !urlcache_encode_url_alloc(lpszOriginalUrl
, &original_url
)) {
3096 heap_free(header_info
);
3097 heap_free(file_ext
);
3101 ret
= urlcache_entry_commit(url
, lpszLocalFileName
, ExpireTime
, LastModifiedTime
,
3102 CacheEntryType
, (BYTE
*)header_info
, dwHeaderSize
, file_ext
, original_url
);
3104 heap_free(header_info
);
3105 heap_free(file_ext
);
3106 heap_free(original_url
);
3110 /***********************************************************************
3111 * ReadUrlCacheEntryStream (WININET.@)
3114 BOOL WINAPI
ReadUrlCacheEntryStream(
3115 IN HANDLE hUrlCacheStream
,
3116 IN DWORD dwLocation
,
3117 IN OUT LPVOID lpBuffer
,
3118 IN OUT LPDWORD lpdwLen
,
3122 /* Get handle to file from 'stream' */
3123 stream_handle
*pStream
= (stream_handle
*)hUrlCacheStream
;
3125 if (dwReserved
!= 0)
3127 ERR("dwReserved != 0\n");
3128 SetLastError(ERROR_INVALID_PARAMETER
);
3132 if (IsBadReadPtr(pStream
, sizeof(*pStream
)) || IsBadStringPtrA(pStream
->url
, INTERNET_MAX_URL_LENGTH
))
3134 SetLastError(ERROR_INVALID_HANDLE
);
3138 if (SetFilePointer(pStream
->file
, dwLocation
, NULL
, FILE_CURRENT
) == INVALID_SET_FILE_POINTER
)
3140 return ReadFile(pStream
->file
, lpBuffer
, *lpdwLen
, lpdwLen
, NULL
);
3143 /***********************************************************************
3144 * RetrieveUrlCacheEntryStreamA (WININET.@)
3147 HANDLE WINAPI
RetrieveUrlCacheEntryStreamA(LPCSTR lpszUrlName
,
3148 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
3149 LPDWORD lpdwCacheEntryInfoBufferSize
, BOOL fRandomRead
, DWORD dwReserved
)
3151 /* NOTE: this is not the same as the way that the native
3152 * version allocates 'stream' handles. I did it this way
3153 * as it is much easier and no applications should depend
3154 * on this behaviour. (Native version appears to allocate
3155 * indices into a table)
3157 stream_handle
*stream
;
3160 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName
), lpCacheEntryInfo
,
3161 lpdwCacheEntryInfoBufferSize
, fRandomRead
, dwReserved
);
3163 if(!RetrieveUrlCacheEntryFileA(lpszUrlName
, lpCacheEntryInfo
,
3164 lpdwCacheEntryInfoBufferSize
, dwReserved
))
3167 file
= CreateFileA(lpCacheEntryInfo
->lpszLocalFileName
, GENERIC_READ
, FILE_SHARE_READ
,
3168 NULL
, OPEN_EXISTING
, fRandomRead
? FILE_FLAG_RANDOM_ACCESS
: 0, NULL
);
3169 if(file
== INVALID_HANDLE_VALUE
) {
3170 UnlockUrlCacheEntryFileA(lpszUrlName
, 0);
3174 /* allocate handle storage space */
3175 stream
= heap_alloc(sizeof(stream_handle
) + strlen(lpszUrlName
) * sizeof(CHAR
));
3178 UnlockUrlCacheEntryFileA(lpszUrlName
, 0);
3179 SetLastError(ERROR_OUTOFMEMORY
);
3183 stream
->file
= file
;
3184 strcpy(stream
->url
, lpszUrlName
);
3188 /***********************************************************************
3189 * RetrieveUrlCacheEntryStreamW (WININET.@)
3192 HANDLE WINAPI
RetrieveUrlCacheEntryStreamW(LPCWSTR lpszUrlName
,
3193 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
3194 LPDWORD lpdwCacheEntryInfoBufferSize
, BOOL fRandomRead
, DWORD dwReserved
)
3197 /* NOTE: this is not the same as the way that the native
3198 * version allocates 'stream' handles. I did it this way
3199 * as it is much easier and no applications should depend
3200 * on this behaviour. (Native version appears to allocate
3201 * indices into a table)
3203 stream_handle
*stream
;
3206 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName
), lpCacheEntryInfo
,
3207 lpdwCacheEntryInfoBufferSize
, fRandomRead
, dwReserved
);
3209 if(!(len
= urlcache_encode_url(lpszUrlName
, NULL
, 0)))
3212 if(!RetrieveUrlCacheEntryFileW(lpszUrlName
, lpCacheEntryInfo
,
3213 lpdwCacheEntryInfoBufferSize
, dwReserved
))
3216 file
= CreateFileW(lpCacheEntryInfo
->lpszLocalFileName
, GENERIC_READ
, FILE_SHARE_READ
,
3217 NULL
, OPEN_EXISTING
, fRandomRead
? FILE_FLAG_RANDOM_ACCESS
: 0, NULL
);
3218 if(file
== INVALID_HANDLE_VALUE
) {
3219 UnlockUrlCacheEntryFileW(lpszUrlName
, 0);
3223 /* allocate handle storage space */
3224 stream
= heap_alloc(sizeof(stream_handle
) + len
*sizeof(WCHAR
));
3227 UnlockUrlCacheEntryFileW(lpszUrlName
, 0);
3228 SetLastError(ERROR_OUTOFMEMORY
);
3232 stream
->file
= file
;
3233 if(!urlcache_encode_url(lpszUrlName
, stream
->url
, len
)) {
3235 UnlockUrlCacheEntryFileW(lpszUrlName
, 0);
3242 /***********************************************************************
3243 * UnlockUrlCacheEntryStream (WININET.@)
3246 BOOL WINAPI
UnlockUrlCacheEntryStream(
3247 IN HANDLE hUrlCacheStream
,
3251 stream_handle
*pStream
= (stream_handle
*)hUrlCacheStream
;
3253 if (dwReserved
!= 0)
3255 ERR("dwReserved != 0\n");
3256 SetLastError(ERROR_INVALID_PARAMETER
);
3260 if (IsBadReadPtr(pStream
, sizeof(*pStream
)) || IsBadStringPtrA(pStream
->url
, INTERNET_MAX_URL_LENGTH
))
3262 SetLastError(ERROR_INVALID_HANDLE
);
3266 if (!UnlockUrlCacheEntryFileA(pStream
->url
, 0))
3269 CloseHandle(pStream
->file
);
3275 /***********************************************************************
3276 * DeleteUrlCacheEntryA (WININET.@)
3279 BOOL WINAPI
DeleteUrlCacheEntryA(LPCSTR lpszUrlName
)
3281 cache_container
*pContainer
;
3282 urlcache_header
*pHeader
;
3283 struct hash_entry
*pHashEntry
;
3287 TRACE("(%s)\n", debugstr_a(lpszUrlName
));
3289 error
= cache_containers_find(lpszUrlName
, &pContainer
);
3290 if (error
!= ERROR_SUCCESS
)
3292 SetLastError(error
);
3296 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
3297 if (error
!= ERROR_SUCCESS
)
3299 SetLastError(error
);
3303 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3306 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
3308 cache_container_unlock_index(pContainer
, pHeader
);
3309 TRACE("entry %s not found!\n", lpszUrlName
);
3310 SetLastError(ERROR_FILE_NOT_FOUND
);
3314 ret
= urlcache_entry_delete(pContainer
, pHeader
, pHashEntry
);
3316 cache_container_unlock_index(pContainer
, pHeader
);
3321 /***********************************************************************
3322 * DeleteUrlCacheEntryW (WININET.@)
3325 BOOL WINAPI
DeleteUrlCacheEntryW(LPCWSTR lpszUrlName
)
3330 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
3333 ret
= DeleteUrlCacheEntryA(url
);
3338 BOOL WINAPI
DeleteUrlCacheContainerA(DWORD d1
, DWORD d2
)
3340 FIXME("(0x%08x, 0x%08x) stub\n", d1
, d2
);
3344 BOOL WINAPI
DeleteUrlCacheContainerW(DWORD d1
, DWORD d2
)
3346 FIXME("(0x%08x, 0x%08x) stub\n", d1
, d2
);
3350 /***********************************************************************
3351 * CreateCacheContainerA (WININET.@)
3353 BOOL WINAPI
CreateUrlCacheContainerA(DWORD d1
, DWORD d2
, DWORD d3
, DWORD d4
,
3354 DWORD d5
, DWORD d6
, DWORD d7
, DWORD d8
)
3356 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3357 d1
, d2
, d3
, d4
, d5
, d6
, d7
, d8
);
3361 /***********************************************************************
3362 * CreateCacheContainerW (WININET.@)
3364 BOOL WINAPI
CreateUrlCacheContainerW(DWORD d1
, DWORD d2
, DWORD d3
, DWORD d4
,
3365 DWORD d5
, DWORD d6
, DWORD d7
, DWORD d8
)
3367 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3368 d1
, d2
, d3
, d4
, d5
, d6
, d7
, d8
);
3372 /***********************************************************************
3373 * FindFirstUrlCacheContainerA (WININET.@)
3375 HANDLE WINAPI
FindFirstUrlCacheContainerA( LPVOID p1
, LPVOID p2
, LPVOID p3
, DWORD d1
)
3377 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1
, p2
, p3
, d1
);
3381 /***********************************************************************
3382 * FindFirstUrlCacheContainerW (WININET.@)
3384 HANDLE WINAPI
FindFirstUrlCacheContainerW( LPVOID p1
, LPVOID p2
, LPVOID p3
, DWORD d1
)
3386 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1
, p2
, p3
, d1
);
3390 /***********************************************************************
3391 * FindNextUrlCacheContainerA (WININET.@)
3393 BOOL WINAPI
FindNextUrlCacheContainerA( HANDLE handle
, LPVOID p1
, LPVOID p2
)
3395 FIXME("(%p, %p, %p) stub\n", handle
, p1
, p2
);
3399 /***********************************************************************
3400 * FindNextUrlCacheContainerW (WININET.@)
3402 BOOL WINAPI
FindNextUrlCacheContainerW( HANDLE handle
, LPVOID p1
, LPVOID p2
)
3404 FIXME("(%p, %p, %p) stub\n", handle
, p1
, p2
);
3408 HANDLE WINAPI
FindFirstUrlCacheEntryExA(
3409 LPCSTR lpszUrlSearchPattern
,
3413 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
,
3414 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3416 LPDWORD pcbReserved2
,
3420 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern
),
3421 dwFlags
, dwFilter
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
, lpFirstCacheEntryInfo
,
3422 lpdwFirstCacheEntryInfoBufferSize
, lpReserved
, pcbReserved2
,lpReserved3
);
3423 SetLastError(ERROR_FILE_NOT_FOUND
);
3427 HANDLE WINAPI
FindFirstUrlCacheEntryExW(
3428 LPCWSTR lpszUrlSearchPattern
,
3432 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
,
3433 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3435 LPDWORD pcbReserved2
,
3439 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern
),
3440 dwFlags
, dwFilter
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
, lpFirstCacheEntryInfo
,
3441 lpdwFirstCacheEntryInfoBufferSize
, lpReserved
, pcbReserved2
,lpReserved3
);
3442 SetLastError(ERROR_FILE_NOT_FOUND
);
3446 /***********************************************************************
3447 * FindFirstUrlCacheEntryA (WININET.@)
3450 INTERNETAPI HANDLE WINAPI
FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern
,
3451 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
, LPDWORD lpdwFirstCacheEntryInfoBufferSize
)
3453 find_handle
*pEntryHandle
;
3455 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern
), lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
);
3457 pEntryHandle
= heap_alloc(sizeof(*pEntryHandle
));
3461 pEntryHandle
->magic
= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
;
3462 if (lpszUrlSearchPattern
)
3464 pEntryHandle
->url_search_pattern
= heap_strdupA(lpszUrlSearchPattern
);
3465 if (!pEntryHandle
->url_search_pattern
)
3467 heap_free(pEntryHandle
);
3472 pEntryHandle
->url_search_pattern
= NULL
;
3473 pEntryHandle
->container_idx
= 0;
3474 pEntryHandle
->hash_table_idx
= 0;
3475 pEntryHandle
->hash_entry_idx
= 0;
3477 if (!FindNextUrlCacheEntryA(pEntryHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
))
3479 heap_free(pEntryHandle
);
3482 return pEntryHandle
;
3485 /***********************************************************************
3486 * FindFirstUrlCacheEntryW (WININET.@)
3489 INTERNETAPI HANDLE WINAPI
FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern
,
3490 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
, LPDWORD lpdwFirstCacheEntryInfoBufferSize
)
3492 find_handle
*pEntryHandle
;
3494 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern
), lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
);
3496 pEntryHandle
= heap_alloc(sizeof(*pEntryHandle
));
3500 pEntryHandle
->magic
= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
;
3501 if (lpszUrlSearchPattern
)
3503 pEntryHandle
->url_search_pattern
= heap_strdupWtoA(lpszUrlSearchPattern
);
3504 if (!pEntryHandle
->url_search_pattern
)
3506 heap_free(pEntryHandle
);
3511 pEntryHandle
->url_search_pattern
= NULL
;
3512 pEntryHandle
->container_idx
= 0;
3513 pEntryHandle
->hash_table_idx
= 0;
3514 pEntryHandle
->hash_entry_idx
= 0;
3516 if (!FindNextUrlCacheEntryW(pEntryHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
))
3518 heap_free(pEntryHandle
);
3521 return pEntryHandle
;
3524 static BOOL
urlcache_find_next_entry(
3526 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo
,
3527 LPDWORD lpdwNextCacheEntryInfoBufferSize
,
3530 find_handle
*pEntryHandle
= (find_handle
*)hEnumHandle
;
3531 cache_container
*pContainer
;
3533 if (pEntryHandle
->magic
!= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
)
3535 SetLastError(ERROR_INVALID_HANDLE
);
3539 for (; cache_containers_enum(pEntryHandle
->url_search_pattern
, pEntryHandle
->container_idx
, &pContainer
);
3540 pEntryHandle
->container_idx
++, pEntryHandle
->hash_table_idx
= 0)
3542 urlcache_header
*pHeader
;
3543 entry_hash_table
*pHashTableEntry
;
3546 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
3547 if (error
!= ERROR_SUCCESS
)
3549 SetLastError(error
);
3553 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3556 for (; urlcache_enum_hash_tables(pHeader
, &pEntryHandle
->hash_table_idx
, &pHashTableEntry
);
3557 pEntryHandle
->hash_table_idx
++, pEntryHandle
->hash_entry_idx
= 0)
3559 const struct hash_entry
*pHashEntry
= NULL
;
3560 for (; urlcache_enum_hash_table_entries(pHeader
, pHashTableEntry
, &pEntryHandle
->hash_entry_idx
, &pHashEntry
);
3561 pEntryHandle
->hash_entry_idx
++)
3563 const entry_url
*pUrlEntry
;
3564 const entry_header
*pEntry
= (const entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
3566 if (pEntry
->signature
!= URL_SIGNATURE
)
3569 pUrlEntry
= (const entry_url
*)pEntry
;
3570 TRACE("Found URL: %s\n",
3571 debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->url_off
));
3572 TRACE("Header info: %s\n",
3573 debugstr_an((LPCSTR
)pUrlEntry
+ pUrlEntry
->header_info_off
,
3574 pUrlEntry
->header_info_size
));
3576 error
= urlcache_copy_entry(
3579 lpNextCacheEntryInfo
,
3580 lpdwNextCacheEntryInfoBufferSize
,
3583 if (error
!= ERROR_SUCCESS
)
3585 cache_container_unlock_index(pContainer
, pHeader
);
3586 SetLastError(error
);
3589 if(pUrlEntry
->local_name_off
)
3590 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->local_name_off
));
3592 /* increment the current index so that next time the function
3593 * is called the next entry is returned */
3594 pEntryHandle
->hash_entry_idx
++;
3595 cache_container_unlock_index(pContainer
, pHeader
);
3600 cache_container_unlock_index(pContainer
, pHeader
);
3603 SetLastError(ERROR_NO_MORE_ITEMS
);
3607 /***********************************************************************
3608 * FindNextUrlCacheEntryA (WININET.@)
3610 BOOL WINAPI
FindNextUrlCacheEntryA(
3612 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo
,
3613 LPDWORD lpdwNextCacheEntryInfoBufferSize
)
3615 TRACE("(%p, %p, %p)\n", hEnumHandle
, lpNextCacheEntryInfo
, lpdwNextCacheEntryInfoBufferSize
);
3617 return urlcache_find_next_entry(hEnumHandle
, lpNextCacheEntryInfo
,
3618 lpdwNextCacheEntryInfoBufferSize
, FALSE
/* not UNICODE */);
3621 /***********************************************************************
3622 * FindNextUrlCacheEntryW (WININET.@)
3624 BOOL WINAPI
FindNextUrlCacheEntryW(
3626 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo
,
3627 LPDWORD lpdwNextCacheEntryInfoBufferSize
3630 TRACE("(%p, %p, %p)\n", hEnumHandle
, lpNextCacheEntryInfo
, lpdwNextCacheEntryInfoBufferSize
);
3632 return urlcache_find_next_entry(hEnumHandle
,
3633 (LPINTERNET_CACHE_ENTRY_INFOA
)lpNextCacheEntryInfo
,
3634 lpdwNextCacheEntryInfoBufferSize
, TRUE
/* UNICODE */);
3637 /***********************************************************************
3638 * FindCloseUrlCache (WININET.@)
3640 BOOL WINAPI
FindCloseUrlCache(HANDLE hEnumHandle
)
3642 find_handle
*pEntryHandle
= (find_handle
*)hEnumHandle
;
3644 TRACE("(%p)\n", hEnumHandle
);
3646 if (!pEntryHandle
|| pEntryHandle
->magic
!= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
)
3648 SetLastError(ERROR_INVALID_HANDLE
);
3652 pEntryHandle
->magic
= 0;
3653 heap_free(pEntryHandle
->url_search_pattern
);
3654 heap_free(pEntryHandle
);
3658 HANDLE WINAPI
FindFirstUrlCacheGroup( DWORD dwFlags
, DWORD dwFilter
, LPVOID lpSearchCondition
,
3659 DWORD dwSearchCondition
, GROUPID
* lpGroupId
, LPVOID lpReserved
)
3661 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags
, dwFilter
, lpSearchCondition
,
3662 dwSearchCondition
, lpGroupId
, lpReserved
);
3666 BOOL WINAPI
FindNextUrlCacheEntryExA(
3668 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
,
3669 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3671 LPDWORD pcbReserved2
,
3675 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
,
3676 lpReserved
, pcbReserved2
, lpReserved3
);
3680 BOOL WINAPI
FindNextUrlCacheEntryExW(
3682 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
,
3683 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3685 LPDWORD pcbReserved2
,
3689 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
,
3690 lpReserved
, pcbReserved2
, lpReserved3
);
3694 BOOL WINAPI
FindNextUrlCacheGroup( HANDLE hFind
, GROUPID
* lpGroupId
, LPVOID lpReserved
)
3696 FIXME("(%p, %p, %p) stub\n", hFind
, lpGroupId
, lpReserved
);
3700 /***********************************************************************
3701 * CreateUrlCacheGroup (WININET.@)
3704 INTERNETAPI GROUPID WINAPI
CreateUrlCacheGroup(DWORD dwFlags
, LPVOID lpReserved
)
3706 FIXME("(0x%08x, %p): stub\n", dwFlags
, lpReserved
);
3710 /***********************************************************************
3711 * DeleteUrlCacheGroup (WININET.@)
3714 BOOL WINAPI
DeleteUrlCacheGroup(GROUPID GroupId
, DWORD dwFlags
, LPVOID lpReserved
)
3716 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3717 (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
, dwFlags
, lpReserved
);
3721 /***********************************************************************
3722 * DeleteWpadCacheForNetworks (WININET.@)
3723 * Undocumented, added in IE8
3725 BOOL WINAPI
DeleteWpadCacheForNetworks(DWORD unk1
)
3727 FIXME("(%d) stub\n", unk1
);
3731 /***********************************************************************
3732 * SetUrlCacheEntryGroupA (WININET.@)
3735 BOOL WINAPI
SetUrlCacheEntryGroupA(LPCSTR lpszUrlName
, DWORD dwFlags
,
3736 GROUPID GroupId
, LPBYTE pbGroupAttributes
, DWORD cbGroupAttributes
,
3739 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3740 debugstr_a(lpszUrlName
), dwFlags
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
,
3741 pbGroupAttributes
, cbGroupAttributes
, lpReserved
);
3742 SetLastError(ERROR_FILE_NOT_FOUND
);
3746 /***********************************************************************
3747 * SetUrlCacheEntryGroupW (WININET.@)
3750 BOOL WINAPI
SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName
, DWORD dwFlags
,
3751 GROUPID GroupId
, LPBYTE pbGroupAttributes
, DWORD cbGroupAttributes
,
3754 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3755 debugstr_w(lpszUrlName
), dwFlags
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
,
3756 pbGroupAttributes
, cbGroupAttributes
, lpReserved
);
3757 SetLastError(ERROR_FILE_NOT_FOUND
);
3761 /***********************************************************************
3762 * GetUrlCacheConfigInfoW (WININET.@)
3764 BOOL WINAPI
GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo
, LPDWORD size
, DWORD bitmask
)
3766 FIXME("(%p, %p, %x)\n", CacheInfo
, size
, bitmask
);
3767 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
3771 /***********************************************************************
3772 * GetUrlCacheConfigInfoA (WININET.@)
3774 BOOL WINAPI
GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo
, LPDWORD size
, DWORD bitmask
)
3776 FIXME("(%p, %p, %x)\n", CacheInfo
, size
, bitmask
);
3777 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
3781 BOOL WINAPI
GetUrlCacheGroupAttributeA( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3782 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo
,
3783 LPDWORD lpdwGroupInfo
, LPVOID lpReserved
)
3785 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3786 (ULONG
)(gid
>> 32), (ULONG
)gid
, dwFlags
, dwAttributes
, lpGroupInfo
,
3787 lpdwGroupInfo
, lpReserved
);
3791 BOOL WINAPI
GetUrlCacheGroupAttributeW( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3792 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo
,
3793 LPDWORD lpdwGroupInfo
, LPVOID lpReserved
)
3795 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3796 (ULONG
)(gid
>> 32), (ULONG
)gid
, dwFlags
, dwAttributes
, lpGroupInfo
,
3797 lpdwGroupInfo
, lpReserved
);
3801 BOOL WINAPI
SetUrlCacheGroupAttributeA( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3802 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo
, LPVOID lpReserved
)
3804 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3805 (ULONG
)(gid
>> 32), (ULONG
)gid
, dwFlags
, dwAttributes
, lpGroupInfo
, lpReserved
);
3809 BOOL WINAPI
SetUrlCacheGroupAttributeW( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3810 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo
, LPVOID lpReserved
)
3812 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3813 (ULONG
)(gid
>> 32), (ULONG
)gid
, dwFlags
, dwAttributes
, lpGroupInfo
, lpReserved
);
3817 BOOL WINAPI
SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo
, DWORD dwFieldControl
)
3819 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo
, dwFieldControl
);
3823 BOOL WINAPI
SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo
, DWORD dwFieldControl
)
3825 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo
, dwFieldControl
);
3829 /***********************************************************************
3830 * DeleteIE3Cache (WININET.@)
3832 * Deletes the files used by the IE3 URL caching system.
3835 * hWnd [I] A dummy window.
3836 * hInst [I] Instance of process calling the function.
3837 * lpszCmdLine [I] Options used by function.
3838 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3840 DWORD WINAPI
DeleteIE3Cache(HWND hWnd
, HINSTANCE hInst
, LPSTR lpszCmdLine
, int nCmdShow
)
3842 FIXME("(%p, %p, %s, %d)\n", hWnd
, hInst
, debugstr_a(lpszCmdLine
), nCmdShow
);
3846 static BOOL
urlcache_entry_is_expired(const entry_url
*pUrlEntry
,
3847 FILETIME
*pftLastModified
)
3850 FILETIME now
, expired
;
3852 *pftLastModified
= pUrlEntry
->modification_time
;
3853 GetSystemTimeAsFileTime(&now
);
3854 dos_date_time_to_file_time(pUrlEntry
->expire_date
,
3855 pUrlEntry
->expire_time
, &expired
);
3856 /* If the expired time is 0, it's interpreted as not expired */
3857 if (!expired
.dwLowDateTime
&& !expired
.dwHighDateTime
)
3860 ret
= CompareFileTime(&expired
, &now
) < 0;
3864 /***********************************************************************
3865 * IsUrlCacheEntryExpiredA (WININET.@)
3869 * dwFlags [I] Unknown
3870 * pftLastModified [O] Last modified time
3872 BOOL WINAPI
IsUrlCacheEntryExpiredA(LPCSTR url
, DWORD dwFlags
, FILETIME
* pftLastModified
)
3874 urlcache_header
*pHeader
;
3875 struct hash_entry
*pHashEntry
;
3876 const entry_header
*pEntry
;
3877 const entry_url
* pUrlEntry
;
3878 cache_container
*pContainer
;
3881 TRACE("(%s, %08x, %p)\n", debugstr_a(url
), dwFlags
, pftLastModified
);
3883 if (!url
|| !pftLastModified
)
3886 FIXME("unknown flags 0x%08x\n", dwFlags
);
3888 /* Any error implies that the URL is expired, i.e. not in the cache */
3889 if (cache_containers_find(url
, &pContainer
))
3891 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3895 if (cache_container_open_index(pContainer
, MIN_BLOCK_NO
))
3897 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3901 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3903 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3907 if (!urlcache_find_hash_entry(pHeader
, url
, &pHashEntry
))
3909 cache_container_unlock_index(pContainer
, pHeader
);
3910 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3911 TRACE("entry %s not found!\n", url
);
3915 pEntry
= (const entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
3916 if (pEntry
->signature
!= URL_SIGNATURE
)
3918 cache_container_unlock_index(pContainer
, pHeader
);
3919 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3920 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR
)&pEntry
->signature
, sizeof(DWORD
)));
3924 pUrlEntry
= (const entry_url
*)pEntry
;
3925 expired
= urlcache_entry_is_expired(pUrlEntry
, pftLastModified
);
3927 cache_container_unlock_index(pContainer
, pHeader
);
3932 /***********************************************************************
3933 * IsUrlCacheEntryExpiredW (WININET.@)
3937 * dwFlags [I] Unknown
3938 * pftLastModified [O] Last modified time
3940 BOOL WINAPI
IsUrlCacheEntryExpiredW(LPCWSTR url
, DWORD dwFlags
, FILETIME
* pftLastModified
)
3945 if(!urlcache_encode_url_alloc(url
, &encoded_url
))
3948 ret
= IsUrlCacheEntryExpiredA(encoded_url
, dwFlags
, pftLastModified
);
3949 heap_free(encoded_url
);
3953 /***********************************************************************
3954 * GetDiskInfoA (WININET.@)
3956 BOOL WINAPI
GetDiskInfoA(PCSTR path
, PDWORD cluster_size
, PDWORDLONG free
, PDWORDLONG total
)
3959 ULARGE_INTEGER bytes_free
, bytes_total
;
3961 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path
), cluster_size
, free
, total
);
3965 SetLastError(ERROR_INVALID_PARAMETER
);
3969 if ((ret
= GetDiskFreeSpaceExA(path
, NULL
, &bytes_total
, &bytes_free
)))
3971 if (cluster_size
) *cluster_size
= 1;
3972 if (free
) *free
= bytes_free
.QuadPart
;
3973 if (total
) *total
= bytes_total
.QuadPart
;
3978 /***********************************************************************
3979 * RegisterUrlCacheNotification (WININET.@)
3981 DWORD WINAPI
RegisterUrlCacheNotification(LPVOID a
, DWORD b
, DWORD c
, DWORD d
, DWORD e
, DWORD f
)
3983 FIXME("(%p %x %x %x %x %x)\n", a
, b
, c
, d
, e
, f
);
3987 /***********************************************************************
3988 * IncrementUrlCacheHeaderData (WININET.@)
3990 BOOL WINAPI
IncrementUrlCacheHeaderData(DWORD index
, LPDWORD data
)
3992 FIXME("(%u, %p)\n", index
, data
);
3996 /***********************************************************************
3997 * RunOnceUrlCache (WININET.@)
4000 DWORD WINAPI
RunOnceUrlCache(HWND hwnd
, HINSTANCE hinst
, LPSTR cmd
, int cmdshow
)
4002 FIXME("(%p, %p, %s, %d): stub\n", hwnd
, hinst
, debugstr_a(cmd
), cmdshow
);
4006 BOOL
init_urlcache(void)
4008 dll_unload_event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
4009 if(!dll_unload_event
)
4012 free_cache_running
= CreateSemaphoreW(NULL
, 1, 1, NULL
);
4013 if(!free_cache_running
) {
4014 CloseHandle(dll_unload_event
);
4018 cache_containers_init();
4022 void free_urlcache(void)
4024 SetEvent(dll_unload_event
);
4025 WaitForSingleObject(free_cache_running
, INFINITE
);
4026 ReleaseSemaphore(free_cache_running
, 1, NULL
);
4027 CloseHandle(free_cache_running
);
4028 CloseHandle(dll_unload_event
);
4030 cache_containers_free();
4033 /***********************************************************************
4034 * LoadUrlCacheContent (WININET.@)
4036 BOOL WINAPI
LoadUrlCacheContent(void)