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
25 #define NONAMELESSUNION
48 #include "wine/unicode.h"
49 #include "wine/debug.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(wininet
);
53 static const char urlcache_ver_prefix
[] = "WINE URLCache Ver ";
54 static const char urlcache_ver
[] = "0.2012001";
57 #define CHAR_BIT (8 * sizeof(CHAR))
60 #define ENTRY_START_OFFSET 0x4000
62 #define MAX_DIR_NO 0x20
64 #define HASHTABLE_SIZE 448
65 #define HASHTABLE_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
66 #define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
67 #define ALLOCATION_TABLE_OFFSET 0x250
68 #define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
69 #define MIN_BLOCK_NO 0x80
70 #define MAX_BLOCK_NO (ALLOCATION_TABLE_SIZE * CHAR_BIT)
71 #define FILE_SIZE(blocks) ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
73 #define HASHTABLE_URL 0
74 #define HASHTABLE_DEL 1
75 #define HASHTABLE_LOCK 2
76 #define HASHTABLE_FREE 3
77 #define HASHTABLE_REDR 5
78 #define HASHTABLE_FLAG_BITS 6
80 #define PENDING_DELETE_CACHE_ENTRY 0x00400000
81 #define INSTALLED_CACHE_ENTRY 0x10000000
82 #define GET_INSTALLED_ENTRY 0x200
83 #define CACHE_CONTAINER_NO_SUBDIR 0xFE
85 #define CACHE_HEADER_DATA_ROOT_LEAK_OFFSET 0x16
87 #define FILETIME_SECOND 10000000
89 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
90 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
91 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
92 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
93 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
95 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
97 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
102 DWORD blocks_used
; /* number of 128byte blocks used by this entry */
108 FILETIME modification_time
;
109 FILETIME access_time
;
110 WORD expire_date
; /* expire date in dos format */
111 WORD expire_time
; /* expire time in dos format */
112 DWORD unk1
; /* usually zero */
113 ULARGE_INTEGER size
; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
114 DWORD unk2
; /* usually zero */
115 DWORD exempt_delta
; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
116 DWORD unk3
; /* usually 0x60 */
117 DWORD url_off
; /* offset of start of url from start of entry */
118 BYTE cache_dir
; /* index of cache directory this url is stored in */
119 BYTE unk4
; /* usually zero */
120 WORD unk5
; /* usually 0x1010 */
121 DWORD local_name_off
; /* offset of start of local filename from start of entry */
122 DWORD cache_entry_type
; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
123 DWORD header_info_off
; /* offset of start of header info from start of entry */
124 DWORD header_info_size
;
125 DWORD file_extension_off
; /* offset of start of file extension from start of entry */
126 WORD sync_date
; /* last sync date in dos format */
127 WORD sync_time
; /* last sync time in dos format */
128 DWORD hit_rate
; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
129 DWORD use_count
; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
132 DWORD unk7
; /* usually zero */
133 DWORD unk8
; /* usually zero */
134 /* packing to dword align start of next field */
135 /* CHAR szSourceUrlName[]; (url) */
136 /* packing to dword align start of next field */
137 /* CHAR szLocalFileName[]; (local file name excluding path) */
138 /* packing to dword align start of next field */
139 /* CHAR szHeaderInfo[]; (header info) */
153 struct hash_entry hash_table
[HASHTABLE_SIZE
];
160 DWORD hash_table_off
;
161 DWORD capacity_in_blocks
;
164 ULARGE_INTEGER cache_limit
;
165 ULARGE_INTEGER cache_usage
;
166 ULARGE_INTEGER exempt_usage
;
168 struct _directory_data
171 char name
[DIR_LENGTH
];
172 } directory_data
[MAX_DIR_NO
];
174 BYTE allocation_table
[ALLOCATION_TABLE_SIZE
];
185 struct list entry
; /* part of a list */
186 char *cache_prefix
; /* string that has to be prefixed for this container to be used */
187 LPWSTR path
; /* path to url container directory */
188 HANDLE mapping
; /* handle of file mapping */
189 DWORD file_size
; /* size of file when mapping was opened */
190 HANDLE mutex
; /* handle of mutex */
191 DWORD default_entry_type
;
197 char *url_search_pattern
;
199 DWORD hash_table_idx
;
200 DWORD hash_entry_idx
;
203 /* List of all containers available */
204 static struct list UrlContainers
= LIST_INIT(UrlContainers
);
206 static inline char *heap_strdupWtoUTF8(LPCWSTR str
)
211 DWORD size
= WideCharToMultiByte(CP_UTF8
, 0, str
, -1, NULL
, 0, NULL
, NULL
);
212 ret
= heap_alloc(size
);
214 WideCharToMultiByte(CP_UTF8
, 0, str
, -1, ret
, size
, NULL
, NULL
);
220 /***********************************************************************
221 * urlcache_block_is_free (Internal)
223 * Is the specified block number free?
230 static inline BYTE
urlcache_block_is_free(BYTE
*allocation_table
, DWORD block_number
)
232 BYTE mask
= 1 << (block_number
%CHAR_BIT
);
233 return (allocation_table
[block_number
/CHAR_BIT
] & mask
) == 0;
236 /***********************************************************************
237 * urlcache_block_free (Internal)
239 * Marks the specified block as free
242 * this function is not updating used blocks count
248 static inline void urlcache_block_free(BYTE
*allocation_table
, DWORD block_number
)
250 BYTE mask
= ~(1 << (block_number
%CHAR_BIT
));
251 allocation_table
[block_number
/CHAR_BIT
] &= mask
;
254 /***********************************************************************
255 * urlcache_block_alloc (Internal)
257 * Marks the specified block as allocated
260 * this function is not updating used blocks count
266 static inline void urlcache_block_alloc(BYTE
*allocation_table
, DWORD block_number
)
268 BYTE mask
= 1 << (block_number
%CHAR_BIT
);
269 allocation_table
[block_number
/CHAR_BIT
] |= mask
;
272 /***********************************************************************
273 * urlcache_entry_alloc (Internal)
275 * Finds and allocates the first block of free space big enough and
276 * sets entry to point to it.
279 * ERROR_SUCCESS when free memory block was found
280 * Any other Win32 error code if the entry could not be added
283 static DWORD
urlcache_entry_alloc(urlcache_header
*header
, DWORD blocks_needed
, entry_header
**entry
)
285 DWORD block
, block_size
;
287 for(block
=0; block
<header
->capacity_in_blocks
; block
+=block_size
+1)
290 while(block_size
<blocks_needed
&& block_size
+block
<header
->capacity_in_blocks
291 && urlcache_block_is_free(header
->allocation_table
, block
+block_size
))
294 if(block_size
== blocks_needed
)
298 TRACE("Found free blocks starting at no. %d (0x%x)\n", block
, ENTRY_START_OFFSET
+block
*BLOCKSIZE
);
300 for(index
=0; index
<blocks_needed
; index
++)
301 urlcache_block_alloc(header
->allocation_table
, block
+index
);
303 *entry
= (entry_header
*)((BYTE
*)header
+ENTRY_START_OFFSET
+block
*BLOCKSIZE
);
304 for(index
=0; index
<blocks_needed
*BLOCKSIZE
/sizeof(DWORD
); index
++)
305 ((DWORD
*)*entry
)[index
] = 0xdeadbeef;
306 (*entry
)->blocks_used
= blocks_needed
;
308 header
->blocks_in_use
+= blocks_needed
;
309 return ERROR_SUCCESS
;
313 return ERROR_HANDLE_DISK_FULL
;
316 /***********************************************************************
317 * urlcache_entry_free (Internal)
319 * Deletes the specified entry and frees the space allocated to it
322 * TRUE if it succeeded
326 static BOOL
urlcache_entry_free(urlcache_header
*header
, entry_header
*entry
)
328 DWORD start_block
, block
;
330 /* update allocation table */
331 start_block
= ((DWORD
)((BYTE
*)entry
- (BYTE
*)header
) - ENTRY_START_OFFSET
) / BLOCKSIZE
;
332 for(block
= start_block
; block
< start_block
+entry
->blocks_used
; block
++)
333 urlcache_block_free(header
->allocation_table
, block
);
335 header
->blocks_in_use
-= entry
->blocks_used
;
339 /***********************************************************************
340 * urlcache_create_hash_table (Internal)
342 * Creates a new hash table in free space and adds it to the chain of existing
346 * ERROR_SUCCESS if the hash table was created
347 * ERROR_DISK_FULL if the hash table could not be created
350 static DWORD
urlcache_create_hash_table(urlcache_header
*header
, entry_hash_table
*hash_table_prev
, entry_hash_table
**hash_table
)
352 DWORD dwOffset
, error
;
355 if((error
= urlcache_entry_alloc(header
, 0x20, (entry_header
**)hash_table
)) != ERROR_SUCCESS
)
358 dwOffset
= (BYTE
*)*hash_table
-(BYTE
*)header
;
361 hash_table_prev
->next
= dwOffset
;
363 header
->hash_table_off
= dwOffset
;
365 (*hash_table
)->header
.signature
= HASH_SIGNATURE
;
366 (*hash_table
)->next
= 0;
367 (*hash_table
)->id
= hash_table_prev
? hash_table_prev
->id
+1 : 0;
368 for(i
= 0; i
< HASHTABLE_SIZE
; i
++) {
369 (*hash_table
)->hash_table
[i
].offset
= HASHTABLE_FREE
;
370 (*hash_table
)->hash_table
[i
].key
= HASHTABLE_FREE
;
372 return ERROR_SUCCESS
;
375 /***********************************************************************
376 * cache_container_create_object_name (Internal)
378 * Converts a path to a name suitable for use as a Win32 object name.
379 * Replaces '\\' characters in-place with the specified character
380 * (usually '_' or '!')
386 static void cache_container_create_object_name(LPWSTR lpszPath
, WCHAR replace
)
388 for (; *lpszPath
; lpszPath
++)
390 if (*lpszPath
== '\\')
395 /* Caller must hold container lock */
396 static HANDLE
cache_container_map_index(HANDLE file
, const WCHAR
*path
, DWORD size
, BOOL
*validate
)
398 static const WCHAR mapping_name_format
[]
399 = {'%','s','i','n','d','e','x','.','d','a','t','_','%','l','u',0};
400 WCHAR mapping_name
[MAX_PATH
];
403 wsprintfW(mapping_name
, mapping_name_format
, path
, size
);
404 cache_container_create_object_name(mapping_name
, '_');
406 mapping
= OpenFileMappingW(FILE_MAP_WRITE
, FALSE
, mapping_name
);
408 if(validate
) *validate
= FALSE
;
412 if(validate
) *validate
= TRUE
;
413 return CreateFileMappingW(file
, NULL
, PAGE_READWRITE
, 0, 0, mapping_name
);
416 /* Caller must hold container lock */
417 static DWORD
cache_container_set_size(cache_container
*container
, HANDLE file
, DWORD blocks_no
)
419 static const WCHAR cache_content_key
[] = {'S','o','f','t','w','a','r','e','\\',
420 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
421 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
422 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s','\\',
423 'C','a','c','h','e','\\','C','o','n','t','e','n','t',0};
424 static const WCHAR cache_limit
[] = {'C','a','c','h','e','L','i','m','i','t',0};
426 DWORD file_size
= FILE_SIZE(blocks_no
);
427 WCHAR dir_path
[MAX_PATH
], *dir_name
;
428 entry_hash_table
*hashtable_entry
;
429 urlcache_header
*header
;
435 if(SetFilePointer(file
, file_size
, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
436 return GetLastError();
438 if(!SetEndOfFile(file
))
439 return GetLastError();
441 mapping
= cache_container_map_index(file
, container
->path
, file_size
, NULL
);
443 return GetLastError();
445 header
= MapViewOfFile(mapping
, FILE_MAP_WRITE
, 0, 0, 0);
447 CloseHandle(mapping
);
448 return GetLastError();
451 if(blocks_no
!= MIN_BLOCK_NO
) {
452 if(file_size
> header
->size
)
453 memset((char*)header
+header
->size
, 0, file_size
-header
->size
);
454 header
->size
= file_size
;
455 header
->capacity_in_blocks
= blocks_no
;
457 UnmapViewOfFile(header
);
458 CloseHandle(container
->mapping
);
459 container
->mapping
= mapping
;
460 container
->file_size
= file_size
;
461 return ERROR_SUCCESS
;
464 memset(header
, 0, file_size
);
465 /* First set some constants and defaults in the header */
466 memcpy(header
->signature
, urlcache_ver_prefix
, sizeof(urlcache_ver_prefix
)-1);
467 memcpy(header
->signature
+sizeof(urlcache_ver_prefix
)-1, urlcache_ver
, sizeof(urlcache_ver
)-1);
468 header
->size
= file_size
;
469 header
->capacity_in_blocks
= blocks_no
;
470 /* 127MB - taken from default for Windows 2000 */
471 header
->cache_limit
.QuadPart
= 0x07ff5400;
472 /* Copied from a Windows 2000 cache index */
473 header
->dirs_no
= container
->default_entry_type
==NORMAL_CACHE_ENTRY
? 4 : 0;
475 /* If the registry has a cache size set, use the registry value */
476 if(RegOpenKeyW(HKEY_CURRENT_USER
, cache_content_key
, &key
) == ERROR_SUCCESS
) {
477 DWORD dw
, len
= sizeof(dw
), keytype
;
479 if(RegQueryValueExW(key
, cache_limit
, NULL
, &keytype
, (BYTE
*)&dw
, &len
) == ERROR_SUCCESS
&&
480 keytype
== REG_DWORD
)
481 header
->cache_limit
.QuadPart
= (ULONGLONG
)dw
* 1024;
485 urlcache_create_hash_table(header
, NULL
, &hashtable_entry
);
487 /* Last step - create the directories */
488 strcpyW(dir_path
, container
->path
);
489 dir_name
= dir_path
+ strlenW(dir_path
);
492 GetSystemTimeAsFileTime(&ft
);
494 for(i
=0; i
<header
->dirs_no
; ++i
) {
495 header
->directory_data
[i
].files_no
= 0;
497 ULONGLONG n
= ft
.dwHighDateTime
;
500 /* Generate a file name to attempt to create.
501 * This algorithm will create what will appear
502 * to be random and unrelated directory names
503 * of up to 9 characters in length.
506 n
+= ft
.dwLowDateTime
;
507 n
^= ((ULONGLONG
) i
<< 56) | ((ULONGLONG
) j
<< 48);
509 for(k
= 0; k
< 8; ++k
) {
512 /* Dividing by a prime greater than 36 helps
513 * with the appearance of randomness
518 dir_name
[k
] = '0' + r
;
520 dir_name
[k
] = 'A' + (r
- 10);
523 if(CreateDirectoryW(dir_path
, 0)) {
524 /* The following is OK because we generated an
525 * 8 character directory name made from characters
526 * [A-Z0-9], which are equivalent for all code
527 * pages and for UTF-16
529 for (k
= 0; k
< 8; ++k
)
530 header
->directory_data
[i
].name
[k
] = dir_name
[k
];
533 /* Give up. The most likely cause of this
534 * is a full disk, but whatever the cause
535 * is, it should be more than apparent that
538 UnmapViewOfFile(header
);
539 CloseHandle(mapping
);
540 return GetLastError();
545 UnmapViewOfFile(header
);
546 CloseHandle(container
->mapping
);
547 container
->mapping
= mapping
;
548 container
->file_size
= file_size
;
549 return ERROR_SUCCESS
;
552 static BOOL
cache_container_is_valid(urlcache_header
*header
, DWORD file_size
)
554 DWORD allocation_size
, count_bits
, i
;
556 if(file_size
< FILE_SIZE(MIN_BLOCK_NO
))
559 if(file_size
!= header
->size
)
562 if (!memcmp(header
->signature
, urlcache_ver_prefix
, sizeof(urlcache_ver_prefix
)-1) &&
563 memcmp(header
->signature
+sizeof(urlcache_ver_prefix
)-1, urlcache_ver
, sizeof(urlcache_ver
)-1))
566 if(FILE_SIZE(header
->capacity_in_blocks
) != file_size
)
570 for(i
=0; i
<header
->capacity_in_blocks
/8; i
++) {
571 for(count_bits
= header
->allocation_table
[i
]; count_bits
!=0; count_bits
>>=1) {
576 if(allocation_size
!= header
->blocks_in_use
)
579 for(; i
<ALLOCATION_TABLE_SIZE
; i
++) {
580 if(header
->allocation_table
[i
])
587 /***********************************************************************
588 * cache_container_open_index (Internal)
590 * Opens the index file and saves mapping handle
593 * ERROR_SUCCESS if succeeded
594 * Any other Win32 error code if failed
597 static DWORD
cache_container_open_index(cache_container
*container
, DWORD blocks_no
)
599 static const WCHAR index_dat
[] = {'i','n','d','e','x','.','d','a','t',0};
602 WCHAR index_path
[MAX_PATH
];
606 WaitForSingleObject(container
->mutex
, INFINITE
);
608 if(container
->mapping
) {
609 ReleaseMutex(container
->mutex
);
610 return ERROR_SUCCESS
;
613 strcpyW(index_path
, container
->path
);
614 strcatW(index_path
, index_dat
);
616 file
= CreateFileW(index_path
, GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_ALWAYS
, 0, NULL
);
617 if(file
== INVALID_HANDLE_VALUE
) {
618 /* Maybe the directory wasn't there? Try to create it */
619 if(CreateDirectoryW(container
->path
, 0))
620 file
= CreateFileW(index_path
, GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_ALWAYS
, 0, NULL
);
622 if(file
== INVALID_HANDLE_VALUE
) {
623 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(index_path
));
624 ReleaseMutex(container
->mutex
);
625 return GetLastError();
628 file_size
= GetFileSize(file
, NULL
);
629 if(file_size
== INVALID_FILE_SIZE
) {
631 ReleaseMutex(container
->mutex
);
632 return GetLastError();
635 if(blocks_no
< MIN_BLOCK_NO
)
636 blocks_no
= MIN_BLOCK_NO
;
637 else if(blocks_no
> MAX_BLOCK_NO
)
638 blocks_no
= MAX_BLOCK_NO
;
640 if(file_size
< FILE_SIZE(blocks_no
)) {
641 DWORD ret
= cache_container_set_size(container
, file
, blocks_no
);
643 ReleaseMutex(container
->mutex
);
647 container
->file_size
= file_size
;
648 container
->mapping
= cache_container_map_index(file
, container
->path
, file_size
, &validate
);
650 if(container
->mapping
&& validate
) {
651 urlcache_header
*header
= MapViewOfFile(container
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
653 if(header
&& !cache_container_is_valid(header
, file_size
)) {
654 WARN("detected old or broken index.dat file\n");
655 UnmapViewOfFile(header
);
656 FreeUrlCacheSpaceW(container
->path
, 100, 0);
658 UnmapViewOfFile(header
);
660 CloseHandle(container
->mapping
);
661 container
->mapping
= NULL
;
665 if(!container
->mapping
)
667 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
668 ReleaseMutex(container
->mutex
);
669 return GetLastError();
672 ReleaseMutex(container
->mutex
);
673 return ERROR_SUCCESS
;
676 /***********************************************************************
677 * cache_container_close_index (Internal)
685 static void cache_container_close_index(cache_container
*pContainer
)
687 CloseHandle(pContainer
->mapping
);
688 pContainer
->mapping
= NULL
;
691 static BOOL
cache_containers_add(const char *cache_prefix
, LPCWSTR path
,
692 DWORD default_entry_type
, LPWSTR mutex_name
)
694 cache_container
*pContainer
= heap_alloc(sizeof(cache_container
));
695 int cache_prefix_len
= strlen(cache_prefix
);
702 pContainer
->mapping
= NULL
;
703 pContainer
->file_size
= 0;
704 pContainer
->default_entry_type
= default_entry_type
;
706 pContainer
->path
= heap_strdupW(path
);
707 if (!pContainer
->path
)
709 heap_free(pContainer
);
713 pContainer
->cache_prefix
= heap_alloc(cache_prefix_len
+1);
714 if (!pContainer
->cache_prefix
)
716 heap_free(pContainer
->path
);
717 heap_free(pContainer
);
721 memcpy(pContainer
->cache_prefix
, cache_prefix
, cache_prefix_len
+1);
723 CharLowerW(mutex_name
);
724 cache_container_create_object_name(mutex_name
, '!');
726 if ((pContainer
->mutex
= CreateMutexW(NULL
, FALSE
, mutex_name
)) == NULL
)
728 ERR("couldn't create mutex (error is %d)\n", GetLastError());
729 heap_free(pContainer
->path
);
730 heap_free(pContainer
);
734 list_add_head(&UrlContainers
, &pContainer
->entry
);
739 static void cache_container_delete_container(cache_container
*pContainer
)
741 list_remove(&pContainer
->entry
);
743 cache_container_close_index(pContainer
);
744 CloseHandle(pContainer
->mutex
);
745 heap_free(pContainer
->path
);
746 heap_free(pContainer
->cache_prefix
);
747 heap_free(pContainer
);
750 static void cache_containers_init(void)
752 static const WCHAR UrlSuffix
[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
753 static const WCHAR HistorySuffix
[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
754 static const WCHAR CookieSuffix
[] = {0};
757 int nFolder
; /* CSIDL_* constant */
758 const WCHAR
*shpath_suffix
; /* suffix on path returned by SHGetSpecialFolderPath */
759 const char *cache_prefix
; /* prefix used to reference the container */
760 DWORD default_entry_type
;
761 } DefaultContainerData
[] =
763 { CSIDL_INTERNET_CACHE
, UrlSuffix
, "", NORMAL_CACHE_ENTRY
},
764 { CSIDL_HISTORY
, HistorySuffix
, "Visited:", URLHISTORY_CACHE_ENTRY
},
765 { CSIDL_COOKIES
, CookieSuffix
, "Cookie:", COOKIE_CACHE_ENTRY
},
769 for (i
= 0; i
< sizeof(DefaultContainerData
) / sizeof(DefaultContainerData
[0]); i
++)
771 WCHAR wszCachePath
[MAX_PATH
];
772 WCHAR wszMutexName
[MAX_PATH
];
773 int path_len
, suffix_len
;
776 if (!SHGetSpecialFolderPathW(NULL
, wszCachePath
, DefaultContainerData
[i
].nFolder
, TRUE
))
778 ERR("Couldn't get path for default container %u\n", i
);
781 path_len
= strlenW(wszCachePath
);
782 suffix_len
= strlenW(DefaultContainerData
[i
].shpath_suffix
);
784 if (path_len
+ suffix_len
+ 2 > MAX_PATH
)
786 ERR("Path too long\n");
790 wszCachePath
[path_len
] = '\\';
791 wszCachePath
[path_len
+1] = 0;
793 strcpyW(wszMutexName
, wszCachePath
);
797 memcpy(wszCachePath
+ path_len
+ 1, DefaultContainerData
[i
].shpath_suffix
, (suffix_len
+ 1) * sizeof(WCHAR
));
798 wszCachePath
[path_len
+ suffix_len
+ 1] = '\\';
799 wszCachePath
[path_len
+ suffix_len
+ 2] = '\0';
802 if (!WideCharToMultiByte(CP_ACP
, WC_NO_BEST_FIT_CHARS
, wszCachePath
, path_len
,
803 NULL
, 0, NULL
, &def_char
) || def_char
)
807 /* cannot convert path to ANSI code page */
808 if (!(path_len
= GetShortPathNameW(wszCachePath
, tmp
, MAX_PATH
)) ||
809 !WideCharToMultiByte(CP_ACP
, WC_NO_BEST_FIT_CHARS
, tmp
, path_len
,
810 NULL
, 0, NULL
, &def_char
) || def_char
)
811 ERR("Can't create container path accessible by ANSI functions\n");
813 memcpy(wszCachePath
, tmp
, (path_len
+1)*sizeof(WCHAR
));
816 cache_containers_add(DefaultContainerData
[i
].cache_prefix
, wszCachePath
,
817 DefaultContainerData
[i
].default_entry_type
, wszMutexName
);
821 static void cache_containers_free(void)
823 while(!list_empty(&UrlContainers
))
824 cache_container_delete_container(
825 LIST_ENTRY(list_head(&UrlContainers
), cache_container
, entry
)
829 static DWORD
cache_containers_find(const char *url
, cache_container
**ret
)
831 cache_container
*container
;
833 TRACE("searching for prefix for URL: %s\n", debugstr_a(url
));
836 return ERROR_INVALID_PARAMETER
;
838 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
840 int prefix_len
= strlen(container
->cache_prefix
);
842 if(!strncmp(container
->cache_prefix
, url
, prefix_len
)) {
843 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
845 return ERROR_SUCCESS
;
849 ERR("no container found\n");
850 return ERROR_FILE_NOT_FOUND
;
853 static BOOL
cache_containers_enum(char *search_pattern
, DWORD index
, cache_container
**ret
)
856 cache_container
*container
;
858 TRACE("searching for prefix: %s\n", debugstr_a(search_pattern
));
860 /* non-NULL search pattern only returns one container ever */
861 if (search_pattern
&& index
> 0)
864 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
868 if (!strcmp(container
->cache_prefix
, search_pattern
))
870 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
879 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
889 /***********************************************************************
890 * cache_container_lock_index (Internal)
892 * Locks the index for system-wide exclusive access.
895 * Cache file header if successful
896 * NULL if failed and calls SetLastError.
898 static urlcache_header
* cache_container_lock_index(cache_container
*pContainer
)
902 urlcache_header
* pHeader
;
906 WaitForSingleObject(pContainer
->mutex
, INFINITE
);
908 pIndexData
= MapViewOfFile(pContainer
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
912 ReleaseMutex(pContainer
->mutex
);
913 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
916 pHeader
= (urlcache_header
*)pIndexData
;
918 /* file has grown - we need to remap to prevent us getting
919 * access violations when we try and access beyond the end
920 * of the memory mapped file */
921 if (pHeader
->size
!= pContainer
->file_size
)
923 UnmapViewOfFile( pHeader
);
924 cache_container_close_index(pContainer
);
925 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
926 if (error
!= ERROR_SUCCESS
)
928 ReleaseMutex(pContainer
->mutex
);
932 pIndexData
= MapViewOfFile(pContainer
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
936 ReleaseMutex(pContainer
->mutex
);
937 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
940 pHeader
= (urlcache_header
*)pIndexData
;
943 TRACE("Signature: %s, file size: %d bytes\n", pHeader
->signature
, pHeader
->size
);
945 for (index
= 0; index
< pHeader
->dirs_no
; index
++)
947 TRACE("Directory[%d] = \"%.8s\"\n", index
, pHeader
->directory_data
[index
].name
);
953 /***********************************************************************
954 * cache_container_unlock_index (Internal)
957 static BOOL
cache_container_unlock_index(cache_container
*pContainer
, urlcache_header
*pHeader
)
960 ReleaseMutex(pContainer
->mutex
);
961 return UnmapViewOfFile(pHeader
);
964 /***********************************************************************
965 * urlcache_create_file_pathW (Internal)
967 * Copies the full path to the specified buffer given the local file
968 * name and the index of the directory it is in. Always sets value in
969 * lpBufferSize to the required buffer size (in bytes).
972 * TRUE if the buffer was big enough
973 * FALSE if the buffer was too small
976 static BOOL
urlcache_create_file_pathW(
977 const cache_container
*pContainer
,
978 const urlcache_header
*pHeader
,
979 LPCSTR szLocalFileName
,
986 int path_len
= strlenW(pContainer
->path
);
987 int file_name_len
= MultiByteToWideChar(CP_ACP
, 0, szLocalFileName
, -1, NULL
, 0);
988 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
&& Directory
>=pHeader
->dirs_no
)
994 nRequired
= (path_len
+ file_name_len
) * sizeof(WCHAR
);
995 if(Directory
!= CACHE_CONTAINER_NO_SUBDIR
)
996 nRequired
+= (DIR_LENGTH
+ 1) * sizeof(WCHAR
);
997 if (trunc_name
&& nRequired
>= *lpBufferSize
)
998 nRequired
= *lpBufferSize
;
999 if (nRequired
<= *lpBufferSize
)
1003 memcpy(wszPath
, pContainer
->path
, path_len
* sizeof(WCHAR
));
1004 if (Directory
!= CACHE_CONTAINER_NO_SUBDIR
)
1006 dir_len
= MultiByteToWideChar(CP_ACP
, 0, pHeader
->directory_data
[Directory
].name
, DIR_LENGTH
, wszPath
+ path_len
, DIR_LENGTH
);
1007 wszPath
[dir_len
+ path_len
] = '\\';
1014 MultiByteToWideChar(CP_ACP
, 0, szLocalFileName
, -1, wszPath
+ dir_len
+ path_len
,
1015 *lpBufferSize
/sizeof(WCHAR
)-dir_len
-path_len
);
1016 wszPath
[*lpBufferSize
/sizeof(WCHAR
)-1] = 0;
1017 *lpBufferSize
= nRequired
;
1020 *lpBufferSize
= nRequired
;
1024 /***********************************************************************
1025 * urlcache_create_file_pathA (Internal)
1027 * Copies the full path to the specified buffer given the local file
1028 * name and the index of the directory it is in. Always sets value in
1029 * lpBufferSize to the required buffer size.
1032 * TRUE if the buffer was big enough
1033 * FALSE if the buffer was too small
1036 static BOOL
urlcache_create_file_pathA(
1037 const cache_container
*pContainer
,
1038 const urlcache_header
*pHeader
,
1039 LPCSTR szLocalFileName
,
1042 LPLONG lpBufferSize
)
1045 int path_len
, file_name_len
, dir_len
;
1047 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
&& Directory
>=pHeader
->dirs_no
)
1053 path_len
= WideCharToMultiByte(CP_ACP
, 0, pContainer
->path
, -1, NULL
, 0, NULL
, NULL
) - 1;
1054 file_name_len
= strlen(szLocalFileName
) + 1 /* for nul-terminator */;
1055 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
)
1056 dir_len
= DIR_LENGTH
+1;
1060 nRequired
= (path_len
+ dir_len
+ file_name_len
) * sizeof(char);
1061 if (nRequired
<= *lpBufferSize
)
1063 WideCharToMultiByte(CP_ACP
, 0, pContainer
->path
, -1, szPath
, path_len
, NULL
, NULL
);
1065 memcpy(szPath
+path_len
, pHeader
->directory_data
[Directory
].name
, dir_len
-1);
1066 szPath
[path_len
+ dir_len
-1] = '\\';
1068 memcpy(szPath
+ path_len
+ dir_len
, szLocalFileName
, file_name_len
);
1069 *lpBufferSize
= nRequired
;
1072 *lpBufferSize
= nRequired
;
1076 /* Just like FileTimeToDosDateTime, except that it also maps the special
1077 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1079 static void file_time_to_dos_date_time(const FILETIME
*ft
, WORD
*fatdate
,
1082 if (!ft
->dwLowDateTime
&& !ft
->dwHighDateTime
)
1083 *fatdate
= *fattime
= 0;
1085 FileTimeToDosDateTime(ft
, fatdate
, fattime
);
1088 /***********************************************************************
1089 * urlcache_delete_file (Internal)
1091 static DWORD
urlcache_delete_file(const cache_container
*container
,
1092 urlcache_header
*header
, entry_url
*url_entry
)
1094 WIN32_FILE_ATTRIBUTE_DATA attr
;
1095 WCHAR path
[MAX_PATH
];
1096 LONG path_size
= sizeof(path
);
1100 if(!url_entry
->local_name_off
)
1103 if(!urlcache_create_file_pathW(container
, header
,
1104 (LPCSTR
)url_entry
+url_entry
->local_name_off
,
1105 url_entry
->cache_dir
, path
, &path_size
, FALSE
))
1108 if(!GetFileAttributesExW(path
, GetFileExInfoStandard
, &attr
))
1110 file_time_to_dos_date_time(&attr
.ftLastWriteTime
, &date
, &time
);
1111 if(date
!= url_entry
->write_date
|| time
!= url_entry
->write_time
)
1114 err
= (DeleteFileW(path
) ? ERROR_SUCCESS
: GetLastError());
1115 if(err
== ERROR_ACCESS_DENIED
|| err
== ERROR_SHARING_VIOLATION
)
1119 if (url_entry
->cache_dir
< header
->dirs_no
)
1121 if (header
->directory_data
[url_entry
->cache_dir
].files_no
)
1122 header
->directory_data
[url_entry
->cache_dir
].files_no
--;
1124 if (url_entry
->cache_entry_type
& STICKY_CACHE_ENTRY
)
1126 if (url_entry
->size
.QuadPart
< header
->exempt_usage
.QuadPart
)
1127 header
->exempt_usage
.QuadPart
-= url_entry
->size
.QuadPart
;
1129 header
->exempt_usage
.QuadPart
= 0;
1133 if (url_entry
->size
.QuadPart
< header
->cache_usage
.QuadPart
)
1134 header
->cache_usage
.QuadPart
-= url_entry
->size
.QuadPart
;
1136 header
->cache_usage
.QuadPart
= 0;
1139 return ERROR_SUCCESS
;
1142 static BOOL
urlcache_clean_leaked_entries(cache_container
*container
, urlcache_header
*header
)
1147 leak_off
= &header
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
];
1149 entry_url
*url_entry
= (entry_url
*)((LPBYTE
)header
+ *leak_off
);
1151 if(SUCCEEDED(urlcache_delete_file(container
, header
, url_entry
))) {
1152 *leak_off
= url_entry
->exempt_delta
;
1153 urlcache_entry_free(header
, &url_entry
->header
);
1156 leak_off
= &url_entry
->exempt_delta
;
1163 /***********************************************************************
1164 * cache_container_clean_index (Internal)
1166 * This function is meant to make place in index file by removing leaked
1167 * files entries and resizing the file.
1169 * CAUTION: file view may get mapped to new memory
1172 * ERROR_SUCCESS when new memory is available
1173 * error code otherwise
1175 static DWORD
cache_container_clean_index(cache_container
*container
, urlcache_header
**file_view
)
1177 urlcache_header
*header
= *file_view
;
1180 TRACE("(%s %s)\n", debugstr_a(container
->cache_prefix
), debugstr_w(container
->path
));
1182 if(urlcache_clean_leaked_entries(container
, header
))
1183 return ERROR_SUCCESS
;
1185 if(header
->size
>= ALLOCATION_TABLE_SIZE
*8*BLOCKSIZE
+ ENTRY_START_OFFSET
) {
1186 WARN("index file has maximal size\n");
1187 return ERROR_NOT_ENOUGH_MEMORY
;
1190 cache_container_close_index(container
);
1191 ret
= cache_container_open_index(container
, header
->capacity_in_blocks
*2);
1192 if(ret
!= ERROR_SUCCESS
)
1194 header
= MapViewOfFile(container
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
1196 return GetLastError();
1198 UnmapViewOfFile(*file_view
);
1199 *file_view
= header
;
1200 return ERROR_SUCCESS
;
1203 /* Just like DosDateTimeToFileTime, except that it also maps the special
1204 * case of a DOS date/time of (0,0) to a filetime of (0,0).
1206 static void dos_date_time_to_file_time(WORD fatdate
, WORD fattime
,
1209 if (!fatdate
&& !fattime
)
1210 ft
->dwLowDateTime
= ft
->dwHighDateTime
= 0;
1212 DosDateTimeToFileTime(fatdate
, fattime
, ft
);
1215 static int urlcache_decode_url(const char *url
, WCHAR
*decoded_url
, int decoded_len
)
1218 DWORD len
, part_len
;
1221 memset(&uc
, 0, sizeof(uc
));
1222 uc
.dwStructSize
= sizeof(uc
);
1223 uc
.dwHostNameLength
= 1;
1224 if(!InternetCrackUrlA(url
, 0, 0, &uc
))
1225 uc
.nScheme
= INTERNET_SCHEME_UNKNOWN
;
1227 if(uc
.nScheme
!=INTERNET_SCHEME_HTTP
&& uc
.nScheme
!=INTERNET_SCHEME_HTTPS
)
1228 return MultiByteToWideChar(CP_UTF8
, 0, url
, -1, decoded_url
, decoded_len
);
1233 len
= MultiByteToWideChar(CP_UTF8
, 0, url
, uc
.lpszHostName
-url
, decoded_url
, decoded_len
);
1239 host_name
= heap_alloc(uc
.dwHostNameLength
*sizeof(WCHAR
));
1242 if(!MultiByteToWideChar(CP_UTF8
, 0, uc
.lpszHostName
, uc
.dwHostNameLength
,
1243 host_name
, uc
.dwHostNameLength
)) {
1244 heap_free(host_name
);
1247 part_len
= IdnToUnicode(0, host_name
, uc
.dwHostNameLength
,
1248 decoded_url
? decoded_url
+len
: NULL
, decoded_len
);
1249 heap_free(host_name
);
1251 SetLastError(ERROR_INTERNET_INVALID_URL
);
1256 decoded_len
-= part_len
;
1258 part_len
= MultiByteToWideChar(CP_UTF8
, 0,
1259 uc
.lpszHostName
+uc
.dwHostNameLength
,
1260 -1, decoded_url
? decoded_url
+len
: NULL
, decoded_len
);
1268 /***********************************************************************
1269 * urlcache_copy_entry (Internal)
1271 * Copies an entry from the cache index file to the Win32 structure
1274 * ERROR_SUCCESS if the buffer was big enough
1275 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1278 static DWORD
urlcache_copy_entry(cache_container
*container
, const urlcache_header
*header
,
1279 INTERNET_CACHE_ENTRY_INFOA
*entry_info
, DWORD
*info_size
, const entry_url
*url_entry
, BOOL unicode
)
1282 DWORD size
= sizeof(*entry_info
);
1284 if(*info_size
>= size
) {
1285 entry_info
->lpHeaderInfo
= NULL
;
1286 entry_info
->lpszFileExtension
= NULL
;
1287 entry_info
->lpszLocalFileName
= NULL
;
1288 entry_info
->lpszSourceUrlName
= NULL
;
1289 entry_info
->CacheEntryType
= url_entry
->cache_entry_type
;
1290 entry_info
->u
.dwExemptDelta
= url_entry
->exempt_delta
;
1291 entry_info
->dwHeaderInfoSize
= url_entry
->header_info_size
;
1292 entry_info
->dwHitRate
= url_entry
->hit_rate
;
1293 entry_info
->dwSizeHigh
= url_entry
->size
.u
.HighPart
;
1294 entry_info
->dwSizeLow
= url_entry
->size
.u
.LowPart
;
1295 entry_info
->dwStructSize
= sizeof(*entry_info
);
1296 entry_info
->dwUseCount
= url_entry
->use_count
;
1297 dos_date_time_to_file_time(url_entry
->expire_date
, url_entry
->expire_time
, &entry_info
->ExpireTime
);
1298 entry_info
->LastAccessTime
= url_entry
->access_time
;
1299 entry_info
->LastModifiedTime
= url_entry
->modification_time
;
1300 dos_date_time_to_file_time(url_entry
->sync_date
, url_entry
->sync_time
, &entry_info
->LastSyncTime
);
1304 url_len
= urlcache_decode_url((const char*)url_entry
+url_entry
->url_off
, NULL
, 0);
1306 url_len
= strlen((LPCSTR
)url_entry
+url_entry
->url_off
) + 1;
1307 size
+= url_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1309 if(*info_size
>= size
) {
1310 DWORD url_size
= url_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1312 entry_info
->lpszSourceUrlName
= (LPSTR
)entry_info
+size
-url_size
;
1314 urlcache_decode_url((const char*)url_entry
+url_entry
->url_off
, (WCHAR
*)entry_info
->lpszSourceUrlName
, url_len
);
1316 memcpy(entry_info
->lpszSourceUrlName
, (LPCSTR
)url_entry
+url_entry
->url_off
, url_size
);
1319 if(size
%4 && size
<*info_size
)
1320 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1321 size
= DWORD_ALIGN(size
);
1323 if(url_entry
->local_name_off
) {
1324 LONG file_name_size
;
1326 file_name
= (LPSTR
)entry_info
+size
;
1327 file_name_size
= *info_size
-size
;
1328 if((unicode
&& urlcache_create_file_pathW(container
, header
, (LPCSTR
)url_entry
+url_entry
->local_name_off
,
1329 url_entry
->cache_dir
, (LPWSTR
)file_name
, &file_name_size
, FALSE
)) ||
1330 (!unicode
&& urlcache_create_file_pathA(container
, header
, (LPCSTR
)url_entry
+url_entry
->local_name_off
,
1331 url_entry
->cache_dir
, file_name
, &file_name_size
))) {
1332 entry_info
->lpszLocalFileName
= file_name
;
1334 size
+= file_name_size
;
1336 if(size
%4 && size
<*info_size
)
1337 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1338 size
= DWORD_ALIGN(size
);
1341 if(url_entry
->header_info_off
) {
1345 header_len
= MultiByteToWideChar(CP_UTF8
, 0, (const char*)url_entry
+url_entry
->header_info_off
,
1346 url_entry
->header_info_size
, NULL
, 0);
1348 header_len
= url_entry
->header_info_size
;
1349 size
+= header_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1351 if(*info_size
>= size
) {
1352 DWORD header_size
= header_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1353 entry_info
->lpHeaderInfo
= (LPBYTE
)entry_info
+size
-header_size
;
1355 MultiByteToWideChar(CP_UTF8
, 0, (const char*)url_entry
+url_entry
->header_info_off
,
1356 url_entry
->header_info_size
, (LPWSTR
)entry_info
->lpHeaderInfo
, header_len
);
1358 memcpy(entry_info
->lpHeaderInfo
, (LPCSTR
)url_entry
+url_entry
->header_info_off
, header_len
);
1360 if(size
%4 && size
<*info_size
)
1361 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1362 size
= DWORD_ALIGN(size
);
1365 if(url_entry
->file_extension_off
) {
1369 ext_len
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)url_entry
+url_entry
->file_extension_off
, -1, NULL
, 0);
1371 ext_len
= strlen((LPCSTR
)url_entry
+url_entry
->file_extension_off
) + 1;
1372 size
+= ext_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1374 if(*info_size
>= size
) {
1375 DWORD ext_size
= ext_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1376 entry_info
->lpszFileExtension
= (LPSTR
)entry_info
+size
-ext_size
;
1378 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)url_entry
+url_entry
->file_extension_off
, -1, (LPWSTR
)entry_info
->lpszFileExtension
, ext_len
);
1380 memcpy(entry_info
->lpszFileExtension
, (LPCSTR
)url_entry
+url_entry
->file_extension_off
, ext_len
*sizeof(CHAR
));
1383 if(size
%4 && size
<*info_size
)
1384 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1385 size
= DWORD_ALIGN(size
);
1388 if(size
> *info_size
) {
1390 return ERROR_INSUFFICIENT_BUFFER
;
1393 return ERROR_SUCCESS
;
1396 /***********************************************************************
1397 * urlcache_set_entry_info (Internal)
1399 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1400 * according to the flags set by field_control.
1403 * ERROR_SUCCESS if the buffer was big enough
1404 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1407 static DWORD
urlcache_set_entry_info(entry_url
*url_entry
, const INTERNET_CACHE_ENTRY_INFOA
*entry_info
, DWORD field_control
)
1409 if (field_control
& CACHE_ENTRY_ACCTIME_FC
)
1410 url_entry
->access_time
= entry_info
->LastAccessTime
;
1411 if (field_control
& CACHE_ENTRY_ATTRIBUTE_FC
)
1412 url_entry
->cache_entry_type
= entry_info
->CacheEntryType
;
1413 if (field_control
& CACHE_ENTRY_EXEMPT_DELTA_FC
)
1414 url_entry
->exempt_delta
= entry_info
->u
.dwExemptDelta
;
1415 if (field_control
& CACHE_ENTRY_EXPTIME_FC
)
1416 file_time_to_dos_date_time(&entry_info
->ExpireTime
, &url_entry
->expire_date
, &url_entry
->expire_time
);
1417 if (field_control
& CACHE_ENTRY_HEADERINFO_FC
)
1418 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1419 if (field_control
& CACHE_ENTRY_HITRATE_FC
)
1420 url_entry
->hit_rate
= entry_info
->dwHitRate
;
1421 if (field_control
& CACHE_ENTRY_MODTIME_FC
)
1422 url_entry
->modification_time
= entry_info
->LastModifiedTime
;
1423 if (field_control
& CACHE_ENTRY_SYNCTIME_FC
)
1424 file_time_to_dos_date_time(&entry_info
->LastAccessTime
, &url_entry
->sync_date
, &url_entry
->sync_time
);
1426 return ERROR_SUCCESS
;
1429 /***********************************************************************
1430 * urlcache_hash_key (Internal)
1432 * Returns the hash key for a given string
1435 * hash key for the string
1438 static DWORD
urlcache_hash_key(LPCSTR lpszKey
)
1440 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1441 * but the algorithm and result are not the same!
1443 static const unsigned char lookupTable
[256] =
1445 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1446 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1447 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1448 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1449 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1450 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1451 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1452 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1453 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1454 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1455 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1456 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1457 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1458 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1459 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1460 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1461 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1462 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1463 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1464 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1465 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1466 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1467 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1468 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1469 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1470 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1471 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1472 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1473 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1474 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1475 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1476 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1481 for (i
= 0; i
< sizeof(key
) / sizeof(key
[0]); i
++)
1482 key
[i
] = lookupTable
[(*lpszKey
+ i
) & 0xFF];
1484 for (lpszKey
++; *lpszKey
; lpszKey
++)
1486 for (i
= 0; i
< sizeof(key
) / sizeof(key
[0]); i
++)
1487 key
[i
] = lookupTable
[*lpszKey
^ key
[i
]];
1490 return *(DWORD
*)key
;
1493 static inline entry_hash_table
* urlcache_get_hash_table(const urlcache_header
*pHeader
, DWORD dwOffset
)
1497 return (entry_hash_table
*)((LPBYTE
)pHeader
+ dwOffset
);
1500 static BOOL
urlcache_find_hash_entry(const urlcache_header
*pHeader
, LPCSTR lpszUrl
, struct hash_entry
**ppHashEntry
)
1502 /* structure of hash table:
1503 * 448 entries divided into 64 blocks
1504 * each block therefore contains a chain of 7 key/offset pairs
1505 * how position in table is calculated:
1506 * 1. the url is hashed in helper function
1507 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1508 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1511 * there can be multiple hash tables in the file and the offset to
1512 * the next one is stored in the header of the hash table
1514 DWORD key
= urlcache_hash_key(lpszUrl
);
1515 DWORD offset
= (key
& (HASHTABLE_NUM_ENTRIES
-1)) * HASHTABLE_BLOCKSIZE
;
1516 entry_hash_table
* pHashEntry
;
1519 key
>>= HASHTABLE_FLAG_BITS
;
1521 for (pHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1522 pHashEntry
; pHashEntry
= urlcache_get_hash_table(pHeader
, pHashEntry
->next
))
1525 if (pHashEntry
->id
!= id
++)
1527 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry
->id
, id
);
1530 /* make sure that it is in fact a hash entry */
1531 if (pHashEntry
->header
.signature
!= HASH_SIGNATURE
)
1533 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&pHashEntry
->header
.signature
);
1537 for (i
= 0; i
< HASHTABLE_BLOCKSIZE
; i
++)
1539 struct hash_entry
*pHashElement
= &pHashEntry
->hash_table
[offset
+ i
];
1540 if (key
== pHashElement
->key
>>HASHTABLE_FLAG_BITS
)
1542 /* FIXME: we should make sure that this is the right element
1543 * before returning and claiming that it is. We can do this
1544 * by doing a simple compare between the URL we were given
1545 * and the URL stored in the entry. However, this assumes
1546 * we know the format of all the entries stored in the
1548 *ppHashEntry
= pHashElement
;
1556 /***********************************************************************
1557 * urlcache_hash_entry_set_flags (Internal)
1559 * Sets special bits in hash key
1565 static void urlcache_hash_entry_set_flags(struct hash_entry
*pHashEntry
, DWORD dwFlag
)
1567 pHashEntry
->key
= (pHashEntry
->key
>> HASHTABLE_FLAG_BITS
<< HASHTABLE_FLAG_BITS
) | dwFlag
;
1570 /***********************************************************************
1571 * urlcache_hash_entry_delete (Internal)
1573 * Searches all the hash tables in the index for the given URL and
1574 * then if found deletes the entry.
1577 * TRUE if the entry was found
1578 * FALSE if the entry could not be found
1581 static BOOL
urlcache_hash_entry_delete(struct hash_entry
*pHashEntry
)
1583 pHashEntry
->key
= HASHTABLE_DEL
;
1587 /***********************************************************************
1588 * urlcache_hash_entry_create (Internal)
1590 * Searches all the hash tables for a free slot based on the offset
1591 * generated from the hash key. If a free slot is found, the offset and
1592 * key are entered into the hash table.
1595 * ERROR_SUCCESS if the entry was added
1596 * Any other Win32 error code if the entry could not be added
1599 static DWORD
urlcache_hash_entry_create(urlcache_header
*pHeader
, LPCSTR lpszUrl
, DWORD dwOffsetEntry
, DWORD dwFieldType
)
1601 /* see urlcache_find_hash_entry for structure of hash tables */
1603 DWORD key
= urlcache_hash_key(lpszUrl
);
1604 DWORD offset
= (key
& (HASHTABLE_NUM_ENTRIES
-1)) * HASHTABLE_BLOCKSIZE
;
1605 entry_hash_table
* pHashEntry
, *pHashPrev
= NULL
;
1609 key
= ((key
>> HASHTABLE_FLAG_BITS
) << HASHTABLE_FLAG_BITS
) + dwFieldType
;
1611 for (pHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1612 pHashEntry
; pHashEntry
= urlcache_get_hash_table(pHeader
, pHashEntry
->next
))
1615 pHashPrev
= pHashEntry
;
1617 if (pHashEntry
->id
!= id
++)
1619 ERR("not right hash table number (%d) expected %d\n", pHashEntry
->id
, id
);
1622 /* make sure that it is in fact a hash entry */
1623 if (pHashEntry
->header
.signature
!= HASH_SIGNATURE
)
1625 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&pHashEntry
->header
.signature
);
1629 for (i
= 0; i
< HASHTABLE_BLOCKSIZE
; i
++)
1631 struct hash_entry
*pHashElement
= &pHashEntry
->hash_table
[offset
+ i
];
1632 if (pHashElement
->key
==HASHTABLE_FREE
|| pHashElement
->key
==HASHTABLE_DEL
) /* if the slot is free */
1634 pHashElement
->key
= key
;
1635 pHashElement
->offset
= dwOffsetEntry
;
1636 return ERROR_SUCCESS
;
1640 error
= urlcache_create_hash_table(pHeader
, pHashPrev
, &pHashEntry
);
1641 if (error
!= ERROR_SUCCESS
)
1644 pHashEntry
->hash_table
[offset
].key
= key
;
1645 pHashEntry
->hash_table
[offset
].offset
= dwOffsetEntry
;
1646 return ERROR_SUCCESS
;
1649 /***********************************************************************
1650 * urlcache_enum_hash_tables (Internal)
1652 * Enumerates the hash tables in a container.
1655 * TRUE if an entry was found
1656 * FALSE if there are no more tables to enumerate.
1659 static BOOL
urlcache_enum_hash_tables(const urlcache_header
*pHeader
, DWORD
*id
, entry_hash_table
**ppHashEntry
)
1661 for (*ppHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1662 *ppHashEntry
; *ppHashEntry
= urlcache_get_hash_table(pHeader
, (*ppHashEntry
)->next
))
1664 TRACE("looking at hash table number %d\n", (*ppHashEntry
)->id
);
1665 if ((*ppHashEntry
)->id
!= *id
)
1667 /* make sure that it is in fact a hash entry */
1668 if ((*ppHashEntry
)->header
.signature
!= HASH_SIGNATURE
)
1670 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&(*ppHashEntry
)->header
.signature
);
1675 TRACE("hash table number %d found\n", *id
);
1681 /***********************************************************************
1682 * urlcache_enum_hash_table_entries (Internal)
1684 * Enumerates entries in a hash table and returns the next non-free entry.
1687 * TRUE if an entry was found
1688 * FALSE if the hash table is empty or there are no more entries to
1692 static BOOL
urlcache_enum_hash_table_entries(const urlcache_header
*pHeader
, const entry_hash_table
*pHashEntry
,
1693 DWORD
* index
, const struct hash_entry
**ppHashEntry
)
1695 for (; *index
< HASHTABLE_SIZE
; (*index
)++)
1697 if (pHashEntry
->hash_table
[*index
].key
==HASHTABLE_FREE
|| pHashEntry
->hash_table
[*index
].key
==HASHTABLE_DEL
)
1700 *ppHashEntry
= &pHashEntry
->hash_table
[*index
];
1701 TRACE("entry found %d\n", *index
);
1704 TRACE("no more entries (%d)\n", *index
);
1708 /***********************************************************************
1709 * cache_container_delete_dir (Internal)
1711 * Erase a directory containing an URL cache.
1714 * TRUE success, FALSE failure/aborted.
1717 static BOOL
cache_container_delete_dir(LPCWSTR lpszPath
)
1720 WCHAR path
[MAX_PATH
+ 1];
1721 SHFILEOPSTRUCTW shfos
;
1724 path_len
= strlenW(lpszPath
);
1725 if (path_len
>= MAX_PATH
)
1727 strcpyW(path
, lpszPath
);
1728 path
[path_len
+ 1] = 0; /* double-NUL-terminate path */
1731 shfos
.wFunc
= FO_DELETE
;
1734 shfos
.fFlags
= FOF_NOCONFIRMATION
;
1735 shfos
.fAnyOperationsAborted
= FALSE
;
1736 ret
= SHFileOperationW(&shfos
);
1738 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path
), ret
);
1739 return !(ret
|| shfos
.fAnyOperationsAborted
);
1742 /***********************************************************************
1743 * urlcache_hash_entry_is_locked (Internal)
1745 * Checks if entry is locked. Unlocks it if possible.
1747 static BOOL
urlcache_hash_entry_is_locked(struct hash_entry
*hash_entry
, entry_url
*url_entry
)
1750 ULARGE_INTEGER acc_time
, time
;
1752 if ((hash_entry
->key
& ((1<<HASHTABLE_FLAG_BITS
)-1)) != HASHTABLE_LOCK
)
1755 GetSystemTimeAsFileTime(&cur_time
);
1756 time
.u
.LowPart
= cur_time
.dwLowDateTime
;
1757 time
.u
.HighPart
= cur_time
.dwHighDateTime
;
1759 acc_time
.u
.LowPart
= url_entry
->access_time
.dwLowDateTime
;
1760 acc_time
.u
.HighPart
= url_entry
->access_time
.dwHighDateTime
;
1762 time
.QuadPart
-= acc_time
.QuadPart
;
1764 /* check if entry was locked for at least a day */
1765 if(time
.QuadPart
> (ULONGLONG
)24*60*60*FILETIME_SECOND
) {
1766 urlcache_hash_entry_set_flags(hash_entry
, HASHTABLE_URL
);
1767 url_entry
->use_count
= 0;
1774 static BOOL
urlcache_get_entry_info(const char *url
, void *entry_info
,
1775 DWORD
*size
, DWORD flags
, BOOL unicode
)
1777 urlcache_header
*header
;
1778 struct hash_entry
*hash_entry
;
1779 const entry_url
*url_entry
;
1780 cache_container
*container
;
1783 TRACE("(%s, %p, %p, %x, %x)\n", debugstr_a(url
), entry_info
, size
, flags
, unicode
);
1785 if(flags
& ~GET_INSTALLED_ENTRY
)
1786 FIXME("ignoring unsupported flags: %x\n", flags
);
1788 error
= cache_containers_find(url
, &container
);
1789 if(error
!= ERROR_SUCCESS
) {
1790 SetLastError(error
);
1794 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
1795 if(error
!= ERROR_SUCCESS
) {
1796 SetLastError(error
);
1800 if(!(header
= cache_container_lock_index(container
)))
1803 if(!urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
1804 cache_container_unlock_index(container
, header
);
1805 WARN("entry %s not found!\n", debugstr_a(url
));
1806 SetLastError(ERROR_FILE_NOT_FOUND
);
1810 url_entry
= (const entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
1811 if(url_entry
->header
.signature
!= URL_SIGNATURE
) {
1812 cache_container_unlock_index(container
, header
);
1813 FIXME("Trying to retrieve entry of unknown format %s\n",
1814 debugstr_an((LPCSTR
)&url_entry
->header
.signature
, sizeof(DWORD
)));
1815 SetLastError(ERROR_FILE_NOT_FOUND
);
1819 TRACE("Found URL: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->url_off
));
1820 TRACE("Header info: %s\n", debugstr_an((LPCSTR
)url_entry
+
1821 url_entry
->header_info_off
, url_entry
->header_info_size
));
1823 if((flags
& GET_INSTALLED_ENTRY
) && !(url_entry
->cache_entry_type
& INSTALLED_CACHE_ENTRY
)) {
1824 cache_container_unlock_index(container
, header
);
1825 SetLastError(ERROR_FILE_NOT_FOUND
);
1833 error
= urlcache_copy_entry(container
, header
, entry_info
, size
, url_entry
, unicode
);
1834 if(error
!= ERROR_SUCCESS
) {
1835 cache_container_unlock_index(container
, header
);
1836 SetLastError(error
);
1839 if(url_entry
->local_name_off
)
1840 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->local_name_off
));
1843 cache_container_unlock_index(container
, header
);
1847 /***********************************************************************
1848 * GetUrlCacheEntryInfoExA (WININET.@)
1851 BOOL WINAPI
GetUrlCacheEntryInfoExA(LPCSTR lpszUrl
,
1852 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1853 LPDWORD lpdwCacheEntryInfoBufSize
, LPSTR lpszReserved
,
1854 LPDWORD lpdwReserved
, LPVOID lpReserved
, DWORD dwFlags
)
1856 if(lpszReserved
!=NULL
|| lpdwReserved
!=NULL
|| lpReserved
!=NULL
) {
1857 ERR("Reserved value was not 0\n");
1858 SetLastError(ERROR_INVALID_PARAMETER
);
1862 return urlcache_get_entry_info(lpszUrl
, lpCacheEntryInfo
,
1863 lpdwCacheEntryInfoBufSize
, dwFlags
, FALSE
);
1866 /***********************************************************************
1867 * GetUrlCacheEntryInfoA (WININET.@)
1870 BOOL WINAPI
GetUrlCacheEntryInfoA(LPCSTR lpszUrlName
,
1871 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1872 LPDWORD lpdwCacheEntryInfoBufferSize
)
1874 return GetUrlCacheEntryInfoExA(lpszUrlName
, lpCacheEntryInfo
,
1875 lpdwCacheEntryInfoBufferSize
, NULL
, NULL
, NULL
, 0);
1878 static int urlcache_encode_url(const WCHAR
*url
, char *encoded_url
, int encoded_len
)
1881 DWORD len
, part_len
;
1884 TRACE("%s\n", debugstr_w(url
));
1886 memset(&uc
, 0, sizeof(uc
));
1887 uc
.dwStructSize
= sizeof(uc
);
1888 uc
.dwHostNameLength
= 1;
1889 if(!InternetCrackUrlW(url
, 0, 0, &uc
))
1890 uc
.nScheme
= INTERNET_SCHEME_UNKNOWN
;
1892 if(uc
.nScheme
!=INTERNET_SCHEME_HTTP
&& uc
.nScheme
!=INTERNET_SCHEME_HTTPS
)
1893 return WideCharToMultiByte(CP_UTF8
, 0, url
, -1, encoded_url
, encoded_len
, NULL
, NULL
);
1895 len
= WideCharToMultiByte(CP_UTF8
, 0, url
, uc
.lpszHostName
-url
,
1896 encoded_url
, encoded_len
, NULL
, NULL
);
1902 part_len
= IdnToAscii(0, uc
.lpszHostName
, uc
.dwHostNameLength
, NULL
, 0);
1904 SetLastError(ERROR_INTERNET_INVALID_URL
);
1908 punycode
= heap_alloc(part_len
*sizeof(WCHAR
));
1912 part_len
= IdnToAscii(0, uc
.lpszHostName
, uc
.dwHostNameLength
, punycode
, part_len
);
1914 heap_free(punycode
);
1918 part_len
= WideCharToMultiByte(CP_UTF8
, 0, punycode
, part_len
,
1919 encoded_url
? encoded_url
+len
: NULL
, encoded_len
, NULL
, NULL
);
1920 heap_free(punycode
);
1924 encoded_len
-= part_len
;
1927 part_len
= WideCharToMultiByte(CP_UTF8
, 0, uc
.lpszHostName
+uc
.dwHostNameLength
,
1928 -1, encoded_url
? encoded_url
+len
: NULL
, encoded_len
, NULL
, NULL
);
1933 TRACE("got (%d)%s\n", len
, debugstr_a(encoded_url
));
1937 static BOOL
urlcache_encode_url_alloc(const WCHAR
*url
, char **encoded_url
)
1942 encoded_len
= urlcache_encode_url(url
, NULL
, 0);
1946 ret
= heap_alloc(encoded_len
*sizeof(WCHAR
));
1950 encoded_len
= urlcache_encode_url(url
, ret
, encoded_len
);
1960 /***********************************************************************
1961 * GetUrlCacheEntryInfoExW (WININET.@)
1964 BOOL WINAPI
GetUrlCacheEntryInfoExW(LPCWSTR lpszUrl
,
1965 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
1966 LPDWORD lpdwCacheEntryInfoBufSize
, LPWSTR lpszReserved
,
1967 LPDWORD lpdwReserved
, LPVOID lpReserved
, DWORD dwFlags
)
1972 if(lpszReserved
!=NULL
|| lpdwReserved
!=NULL
|| lpReserved
!=NULL
) {
1973 ERR("Reserved value was not 0\n");
1974 SetLastError(ERROR_INVALID_PARAMETER
);
1978 /* Ignore GET_INSTALLED_ENTRY flag in unicode version of function */
1979 dwFlags
&= ~GET_INSTALLED_ENTRY
;
1981 if(!urlcache_encode_url_alloc(lpszUrl
, &url
))
1984 ret
= urlcache_get_entry_info(url
, lpCacheEntryInfo
,
1985 lpdwCacheEntryInfoBufSize
, dwFlags
, TRUE
);
1990 /***********************************************************************
1991 * GetUrlCacheEntryInfoW (WININET.@)
1994 BOOL WINAPI
GetUrlCacheEntryInfoW(LPCWSTR lpszUrl
,
1995 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
1996 LPDWORD lpdwCacheEntryInfoBufferSize
)
1998 return GetUrlCacheEntryInfoExW(lpszUrl
, lpCacheEntryInfo
,
1999 lpdwCacheEntryInfoBufferSize
, NULL
, NULL
, NULL
, 0);
2002 /***********************************************************************
2003 * SetUrlCacheEntryInfoA (WININET.@)
2005 BOOL WINAPI
SetUrlCacheEntryInfoA(LPCSTR lpszUrlName
,
2006 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
2007 DWORD dwFieldControl
)
2009 urlcache_header
*pHeader
;
2010 struct hash_entry
*pHashEntry
;
2011 entry_header
*pEntry
;
2012 cache_container
*pContainer
;
2015 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName
), lpCacheEntryInfo
, dwFieldControl
);
2017 error
= cache_containers_find(lpszUrlName
, &pContainer
);
2018 if (error
!= ERROR_SUCCESS
)
2020 SetLastError(error
);
2024 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
2025 if (error
!= ERROR_SUCCESS
)
2027 SetLastError(error
);
2031 if (!(pHeader
= cache_container_lock_index(pContainer
)))
2034 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
2036 cache_container_unlock_index(pContainer
, pHeader
);
2037 WARN("entry %s not found!\n", debugstr_a(lpszUrlName
));
2038 SetLastError(ERROR_FILE_NOT_FOUND
);
2042 pEntry
= (entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2043 if (pEntry
->signature
!= URL_SIGNATURE
)
2045 cache_container_unlock_index(pContainer
, pHeader
);
2046 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR
)&pEntry
->signature
, sizeof(DWORD
)));
2047 SetLastError(ERROR_FILE_NOT_FOUND
);
2051 urlcache_set_entry_info((entry_url
*)pEntry
, lpCacheEntryInfo
, dwFieldControl
);
2053 cache_container_unlock_index(pContainer
, pHeader
);
2058 /***********************************************************************
2059 * SetUrlCacheEntryInfoW (WININET.@)
2061 BOOL WINAPI
SetUrlCacheEntryInfoW(LPCWSTR lpszUrl
,
2062 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
2063 DWORD dwFieldControl
)
2068 if(!urlcache_encode_url_alloc(lpszUrl
, &url
))
2071 ret
= SetUrlCacheEntryInfoA(url
, (INTERNET_CACHE_ENTRY_INFOA
*)lpCacheEntryInfo
, dwFieldControl
);
2076 static BOOL
urlcache_entry_get_file(const char *url
, void *entry_info
, DWORD
*size
, BOOL unicode
)
2078 urlcache_header
*header
;
2079 struct hash_entry
*hash_entry
;
2080 entry_url
*url_entry
;
2081 cache_container
*container
;
2084 TRACE("(%s, %p, %p, %x)\n", debugstr_a(url
), entry_info
, size
, unicode
);
2086 if(!url
|| !size
|| (!entry_info
&& *size
)) {
2087 SetLastError(ERROR_INVALID_PARAMETER
);
2091 error
= cache_containers_find(url
, &container
);
2092 if(error
!= ERROR_SUCCESS
) {
2093 SetLastError(error
);
2097 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2098 if (error
!= ERROR_SUCCESS
) {
2099 SetLastError(error
);
2103 if (!(header
= cache_container_lock_index(container
)))
2106 if (!urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
2107 cache_container_unlock_index(container
, header
);
2108 TRACE("entry %s not found!\n", debugstr_a(url
));
2109 SetLastError(ERROR_FILE_NOT_FOUND
);
2113 url_entry
= (entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
2114 if(url_entry
->header
.signature
!= URL_SIGNATURE
) {
2115 cache_container_unlock_index(container
, header
);
2116 FIXME("Trying to retrieve entry of unknown format %s\n",
2117 debugstr_an((LPSTR
)&url_entry
->header
.signature
, sizeof(DWORD
)));
2118 SetLastError(ERROR_FILE_NOT_FOUND
);
2122 if(!url_entry
->local_name_off
) {
2123 cache_container_unlock_index(container
, header
);
2124 SetLastError(ERROR_INVALID_DATA
);
2128 TRACE("Found URL: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->url_off
));
2129 TRACE("Header info: %s\n", debugstr_an((LPCSTR
)url_entry
+ url_entry
->header_info_off
,
2130 url_entry
->header_info_size
));
2132 error
= urlcache_copy_entry(container
, header
, entry_info
,
2133 size
, url_entry
, unicode
);
2134 if(error
!= ERROR_SUCCESS
) {
2135 cache_container_unlock_index(container
, header
);
2136 SetLastError(error
);
2139 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->local_name_off
));
2141 url_entry
->hit_rate
++;
2142 url_entry
->use_count
++;
2143 urlcache_hash_entry_set_flags(hash_entry
, HASHTABLE_LOCK
);
2144 GetSystemTimeAsFileTime(&url_entry
->access_time
);
2146 cache_container_unlock_index(container
, header
);
2151 /***********************************************************************
2152 * RetrieveUrlCacheEntryFileA (WININET.@)
2155 BOOL WINAPI
RetrieveUrlCacheEntryFileA(LPCSTR lpszUrlName
,
2156 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
2157 LPDWORD lpdwCacheEntryInfoBufferSize
, DWORD dwReserved
)
2159 return urlcache_entry_get_file(lpszUrlName
, lpCacheEntryInfo
,
2160 lpdwCacheEntryInfoBufferSize
, FALSE
);
2163 /***********************************************************************
2164 * RetrieveUrlCacheEntryFileW (WININET.@)
2167 BOOL WINAPI
RetrieveUrlCacheEntryFileW(LPCWSTR lpszUrlName
,
2168 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
2169 LPDWORD lpdwCacheEntryInfoBufferSize
, DWORD dwReserved
)
2174 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
2177 ret
= urlcache_entry_get_file(url
, lpCacheEntryInfo
,
2178 lpdwCacheEntryInfoBufferSize
, TRUE
);
2183 static BOOL
urlcache_entry_delete(const cache_container
*pContainer
,
2184 urlcache_header
*pHeader
, struct hash_entry
*pHashEntry
)
2186 entry_header
*pEntry
;
2187 entry_url
* pUrlEntry
;
2189 pEntry
= (entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2190 if (pEntry
->signature
!= URL_SIGNATURE
)
2192 FIXME("Trying to delete entry of unknown format %s\n",
2193 debugstr_an((LPCSTR
)&pEntry
->signature
, sizeof(DWORD
)));
2194 SetLastError(ERROR_FILE_NOT_FOUND
);
2198 pUrlEntry
= (entry_url
*)pEntry
;
2199 if(urlcache_hash_entry_is_locked(pHashEntry
, pUrlEntry
))
2201 TRACE("Trying to delete locked entry\n");
2202 pUrlEntry
->cache_entry_type
|= PENDING_DELETE_CACHE_ENTRY
;
2203 SetLastError(ERROR_SHARING_VIOLATION
);
2207 if(!urlcache_delete_file(pContainer
, pHeader
, pUrlEntry
))
2209 urlcache_entry_free(pHeader
, pEntry
);
2213 /* Add entry to leaked files list */
2214 pUrlEntry
->header
.signature
= LEAK_SIGNATURE
;
2215 pUrlEntry
->exempt_delta
= pHeader
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
];
2216 pHeader
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
] = pHashEntry
->offset
;
2219 urlcache_hash_entry_delete(pHashEntry
);
2223 static HANDLE free_cache_running
;
2224 static HANDLE dll_unload_event
;
2225 static DWORD WINAPI
handle_full_cache_worker(void *param
)
2227 FreeUrlCacheSpaceW(NULL
, 20, 0);
2228 ReleaseSemaphore(free_cache_running
, 1, NULL
);
2232 static void handle_full_cache(void)
2234 if(WaitForSingleObject(free_cache_running
, 0) == WAIT_OBJECT_0
) {
2235 if(!QueueUserWorkItem(handle_full_cache_worker
, NULL
, 0))
2236 ReleaseSemaphore(free_cache_running
, 1, NULL
);
2240 /* Enumerates entries in cache, allows cache unlocking between calls. */
2241 static BOOL
urlcache_next_entry(urlcache_header
*header
, DWORD
*hash_table_off
, DWORD
*hash_table_entry
,
2242 struct hash_entry
**hash_entry
, entry_header
**entry
)
2244 entry_hash_table
*hashtable_entry
;
2249 if(!*hash_table_off
) {
2250 *hash_table_off
= header
->hash_table_off
;
2251 *hash_table_entry
= 0;
2253 hashtable_entry
= urlcache_get_hash_table(header
, *hash_table_off
);
2255 if(*hash_table_off
>= header
->size
) {
2256 *hash_table_off
= 0;
2260 hashtable_entry
= urlcache_get_hash_table(header
, *hash_table_off
);
2263 if(hashtable_entry
->header
.signature
!= HASH_SIGNATURE
) {
2264 *hash_table_off
= 0;
2269 if(*hash_table_entry
>= HASHTABLE_SIZE
) {
2270 *hash_table_off
= hashtable_entry
->next
;
2271 if(!*hash_table_off
) {
2272 *hash_table_off
= 0;
2276 hashtable_entry
= urlcache_get_hash_table(header
, *hash_table_off
);
2277 *hash_table_entry
= 0;
2280 if(hashtable_entry
->hash_table
[*hash_table_entry
].key
!= HASHTABLE_DEL
&&
2281 hashtable_entry
->hash_table
[*hash_table_entry
].key
!= HASHTABLE_FREE
) {
2282 *hash_entry
= &hashtable_entry
->hash_table
[*hash_table_entry
];
2283 *entry
= (entry_header
*)((LPBYTE
)header
+ hashtable_entry
->hash_table
[*hash_table_entry
].offset
);
2284 (*hash_table_entry
)++;
2288 (*hash_table_entry
)++;
2291 *hash_table_off
= 0;
2295 /* Rates an urlcache entry to determine if it can be deleted.
2297 * Score 0 means that entry can safely be removed, the bigger rating
2298 * the smaller chance of entry being removed.
2299 * DWORD_MAX means that entry can't be deleted at all.
2301 * Rating system is currently not fully compatible with native implementation.
2303 static DWORD
urlcache_rate_entry(entry_url
*url_entry
, FILETIME
*cur_time
)
2305 ULARGE_INTEGER time
, access_time
;
2308 access_time
.u
.LowPart
= url_entry
->access_time
.dwLowDateTime
;
2309 access_time
.u
.HighPart
= url_entry
->access_time
.dwHighDateTime
;
2311 time
.u
.LowPart
= cur_time
->dwLowDateTime
;
2312 time
.u
.HighPart
= cur_time
->dwHighDateTime
;
2314 /* Don't touch entries that were added less than 10 minutes ago */
2315 if(time
.QuadPart
< access_time
.QuadPart
+ (ULONGLONG
)10*60*FILETIME_SECOND
)
2318 if(url_entry
->cache_entry_type
& STICKY_CACHE_ENTRY
)
2319 if(time
.QuadPart
< access_time
.QuadPart
+ (ULONGLONG
)url_entry
->exempt_delta
*FILETIME_SECOND
)
2322 time
.QuadPart
= (time
.QuadPart
-access_time
.QuadPart
)/FILETIME_SECOND
;
2323 rating
= 400*60*60*24/(60*60*24+time
.QuadPart
);
2325 if(url_entry
->hit_rate
> 100)
2328 rating
+= url_entry
->hit_rate
;
2333 static int dword_cmp(const void *p1
, const void *p2
)
2335 return *(const DWORD
*)p1
- *(const DWORD
*)p2
;
2338 /***********************************************************************
2339 * FreeUrlCacheSpaceW (WININET.@)
2341 * Frees up some cache.
2344 * cache_path [I] Which volume to free up from, or NULL if you don't care.
2345 * size [I] Percentage of the cache that should be free.
2346 * filter [I] Which entries can't be deleted (CacheEntryType)
2349 * TRUE success. FALSE failure.
2352 * This implementation just retrieves the path of the cache directory, and
2353 * deletes its contents from the filesystem. The correct approach would
2354 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
2356 BOOL WINAPI
FreeUrlCacheSpaceW(LPCWSTR cache_path
, DWORD size
, DWORD filter
)
2358 cache_container
*container
;
2359 DWORD path_len
, err
;
2361 TRACE("(%s, %x, %x)\n", debugstr_w(cache_path
), size
, filter
);
2363 if(size
<1 || size
>100) {
2364 SetLastError(ERROR_INVALID_PARAMETER
);
2369 path_len
= strlenW(cache_path
);
2370 if(cache_path
[path_len
-1] == '\\')
2376 if(size
==100 && !filter
) {
2377 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
2379 /* When cache_path==NULL only clean Temporary Internet Files */
2380 if((!path_len
&& container
->cache_prefix
[0]==0) ||
2381 (path_len
&& !strncmpiW(container
->path
, cache_path
, path_len
) &&
2382 (container
->path
[path_len
]=='\0' || container
->path
[path_len
]=='\\')))
2386 WaitForSingleObject(container
->mutex
, INFINITE
);
2388 /* unlock, delete, recreate and lock cache */
2389 cache_container_close_index(container
);
2390 ret_del
= cache_container_delete_dir(container
->path
);
2391 err
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2393 ReleaseMutex(container
->mutex
);
2394 if(!ret_del
|| (err
!= ERROR_SUCCESS
))
2402 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
2404 urlcache_header
*header
;
2405 struct hash_entry
*hash_entry
;
2406 entry_header
*entry
;
2407 entry_url
*url_entry
;
2408 ULONGLONG desired_size
, cur_size
;
2409 DWORD delete_factor
, hash_table_off
, hash_table_entry
;
2410 DWORD rate
[100], rate_no
;
2413 if((path_len
|| container
->cache_prefix
[0]!=0) &&
2414 (!path_len
|| strncmpiW(container
->path
, cache_path
, path_len
) ||
2415 (container
->path
[path_len
]!='\0' && container
->path
[path_len
]!='\\')))
2418 err
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2419 if(err
!= ERROR_SUCCESS
)
2422 header
= cache_container_lock_index(container
);
2426 urlcache_clean_leaked_entries(container
, header
);
2428 desired_size
= header
->cache_limit
.QuadPart
*(100-size
)/100;
2429 cur_size
= header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
;
2430 if(cur_size
<= desired_size
)
2433 delete_factor
= (cur_size
-desired_size
)*100/cur_size
;
2435 if(!delete_factor
) {
2436 cache_container_unlock_index(container
, header
);
2441 hash_table_entry
= 0;
2443 GetSystemTimeAsFileTime(&cur_time
);
2444 while(rate_no
<sizeof(rate
)/sizeof(*rate
) &&
2445 urlcache_next_entry(header
, &hash_table_off
, &hash_table_entry
, &hash_entry
, &entry
)) {
2446 if(entry
->signature
!= URL_SIGNATURE
) {
2447 WARN("only url entries are currently supported\n");
2451 url_entry
= (entry_url
*)entry
;
2452 if(url_entry
->cache_entry_type
& filter
)
2455 rate
[rate_no
] = urlcache_rate_entry(url_entry
, &cur_time
);
2456 if(rate
[rate_no
] != -1)
2461 TRACE("nothing to delete\n");
2462 cache_container_unlock_index(container
, header
);
2466 qsort(rate
, rate_no
, sizeof(DWORD
), dword_cmp
);
2468 delete_factor
= delete_factor
*rate_no
/100;
2469 delete_factor
= rate
[delete_factor
];
2470 TRACE("deleting files with rating %d or less\n", delete_factor
);
2473 while(urlcache_next_entry(header
, &hash_table_off
, &hash_table_entry
, &hash_entry
, &entry
)) {
2474 if(entry
->signature
!= URL_SIGNATURE
)
2477 url_entry
= (entry_url
*)entry
;
2478 if(url_entry
->cache_entry_type
& filter
)
2481 if(urlcache_rate_entry(url_entry
, &cur_time
) <= delete_factor
) {
2482 TRACE("deleting file: %s\n", debugstr_a((char*)url_entry
+url_entry
->local_name_off
));
2483 urlcache_entry_delete(container
, header
, hash_entry
);
2485 if(header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
<= desired_size
)
2488 /* Allow other threads to use cache while cleaning */
2489 cache_container_unlock_index(container
, header
);
2490 if(WaitForSingleObject(dll_unload_event
, 0) == WAIT_OBJECT_0
) {
2491 TRACE("got dll_unload_event - finishing\n");
2495 header
= cache_container_lock_index(container
);
2499 TRACE("cache size after cleaning 0x%s/0x%s\n",
2500 wine_dbgstr_longlong(header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
),
2501 wine_dbgstr_longlong(header
->cache_limit
.QuadPart
));
2502 cache_container_unlock_index(container
, header
);
2508 /***********************************************************************
2509 * FreeUrlCacheSpaceA (WININET.@)
2511 * See FreeUrlCacheSpaceW.
2513 BOOL WINAPI
FreeUrlCacheSpaceA(LPCSTR lpszCachePath
, DWORD dwSize
, DWORD dwFilter
)
2516 LPWSTR path
= heap_strdupAtoW(lpszCachePath
);
2517 if (lpszCachePath
== NULL
|| path
!= NULL
)
2518 ret
= FreeUrlCacheSpaceW(path
, dwSize
, dwFilter
);
2523 /***********************************************************************
2524 * UnlockUrlCacheEntryFileA (WININET.@)
2527 BOOL WINAPI
UnlockUrlCacheEntryFileA(LPCSTR lpszUrlName
, DWORD dwReserved
)
2529 urlcache_header
*pHeader
;
2530 struct hash_entry
*pHashEntry
;
2531 entry_header
*pEntry
;
2532 entry_url
* pUrlEntry
;
2533 cache_container
*pContainer
;
2536 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName
), dwReserved
);
2540 ERR("dwReserved != 0\n");
2541 SetLastError(ERROR_INVALID_PARAMETER
);
2545 error
= cache_containers_find(lpszUrlName
, &pContainer
);
2546 if (error
!= ERROR_SUCCESS
)
2548 SetLastError(error
);
2552 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
2553 if (error
!= ERROR_SUCCESS
)
2555 SetLastError(error
);
2559 if (!(pHeader
= cache_container_lock_index(pContainer
)))
2562 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
2564 cache_container_unlock_index(pContainer
, pHeader
);
2565 TRACE("entry %s not found!\n", debugstr_a(lpszUrlName
));
2566 SetLastError(ERROR_FILE_NOT_FOUND
);
2570 pEntry
= (entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2571 if (pEntry
->signature
!= URL_SIGNATURE
)
2573 cache_container_unlock_index(pContainer
, pHeader
);
2574 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR
)&pEntry
->signature
, sizeof(DWORD
)));
2575 SetLastError(ERROR_FILE_NOT_FOUND
);
2579 pUrlEntry
= (entry_url
*)pEntry
;
2581 if (pUrlEntry
->use_count
== 0)
2583 cache_container_unlock_index(pContainer
, pHeader
);
2586 pUrlEntry
->use_count
--;
2587 if (!pUrlEntry
->use_count
)
2589 urlcache_hash_entry_set_flags(pHashEntry
, HASHTABLE_URL
);
2590 if (pUrlEntry
->cache_entry_type
& PENDING_DELETE_CACHE_ENTRY
)
2591 urlcache_entry_delete(pContainer
, pHeader
, pHashEntry
);
2594 cache_container_unlock_index(pContainer
, pHeader
);
2599 /***********************************************************************
2600 * UnlockUrlCacheEntryFileW (WININET.@)
2603 BOOL WINAPI
UnlockUrlCacheEntryFileW(LPCWSTR lpszUrlName
, DWORD dwReserved
)
2608 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
2611 ret
= UnlockUrlCacheEntryFileA(url
, dwReserved
);
2616 static BOOL
urlcache_entry_create(const char *url
, const char *ext
, WCHAR
*full_path
)
2618 cache_container
*container
;
2619 urlcache_header
*header
;
2620 char file_name
[MAX_PATH
];
2621 WCHAR extW
[MAX_PATH
];
2623 LONG full_path_len
, ext_len
= 0;
2624 BOOL generate_name
= FALSE
;
2631 TRACE("(%s, %s, %p)\n", debugstr_a(url
), debugstr_a(ext
), full_path
);
2633 memset(&uc
, 0, sizeof(uc
));
2634 uc
.dwStructSize
= sizeof(uc
);
2635 uc
.dwUrlPathLength
= 1;
2636 uc
.dwExtraInfoLength
= 1;
2637 if(!InternetCrackUrlA(url
, 0, 0, &uc
))
2638 uc
.dwUrlPathLength
= 0;
2640 if(!uc
.dwUrlPathLength
) {
2645 p
= e
= uc
.lpszUrlPath
+uc
.dwUrlPathLength
;
2646 while(p
>uc
.lpszUrlPath
&& *(p
-1)!='/' && *(p
-1)!='\\' && *(p
-1)!='.')
2648 if(p
>uc
.lpszUrlPath
&& *(p
-1)=='.') {
2650 while(p
>uc
.lpszUrlPath
&& *(p
-1)!='/' && *(p
-1)!='\\')
2656 memcpy(file_name
, p
, e
-p
);
2659 for(p
=file_name
; *p
; p
++) {
2672 generate_name
= TRUE
;
2674 error
= cache_containers_find(url
, &container
);
2675 if(error
!= ERROR_SUCCESS
) {
2676 SetLastError(error
);
2680 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2681 if(error
!= ERROR_SUCCESS
) {
2682 SetLastError(error
);
2686 if(!(header
= cache_container_lock_index(container
)))
2690 cache_dir
= (BYTE
)(rand() % header
->dirs_no
);
2692 cache_dir
= CACHE_CONTAINER_NO_SUBDIR
;
2694 full_path_len
= MAX_PATH
* sizeof(WCHAR
);
2695 if(!urlcache_create_file_pathW(container
, header
, file_name
, cache_dir
, full_path
, &full_path_len
, TRUE
)) {
2696 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2697 debugstr_a(file_name
), full_path_len
);
2698 cache_container_unlock_index(container
, header
);
2701 full_path_len
= full_path_len
/sizeof(WCHAR
) - 1;
2703 cache_container_unlock_index(container
, header
);
2709 ext_len
= MultiByteToWideChar(CP_ACP
, 0, ext
, -1, extW
+1, MAX_PATH
-1);
2711 for(p
=extW
; *p
; p
++) {
2721 if(p
[-1]==' ' || p
[-1]=='.')
2727 if(!generate_name
&& full_path_len
+5+ext_len
>=MAX_PATH
) { /* strlen("[255]") = 5 */
2728 full_path_len
= MAX_PATH
-5-ext_len
-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 if(full_path_len
+8+ext_len
>= MAX_PATH
)
2745 full_path_len
= MAX_PATH
-8-ext_len
-1;
2747 /* Try to generate random name */
2748 GetSystemTimeAsFileTime(&ft
);
2749 strcpyW(full_path
+full_path_len
+8, extW
);
2751 for(i
=0; i
<255; i
++) {
2753 ULONGLONG n
= ft
.dwHighDateTime
;
2755 n
+= ft
.dwLowDateTime
;
2756 n
^= (ULONGLONG
)i
<<48;
2758 for(j
=0; j
<8; j
++) {
2761 full_path
[full_path_len
+j
] = (r
< 10 ? '0' + r
: 'A' + r
- 10);
2764 TRACE("Trying: %s\n", debugstr_w(full_path
));
2765 file
= CreateFileW(full_path
, GENERIC_READ
, 0, NULL
, CREATE_NEW
, 0, NULL
);
2766 if(file
!= INVALID_HANDLE_VALUE
) {
2772 WARN("Could not find a unique filename\n");
2776 /***********************************************************************
2777 * CreateUrlCacheEntryA (WININET.@)
2780 BOOL WINAPI
CreateUrlCacheEntryA(LPCSTR lpszUrlName
, DWORD dwExpectedFileSize
,
2781 LPCSTR lpszFileExtension
, LPSTR lpszFileName
, DWORD dwReserved
)
2783 WCHAR file_name
[MAX_PATH
];
2786 FIXME("dwReserved 0x%08x\n", dwReserved
);
2788 if(!urlcache_entry_create(lpszUrlName
, lpszFileExtension
, file_name
))
2791 if(!WideCharToMultiByte(CP_ACP
, 0, file_name
, -1, lpszFileName
, MAX_PATH
, NULL
, NULL
))
2795 /***********************************************************************
2796 * CreateUrlCacheEntryW (WININET.@)
2799 BOOL WINAPI
CreateUrlCacheEntryW(LPCWSTR lpszUrlName
, DWORD dwExpectedFileSize
,
2800 LPCWSTR lpszFileExtension
, LPWSTR lpszFileName
, DWORD dwReserved
)
2802 char *url
, *ext
= NULL
;
2806 FIXME("dwReserved 0x%08x\n", dwReserved
);
2808 if(lpszFileExtension
) {
2809 ext
= heap_strdupWtoUTF8(lpszFileExtension
);
2814 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
)) {
2819 ret
= urlcache_entry_create(url
, ext
, lpszFileName
);
2825 static BOOL
urlcache_entry_commit(const char *url
, const WCHAR
*file_name
,
2826 FILETIME expire_time
, FILETIME modify_time
, DWORD entry_type
,
2827 BYTE
*header_info
, DWORD header_size
, const char *file_ext
,
2828 const char *original_url
)
2830 cache_container
*container
;
2831 urlcache_header
*header
;
2832 struct hash_entry
*hash_entry
;
2833 entry_header
*entry
;
2834 entry_url
*url_entry
;
2835 DWORD url_entry_offset
;
2836 DWORD size
= DWORD_ALIGN(sizeof(*url_entry
));
2837 DWORD file_name_off
= 0;
2838 DWORD header_info_off
= 0;
2839 DWORD file_ext_off
= 0;
2840 WIN32_FILE_ATTRIBUTE_DATA file_attr
;
2841 LARGE_INTEGER file_size
;
2843 char file_name_no_container
[MAX_PATH
];
2844 char *local_file_name
= 0;
2846 DWORD exempt_delta
= 0;
2849 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n", debugstr_a(url
), debugstr_w(file_name
),
2850 entry_type
, header_info
, header_size
, debugstr_a(file_ext
), debugstr_a(original_url
));
2852 if(entry_type
& STICKY_CACHE_ENTRY
&& !file_name
) {
2853 SetLastError(ERROR_INVALID_PARAMETER
);
2857 WARN(": original_url ignored\n");
2859 memset(&file_attr
, 0, sizeof(file_attr
));
2861 if(!GetFileAttributesExW(file_name
, GetFileExInfoStandard
, &file_attr
))
2864 file_size
.u
.LowPart
= file_attr
.nFileSizeLow
;
2865 file_size
.u
.HighPart
= file_attr
.nFileSizeHigh
;
2867 error
= cache_containers_find(url
, &container
);
2868 if(error
!= ERROR_SUCCESS
) {
2869 SetLastError(error
);
2873 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2874 if(error
!= ERROR_SUCCESS
) {
2875 SetLastError(error
);
2879 if(!(header
= cache_container_lock_index(container
)))
2882 if(urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
2883 entry_url
*url_entry
= (entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
2885 if(urlcache_hash_entry_is_locked(hash_entry
, url_entry
)) {
2886 TRACE("Trying to overwrite locked entry\n");
2887 cache_container_unlock_index(container
, header
);
2888 SetLastError(ERROR_SHARING_VIOLATION
);
2892 hit_rate
= url_entry
->hit_rate
;
2893 exempt_delta
= url_entry
->exempt_delta
;
2894 urlcache_entry_delete(container
, header
, hash_entry
);
2900 dir_id
= CACHE_CONTAINER_NO_SUBDIR
;
2903 BOOL bFound
= FALSE
;
2905 if(strncmpW(file_name
, container
->path
, lstrlenW(container
->path
))) {
2906 ERR("path %s must begin with cache content path %s\n", debugstr_w(file_name
), debugstr_w(container
->path
));
2907 cache_container_unlock_index(container
, header
);
2908 SetLastError(ERROR_INVALID_PARAMETER
);
2912 /* skip container path prefix */
2913 file_name
+= lstrlenW(container
->path
);
2915 WideCharToMultiByte(CP_ACP
, 0, file_name
, -1, file_name_no_container
, MAX_PATH
, NULL
, NULL
);
2916 local_file_name
= file_name_no_container
;
2918 if(header
->dirs_no
) {
2919 for(dir_id
= 0; dir_id
< header
->dirs_no
; dir_id
++) {
2920 if(!strncmp(header
->directory_data
[dir_id
].name
, local_file_name
, DIR_LENGTH
)) {
2927 ERR("cache directory not found in path %s\n", debugstr_w(file_name
));
2928 cache_container_unlock_index(container
, header
);
2929 SetLastError(ERROR_INVALID_PARAMETER
);
2933 file_name
+= DIR_LENGTH
+ 1;
2934 local_file_name
+= DIR_LENGTH
+ 1;
2938 size
= DWORD_ALIGN(size
+ strlen(url
) + 1);
2940 file_name_off
= size
;
2941 size
= DWORD_ALIGN(size
+ strlen(local_file_name
) + 1);
2943 if(header_info
&& header_size
) {
2944 header_info_off
= size
;
2945 size
= DWORD_ALIGN(size
+ header_size
);
2947 if(file_ext
&& (file_ext_off
= strlen(file_ext
))) {
2948 DWORD len
= file_ext_off
;
2950 file_ext_off
= size
;
2951 size
= DWORD_ALIGN(size
+ len
+ 1);
2954 /* round up to next block */
2955 if(size
% BLOCKSIZE
) {
2956 size
-= size
% BLOCKSIZE
;
2960 error
= urlcache_entry_alloc(header
, size
/ BLOCKSIZE
, &entry
);
2961 while(error
== ERROR_HANDLE_DISK_FULL
) {
2962 error
= cache_container_clean_index(container
, &header
);
2963 if(error
== ERROR_SUCCESS
)
2964 error
= urlcache_entry_alloc(header
, size
/ BLOCKSIZE
, &entry
);
2966 if(error
!= ERROR_SUCCESS
) {
2967 cache_container_unlock_index(container
, header
);
2968 SetLastError(error
);
2972 /* FindFirstFreeEntry fills in blocks used */
2973 url_entry
= (entry_url
*)entry
;
2974 url_entry_offset
= (LPBYTE
)url_entry
- (LPBYTE
)header
;
2975 url_entry
->header
.signature
= URL_SIGNATURE
;
2976 url_entry
->cache_dir
= dir_id
;
2977 url_entry
->cache_entry_type
= entry_type
| container
->default_entry_type
;
2978 url_entry
->header_info_size
= header_size
;
2979 if((entry_type
& STICKY_CACHE_ENTRY
) && !exempt_delta
) {
2980 /* Sticky entries have a default exempt time of one day */
2981 exempt_delta
= 86400;
2983 url_entry
->exempt_delta
= exempt_delta
;
2984 url_entry
->hit_rate
= hit_rate
+1;
2985 url_entry
->file_extension_off
= file_ext_off
;
2986 url_entry
->header_info_off
= header_info_off
;
2987 url_entry
->local_name_off
= file_name_off
;
2988 url_entry
->url_off
= DWORD_ALIGN(sizeof(*url_entry
));
2989 url_entry
->size
.QuadPart
= file_size
.QuadPart
;
2990 url_entry
->use_count
= 0;
2991 GetSystemTimeAsFileTime(&url_entry
->access_time
);
2992 url_entry
->modification_time
= modify_time
;
2993 file_time_to_dos_date_time(&url_entry
->access_time
, &url_entry
->sync_date
, &url_entry
->sync_time
);
2994 file_time_to_dos_date_time(&expire_time
, &url_entry
->expire_date
, &url_entry
->expire_time
);
2995 file_time_to_dos_date_time(&file_attr
.ftLastWriteTime
, &url_entry
->write_date
, &url_entry
->write_time
);
2998 url_entry
->unk1
= 0;
2999 url_entry
->unk2
= 0;
3000 url_entry
->unk3
= 0x60;
3001 url_entry
->unk4
= 0;
3002 url_entry
->unk5
= 0x1010;
3003 url_entry
->unk7
= 0;
3004 url_entry
->unk8
= 0;
3007 strcpy((LPSTR
)url_entry
+ url_entry
->url_off
, url
);
3009 strcpy((LPSTR
)((LPBYTE
)url_entry
+ file_name_off
), local_file_name
);
3011 memcpy((LPBYTE
)url_entry
+ header_info_off
, header_info
, header_size
);
3013 strcpy((LPSTR
)((LPBYTE
)url_entry
+ file_ext_off
), file_ext
);
3015 error
= urlcache_hash_entry_create(header
, url
, url_entry_offset
, HASHTABLE_URL
);
3016 while(error
== ERROR_HANDLE_DISK_FULL
) {
3017 error
= cache_container_clean_index(container
, &header
);
3018 if(error
== ERROR_SUCCESS
) {
3019 url_entry
= (entry_url
*)((LPBYTE
)header
+ url_entry_offset
);
3020 error
= urlcache_hash_entry_create(header
, url
,
3021 url_entry_offset
, HASHTABLE_URL
);
3024 if(error
!= ERROR_SUCCESS
) {
3025 urlcache_entry_free(header
, &url_entry
->header
);
3026 cache_container_unlock_index(container
, header
);
3027 SetLastError(error
);
3031 if(url_entry
->cache_dir
< header
->dirs_no
)
3032 header
->directory_data
[url_entry
->cache_dir
].files_no
++;
3033 if(entry_type
& STICKY_CACHE_ENTRY
)
3034 header
->exempt_usage
.QuadPart
+= file_size
.QuadPart
;
3036 header
->cache_usage
.QuadPart
+= file_size
.QuadPart
;
3037 if(header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
> header
->cache_limit
.QuadPart
)
3038 handle_full_cache();
3040 cache_container_unlock_index(container
, header
);
3044 /***********************************************************************
3045 * CommitUrlCacheEntryA (WININET.@)
3047 BOOL WINAPI
CommitUrlCacheEntryA(LPCSTR lpszUrlName
, LPCSTR lpszLocalFileName
,
3048 FILETIME ExpireTime
, FILETIME LastModifiedTime
, DWORD CacheEntryType
,
3049 LPBYTE lpHeaderInfo
, DWORD dwHeaderSize
, LPCSTR lpszFileExtension
, LPCSTR lpszOriginalUrl
)
3051 WCHAR
*file_name
= NULL
;
3054 if(lpszLocalFileName
) {
3055 file_name
= heap_strdupAtoW(lpszLocalFileName
);
3060 ret
= urlcache_entry_commit(lpszUrlName
, file_name
, ExpireTime
, LastModifiedTime
,
3061 CacheEntryType
, lpHeaderInfo
, dwHeaderSize
, lpszFileExtension
, lpszOriginalUrl
);
3062 heap_free(file_name
);
3066 /***********************************************************************
3067 * CommitUrlCacheEntryW (WININET.@)
3069 BOOL WINAPI
CommitUrlCacheEntryW(LPCWSTR lpszUrlName
, LPCWSTR lpszLocalFileName
,
3070 FILETIME ExpireTime
, FILETIME LastModifiedTime
, DWORD CacheEntryType
,
3071 LPWSTR lpHeaderInfo
, DWORD dwHeaderSize
, LPCWSTR lpszFileExtension
, LPCWSTR lpszOriginalUrl
)
3073 char *url
, *original_url
=NULL
, *file_ext
=NULL
, *header_info
=NULL
;
3076 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
3080 header_info
= heap_strdupWtoUTF8(lpHeaderInfo
);
3085 dwHeaderSize
= strlen(header_info
);
3088 if(lpszFileExtension
) {
3089 file_ext
= heap_strdupWtoA(lpszFileExtension
);
3092 heap_free(header_info
);
3097 if(lpszOriginalUrl
&& !urlcache_encode_url_alloc(lpszOriginalUrl
, &original_url
)) {
3099 heap_free(header_info
);
3100 heap_free(file_ext
);
3104 ret
= urlcache_entry_commit(url
, lpszLocalFileName
, ExpireTime
, LastModifiedTime
,
3105 CacheEntryType
, (BYTE
*)header_info
, dwHeaderSize
, file_ext
, original_url
);
3107 heap_free(header_info
);
3108 heap_free(file_ext
);
3109 heap_free(original_url
);
3113 /***********************************************************************
3114 * ReadUrlCacheEntryStream (WININET.@)
3117 BOOL WINAPI
ReadUrlCacheEntryStream(
3118 IN HANDLE hUrlCacheStream
,
3119 IN DWORD dwLocation
,
3120 IN OUT LPVOID lpBuffer
,
3121 IN OUT LPDWORD lpdwLen
,
3125 /* Get handle to file from 'stream' */
3126 stream_handle
*pStream
= (stream_handle
*)hUrlCacheStream
;
3128 if (dwReserved
!= 0)
3130 ERR("dwReserved != 0\n");
3131 SetLastError(ERROR_INVALID_PARAMETER
);
3135 if (IsBadReadPtr(pStream
, sizeof(*pStream
)) || IsBadStringPtrA(pStream
->url
, INTERNET_MAX_URL_LENGTH
))
3137 SetLastError(ERROR_INVALID_HANDLE
);
3141 if (SetFilePointer(pStream
->file
, dwLocation
, NULL
, FILE_CURRENT
) == INVALID_SET_FILE_POINTER
)
3143 return ReadFile(pStream
->file
, lpBuffer
, *lpdwLen
, lpdwLen
, NULL
);
3146 /***********************************************************************
3147 * RetrieveUrlCacheEntryStreamA (WININET.@)
3150 HANDLE WINAPI
RetrieveUrlCacheEntryStreamA(LPCSTR lpszUrlName
,
3151 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
3152 LPDWORD lpdwCacheEntryInfoBufferSize
, BOOL fRandomRead
, DWORD dwReserved
)
3154 /* NOTE: this is not the same as the way that the native
3155 * version allocates 'stream' handles. I did it this way
3156 * as it is much easier and no applications should depend
3157 * on this behaviour. (Native version appears to allocate
3158 * indices into a table)
3160 stream_handle
*stream
;
3163 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName
), lpCacheEntryInfo
,
3164 lpdwCacheEntryInfoBufferSize
, fRandomRead
, dwReserved
);
3166 if(!RetrieveUrlCacheEntryFileA(lpszUrlName
, lpCacheEntryInfo
,
3167 lpdwCacheEntryInfoBufferSize
, dwReserved
))
3170 file
= CreateFileA(lpCacheEntryInfo
->lpszLocalFileName
, GENERIC_READ
, FILE_SHARE_READ
,
3171 NULL
, OPEN_EXISTING
, fRandomRead
? FILE_FLAG_RANDOM_ACCESS
: 0, NULL
);
3172 if(file
== INVALID_HANDLE_VALUE
) {
3173 UnlockUrlCacheEntryFileA(lpszUrlName
, 0);
3177 /* allocate handle storage space */
3178 stream
= heap_alloc(sizeof(stream_handle
) + strlen(lpszUrlName
) * sizeof(CHAR
));
3181 UnlockUrlCacheEntryFileA(lpszUrlName
, 0);
3182 SetLastError(ERROR_OUTOFMEMORY
);
3186 stream
->file
= file
;
3187 strcpy(stream
->url
, lpszUrlName
);
3191 /***********************************************************************
3192 * RetrieveUrlCacheEntryStreamW (WININET.@)
3195 HANDLE WINAPI
RetrieveUrlCacheEntryStreamW(LPCWSTR lpszUrlName
,
3196 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
3197 LPDWORD lpdwCacheEntryInfoBufferSize
, BOOL fRandomRead
, DWORD dwReserved
)
3200 /* NOTE: this is not the same as the way that the native
3201 * version allocates 'stream' handles. I did it this way
3202 * as it is much easier and no applications should depend
3203 * on this behaviour. (Native version appears to allocate
3204 * indices into a table)
3206 stream_handle
*stream
;
3209 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName
), lpCacheEntryInfo
,
3210 lpdwCacheEntryInfoBufferSize
, fRandomRead
, dwReserved
);
3212 if(!(len
= urlcache_encode_url(lpszUrlName
, NULL
, 0)))
3215 if(!RetrieveUrlCacheEntryFileW(lpszUrlName
, lpCacheEntryInfo
,
3216 lpdwCacheEntryInfoBufferSize
, dwReserved
))
3219 file
= CreateFileW(lpCacheEntryInfo
->lpszLocalFileName
, GENERIC_READ
, FILE_SHARE_READ
,
3220 NULL
, OPEN_EXISTING
, fRandomRead
? FILE_FLAG_RANDOM_ACCESS
: 0, NULL
);
3221 if(file
== INVALID_HANDLE_VALUE
) {
3222 UnlockUrlCacheEntryFileW(lpszUrlName
, 0);
3226 /* allocate handle storage space */
3227 stream
= heap_alloc(sizeof(stream_handle
) + len
*sizeof(WCHAR
));
3230 UnlockUrlCacheEntryFileW(lpszUrlName
, 0);
3231 SetLastError(ERROR_OUTOFMEMORY
);
3235 stream
->file
= file
;
3236 if(!urlcache_encode_url(lpszUrlName
, stream
->url
, len
)) {
3238 UnlockUrlCacheEntryFileW(lpszUrlName
, 0);
3245 /***********************************************************************
3246 * UnlockUrlCacheEntryStream (WININET.@)
3249 BOOL WINAPI
UnlockUrlCacheEntryStream(
3250 IN HANDLE hUrlCacheStream
,
3254 stream_handle
*pStream
= (stream_handle
*)hUrlCacheStream
;
3256 if (dwReserved
!= 0)
3258 ERR("dwReserved != 0\n");
3259 SetLastError(ERROR_INVALID_PARAMETER
);
3263 if (IsBadReadPtr(pStream
, sizeof(*pStream
)) || IsBadStringPtrA(pStream
->url
, INTERNET_MAX_URL_LENGTH
))
3265 SetLastError(ERROR_INVALID_HANDLE
);
3269 if (!UnlockUrlCacheEntryFileA(pStream
->url
, 0))
3272 CloseHandle(pStream
->file
);
3278 /***********************************************************************
3279 * DeleteUrlCacheEntryA (WININET.@)
3282 BOOL WINAPI
DeleteUrlCacheEntryA(LPCSTR lpszUrlName
)
3284 cache_container
*pContainer
;
3285 urlcache_header
*pHeader
;
3286 struct hash_entry
*pHashEntry
;
3290 TRACE("(%s)\n", debugstr_a(lpszUrlName
));
3292 error
= cache_containers_find(lpszUrlName
, &pContainer
);
3293 if (error
!= ERROR_SUCCESS
)
3295 SetLastError(error
);
3299 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
3300 if (error
!= ERROR_SUCCESS
)
3302 SetLastError(error
);
3306 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3309 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
3311 cache_container_unlock_index(pContainer
, pHeader
);
3312 TRACE("entry %s not found!\n", debugstr_a(lpszUrlName
));
3313 SetLastError(ERROR_FILE_NOT_FOUND
);
3317 ret
= urlcache_entry_delete(pContainer
, pHeader
, pHashEntry
);
3319 cache_container_unlock_index(pContainer
, pHeader
);
3324 /***********************************************************************
3325 * DeleteUrlCacheEntryW (WININET.@)
3328 BOOL WINAPI
DeleteUrlCacheEntryW(LPCWSTR lpszUrlName
)
3333 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
3336 ret
= DeleteUrlCacheEntryA(url
);
3341 BOOL WINAPI
DeleteUrlCacheContainerA(DWORD d1
, DWORD d2
)
3343 FIXME("(0x%08x, 0x%08x) stub\n", d1
, d2
);
3347 BOOL WINAPI
DeleteUrlCacheContainerW(DWORD d1
, DWORD d2
)
3349 FIXME("(0x%08x, 0x%08x) stub\n", d1
, d2
);
3353 /***********************************************************************
3354 * CreateCacheContainerA (WININET.@)
3356 BOOL WINAPI
CreateUrlCacheContainerA(DWORD d1
, DWORD d2
, DWORD d3
, DWORD d4
,
3357 DWORD d5
, DWORD d6
, DWORD d7
, DWORD d8
)
3359 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3360 d1
, d2
, d3
, d4
, d5
, d6
, d7
, d8
);
3364 /***********************************************************************
3365 * CreateCacheContainerW (WININET.@)
3367 BOOL WINAPI
CreateUrlCacheContainerW(DWORD d1
, DWORD d2
, DWORD d3
, DWORD d4
,
3368 DWORD d5
, DWORD d6
, DWORD d7
, DWORD d8
)
3370 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3371 d1
, d2
, d3
, d4
, d5
, d6
, d7
, d8
);
3375 /***********************************************************************
3376 * FindFirstUrlCacheContainerA (WININET.@)
3378 HANDLE WINAPI
FindFirstUrlCacheContainerA( LPVOID p1
, LPVOID p2
, LPVOID p3
, DWORD d1
)
3380 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1
, p2
, p3
, d1
);
3384 /***********************************************************************
3385 * FindFirstUrlCacheContainerW (WININET.@)
3387 HANDLE WINAPI
FindFirstUrlCacheContainerW( LPVOID p1
, LPVOID p2
, LPVOID p3
, DWORD d1
)
3389 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1
, p2
, p3
, d1
);
3393 /***********************************************************************
3394 * FindNextUrlCacheContainerA (WININET.@)
3396 BOOL WINAPI
FindNextUrlCacheContainerA( HANDLE handle
, LPVOID p1
, LPVOID p2
)
3398 FIXME("(%p, %p, %p) stub\n", handle
, p1
, p2
);
3402 /***********************************************************************
3403 * FindNextUrlCacheContainerW (WININET.@)
3405 BOOL WINAPI
FindNextUrlCacheContainerW( HANDLE handle
, LPVOID p1
, LPVOID p2
)
3407 FIXME("(%p, %p, %p) stub\n", handle
, p1
, p2
);
3411 HANDLE WINAPI
FindFirstUrlCacheEntryExA(
3412 LPCSTR lpszUrlSearchPattern
,
3416 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
,
3417 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3419 LPDWORD pcbReserved2
,
3423 FIXME("(%s, 0x%08x, 0x%08x, 0x%s, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern
),
3424 dwFlags
, dwFilter
, wine_dbgstr_longlong(GroupId
), lpFirstCacheEntryInfo
,
3425 lpdwFirstCacheEntryInfoBufferSize
, lpReserved
, pcbReserved2
,lpReserved3
);
3426 SetLastError(ERROR_FILE_NOT_FOUND
);
3430 HANDLE WINAPI
FindFirstUrlCacheEntryExW(
3431 LPCWSTR lpszUrlSearchPattern
,
3435 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
,
3436 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3438 LPDWORD pcbReserved2
,
3442 FIXME("(%s, 0x%08x, 0x%08x, 0x%s, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern
),
3443 dwFlags
, dwFilter
, wine_dbgstr_longlong(GroupId
), lpFirstCacheEntryInfo
,
3444 lpdwFirstCacheEntryInfoBufferSize
, lpReserved
, pcbReserved2
,lpReserved3
);
3445 SetLastError(ERROR_FILE_NOT_FOUND
);
3449 /***********************************************************************
3450 * FindFirstUrlCacheEntryA (WININET.@)
3453 INTERNETAPI HANDLE WINAPI
FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern
,
3454 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
, LPDWORD lpdwFirstCacheEntryInfoBufferSize
)
3456 find_handle
*pEntryHandle
;
3458 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern
), lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
);
3460 pEntryHandle
= heap_alloc(sizeof(*pEntryHandle
));
3464 pEntryHandle
->magic
= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
;
3465 if (lpszUrlSearchPattern
)
3467 pEntryHandle
->url_search_pattern
= heap_strdupA(lpszUrlSearchPattern
);
3468 if (!pEntryHandle
->url_search_pattern
)
3470 heap_free(pEntryHandle
);
3475 pEntryHandle
->url_search_pattern
= NULL
;
3476 pEntryHandle
->container_idx
= 0;
3477 pEntryHandle
->hash_table_idx
= 0;
3478 pEntryHandle
->hash_entry_idx
= 0;
3480 if (!FindNextUrlCacheEntryA(pEntryHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
))
3482 heap_free(pEntryHandle
);
3485 return pEntryHandle
;
3488 /***********************************************************************
3489 * FindFirstUrlCacheEntryW (WININET.@)
3492 INTERNETAPI HANDLE WINAPI
FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern
,
3493 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
, LPDWORD lpdwFirstCacheEntryInfoBufferSize
)
3495 find_handle
*pEntryHandle
;
3497 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern
), lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
);
3499 pEntryHandle
= heap_alloc(sizeof(*pEntryHandle
));
3503 pEntryHandle
->magic
= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
;
3504 if (lpszUrlSearchPattern
)
3506 pEntryHandle
->url_search_pattern
= heap_strdupWtoA(lpszUrlSearchPattern
);
3507 if (!pEntryHandle
->url_search_pattern
)
3509 heap_free(pEntryHandle
);
3514 pEntryHandle
->url_search_pattern
= NULL
;
3515 pEntryHandle
->container_idx
= 0;
3516 pEntryHandle
->hash_table_idx
= 0;
3517 pEntryHandle
->hash_entry_idx
= 0;
3519 if (!FindNextUrlCacheEntryW(pEntryHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
))
3521 heap_free(pEntryHandle
);
3524 return pEntryHandle
;
3527 static BOOL
urlcache_find_next_entry(
3529 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo
,
3530 LPDWORD lpdwNextCacheEntryInfoBufferSize
,
3533 find_handle
*pEntryHandle
= (find_handle
*)hEnumHandle
;
3534 cache_container
*pContainer
;
3536 if (pEntryHandle
->magic
!= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
)
3538 SetLastError(ERROR_INVALID_HANDLE
);
3542 for (; cache_containers_enum(pEntryHandle
->url_search_pattern
, pEntryHandle
->container_idx
, &pContainer
);
3543 pEntryHandle
->container_idx
++, pEntryHandle
->hash_table_idx
= 0)
3545 urlcache_header
*pHeader
;
3546 entry_hash_table
*pHashTableEntry
;
3549 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
3550 if (error
!= ERROR_SUCCESS
)
3552 SetLastError(error
);
3556 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3559 for (; urlcache_enum_hash_tables(pHeader
, &pEntryHandle
->hash_table_idx
, &pHashTableEntry
);
3560 pEntryHandle
->hash_table_idx
++, pEntryHandle
->hash_entry_idx
= 0)
3562 const struct hash_entry
*pHashEntry
= NULL
;
3563 for (; urlcache_enum_hash_table_entries(pHeader
, pHashTableEntry
, &pEntryHandle
->hash_entry_idx
, &pHashEntry
);
3564 pEntryHandle
->hash_entry_idx
++)
3566 const entry_url
*pUrlEntry
;
3567 const entry_header
*pEntry
= (const entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
3569 if (pEntry
->signature
!= URL_SIGNATURE
)
3572 pUrlEntry
= (const entry_url
*)pEntry
;
3573 TRACE("Found URL: %s\n",
3574 debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->url_off
));
3575 TRACE("Header info: %s\n",
3576 debugstr_an((LPCSTR
)pUrlEntry
+ pUrlEntry
->header_info_off
,
3577 pUrlEntry
->header_info_size
));
3579 error
= urlcache_copy_entry(
3582 lpNextCacheEntryInfo
,
3583 lpdwNextCacheEntryInfoBufferSize
,
3586 if (error
!= ERROR_SUCCESS
)
3588 cache_container_unlock_index(pContainer
, pHeader
);
3589 SetLastError(error
);
3592 if(pUrlEntry
->local_name_off
)
3593 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->local_name_off
));
3595 /* increment the current index so that next time the function
3596 * is called the next entry is returned */
3597 pEntryHandle
->hash_entry_idx
++;
3598 cache_container_unlock_index(pContainer
, pHeader
);
3603 cache_container_unlock_index(pContainer
, pHeader
);
3606 SetLastError(ERROR_NO_MORE_ITEMS
);
3610 /***********************************************************************
3611 * FindNextUrlCacheEntryA (WININET.@)
3613 BOOL WINAPI
FindNextUrlCacheEntryA(
3615 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo
,
3616 LPDWORD lpdwNextCacheEntryInfoBufferSize
)
3618 TRACE("(%p, %p, %p)\n", hEnumHandle
, lpNextCacheEntryInfo
, lpdwNextCacheEntryInfoBufferSize
);
3620 return urlcache_find_next_entry(hEnumHandle
, lpNextCacheEntryInfo
,
3621 lpdwNextCacheEntryInfoBufferSize
, FALSE
/* not UNICODE */);
3624 /***********************************************************************
3625 * FindNextUrlCacheEntryW (WININET.@)
3627 BOOL WINAPI
FindNextUrlCacheEntryW(
3629 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo
,
3630 LPDWORD lpdwNextCacheEntryInfoBufferSize
3633 TRACE("(%p, %p, %p)\n", hEnumHandle
, lpNextCacheEntryInfo
, lpdwNextCacheEntryInfoBufferSize
);
3635 return urlcache_find_next_entry(hEnumHandle
,
3636 (LPINTERNET_CACHE_ENTRY_INFOA
)lpNextCacheEntryInfo
,
3637 lpdwNextCacheEntryInfoBufferSize
, TRUE
/* UNICODE */);
3640 /***********************************************************************
3641 * FindCloseUrlCache (WININET.@)
3643 BOOL WINAPI
FindCloseUrlCache(HANDLE hEnumHandle
)
3645 find_handle
*pEntryHandle
= (find_handle
*)hEnumHandle
;
3647 TRACE("(%p)\n", hEnumHandle
);
3649 if (!pEntryHandle
|| pEntryHandle
->magic
!= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
)
3651 SetLastError(ERROR_INVALID_HANDLE
);
3655 pEntryHandle
->magic
= 0;
3656 heap_free(pEntryHandle
->url_search_pattern
);
3657 heap_free(pEntryHandle
);
3661 HANDLE WINAPI
FindFirstUrlCacheGroup( DWORD dwFlags
, DWORD dwFilter
, LPVOID lpSearchCondition
,
3662 DWORD dwSearchCondition
, GROUPID
* lpGroupId
, LPVOID lpReserved
)
3664 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags
, dwFilter
, lpSearchCondition
,
3665 dwSearchCondition
, lpGroupId
, lpReserved
);
3669 BOOL WINAPI
FindNextUrlCacheEntryExA(
3671 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
,
3672 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3674 LPDWORD pcbReserved2
,
3678 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
,
3679 lpReserved
, pcbReserved2
, lpReserved3
);
3683 BOOL WINAPI
FindNextUrlCacheEntryExW(
3685 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
,
3686 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3688 LPDWORD pcbReserved2
,
3692 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
,
3693 lpReserved
, pcbReserved2
, lpReserved3
);
3697 BOOL WINAPI
FindNextUrlCacheGroup( HANDLE hFind
, GROUPID
* lpGroupId
, LPVOID lpReserved
)
3699 FIXME("(%p, %p, %p) stub\n", hFind
, lpGroupId
, lpReserved
);
3703 /***********************************************************************
3704 * CreateUrlCacheGroup (WININET.@)
3707 INTERNETAPI GROUPID WINAPI
CreateUrlCacheGroup(DWORD dwFlags
, LPVOID lpReserved
)
3709 FIXME("(0x%08x, %p): stub\n", dwFlags
, lpReserved
);
3713 /***********************************************************************
3714 * DeleteUrlCacheGroup (WININET.@)
3717 BOOL WINAPI
DeleteUrlCacheGroup(GROUPID GroupId
, DWORD dwFlags
, LPVOID lpReserved
)
3719 FIXME("(0x%s, 0x%08x, %p) stub\n",
3720 wine_dbgstr_longlong(GroupId
), dwFlags
, lpReserved
);
3724 /***********************************************************************
3725 * DeleteWpadCacheForNetworks (WININET.@)
3726 * Undocumented, added in IE8
3728 BOOL WINAPI
DeleteWpadCacheForNetworks(DWORD unk1
)
3730 FIXME("(%d) stub\n", unk1
);
3734 /***********************************************************************
3735 * SetUrlCacheEntryGroupA (WININET.@)
3738 BOOL WINAPI
SetUrlCacheEntryGroupA(LPCSTR lpszUrlName
, DWORD dwFlags
,
3739 GROUPID GroupId
, LPBYTE pbGroupAttributes
, DWORD cbGroupAttributes
,
3742 FIXME("(%s, 0x%08x, 0x%s, %p, 0x%08x, %p) stub\n",
3743 debugstr_a(lpszUrlName
), dwFlags
, wine_dbgstr_longlong(GroupId
),
3744 pbGroupAttributes
, cbGroupAttributes
, lpReserved
);
3745 SetLastError(ERROR_FILE_NOT_FOUND
);
3749 /***********************************************************************
3750 * SetUrlCacheEntryGroupW (WININET.@)
3753 BOOL WINAPI
SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName
, DWORD dwFlags
,
3754 GROUPID GroupId
, LPBYTE pbGroupAttributes
, DWORD cbGroupAttributes
,
3757 FIXME("(%s, 0x%08x, 0x%s, %p, 0x%08x, %p) stub\n",
3758 debugstr_w(lpszUrlName
), dwFlags
, wine_dbgstr_longlong(GroupId
),
3759 pbGroupAttributes
, cbGroupAttributes
, lpReserved
);
3760 SetLastError(ERROR_FILE_NOT_FOUND
);
3764 /***********************************************************************
3765 * GetUrlCacheConfigInfoW (WININET.@)
3767 BOOL WINAPI
GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo
, LPDWORD size
, DWORD bitmask
)
3769 FIXME("(%p, %p, %x)\n", CacheInfo
, size
, bitmask
);
3770 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
3774 /***********************************************************************
3775 * GetUrlCacheConfigInfoA (WININET.@)
3777 BOOL WINAPI
GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo
, LPDWORD size
, DWORD bitmask
)
3779 FIXME("(%p, %p, %x)\n", CacheInfo
, size
, bitmask
);
3780 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
3784 BOOL WINAPI
GetUrlCacheGroupAttributeA( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3785 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo
,
3786 LPDWORD lpdwGroupInfo
, LPVOID lpReserved
)
3788 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3789 wine_dbgstr_longlong(gid
), dwFlags
, dwAttributes
, lpGroupInfo
,
3790 lpdwGroupInfo
, lpReserved
);
3794 BOOL WINAPI
GetUrlCacheGroupAttributeW( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3795 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo
,
3796 LPDWORD lpdwGroupInfo
, LPVOID lpReserved
)
3798 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3799 wine_dbgstr_longlong(gid
), dwFlags
, dwAttributes
, lpGroupInfo
,
3800 lpdwGroupInfo
, lpReserved
);
3804 BOOL WINAPI
SetUrlCacheGroupAttributeA( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3805 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo
, LPVOID lpReserved
)
3807 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p) stub\n",
3808 wine_dbgstr_longlong(gid
), dwFlags
, dwAttributes
, lpGroupInfo
, lpReserved
);
3812 BOOL WINAPI
SetUrlCacheGroupAttributeW( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3813 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo
, LPVOID lpReserved
)
3815 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p) stub\n",
3816 wine_dbgstr_longlong(gid
), dwFlags
, dwAttributes
, lpGroupInfo
, lpReserved
);
3820 BOOL WINAPI
SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo
, DWORD dwFieldControl
)
3822 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo
, dwFieldControl
);
3826 BOOL WINAPI
SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo
, DWORD dwFieldControl
)
3828 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo
, dwFieldControl
);
3832 /***********************************************************************
3833 * DeleteIE3Cache (WININET.@)
3835 * Deletes the files used by the IE3 URL caching system.
3838 * hWnd [I] A dummy window.
3839 * hInst [I] Instance of process calling the function.
3840 * lpszCmdLine [I] Options used by function.
3841 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3843 DWORD WINAPI
DeleteIE3Cache(HWND hWnd
, HINSTANCE hInst
, LPSTR lpszCmdLine
, int nCmdShow
)
3845 FIXME("(%p, %p, %s, %d)\n", hWnd
, hInst
, debugstr_a(lpszCmdLine
), nCmdShow
);
3849 static BOOL
urlcache_entry_is_expired(const entry_url
*pUrlEntry
,
3850 FILETIME
*pftLastModified
)
3853 FILETIME now
, expired
;
3855 *pftLastModified
= pUrlEntry
->modification_time
;
3856 GetSystemTimeAsFileTime(&now
);
3857 dos_date_time_to_file_time(pUrlEntry
->expire_date
,
3858 pUrlEntry
->expire_time
, &expired
);
3859 /* If the expired time is 0, it's interpreted as not expired */
3860 if (!expired
.dwLowDateTime
&& !expired
.dwHighDateTime
)
3863 ret
= CompareFileTime(&expired
, &now
) < 0;
3867 /***********************************************************************
3868 * IsUrlCacheEntryExpiredA (WININET.@)
3872 * dwFlags [I] Unknown
3873 * pftLastModified [O] Last modified time
3875 BOOL WINAPI
IsUrlCacheEntryExpiredA(LPCSTR url
, DWORD dwFlags
, FILETIME
* pftLastModified
)
3877 urlcache_header
*pHeader
;
3878 struct hash_entry
*pHashEntry
;
3879 const entry_header
*pEntry
;
3880 const entry_url
* pUrlEntry
;
3881 cache_container
*pContainer
;
3884 TRACE("(%s, %08x, %p)\n", debugstr_a(url
), dwFlags
, pftLastModified
);
3886 if (!url
|| !pftLastModified
)
3889 FIXME("unknown flags 0x%08x\n", dwFlags
);
3891 /* Any error implies that the URL is expired, i.e. not in the cache */
3892 if (cache_containers_find(url
, &pContainer
))
3894 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3898 if (cache_container_open_index(pContainer
, MIN_BLOCK_NO
))
3900 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3904 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3906 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3910 if (!urlcache_find_hash_entry(pHeader
, url
, &pHashEntry
))
3912 cache_container_unlock_index(pContainer
, pHeader
);
3913 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3914 TRACE("entry %s not found!\n", url
);
3918 pEntry
= (const entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
3919 if (pEntry
->signature
!= URL_SIGNATURE
)
3921 cache_container_unlock_index(pContainer
, pHeader
);
3922 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3923 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR
)&pEntry
->signature
, sizeof(DWORD
)));
3927 pUrlEntry
= (const entry_url
*)pEntry
;
3928 expired
= urlcache_entry_is_expired(pUrlEntry
, pftLastModified
);
3930 cache_container_unlock_index(pContainer
, pHeader
);
3935 /***********************************************************************
3936 * IsUrlCacheEntryExpiredW (WININET.@)
3940 * dwFlags [I] Unknown
3941 * pftLastModified [O] Last modified time
3943 BOOL WINAPI
IsUrlCacheEntryExpiredW(LPCWSTR url
, DWORD dwFlags
, FILETIME
* pftLastModified
)
3948 if(!urlcache_encode_url_alloc(url
, &encoded_url
))
3951 ret
= IsUrlCacheEntryExpiredA(encoded_url
, dwFlags
, pftLastModified
);
3952 heap_free(encoded_url
);
3956 /***********************************************************************
3957 * GetDiskInfoA (WININET.@)
3959 BOOL WINAPI
GetDiskInfoA(PCSTR path
, PDWORD cluster_size
, PDWORDLONG free
, PDWORDLONG total
)
3962 ULARGE_INTEGER bytes_free
, bytes_total
;
3964 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path
), cluster_size
, free
, total
);
3968 SetLastError(ERROR_INVALID_PARAMETER
);
3972 if ((ret
= GetDiskFreeSpaceExA(path
, NULL
, &bytes_total
, &bytes_free
)))
3974 if (cluster_size
) *cluster_size
= 1;
3975 if (free
) *free
= bytes_free
.QuadPart
;
3976 if (total
) *total
= bytes_total
.QuadPart
;
3981 /***********************************************************************
3982 * RegisterUrlCacheNotification (WININET.@)
3984 DWORD WINAPI
RegisterUrlCacheNotification(LPVOID a
, DWORD b
, DWORD c
, DWORD d
, DWORD e
, DWORD f
)
3986 FIXME("(%p %x %x %x %x %x)\n", a
, b
, c
, d
, e
, f
);
3990 /***********************************************************************
3991 * IncrementUrlCacheHeaderData (WININET.@)
3993 BOOL WINAPI
IncrementUrlCacheHeaderData(DWORD index
, LPDWORD data
)
3995 FIXME("(%u, %p)\n", index
, data
);
3999 /***********************************************************************
4000 * RunOnceUrlCache (WININET.@)
4003 DWORD WINAPI
RunOnceUrlCache(HWND hwnd
, HINSTANCE hinst
, LPSTR cmd
, int cmdshow
)
4005 FIXME("(%p, %p, %s, %d): stub\n", hwnd
, hinst
, debugstr_a(cmd
), cmdshow
);
4009 BOOL
init_urlcache(void)
4011 dll_unload_event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
4012 if(!dll_unload_event
)
4015 free_cache_running
= CreateSemaphoreW(NULL
, 1, 1, NULL
);
4016 if(!free_cache_running
) {
4017 CloseHandle(dll_unload_event
);
4021 cache_containers_init();
4025 void free_urlcache(void)
4027 SetEvent(dll_unload_event
);
4028 WaitForSingleObject(free_cache_running
, INFINITE
);
4029 ReleaseSemaphore(free_cache_running
, 1, NULL
);
4030 CloseHandle(free_cache_running
);
4031 CloseHandle(dll_unload_event
);
4033 cache_containers_free();
4036 /***********************************************************************
4037 * LoadUrlCacheContent (WININET.@)
4039 BOOL WINAPI
LoadUrlCacheContent(void)