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
30 static const char urlcache_ver_prefix
[] = "WINE URLCache Ver ";
31 static const char urlcache_ver
[] = "0.2012001";
34 #define CHAR_BIT (8 * sizeof(CHAR))
37 #define ENTRY_START_OFFSET 0x4000
39 #define MAX_DIR_NO 0x20
41 #define HASHTABLE_SIZE 448
42 #define HASHTABLE_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
43 #define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
44 #define ALLOCATION_TABLE_OFFSET 0x250
45 #define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
46 #define MIN_BLOCK_NO 0x80
47 #define MAX_BLOCK_NO (ALLOCATION_TABLE_SIZE * CHAR_BIT)
48 #define FILE_SIZE(blocks) ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
50 #define HASHTABLE_URL 0
51 #define HASHTABLE_DEL 1
52 #define HASHTABLE_LOCK 2
53 #define HASHTABLE_FREE 3
54 #define HASHTABLE_REDR 5
55 #define HASHTABLE_FLAG_BITS 6
57 #define PENDING_DELETE_CACHE_ENTRY 0x00400000
58 #define INSTALLED_CACHE_ENTRY 0x10000000
59 #define GET_INSTALLED_ENTRY 0x200
60 #define CACHE_CONTAINER_NO_SUBDIR 0xFE
62 #define CACHE_HEADER_DATA_ROOT_LEAK_OFFSET 0x16
64 #define FILETIME_SECOND 10000000
66 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
67 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
68 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
69 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
70 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
72 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
74 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
79 DWORD blocks_used
; /* number of 128byte blocks used by this entry */
85 FILETIME modification_time
;
87 WORD expire_date
; /* expire date in dos format */
88 WORD expire_time
; /* expire time in dos format */
89 DWORD unk1
; /* usually zero */
90 ULARGE_INTEGER size
; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
91 DWORD unk2
; /* usually zero */
92 DWORD exempt_delta
; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
93 DWORD unk3
; /* usually 0x60 */
94 DWORD url_off
; /* offset of start of url from start of entry */
95 BYTE cache_dir
; /* index of cache directory this url is stored in */
96 BYTE unk4
; /* usually zero */
97 WORD unk5
; /* usually 0x1010 */
98 DWORD local_name_off
; /* offset of start of local filename from start of entry */
99 DWORD cache_entry_type
; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
100 DWORD header_info_off
; /* offset of start of header info from start of entry */
101 DWORD header_info_size
;
102 DWORD file_extension_off
; /* offset of start of file extension from start of entry */
103 WORD sync_date
; /* last sync date in dos format */
104 WORD sync_time
; /* last sync time in dos format */
105 DWORD hit_rate
; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
106 DWORD use_count
; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
109 DWORD unk7
; /* usually zero */
110 DWORD unk8
; /* usually zero */
111 /* packing to dword align start of next field */
112 /* CHAR szSourceUrlName[]; (url) */
113 /* packing to dword align start of next field */
114 /* CHAR szLocalFileName[]; (local file name excluding path) */
115 /* packing to dword align start of next field */
116 /* CHAR szHeaderInfo[]; (header info) */
130 struct hash_entry hash_table
[HASHTABLE_SIZE
];
137 DWORD hash_table_off
;
138 DWORD capacity_in_blocks
;
141 ULARGE_INTEGER cache_limit
;
142 ULARGE_INTEGER cache_usage
;
143 ULARGE_INTEGER exempt_usage
;
145 struct _directory_data
148 char name
[DIR_LENGTH
];
149 } directory_data
[MAX_DIR_NO
];
151 BYTE allocation_table
[ALLOCATION_TABLE_SIZE
];
162 struct list entry
; /* part of a list */
163 char *cache_prefix
; /* string that has to be prefixed for this container to be used */
164 LPWSTR path
; /* path to url container directory */
165 HANDLE mapping
; /* handle of file mapping */
166 DWORD file_size
; /* size of file when mapping was opened */
167 HANDLE mutex
; /* handle of mutex */
168 DWORD default_entry_type
;
174 char *url_search_pattern
;
176 DWORD hash_table_idx
;
177 DWORD hash_entry_idx
;
180 /* List of all containers available */
181 static struct list UrlContainers
= LIST_INIT(UrlContainers
);
183 BOOL bDefaultContainersAdded
= FALSE
;
185 static inline char *heap_strdupWtoUTF8(LPCWSTR str
)
190 DWORD size
= WideCharToMultiByte(CP_UTF8
, 0, str
, -1, NULL
, 0, NULL
, NULL
);
191 ret
= heap_alloc(size
);
193 WideCharToMultiByte(CP_UTF8
, 0, str
, -1, ret
, size
, NULL
, NULL
);
199 /***********************************************************************
200 * urlcache_block_is_free (Internal)
202 * Is the specified block number free?
209 static inline BYTE
urlcache_block_is_free(BYTE
*allocation_table
, DWORD block_number
)
211 BYTE mask
= 1 << (block_number
%CHAR_BIT
);
212 return (allocation_table
[block_number
/CHAR_BIT
] & mask
) == 0;
215 /***********************************************************************
216 * urlcache_block_free (Internal)
218 * Marks the specified block as free
221 * this function is not updating used blocks count
227 static inline void urlcache_block_free(BYTE
*allocation_table
, DWORD block_number
)
229 BYTE mask
= ~(1 << (block_number
%CHAR_BIT
));
230 allocation_table
[block_number
/CHAR_BIT
] &= mask
;
233 /***********************************************************************
234 * urlcache_block_alloc (Internal)
236 * Marks the specified block as allocated
239 * this function is not updating used blocks count
245 static inline void urlcache_block_alloc(BYTE
*allocation_table
, DWORD block_number
)
247 BYTE mask
= 1 << (block_number
%CHAR_BIT
);
248 allocation_table
[block_number
/CHAR_BIT
] |= mask
;
251 /***********************************************************************
252 * urlcache_entry_alloc (Internal)
254 * Finds and allocates the first block of free space big enough and
255 * sets entry to point to it.
258 * ERROR_SUCCESS when free memory block was found
259 * Any other Win32 error code if the entry could not be added
262 static DWORD
urlcache_entry_alloc(urlcache_header
*header
, DWORD blocks_needed
, entry_header
**entry
)
264 DWORD block
, block_size
;
266 for(block
=0; block
<header
->capacity_in_blocks
; block
+=block_size
+1)
269 while(block_size
<blocks_needed
&& block_size
+block
<header
->capacity_in_blocks
270 && urlcache_block_is_free(header
->allocation_table
, block
+block_size
))
273 if(block_size
== blocks_needed
)
277 TRACE("Found free blocks starting at no. %d (0x%x)\n", block
, ENTRY_START_OFFSET
+block
*BLOCKSIZE
);
279 for(index
=0; index
<blocks_needed
; index
++)
280 urlcache_block_alloc(header
->allocation_table
, block
+index
);
282 *entry
= (entry_header
*)((BYTE
*)header
+ENTRY_START_OFFSET
+block
*BLOCKSIZE
);
283 for(index
=0; index
<blocks_needed
*BLOCKSIZE
/sizeof(DWORD
); index
++)
284 ((DWORD
*)*entry
)[index
] = 0xdeadbeef;
285 (*entry
)->blocks_used
= blocks_needed
;
287 header
->blocks_in_use
+= blocks_needed
;
288 return ERROR_SUCCESS
;
292 return ERROR_HANDLE_DISK_FULL
;
295 /***********************************************************************
296 * urlcache_entry_free (Internal)
298 * Deletes the specified entry and frees the space allocated to it
301 * TRUE if it succeeded
305 static BOOL
urlcache_entry_free(urlcache_header
*header
, entry_header
*entry
)
307 DWORD start_block
, block
;
309 /* update allocation table */
310 start_block
= ((DWORD
)((BYTE
*)entry
- (BYTE
*)header
) - ENTRY_START_OFFSET
) / BLOCKSIZE
;
311 for(block
= start_block
; block
< start_block
+entry
->blocks_used
; block
++)
312 urlcache_block_free(header
->allocation_table
, block
);
314 header
->blocks_in_use
-= entry
->blocks_used
;
318 /***********************************************************************
319 * urlcache_create_hash_table (Internal)
321 * Creates a new hash table in free space and adds it to the chain of existing
325 * ERROR_SUCCESS if the hash table was created
326 * ERROR_DISK_FULL if the hash table could not be created
329 static DWORD
urlcache_create_hash_table(urlcache_header
*header
, entry_hash_table
*hash_table_prev
, entry_hash_table
**hash_table
)
331 DWORD dwOffset
, error
;
334 if((error
= urlcache_entry_alloc(header
, 0x20, (entry_header
**)hash_table
)) != ERROR_SUCCESS
)
337 dwOffset
= (BYTE
*)*hash_table
-(BYTE
*)header
;
340 hash_table_prev
->next
= dwOffset
;
342 header
->hash_table_off
= dwOffset
;
344 (*hash_table
)->header
.signature
= HASH_SIGNATURE
;
345 (*hash_table
)->next
= 0;
346 (*hash_table
)->id
= hash_table_prev
? hash_table_prev
->id
+1 : 0;
347 for(i
= 0; i
< HASHTABLE_SIZE
; i
++) {
348 (*hash_table
)->hash_table
[i
].offset
= HASHTABLE_FREE
;
349 (*hash_table
)->hash_table
[i
].key
= HASHTABLE_FREE
;
351 return ERROR_SUCCESS
;
354 /***********************************************************************
355 * cache_container_create_object_name (Internal)
357 * Converts a path to a name suitable for use as a Win32 object name.
358 * Replaces '\\' characters in-place with the specified character
359 * (usually '_' or '!')
365 static void cache_container_create_object_name(LPWSTR lpszPath
, WCHAR replace
)
367 for (; *lpszPath
; lpszPath
++)
369 if (*lpszPath
== '\\')
374 /* Caller must hold container lock */
375 static HANDLE
cache_container_map_index(HANDLE file
, const WCHAR
*path
, DWORD size
, BOOL
*validate
)
377 static const WCHAR mapping_name_format
[]
378 = {'%','s','i','n','d','e','x','.','d','a','t','_','%','l','u',0};
379 WCHAR mapping_name
[MAX_PATH
];
382 wsprintfW(mapping_name
, mapping_name_format
, path
, size
);
383 cache_container_create_object_name(mapping_name
, '_');
385 mapping
= OpenFileMappingW(FILE_MAP_WRITE
, FALSE
, mapping_name
);
387 if(validate
) *validate
= FALSE
;
391 if(validate
) *validate
= TRUE
;
392 return CreateFileMappingW(file
, NULL
, PAGE_READWRITE
, 0, 0, mapping_name
);
395 /* Caller must hold container lock */
396 static DWORD
cache_container_set_size(cache_container
*container
, HANDLE file
, DWORD blocks_no
)
398 static const WCHAR cache_content_key
[] = {'S','o','f','t','w','a','r','e','\\',
399 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
400 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
401 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s','\\',
402 'C','a','c','h','e','\\','C','o','n','t','e','n','t',0};
403 static const WCHAR cache_limit
[] = {'C','a','c','h','e','L','i','m','i','t',0};
405 DWORD file_size
= FILE_SIZE(blocks_no
);
406 WCHAR dir_path
[MAX_PATH
], *dir_name
;
407 entry_hash_table
*hashtable_entry
;
408 urlcache_header
*header
;
414 if(SetFilePointer(file
, file_size
, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
415 return GetLastError();
417 if(!SetEndOfFile(file
))
418 return GetLastError();
420 mapping
= cache_container_map_index(file
, container
->path
, file_size
, NULL
);
422 return GetLastError();
424 header
= MapViewOfFile(mapping
, FILE_MAP_WRITE
, 0, 0, 0);
426 CloseHandle(mapping
);
427 return GetLastError();
430 if(blocks_no
!= MIN_BLOCK_NO
) {
431 if(file_size
> header
->size
)
432 memset((char*)header
+header
->size
, 0, file_size
-header
->size
);
433 header
->size
= file_size
;
434 header
->capacity_in_blocks
= blocks_no
;
436 UnmapViewOfFile(header
);
437 CloseHandle(container
->mapping
);
438 container
->mapping
= mapping
;
439 container
->file_size
= file_size
;
440 return ERROR_SUCCESS
;
443 memset(header
, 0, file_size
);
444 /* First set some constants and defaults in the header */
445 memcpy(header
->signature
, urlcache_ver_prefix
, sizeof(urlcache_ver_prefix
)-1);
446 memcpy(header
->signature
+sizeof(urlcache_ver_prefix
)-1, urlcache_ver
, sizeof(urlcache_ver
)-1);
447 header
->size
= file_size
;
448 header
->capacity_in_blocks
= blocks_no
;
449 /* 127MB - taken from default for Windows 2000 */
450 header
->cache_limit
.QuadPart
= 0x07ff5400;
451 /* Copied from a Windows 2000 cache index */
452 header
->dirs_no
= container
->default_entry_type
==NORMAL_CACHE_ENTRY
? 4 : 0;
454 /* If the registry has a cache size set, use the registry value */
455 if(RegOpenKeyW(HKEY_CURRENT_USER
, cache_content_key
, &key
) == ERROR_SUCCESS
) {
456 DWORD dw
, len
= sizeof(dw
), keytype
;
458 if(RegQueryValueExW(key
, cache_limit
, NULL
, &keytype
, (BYTE
*)&dw
, &len
) == ERROR_SUCCESS
&&
459 keytype
== REG_DWORD
)
460 header
->cache_limit
.QuadPart
= (ULONGLONG
)dw
* 1024;
464 urlcache_create_hash_table(header
, NULL
, &hashtable_entry
);
466 /* Last step - create the directories */
467 strcpyW(dir_path
, container
->path
);
468 dir_name
= dir_path
+ strlenW(dir_path
);
471 GetSystemTimeAsFileTime(&ft
);
473 for(i
=0; i
<header
->dirs_no
; ++i
) {
474 header
->directory_data
[i
].files_no
= 0;
476 ULONGLONG n
= ft
.dwHighDateTime
;
479 /* Generate a file name to attempt to create.
480 * This algorithm will create what will appear
481 * to be random and unrelated directory names
482 * of up to 9 characters in length.
485 n
+= ft
.dwLowDateTime
;
486 n
^= ((ULONGLONG
) i
<< 56) | ((ULONGLONG
) j
<< 48);
488 for(k
= 0; k
< 8; ++k
) {
491 /* Dividing by a prime greater than 36 helps
492 * with the appearance of randomness
497 dir_name
[k
] = '0' + r
;
499 dir_name
[k
] = 'A' + (r
- 10);
502 if(CreateDirectoryW(dir_path
, 0)) {
503 /* The following is OK because we generated an
504 * 8 character directory name made from characters
505 * [A-Z0-9], which are equivalent for all code
506 * pages and for UTF-16
508 for (k
= 0; k
< 8; ++k
)
509 header
->directory_data
[i
].name
[k
] = dir_name
[k
];
512 /* Give up. The most likely cause of this
513 * is a full disk, but whatever the cause
514 * is, it should be more than apparent that
517 UnmapViewOfFile(header
);
518 CloseHandle(mapping
);
519 return GetLastError();
524 UnmapViewOfFile(header
);
525 CloseHandle(container
->mapping
);
526 container
->mapping
= mapping
;
527 container
->file_size
= file_size
;
528 return ERROR_SUCCESS
;
531 static BOOL
cache_container_is_valid(urlcache_header
*header
, DWORD file_size
)
533 DWORD allocation_size
, count_bits
, i
;
535 if(file_size
< FILE_SIZE(MIN_BLOCK_NO
))
538 if(file_size
!= header
->size
)
541 if (!memcmp(header
->signature
, urlcache_ver_prefix
, sizeof(urlcache_ver_prefix
)-1) &&
542 memcmp(header
->signature
+sizeof(urlcache_ver_prefix
)-1, urlcache_ver
, sizeof(urlcache_ver
)-1))
545 if(FILE_SIZE(header
->capacity_in_blocks
) != file_size
)
549 for(i
=0; i
<header
->capacity_in_blocks
/8; i
++) {
550 for(count_bits
= header
->allocation_table
[i
]; count_bits
!=0; count_bits
>>=1) {
555 if(allocation_size
!= header
->blocks_in_use
)
558 for(; i
<ALLOCATION_TABLE_SIZE
; i
++) {
559 if(header
->allocation_table
[i
])
566 /***********************************************************************
567 * cache_container_open_index (Internal)
569 * Opens the index file and saves mapping handle
572 * ERROR_SUCCESS if succeeded
573 * Any other Win32 error code if failed
576 static DWORD
cache_container_open_index(cache_container
*container
, DWORD blocks_no
)
578 static const WCHAR index_dat
[] = {'i','n','d','e','x','.','d','a','t',0};
581 WCHAR index_path
[MAX_PATH
];
585 WaitForSingleObject(container
->mutex
, INFINITE
);
587 if(container
->mapping
) {
588 ReleaseMutex(container
->mutex
);
589 return ERROR_SUCCESS
;
592 strcpyW(index_path
, container
->path
);
593 strcatW(index_path
, index_dat
);
595 file
= CreateFileW(index_path
, GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_ALWAYS
, 0, NULL
);
596 if(file
== INVALID_HANDLE_VALUE
) {
597 /* Maybe the directory wasn't there? Try to create it */
598 if(CreateDirectoryW(container
->path
, 0))
599 file
= CreateFileW(index_path
, GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_ALWAYS
, 0, NULL
);
601 if(file
== INVALID_HANDLE_VALUE
) {
602 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(index_path
));
603 ReleaseMutex(container
->mutex
);
604 return GetLastError();
607 file_size
= GetFileSize(file
, NULL
);
608 if(file_size
== INVALID_FILE_SIZE
) {
610 ReleaseMutex(container
->mutex
);
611 return GetLastError();
614 if(blocks_no
< MIN_BLOCK_NO
)
615 blocks_no
= MIN_BLOCK_NO
;
616 else if(blocks_no
> MAX_BLOCK_NO
)
617 blocks_no
= MAX_BLOCK_NO
;
619 if(file_size
< FILE_SIZE(blocks_no
)) {
620 DWORD ret
= cache_container_set_size(container
, file
, blocks_no
);
622 ReleaseMutex(container
->mutex
);
626 container
->file_size
= file_size
;
627 container
->mapping
= cache_container_map_index(file
, container
->path
, file_size
, &validate
);
629 if(container
->mapping
&& validate
) {
630 urlcache_header
*header
= MapViewOfFile(container
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
632 if(header
&& !cache_container_is_valid(header
, file_size
)) {
633 WARN("detected old or broken index.dat file\n");
634 UnmapViewOfFile(header
);
635 FreeUrlCacheSpaceW(container
->path
, 100, 0);
637 UnmapViewOfFile(header
);
639 CloseHandle(container
->mapping
);
640 container
->mapping
= NULL
;
644 if(!container
->mapping
)
646 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
647 ReleaseMutex(container
->mutex
);
648 return GetLastError();
651 ReleaseMutex(container
->mutex
);
652 return ERROR_SUCCESS
;
655 /***********************************************************************
656 * cache_container_close_index (Internal)
664 static void cache_container_close_index(cache_container
*pContainer
)
666 CloseHandle(pContainer
->mapping
);
667 pContainer
->mapping
= NULL
;
670 static BOOL
cache_containers_add(const char *cache_prefix
, LPCWSTR path
,
671 DWORD default_entry_type
, LPWSTR mutex_name
)
673 cache_container
*pContainer
= heap_alloc(sizeof(cache_container
));
674 int cache_prefix_len
= strlen(cache_prefix
);
681 pContainer
->mapping
= NULL
;
682 pContainer
->file_size
= 0;
683 pContainer
->default_entry_type
= default_entry_type
;
685 pContainer
->path
= heap_strdupW(path
);
686 if (!pContainer
->path
)
688 heap_free(pContainer
);
692 pContainer
->cache_prefix
= heap_alloc(cache_prefix_len
+1);
693 if (!pContainer
->cache_prefix
)
695 heap_free(pContainer
->path
);
696 heap_free(pContainer
);
700 memcpy(pContainer
->cache_prefix
, cache_prefix
, cache_prefix_len
+1);
702 CharLowerW(mutex_name
);
703 cache_container_create_object_name(mutex_name
, '!');
705 if ((pContainer
->mutex
= CreateMutexW(NULL
, FALSE
, mutex_name
)) == NULL
)
707 ERR("couldn't create mutex (error is %d)\n", GetLastError());
708 heap_free(pContainer
->path
);
709 heap_free(pContainer
);
713 list_add_head(&UrlContainers
, &pContainer
->entry
);
718 static void cache_container_delete_container(cache_container
*pContainer
)
720 list_remove(&pContainer
->entry
);
722 cache_container_close_index(pContainer
);
723 CloseHandle(pContainer
->mutex
);
724 heap_free(pContainer
->path
);
725 heap_free(pContainer
->cache_prefix
);
726 heap_free(pContainer
);
729 static void cache_containers_init(void)
731 static const WCHAR UrlSuffix
[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
732 static const WCHAR HistorySuffix
[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
733 static const WCHAR CookieSuffix
[] = {0};
735 static const WCHAR UserProfile
[] = {'U','S','E','R','P','R','O','F','I','L','E',0};
738 int nFolder
; /* CSIDL_* constant */
739 const WCHAR
*shpath_suffix
; /* suffix on path returned by SHGetSpecialFolderPath */
740 const char *cache_prefix
; /* prefix used to reference the container */
741 DWORD default_entry_type
;
742 } DefaultContainerData
[] =
744 { CSIDL_INTERNET_CACHE
, UrlSuffix
, "", NORMAL_CACHE_ENTRY
},
745 { CSIDL_HISTORY
, HistorySuffix
, "Visited:", URLHISTORY_CACHE_ENTRY
},
746 { CSIDL_COOKIES
, CookieSuffix
, "Cookie:", COOKIE_CACHE_ENTRY
},
751 if (GetEnvironmentVariableW(UserProfile
, NULL
, 0) == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND
)
753 TRACE("Environment variable 'USERPROFILE' does not exist!\n");
757 for (i
= 0; i
< sizeof(DefaultContainerData
) / sizeof(DefaultContainerData
[0]); i
++)
759 WCHAR wszCachePath
[MAX_PATH
];
760 WCHAR wszMutexName
[MAX_PATH
];
761 int path_len
, suffix_len
;
764 if (!SHGetSpecialFolderPathW(NULL
, wszCachePath
, DefaultContainerData
[i
].nFolder
, TRUE
))
766 ERR("Couldn't get path for default container %u\n", i
);
769 path_len
= strlenW(wszCachePath
);
770 suffix_len
= strlenW(DefaultContainerData
[i
].shpath_suffix
);
772 if (path_len
+ suffix_len
+ 2 > MAX_PATH
)
774 ERR("Path too long\n");
778 wszCachePath
[path_len
] = '\\';
779 wszCachePath
[path_len
+1] = 0;
781 strcpyW(wszMutexName
, wszCachePath
);
785 memcpy(wszCachePath
+ path_len
+ 1, DefaultContainerData
[i
].shpath_suffix
, (suffix_len
+ 1) * sizeof(WCHAR
));
786 wszCachePath
[path_len
+ suffix_len
+ 1] = '\\';
787 wszCachePath
[path_len
+ suffix_len
+ 2] = '\0';
790 if (!WideCharToMultiByte(CP_ACP
, WC_NO_BEST_FIT_CHARS
, wszCachePath
, path_len
,
791 NULL
, 0, NULL
, &def_char
) || def_char
)
795 /* cannot convert path to ANSI code page */
796 if (!(path_len
= GetShortPathNameW(wszCachePath
, tmp
, MAX_PATH
)) ||
797 !WideCharToMultiByte(CP_ACP
, WC_NO_BEST_FIT_CHARS
, tmp
, path_len
,
798 NULL
, 0, NULL
, &def_char
) || def_char
)
799 ERR("Can't create container path accessible by ANSI functions\n");
801 memcpy(wszCachePath
, tmp
, (path_len
+1)*sizeof(WCHAR
));
804 cache_containers_add(DefaultContainerData
[i
].cache_prefix
, wszCachePath
,
805 DefaultContainerData
[i
].default_entry_type
, wszMutexName
);
809 static void cache_containers_free(void)
811 while(!list_empty(&UrlContainers
))
812 cache_container_delete_container(
813 LIST_ENTRY(list_head(&UrlContainers
), cache_container
, entry
)
817 static DWORD
cache_containers_find(const char *url
, cache_container
**ret
)
819 cache_container
*container
;
821 TRACE("searching for prefix for URL: %s\n", debugstr_a(url
));
824 return ERROR_INVALID_PARAMETER
;
827 if (!bDefaultContainersAdded
)
828 cache_containers_init();
830 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
832 int prefix_len
= strlen(container
->cache_prefix
);
834 if(!strncmp(container
->cache_prefix
, url
, prefix_len
)) {
835 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
837 return ERROR_SUCCESS
;
841 ERR("no container found\n");
842 return ERROR_FILE_NOT_FOUND
;
845 static BOOL
cache_containers_enum(char *search_pattern
, DWORD index
, cache_container
**ret
)
848 cache_container
*container
;
850 TRACE("searching for prefix: %s\n", debugstr_a(search_pattern
));
852 /* non-NULL search pattern only returns one container ever */
853 if (search_pattern
&& index
> 0)
857 if (!bDefaultContainersAdded
)
858 cache_containers_init();
860 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
864 if (!strcmp(container
->cache_prefix
, search_pattern
))
866 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
875 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
885 /***********************************************************************
886 * cache_container_lock_index (Internal)
888 * Locks the index for system-wide exclusive access.
891 * Cache file header if successful
892 * NULL if failed and calls SetLastError.
894 static urlcache_header
* cache_container_lock_index(cache_container
*pContainer
)
898 urlcache_header
* pHeader
;
902 WaitForSingleObject(pContainer
->mutex
, INFINITE
);
904 pIndexData
= MapViewOfFile(pContainer
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
908 ReleaseMutex(pContainer
->mutex
);
909 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
912 pHeader
= (urlcache_header
*)pIndexData
;
914 /* file has grown - we need to remap to prevent us getting
915 * access violations when we try and access beyond the end
916 * of the memory mapped file */
917 if (pHeader
->size
!= pContainer
->file_size
)
919 UnmapViewOfFile( pHeader
);
920 cache_container_close_index(pContainer
);
921 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
922 if (error
!= ERROR_SUCCESS
)
924 ReleaseMutex(pContainer
->mutex
);
928 pIndexData
= MapViewOfFile(pContainer
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
932 ReleaseMutex(pContainer
->mutex
);
933 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
936 pHeader
= (urlcache_header
*)pIndexData
;
939 TRACE("Signature: %s, file size: %d bytes\n", pHeader
->signature
, pHeader
->size
);
941 for (index
= 0; index
< pHeader
->dirs_no
; index
++)
943 TRACE("Directory[%d] = \"%.8s\"\n", index
, pHeader
->directory_data
[index
].name
);
949 /***********************************************************************
950 * cache_container_unlock_index (Internal)
953 static BOOL
cache_container_unlock_index(cache_container
*pContainer
, urlcache_header
*pHeader
)
956 ReleaseMutex(pContainer
->mutex
);
957 return UnmapViewOfFile(pHeader
);
960 /***********************************************************************
961 * urlcache_create_file_pathW (Internal)
963 * Copies the full path to the specified buffer given the local file
964 * name and the index of the directory it is in. Always sets value in
965 * lpBufferSize to the required buffer size (in bytes).
968 * TRUE if the buffer was big enough
969 * FALSE if the buffer was too small
972 static BOOL
urlcache_create_file_pathW(
973 const cache_container
*pContainer
,
974 const urlcache_header
*pHeader
,
975 LPCSTR szLocalFileName
,
981 int path_len
= strlenW(pContainer
->path
);
982 int file_name_len
= MultiByteToWideChar(CP_ACP
, 0, szLocalFileName
, -1, NULL
, 0);
983 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
&& Directory
>=pHeader
->dirs_no
)
989 nRequired
= (path_len
+ file_name_len
) * sizeof(WCHAR
);
990 if(Directory
!= CACHE_CONTAINER_NO_SUBDIR
)
991 nRequired
+= (DIR_LENGTH
+ 1) * sizeof(WCHAR
);
992 if (nRequired
<= *lpBufferSize
)
996 memcpy(wszPath
, pContainer
->path
, path_len
* sizeof(WCHAR
));
997 if (Directory
!= CACHE_CONTAINER_NO_SUBDIR
)
999 dir_len
= MultiByteToWideChar(CP_ACP
, 0, pHeader
->directory_data
[Directory
].name
, DIR_LENGTH
, wszPath
+ path_len
, DIR_LENGTH
);
1000 wszPath
[dir_len
+ path_len
] = '\\';
1007 MultiByteToWideChar(CP_ACP
, 0, szLocalFileName
, -1, wszPath
+ dir_len
+ path_len
, file_name_len
);
1008 *lpBufferSize
= nRequired
;
1011 *lpBufferSize
= nRequired
;
1015 /***********************************************************************
1016 * urlcache_create_file_pathA (Internal)
1018 * Copies the full path to the specified buffer given the local file
1019 * name and the index of the directory it is in. Always sets value in
1020 * lpBufferSize to the required buffer size.
1023 * TRUE if the buffer was big enough
1024 * FALSE if the buffer was too small
1027 static BOOL
urlcache_create_file_pathA(
1028 const cache_container
*pContainer
,
1029 const urlcache_header
*pHeader
,
1030 LPCSTR szLocalFileName
,
1033 LPLONG lpBufferSize
)
1036 int path_len
, file_name_len
, dir_len
;
1038 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
&& Directory
>=pHeader
->dirs_no
)
1044 path_len
= WideCharToMultiByte(CP_ACP
, 0, pContainer
->path
, -1, NULL
, 0, NULL
, NULL
) - 1;
1045 file_name_len
= strlen(szLocalFileName
) + 1 /* for nul-terminator */;
1046 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
)
1047 dir_len
= DIR_LENGTH
+1;
1051 nRequired
= (path_len
+ dir_len
+ file_name_len
) * sizeof(char);
1052 if (nRequired
<= *lpBufferSize
)
1054 WideCharToMultiByte(CP_ACP
, 0, pContainer
->path
, -1, szPath
, path_len
, NULL
, NULL
);
1056 memcpy(szPath
+path_len
, pHeader
->directory_data
[Directory
].name
, dir_len
-1);
1057 szPath
[path_len
+ dir_len
-1] = '\\';
1059 memcpy(szPath
+ path_len
+ dir_len
, szLocalFileName
, file_name_len
);
1060 *lpBufferSize
= nRequired
;
1063 *lpBufferSize
= nRequired
;
1067 /* Just like FileTimeToDosDateTime, except that it also maps the special
1068 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1070 static void file_time_to_dos_date_time(const FILETIME
*ft
, WORD
*fatdate
,
1073 if (!ft
->dwLowDateTime
&& !ft
->dwHighDateTime
)
1074 *fatdate
= *fattime
= 0;
1076 FileTimeToDosDateTime(ft
, fatdate
, fattime
);
1079 /***********************************************************************
1080 * urlcache_delete_file (Internal)
1082 static DWORD
urlcache_delete_file(const cache_container
*container
,
1083 urlcache_header
*header
, entry_url
*url_entry
)
1085 WIN32_FILE_ATTRIBUTE_DATA attr
;
1086 WCHAR path
[MAX_PATH
];
1087 LONG path_size
= sizeof(path
);
1091 if(!url_entry
->local_name_off
)
1094 if(!urlcache_create_file_pathW(container
, header
,
1095 (LPCSTR
)url_entry
+url_entry
->local_name_off
,
1096 url_entry
->cache_dir
, path
, &path_size
))
1099 if(!GetFileAttributesExW(path
, GetFileExInfoStandard
, &attr
))
1101 file_time_to_dos_date_time(&attr
.ftLastWriteTime
, &date
, &time
);
1102 if(date
!= url_entry
->write_date
|| time
!= url_entry
->write_time
)
1105 err
= (DeleteFileW(path
) ? ERROR_SUCCESS
: GetLastError());
1106 if(err
== ERROR_ACCESS_DENIED
|| err
== ERROR_SHARING_VIOLATION
)
1110 if (url_entry
->cache_dir
< header
->dirs_no
)
1112 if (header
->directory_data
[url_entry
->cache_dir
].files_no
)
1113 header
->directory_data
[url_entry
->cache_dir
].files_no
--;
1115 if (url_entry
->cache_entry_type
& STICKY_CACHE_ENTRY
)
1117 if (url_entry
->size
.QuadPart
< header
->exempt_usage
.QuadPart
)
1118 header
->exempt_usage
.QuadPart
-= url_entry
->size
.QuadPart
;
1120 header
->exempt_usage
.QuadPart
= 0;
1124 if (url_entry
->size
.QuadPart
< header
->cache_usage
.QuadPart
)
1125 header
->cache_usage
.QuadPart
-= url_entry
->size
.QuadPart
;
1127 header
->cache_usage
.QuadPart
= 0;
1130 return ERROR_SUCCESS
;
1133 static BOOL
urlcache_clean_leaked_entries(cache_container
*container
, urlcache_header
*header
)
1138 leak_off
= &header
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
];
1140 entry_url
*url_entry
= (entry_url
*)((LPBYTE
)header
+ *leak_off
);
1142 if(SUCCEEDED(urlcache_delete_file(container
, header
, url_entry
))) {
1143 *leak_off
= url_entry
->exempt_delta
;
1144 urlcache_entry_free(header
, &url_entry
->header
);
1147 leak_off
= &url_entry
->exempt_delta
;
1154 /***********************************************************************
1155 * cache_container_clean_index (Internal)
1157 * This function is meant to make place in index file by removing leaked
1158 * files entries and resizing the file.
1160 * CAUTION: file view may get mapped to new memory
1163 * ERROR_SUCCESS when new memory is available
1164 * error code otherwise
1166 static DWORD
cache_container_clean_index(cache_container
*container
, urlcache_header
**file_view
)
1168 urlcache_header
*header
= *file_view
;
1171 TRACE("(%s %s)\n", debugstr_a(container
->cache_prefix
), debugstr_w(container
->path
));
1173 if(urlcache_clean_leaked_entries(container
, header
))
1174 return ERROR_SUCCESS
;
1176 if(header
->size
>= ALLOCATION_TABLE_SIZE
*8*BLOCKSIZE
+ ENTRY_START_OFFSET
) {
1177 WARN("index file has maximal size\n");
1178 return ERROR_NOT_ENOUGH_MEMORY
;
1181 cache_container_close_index(container
);
1182 ret
= cache_container_open_index(container
, header
->capacity_in_blocks
*2);
1183 if(ret
!= ERROR_SUCCESS
)
1185 header
= MapViewOfFile(container
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
1187 return GetLastError();
1189 UnmapViewOfFile(*file_view
);
1190 *file_view
= header
;
1191 return ERROR_SUCCESS
;
1194 /* Just like DosDateTimeToFileTime, except that it also maps the special
1195 * case of a DOS date/time of (0,0) to a filetime of (0,0).
1197 static void dos_date_time_to_file_time(WORD fatdate
, WORD fattime
,
1200 if (!fatdate
&& !fattime
)
1201 ft
->dwLowDateTime
= ft
->dwHighDateTime
= 0;
1203 DosDateTimeToFileTime(fatdate
, fattime
, ft
);
1206 static int urlcache_decode_url(const char *url
, WCHAR
*decoded_url
, int decoded_len
)
1208 #ifndef __REACTOS__ /* FIXME: Vista+ */
1210 DWORD len
, part_len
;
1213 memset(&uc
, 0, sizeof(uc
));
1214 uc
.dwStructSize
= sizeof(uc
);
1215 uc
.dwHostNameLength
= 1;
1216 if(!InternetCrackUrlA(url
, 0, 0, &uc
))
1217 uc
.nScheme
= INTERNET_SCHEME_UNKNOWN
;
1219 if(uc
.nScheme
!=INTERNET_SCHEME_HTTP
&& uc
.nScheme
!=INTERNET_SCHEME_HTTPS
)
1221 return MultiByteToWideChar(CP_UTF8
, 0, url
, -1, decoded_url
, decoded_len
);
1223 #ifndef __REACTOS__ /* FIXME: Vista+ */
1227 len
= MultiByteToWideChar(CP_UTF8
, 0, url
, uc
.lpszHostName
-url
, decoded_url
, decoded_len
);
1233 host_name
= heap_alloc(uc
.dwHostNameLength
*sizeof(WCHAR
));
1236 if(!MultiByteToWideChar(CP_UTF8
, 0, uc
.lpszHostName
, uc
.dwHostNameLength
,
1237 host_name
, uc
.dwHostNameLength
)) {
1238 heap_free(host_name
);
1241 part_len
= IdnToUnicode(0, host_name
, uc
.dwHostNameLength
,
1242 decoded_url
? decoded_url
+len
: NULL
, decoded_len
);
1243 heap_free(host_name
);
1245 SetLastError(ERROR_INTERNET_INVALID_URL
);
1250 decoded_len
-= part_len
;
1252 part_len
= MultiByteToWideChar(CP_UTF8
, 0,
1253 uc
.lpszHostName
+uc
.dwHostNameLength
,
1254 -1, decoded_url
? decoded_url
+len
: NULL
, decoded_len
);
1260 #endif /* !__REACTOS__ */
1263 /***********************************************************************
1264 * urlcache_copy_entry (Internal)
1266 * Copies an entry from the cache index file to the Win32 structure
1269 * ERROR_SUCCESS if the buffer was big enough
1270 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1273 static DWORD
urlcache_copy_entry(cache_container
*container
, const urlcache_header
*header
,
1274 INTERNET_CACHE_ENTRY_INFOA
*entry_info
, DWORD
*info_size
, const entry_url
*url_entry
, BOOL unicode
)
1277 DWORD size
= sizeof(*entry_info
);
1279 if(*info_size
>= size
) {
1280 entry_info
->lpHeaderInfo
= NULL
;
1281 entry_info
->lpszFileExtension
= NULL
;
1282 entry_info
->lpszLocalFileName
= NULL
;
1283 entry_info
->lpszSourceUrlName
= NULL
;
1284 entry_info
->CacheEntryType
= url_entry
->cache_entry_type
;
1285 entry_info
->u
.dwExemptDelta
= url_entry
->exempt_delta
;
1286 entry_info
->dwHeaderInfoSize
= url_entry
->header_info_size
;
1287 entry_info
->dwHitRate
= url_entry
->hit_rate
;
1288 entry_info
->dwSizeHigh
= url_entry
->size
.u
.HighPart
;
1289 entry_info
->dwSizeLow
= url_entry
->size
.u
.LowPart
;
1290 entry_info
->dwStructSize
= sizeof(*entry_info
);
1291 entry_info
->dwUseCount
= url_entry
->use_count
;
1292 dos_date_time_to_file_time(url_entry
->expire_date
, url_entry
->expire_time
, &entry_info
->ExpireTime
);
1293 entry_info
->LastAccessTime
= url_entry
->access_time
;
1294 entry_info
->LastModifiedTime
= url_entry
->modification_time
;
1295 dos_date_time_to_file_time(url_entry
->sync_date
, url_entry
->sync_time
, &entry_info
->LastSyncTime
);
1298 if(size
%4 && size
<*info_size
)
1299 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1300 size
= DWORD_ALIGN(size
);
1302 url_len
= urlcache_decode_url((const char*)url_entry
+url_entry
->url_off
, NULL
, 0);
1304 url_len
= strlen((LPCSTR
)url_entry
+url_entry
->url_off
) + 1;
1305 size
+= url_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1307 if(*info_size
>= size
) {
1308 DWORD url_size
= url_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1310 entry_info
->lpszSourceUrlName
= (LPSTR
)entry_info
+size
-url_size
;
1312 urlcache_decode_url((const char*)url_entry
+url_entry
->url_off
, (WCHAR
*)entry_info
->lpszSourceUrlName
, url_len
);
1314 memcpy(entry_info
->lpszSourceUrlName
, (LPCSTR
)url_entry
+url_entry
->url_off
, url_size
);
1317 if(size
%4 && size
<*info_size
)
1318 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1319 size
= DWORD_ALIGN(size
);
1321 if(url_entry
->local_name_off
) {
1322 LONG file_name_size
;
1324 file_name
= (LPSTR
)entry_info
+size
;
1325 file_name_size
= *info_size
-size
;
1326 if((unicode
&& urlcache_create_file_pathW(container
, header
, (LPCSTR
)url_entry
+url_entry
->local_name_off
, url_entry
->cache_dir
, (LPWSTR
)file_name
, &file_name_size
)) ||
1327 (!unicode
&& urlcache_create_file_pathA(container
, header
, (LPCSTR
)url_entry
+url_entry
->local_name_off
, url_entry
->cache_dir
, file_name
, &file_name_size
))) {
1328 entry_info
->lpszLocalFileName
= file_name
;
1330 size
+= file_name_size
;
1332 if(size
%4 && size
<*info_size
)
1333 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1334 size
= DWORD_ALIGN(size
);
1337 if(url_entry
->header_info_off
) {
1341 header_len
= MultiByteToWideChar(CP_UTF8
, 0, (const char*)url_entry
+url_entry
->header_info_off
,
1342 url_entry
->header_info_size
, NULL
, 0);
1344 header_len
= url_entry
->header_info_size
;
1345 size
+= header_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1347 if(*info_size
>= size
) {
1348 DWORD header_size
= header_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1349 entry_info
->lpHeaderInfo
= (LPBYTE
)entry_info
+size
-header_size
;
1351 MultiByteToWideChar(CP_UTF8
, 0, (const char*)url_entry
+url_entry
->header_info_off
,
1352 url_entry
->header_info_size
, (LPWSTR
)entry_info
->lpHeaderInfo
, header_len
);
1354 memcpy(entry_info
->lpHeaderInfo
, (LPCSTR
)url_entry
+url_entry
->header_info_off
, header_len
);
1356 if(size
%4 && size
<*info_size
)
1357 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1358 size
= DWORD_ALIGN(size
);
1361 if(url_entry
->file_extension_off
) {
1365 ext_len
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)url_entry
+url_entry
->file_extension_off
, -1, NULL
, 0);
1367 ext_len
= strlen((LPCSTR
)url_entry
+url_entry
->file_extension_off
) + 1;
1368 size
+= ext_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1370 if(*info_size
>= size
) {
1371 DWORD ext_size
= ext_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1372 entry_info
->lpszFileExtension
= (LPSTR
)entry_info
+size
-ext_size
;
1374 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)url_entry
+url_entry
->file_extension_off
, -1, (LPWSTR
)entry_info
->lpszFileExtension
, ext_len
);
1376 memcpy(entry_info
->lpszFileExtension
, (LPCSTR
)url_entry
+url_entry
->file_extension_off
, ext_len
*sizeof(CHAR
));
1379 if(size
%4 && size
<*info_size
)
1380 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1381 size
= DWORD_ALIGN(size
);
1384 if(size
> *info_size
) {
1386 return ERROR_INSUFFICIENT_BUFFER
;
1389 return ERROR_SUCCESS
;
1392 /***********************************************************************
1393 * urlcache_set_entry_info (Internal)
1395 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1396 * according to the flags set by field_control.
1399 * ERROR_SUCCESS if the buffer was big enough
1400 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1403 static DWORD
urlcache_set_entry_info(entry_url
*url_entry
, const INTERNET_CACHE_ENTRY_INFOA
*entry_info
, DWORD field_control
)
1405 if (field_control
& CACHE_ENTRY_ACCTIME_FC
)
1406 url_entry
->access_time
= entry_info
->LastAccessTime
;
1407 if (field_control
& CACHE_ENTRY_ATTRIBUTE_FC
)
1408 url_entry
->cache_entry_type
= entry_info
->CacheEntryType
;
1409 if (field_control
& CACHE_ENTRY_EXEMPT_DELTA_FC
)
1410 url_entry
->exempt_delta
= entry_info
->u
.dwExemptDelta
;
1411 if (field_control
& CACHE_ENTRY_EXPTIME_FC
)
1412 file_time_to_dos_date_time(&entry_info
->ExpireTime
, &url_entry
->expire_date
, &url_entry
->expire_time
);
1413 if (field_control
& CACHE_ENTRY_HEADERINFO_FC
)
1414 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1415 if (field_control
& CACHE_ENTRY_HITRATE_FC
)
1416 url_entry
->hit_rate
= entry_info
->dwHitRate
;
1417 if (field_control
& CACHE_ENTRY_MODTIME_FC
)
1418 url_entry
->modification_time
= entry_info
->LastModifiedTime
;
1419 if (field_control
& CACHE_ENTRY_SYNCTIME_FC
)
1420 file_time_to_dos_date_time(&entry_info
->LastAccessTime
, &url_entry
->sync_date
, &url_entry
->sync_time
);
1422 return ERROR_SUCCESS
;
1425 /***********************************************************************
1426 * urlcache_hash_key (Internal)
1428 * Returns the hash key for a given string
1431 * hash key for the string
1434 static DWORD
urlcache_hash_key(LPCSTR lpszKey
)
1436 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1437 * but the algorithm and result are not the same!
1439 static const unsigned char lookupTable
[256] =
1441 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1442 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1443 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1444 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1445 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1446 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1447 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1448 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1449 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1450 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1451 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1452 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1453 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1454 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1455 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1456 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1457 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1458 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1459 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1460 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1461 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1462 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1463 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1464 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1465 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1466 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1467 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1468 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1469 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1470 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1471 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1472 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1477 for (i
= 0; i
< sizeof(key
) / sizeof(key
[0]); i
++)
1478 key
[i
] = lookupTable
[(*lpszKey
+ i
) & 0xFF];
1480 for (lpszKey
++; *lpszKey
&& ((lpszKey
[0] != '/') || (lpszKey
[1] != 0)); lpszKey
++)
1482 for (i
= 0; i
< sizeof(key
) / sizeof(key
[0]); i
++)
1483 key
[i
] = lookupTable
[*lpszKey
^ key
[i
]];
1486 return *(DWORD
*)key
;
1489 static inline entry_hash_table
* urlcache_get_hash_table(const urlcache_header
*pHeader
, DWORD dwOffset
)
1493 return (entry_hash_table
*)((LPBYTE
)pHeader
+ dwOffset
);
1496 static BOOL
urlcache_find_hash_entry(const urlcache_header
*pHeader
, LPCSTR lpszUrl
, struct hash_entry
**ppHashEntry
)
1498 /* structure of hash table:
1499 * 448 entries divided into 64 blocks
1500 * each block therefore contains a chain of 7 key/offset pairs
1501 * how position in table is calculated:
1502 * 1. the url is hashed in helper function
1503 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1504 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1507 * there can be multiple hash tables in the file and the offset to
1508 * the next one is stored in the header of the hash table
1510 DWORD key
= urlcache_hash_key(lpszUrl
);
1511 DWORD offset
= (key
& (HASHTABLE_NUM_ENTRIES
-1)) * HASHTABLE_BLOCKSIZE
;
1512 entry_hash_table
* pHashEntry
;
1515 key
>>= HASHTABLE_FLAG_BITS
;
1517 for (pHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1518 pHashEntry
; pHashEntry
= urlcache_get_hash_table(pHeader
, pHashEntry
->next
))
1521 if (pHashEntry
->id
!= id
++)
1523 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry
->id
, id
);
1526 /* make sure that it is in fact a hash entry */
1527 if (pHashEntry
->header
.signature
!= HASH_SIGNATURE
)
1529 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&pHashEntry
->header
.signature
);
1533 for (i
= 0; i
< HASHTABLE_BLOCKSIZE
; i
++)
1535 struct hash_entry
*pHashElement
= &pHashEntry
->hash_table
[offset
+ i
];
1536 if (key
== pHashElement
->key
>>HASHTABLE_FLAG_BITS
)
1538 /* FIXME: we should make sure that this is the right element
1539 * before returning and claiming that it is. We can do this
1540 * by doing a simple compare between the URL we were given
1541 * and the URL stored in the entry. However, this assumes
1542 * we know the format of all the entries stored in the
1544 *ppHashEntry
= pHashElement
;
1552 /***********************************************************************
1553 * urlcache_hash_entry_set_flags (Internal)
1555 * Sets special bits in hash key
1561 static void urlcache_hash_entry_set_flags(struct hash_entry
*pHashEntry
, DWORD dwFlag
)
1563 pHashEntry
->key
= (pHashEntry
->key
>> HASHTABLE_FLAG_BITS
<< HASHTABLE_FLAG_BITS
) | dwFlag
;
1566 /***********************************************************************
1567 * urlcache_hash_entry_delete (Internal)
1569 * Searches all the hash tables in the index for the given URL and
1570 * then if found deletes the entry.
1573 * TRUE if the entry was found
1574 * FALSE if the entry could not be found
1577 static BOOL
urlcache_hash_entry_delete(struct hash_entry
*pHashEntry
)
1579 pHashEntry
->key
= HASHTABLE_DEL
;
1583 /***********************************************************************
1584 * urlcache_hash_entry_create (Internal)
1586 * Searches all the hash tables for a free slot based on the offset
1587 * generated from the hash key. If a free slot is found, the offset and
1588 * key are entered into the hash table.
1591 * ERROR_SUCCESS if the entry was added
1592 * Any other Win32 error code if the entry could not be added
1595 static DWORD
urlcache_hash_entry_create(urlcache_header
*pHeader
, LPCSTR lpszUrl
, DWORD dwOffsetEntry
, DWORD dwFieldType
)
1597 /* see urlcache_find_hash_entry for structure of hash tables */
1599 DWORD key
= urlcache_hash_key(lpszUrl
);
1600 DWORD offset
= (key
& (HASHTABLE_NUM_ENTRIES
-1)) * HASHTABLE_BLOCKSIZE
;
1601 entry_hash_table
* pHashEntry
, *pHashPrev
= NULL
;
1605 key
= ((key
>> HASHTABLE_FLAG_BITS
) << HASHTABLE_FLAG_BITS
) + dwFieldType
;
1607 for (pHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1608 pHashEntry
; pHashEntry
= urlcache_get_hash_table(pHeader
, pHashEntry
->next
))
1611 pHashPrev
= pHashEntry
;
1613 if (pHashEntry
->id
!= id
++)
1615 ERR("not right hash table number (%d) expected %d\n", pHashEntry
->id
, id
);
1618 /* make sure that it is in fact a hash entry */
1619 if (pHashEntry
->header
.signature
!= HASH_SIGNATURE
)
1621 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&pHashEntry
->header
.signature
);
1625 for (i
= 0; i
< HASHTABLE_BLOCKSIZE
; i
++)
1627 struct hash_entry
*pHashElement
= &pHashEntry
->hash_table
[offset
+ i
];
1628 if (pHashElement
->key
==HASHTABLE_FREE
|| pHashElement
->key
==HASHTABLE_DEL
) /* if the slot is free */
1630 pHashElement
->key
= key
;
1631 pHashElement
->offset
= dwOffsetEntry
;
1632 return ERROR_SUCCESS
;
1636 error
= urlcache_create_hash_table(pHeader
, pHashPrev
, &pHashEntry
);
1637 if (error
!= ERROR_SUCCESS
)
1640 pHashEntry
->hash_table
[offset
].key
= key
;
1641 pHashEntry
->hash_table
[offset
].offset
= dwOffsetEntry
;
1642 return ERROR_SUCCESS
;
1645 /***********************************************************************
1646 * urlcache_enum_hash_tables (Internal)
1648 * Enumerates the hash tables in a container.
1651 * TRUE if an entry was found
1652 * FALSE if there are no more tables to enumerate.
1655 static BOOL
urlcache_enum_hash_tables(const urlcache_header
*pHeader
, DWORD
*id
, entry_hash_table
**ppHashEntry
)
1657 for (*ppHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1658 *ppHashEntry
; *ppHashEntry
= urlcache_get_hash_table(pHeader
, (*ppHashEntry
)->next
))
1660 TRACE("looking at hash table number %d\n", (*ppHashEntry
)->id
);
1661 if ((*ppHashEntry
)->id
!= *id
)
1663 /* make sure that it is in fact a hash entry */
1664 if ((*ppHashEntry
)->header
.signature
!= HASH_SIGNATURE
)
1666 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&(*ppHashEntry
)->header
.signature
);
1671 TRACE("hash table number %d found\n", *id
);
1677 /***********************************************************************
1678 * urlcache_enum_hash_table_entries (Internal)
1680 * Enumerates entries in a hash table and returns the next non-free entry.
1683 * TRUE if an entry was found
1684 * FALSE if the hash table is empty or there are no more entries to
1688 static BOOL
urlcache_enum_hash_table_entries(const urlcache_header
*pHeader
, const entry_hash_table
*pHashEntry
,
1689 DWORD
* index
, const struct hash_entry
**ppHashEntry
)
1691 for (; *index
< HASHTABLE_SIZE
; (*index
)++)
1693 if (pHashEntry
->hash_table
[*index
].key
==HASHTABLE_FREE
|| pHashEntry
->hash_table
[*index
].key
==HASHTABLE_DEL
)
1696 *ppHashEntry
= &pHashEntry
->hash_table
[*index
];
1697 TRACE("entry found %d\n", *index
);
1700 TRACE("no more entries (%d)\n", *index
);
1704 /***********************************************************************
1705 * cache_container_delete_dir (Internal)
1707 * Erase a directory containing an URL cache.
1710 * TRUE success, FALSE failure/aborted.
1713 static BOOL
cache_container_delete_dir(LPCWSTR lpszPath
)
1716 WCHAR path
[MAX_PATH
+ 1];
1717 SHFILEOPSTRUCTW shfos
;
1720 path_len
= strlenW(lpszPath
);
1721 if (path_len
>= MAX_PATH
)
1723 strcpyW(path
, lpszPath
);
1724 path
[path_len
+ 1] = 0; /* double-NUL-terminate path */
1727 shfos
.wFunc
= FO_DELETE
;
1730 shfos
.fFlags
= FOF_NOCONFIRMATION
;
1731 shfos
.fAnyOperationsAborted
= FALSE
;
1732 ret
= SHFileOperationW(&shfos
);
1734 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path
), ret
);
1735 return !(ret
|| shfos
.fAnyOperationsAborted
);
1738 /***********************************************************************
1739 * urlcache_hash_entry_is_locked (Internal)
1741 * Checks if entry is locked. Unlocks it if possible.
1743 static BOOL
urlcache_hash_entry_is_locked(struct hash_entry
*hash_entry
, entry_url
*url_entry
)
1746 ULARGE_INTEGER acc_time
, time
;
1748 if ((hash_entry
->key
& ((1<<HASHTABLE_FLAG_BITS
)-1)) != HASHTABLE_LOCK
)
1751 GetSystemTimeAsFileTime(&cur_time
);
1752 time
.u
.LowPart
= cur_time
.dwLowDateTime
;
1753 time
.u
.HighPart
= cur_time
.dwHighDateTime
;
1755 acc_time
.u
.LowPart
= url_entry
->access_time
.dwLowDateTime
;
1756 acc_time
.u
.HighPart
= url_entry
->access_time
.dwHighDateTime
;
1758 time
.QuadPart
-= acc_time
.QuadPart
;
1760 /* check if entry was locked for at least a day */
1761 if(time
.QuadPart
> (ULONGLONG
)24*60*60*FILETIME_SECOND
) {
1762 urlcache_hash_entry_set_flags(hash_entry
, HASHTABLE_URL
);
1763 url_entry
->use_count
= 0;
1770 static BOOL
urlcache_get_entry_info(const char *url
, void *entry_info
,
1771 DWORD
*size
, DWORD flags
, BOOL unicode
)
1773 urlcache_header
*header
;
1774 struct hash_entry
*hash_entry
;
1775 const entry_url
*url_entry
;
1776 cache_container
*container
;
1779 TRACE("(%s, %p, %p, %x, %x)\n", debugstr_a(url
), entry_info
, size
, flags
, unicode
);
1781 if(flags
& ~GET_INSTALLED_ENTRY
)
1782 FIXME("ignoring unsupported flags: %x\n", flags
);
1784 error
= cache_containers_find(url
, &container
);
1785 if(error
!= ERROR_SUCCESS
) {
1786 SetLastError(error
);
1790 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
1791 if(error
!= ERROR_SUCCESS
) {
1792 SetLastError(error
);
1796 if(!(header
= cache_container_lock_index(container
)))
1799 if(!urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
1800 cache_container_unlock_index(container
, header
);
1801 WARN("entry %s not found!\n", debugstr_a(url
));
1802 SetLastError(ERROR_FILE_NOT_FOUND
);
1806 url_entry
= (const entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
1807 if(url_entry
->header
.signature
!= URL_SIGNATURE
) {
1808 cache_container_unlock_index(container
, header
);
1809 FIXME("Trying to retrieve entry of unknown format %s\n",
1810 debugstr_an((LPCSTR
)&url_entry
->header
.signature
, sizeof(DWORD
)));
1811 SetLastError(ERROR_FILE_NOT_FOUND
);
1815 TRACE("Found URL: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->url_off
));
1816 TRACE("Header info: %s\n", debugstr_an((LPCSTR
)url_entry
+
1817 url_entry
->header_info_off
, url_entry
->header_info_size
));
1819 if((flags
& GET_INSTALLED_ENTRY
) && !(url_entry
->cache_entry_type
& INSTALLED_CACHE_ENTRY
)) {
1820 cache_container_unlock_index(container
, header
);
1821 SetLastError(ERROR_FILE_NOT_FOUND
);
1829 error
= urlcache_copy_entry(container
, header
, entry_info
, size
, url_entry
, unicode
);
1830 if(error
!= ERROR_SUCCESS
) {
1831 cache_container_unlock_index(container
, header
);
1832 SetLastError(error
);
1835 if(url_entry
->local_name_off
)
1836 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->local_name_off
));
1839 cache_container_unlock_index(container
, header
);
1843 /***********************************************************************
1844 * GetUrlCacheEntryInfoExA (WININET.@)
1847 BOOL WINAPI
GetUrlCacheEntryInfoExA(LPCSTR lpszUrl
,
1848 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1849 LPDWORD lpdwCacheEntryInfoBufSize
, LPSTR lpszReserved
,
1850 LPDWORD lpdwReserved
, LPVOID lpReserved
, DWORD dwFlags
)
1852 if(lpszReserved
!=NULL
|| lpdwReserved
!=NULL
|| lpReserved
!=NULL
) {
1853 ERR("Reserved value was not 0\n");
1854 SetLastError(ERROR_INVALID_PARAMETER
);
1858 return urlcache_get_entry_info(lpszUrl
, lpCacheEntryInfo
,
1859 lpdwCacheEntryInfoBufSize
, dwFlags
, FALSE
);
1862 /***********************************************************************
1863 * GetUrlCacheEntryInfoA (WININET.@)
1866 BOOL WINAPI
GetUrlCacheEntryInfoA(LPCSTR lpszUrlName
,
1867 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1868 LPDWORD lpdwCacheEntryInfoBufferSize
)
1870 return GetUrlCacheEntryInfoExA(lpszUrlName
, lpCacheEntryInfo
,
1871 lpdwCacheEntryInfoBufferSize
, NULL
, NULL
, NULL
, 0);
1874 static int urlcache_encode_url(const WCHAR
*url
, char *encoded_url
, int encoded_len
)
1876 #ifndef __REACTOS__ /* FIXME: Vista+ */
1878 DWORD len
, part_len
;
1881 TRACE("%s\n", debugstr_w(url
));
1883 memset(&uc
, 0, sizeof(uc
));
1884 uc
.dwStructSize
= sizeof(uc
);
1885 uc
.dwHostNameLength
= 1;
1886 if(!InternetCrackUrlW(url
, 0, 0, &uc
))
1887 uc
.nScheme
= INTERNET_SCHEME_UNKNOWN
;
1889 if(uc
.nScheme
!=INTERNET_SCHEME_HTTP
&& uc
.nScheme
!=INTERNET_SCHEME_HTTPS
)
1891 return WideCharToMultiByte(CP_UTF8
, 0, url
, -1, encoded_url
, encoded_len
, NULL
, NULL
);
1893 #ifndef __REACTOS__ /* FIXME: Vista+ */
1894 len
= WideCharToMultiByte(CP_UTF8
, 0, url
, uc
.lpszHostName
-url
,
1895 encoded_url
, encoded_len
, NULL
, NULL
);
1901 part_len
= IdnToAscii(0, uc
.lpszHostName
, uc
.dwHostNameLength
, NULL
, 0);
1903 SetLastError(ERROR_INTERNET_INVALID_URL
);
1907 punycode
= heap_alloc(part_len
*sizeof(WCHAR
));
1911 part_len
= IdnToAscii(0, uc
.lpszHostName
, uc
.dwHostNameLength
, punycode
, part_len
);
1913 heap_free(punycode
);
1917 part_len
= WideCharToMultiByte(CP_UTF8
, 0, punycode
, part_len
,
1918 encoded_url
? encoded_url
+len
: NULL
, encoded_len
, NULL
, NULL
);
1919 heap_free(punycode
);
1923 encoded_len
-= part_len
;
1926 part_len
= WideCharToMultiByte(CP_UTF8
, 0, uc
.lpszHostName
+uc
.dwHostNameLength
,
1927 -1, encoded_url
? encoded_url
+len
: NULL
, encoded_len
, NULL
, NULL
);
1932 TRACE("got (%d)%s\n", len
, debugstr_a(encoded_url
));
1934 #endif /* !__REACTOS__ */
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", 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", 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
];
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)!='\\')
2654 memcpy(file_name
, p
, e
-p
);
2657 for(p
=file_name
; *p
; p
++) {
2670 generate_name
= TRUE
;
2672 error
= cache_containers_find(url
, &container
);
2673 if(error
!= ERROR_SUCCESS
) {
2674 SetLastError(error
);
2678 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2679 if(error
!= ERROR_SUCCESS
) {
2680 SetLastError(error
);
2684 if(!(header
= cache_container_lock_index(container
)))
2688 cache_dir
= (BYTE
)(rand() % header
->dirs_no
);
2690 cache_dir
= CACHE_CONTAINER_NO_SUBDIR
;
2692 full_path_len
= MAX_PATH
* sizeof(WCHAR
);
2693 if(!urlcache_create_file_pathW(container
, header
, file_name
, cache_dir
, full_path
, &full_path_len
)) {
2694 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2695 debugstr_a(file_name
), full_path_len
);
2696 cache_container_unlock_index(container
, header
);
2699 full_path_len
= full_path_len
/sizeof(WCHAR
) - 1;
2701 cache_container_unlock_index(container
, header
);
2707 MultiByteToWideChar(CP_ACP
, 0, ext
, -1, extW
+1, MAX_PATH
-1);
2709 for(p
=extW
; *p
; p
++) {
2719 if(p
[-1]==' ' || p
[-1]=='.')
2725 for(i
=0; i
<255 && !generate_name
; i
++) {
2726 static const WCHAR format
[] = {'[','%','u',']','%','s',0};
2728 wsprintfW(full_path
+full_path_len
, format
, i
, extW
);
2730 TRACE("Trying: %s\n", debugstr_w(full_path
));
2731 file
= CreateFileW(full_path
, GENERIC_READ
, 0, NULL
, CREATE_NEW
, 0, NULL
);
2732 if(file
!= INVALID_HANDLE_VALUE
) {
2738 /* Try to generate random name */
2739 GetSystemTimeAsFileTime(&ft
);
2740 strcpyW(full_path
+full_path_len
+8, extW
);
2742 for(i
=0; i
<255; i
++) {
2744 ULONGLONG n
= ft
.dwHighDateTime
;
2746 n
+= ft
.dwLowDateTime
;
2747 n
^= (ULONGLONG
)i
<<48;
2749 for(j
=0; j
<8; j
++) {
2752 full_path
[full_path_len
+j
] = (r
< 10 ? '0' + r
: 'A' + r
- 10);
2755 TRACE("Trying: %s\n", debugstr_w(full_path
));
2756 file
= CreateFileW(full_path
, GENERIC_READ
, 0, NULL
, CREATE_NEW
, 0, NULL
);
2757 if(file
!= INVALID_HANDLE_VALUE
) {
2763 WARN("Could not find a unique filename\n");
2767 /***********************************************************************
2768 * CreateUrlCacheEntryA (WININET.@)
2771 BOOL WINAPI
CreateUrlCacheEntryA(LPCSTR lpszUrlName
, DWORD dwExpectedFileSize
,
2772 LPCSTR lpszFileExtension
, LPSTR lpszFileName
, DWORD dwReserved
)
2774 WCHAR file_name
[MAX_PATH
];
2777 FIXME("dwReserved 0x%08x\n", dwReserved
);
2779 if(!urlcache_entry_create(lpszUrlName
, lpszFileExtension
, file_name
))
2782 if(!WideCharToMultiByte(CP_ACP
, 0, file_name
, -1, lpszFileName
, MAX_PATH
, NULL
, NULL
))
2786 /***********************************************************************
2787 * CreateUrlCacheEntryW (WININET.@)
2790 BOOL WINAPI
CreateUrlCacheEntryW(LPCWSTR lpszUrlName
, DWORD dwExpectedFileSize
,
2791 LPCWSTR lpszFileExtension
, LPWSTR lpszFileName
, DWORD dwReserved
)
2793 char *url
, *ext
= NULL
;
2797 FIXME("dwReserved 0x%08x\n", dwReserved
);
2799 if(lpszFileExtension
) {
2800 ext
= heap_strdupWtoUTF8(lpszFileExtension
);
2805 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
)) {
2810 ret
= urlcache_entry_create(url
, ext
, lpszFileName
);
2816 static BOOL
urlcache_entry_commit(const char *url
, const WCHAR
*file_name
,
2817 FILETIME expire_time
, FILETIME modify_time
, DWORD entry_type
,
2818 BYTE
*header_info
, DWORD header_size
, const char *file_ext
,
2819 const char *original_url
)
2821 cache_container
*container
;
2822 urlcache_header
*header
;
2823 struct hash_entry
*hash_entry
;
2824 entry_header
*entry
;
2825 entry_url
*url_entry
;
2826 DWORD url_entry_offset
;
2827 DWORD size
= DWORD_ALIGN(sizeof(*url_entry
));
2828 DWORD file_name_off
= 0;
2829 DWORD header_info_off
= 0;
2830 DWORD file_ext_off
= 0;
2831 WIN32_FILE_ATTRIBUTE_DATA file_attr
;
2832 LARGE_INTEGER file_size
;
2834 char file_name_no_container
[MAX_PATH
];
2835 char *local_file_name
= 0;
2837 DWORD exempt_delta
= 0;
2840 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n", debugstr_a(url
), debugstr_w(file_name
),
2841 entry_type
, header_info
, header_size
, debugstr_a(file_ext
), debugstr_a(original_url
));
2843 if(entry_type
& STICKY_CACHE_ENTRY
&& !file_name
) {
2844 SetLastError(ERROR_INVALID_PARAMETER
);
2848 WARN(": original_url ignored\n");
2850 memset(&file_attr
, 0, sizeof(file_attr
));
2852 if(!GetFileAttributesExW(file_name
, GetFileExInfoStandard
, &file_attr
))
2855 file_size
.u
.LowPart
= file_attr
.nFileSizeLow
;
2856 file_size
.u
.HighPart
= file_attr
.nFileSizeHigh
;
2858 error
= cache_containers_find(url
, &container
);
2859 if(error
!= ERROR_SUCCESS
) {
2860 SetLastError(error
);
2864 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2865 if(error
!= ERROR_SUCCESS
) {
2866 SetLastError(error
);
2870 if(!(header
= cache_container_lock_index(container
)))
2873 if(urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
2874 entry_url
*url_entry
= (entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
2876 if(urlcache_hash_entry_is_locked(hash_entry
, url_entry
)) {
2877 TRACE("Trying to overwrite locked entry\n");
2878 cache_container_unlock_index(container
, header
);
2879 SetLastError(ERROR_SHARING_VIOLATION
);
2883 hit_rate
= url_entry
->hit_rate
;
2884 exempt_delta
= url_entry
->exempt_delta
;
2885 urlcache_entry_delete(container
, header
, hash_entry
);
2891 dir_id
= CACHE_CONTAINER_NO_SUBDIR
;
2894 BOOL bFound
= FALSE
;
2896 if(strncmpW(file_name
, container
->path
, lstrlenW(container
->path
))) {
2897 ERR("path %s must begin with cache content path %s\n", debugstr_w(file_name
), debugstr_w(container
->path
));
2898 cache_container_unlock_index(container
, header
);
2899 SetLastError(ERROR_INVALID_PARAMETER
);
2903 /* skip container path prefix */
2904 file_name
+= lstrlenW(container
->path
);
2906 WideCharToMultiByte(CP_ACP
, 0, file_name
, -1, file_name_no_container
, MAX_PATH
, NULL
, NULL
);
2907 local_file_name
= file_name_no_container
;
2909 if(header
->dirs_no
) {
2910 for(dir_id
= 0; dir_id
< header
->dirs_no
; dir_id
++) {
2911 if(!strncmp(header
->directory_data
[dir_id
].name
, local_file_name
, DIR_LENGTH
)) {
2918 ERR("cache directory not found in path %s\n", debugstr_w(file_name
));
2919 cache_container_unlock_index(container
, header
);
2920 SetLastError(ERROR_INVALID_PARAMETER
);
2924 file_name
+= DIR_LENGTH
+ 1;
2925 local_file_name
+= DIR_LENGTH
+ 1;
2929 size
= DWORD_ALIGN(size
+ strlen(url
) + 1);
2931 file_name_off
= size
;
2932 size
= DWORD_ALIGN(size
+ strlen(local_file_name
) + 1);
2934 if(header_info
&& header_size
) {
2935 header_info_off
= size
;
2936 size
= DWORD_ALIGN(size
+ header_size
);
2938 if(file_ext
&& (file_ext_off
= strlen(file_ext
))) {
2939 DWORD len
= file_ext_off
;
2941 file_ext_off
= size
;
2942 size
= DWORD_ALIGN(size
+ len
+ 1);
2945 /* round up to next block */
2946 if(size
% BLOCKSIZE
) {
2947 size
-= size
% BLOCKSIZE
;
2951 error
= urlcache_entry_alloc(header
, size
/ BLOCKSIZE
, &entry
);
2952 while(error
== ERROR_HANDLE_DISK_FULL
) {
2953 error
= cache_container_clean_index(container
, &header
);
2954 if(error
== ERROR_SUCCESS
)
2955 error
= urlcache_entry_alloc(header
, size
/ BLOCKSIZE
, &entry
);
2957 if(error
!= ERROR_SUCCESS
) {
2958 cache_container_unlock_index(container
, header
);
2959 SetLastError(error
);
2963 /* FindFirstFreeEntry fills in blocks used */
2964 url_entry
= (entry_url
*)entry
;
2965 url_entry_offset
= (LPBYTE
)url_entry
- (LPBYTE
)header
;
2966 url_entry
->header
.signature
= URL_SIGNATURE
;
2967 url_entry
->cache_dir
= dir_id
;
2968 url_entry
->cache_entry_type
= entry_type
| container
->default_entry_type
;
2969 url_entry
->header_info_size
= header_size
;
2970 if((entry_type
& STICKY_CACHE_ENTRY
) && !exempt_delta
) {
2971 /* Sticky entries have a default exempt time of one day */
2972 exempt_delta
= 86400;
2974 url_entry
->exempt_delta
= exempt_delta
;
2975 url_entry
->hit_rate
= hit_rate
+1;
2976 url_entry
->file_extension_off
= file_ext_off
;
2977 url_entry
->header_info_off
= header_info_off
;
2978 url_entry
->local_name_off
= file_name_off
;
2979 url_entry
->url_off
= DWORD_ALIGN(sizeof(*url_entry
));
2980 url_entry
->size
.QuadPart
= file_size
.QuadPart
;
2981 url_entry
->use_count
= 0;
2982 GetSystemTimeAsFileTime(&url_entry
->access_time
);
2983 url_entry
->modification_time
= modify_time
;
2984 file_time_to_dos_date_time(&url_entry
->access_time
, &url_entry
->sync_date
, &url_entry
->sync_time
);
2985 file_time_to_dos_date_time(&expire_time
, &url_entry
->expire_date
, &url_entry
->expire_time
);
2986 file_time_to_dos_date_time(&file_attr
.ftLastWriteTime
, &url_entry
->write_date
, &url_entry
->write_time
);
2989 url_entry
->unk1
= 0;
2990 url_entry
->unk2
= 0;
2991 url_entry
->unk3
= 0x60;
2992 url_entry
->unk4
= 0;
2993 url_entry
->unk5
= 0x1010;
2994 url_entry
->unk7
= 0;
2995 url_entry
->unk8
= 0;
2998 strcpy((LPSTR
)url_entry
+ url_entry
->url_off
, url
);
3000 strcpy((LPSTR
)((LPBYTE
)url_entry
+ file_name_off
), local_file_name
);
3002 memcpy((LPBYTE
)url_entry
+ header_info_off
, header_info
, header_size
);
3004 strcpy((LPSTR
)((LPBYTE
)url_entry
+ file_ext_off
), file_ext
);
3006 error
= urlcache_hash_entry_create(header
, url
, url_entry_offset
, HASHTABLE_URL
);
3007 while(error
== ERROR_HANDLE_DISK_FULL
) {
3008 error
= cache_container_clean_index(container
, &header
);
3009 if(error
== ERROR_SUCCESS
) {
3010 url_entry
= (entry_url
*)((LPBYTE
)header
+ url_entry_offset
);
3011 error
= urlcache_hash_entry_create(header
, url
,
3012 url_entry_offset
, HASHTABLE_URL
);
3015 if(error
!= ERROR_SUCCESS
) {
3016 urlcache_entry_free(header
, &url_entry
->header
);
3017 cache_container_unlock_index(container
, header
);
3018 SetLastError(error
);
3022 if(url_entry
->cache_dir
< header
->dirs_no
)
3023 header
->directory_data
[url_entry
->cache_dir
].files_no
++;
3024 if(entry_type
& STICKY_CACHE_ENTRY
)
3025 header
->exempt_usage
.QuadPart
+= file_size
.QuadPart
;
3027 header
->cache_usage
.QuadPart
+= file_size
.QuadPart
;
3028 if(header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
> header
->cache_limit
.QuadPart
)
3029 handle_full_cache();
3031 cache_container_unlock_index(container
, header
);
3035 /***********************************************************************
3036 * CommitUrlCacheEntryA (WININET.@)
3038 BOOL WINAPI
CommitUrlCacheEntryA(LPCSTR lpszUrlName
, LPCSTR lpszLocalFileName
,
3039 FILETIME ExpireTime
, FILETIME LastModifiedTime
, DWORD CacheEntryType
,
3040 LPBYTE lpHeaderInfo
, DWORD dwHeaderSize
, LPCSTR lpszFileExtension
, LPCSTR lpszOriginalUrl
)
3042 WCHAR
*file_name
= NULL
;
3045 if(lpszLocalFileName
) {
3046 file_name
= heap_strdupAtoW(lpszLocalFileName
);
3051 ret
= urlcache_entry_commit(lpszUrlName
, file_name
, ExpireTime
, LastModifiedTime
,
3052 CacheEntryType
, lpHeaderInfo
, dwHeaderSize
, lpszFileExtension
, lpszOriginalUrl
);
3053 heap_free(file_name
);
3057 /***********************************************************************
3058 * CommitUrlCacheEntryW (WININET.@)
3060 BOOL WINAPI
CommitUrlCacheEntryW(LPCWSTR lpszUrlName
, LPCWSTR lpszLocalFileName
,
3061 FILETIME ExpireTime
, FILETIME LastModifiedTime
, DWORD CacheEntryType
,
3062 LPWSTR lpHeaderInfo
, DWORD dwHeaderSize
, LPCWSTR lpszFileExtension
, LPCWSTR lpszOriginalUrl
)
3064 char *url
, *original_url
=NULL
, *file_ext
=NULL
, *header_info
=NULL
;
3067 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
3071 header_info
= heap_strdupWtoUTF8(lpHeaderInfo
);
3076 dwHeaderSize
= strlen(header_info
);
3079 if(lpszFileExtension
) {
3080 file_ext
= heap_strdupWtoA(lpszFileExtension
);
3083 heap_free(header_info
);
3088 if(lpszOriginalUrl
&& !urlcache_encode_url_alloc(lpszOriginalUrl
, &original_url
)) {
3090 heap_free(header_info
);
3091 heap_free(file_ext
);
3095 ret
= urlcache_entry_commit(url
, lpszLocalFileName
, ExpireTime
, LastModifiedTime
,
3096 CacheEntryType
, (BYTE
*)header_info
, dwHeaderSize
, file_ext
, original_url
);
3098 heap_free(header_info
);
3099 heap_free(file_ext
);
3100 heap_free(original_url
);
3104 /***********************************************************************
3105 * ReadUrlCacheEntryStream (WININET.@)
3108 BOOL WINAPI
ReadUrlCacheEntryStream(
3109 IN HANDLE hUrlCacheStream
,
3110 IN DWORD dwLocation
,
3111 IN OUT LPVOID lpBuffer
,
3112 IN OUT LPDWORD lpdwLen
,
3116 /* Get handle to file from 'stream' */
3117 stream_handle
*pStream
= (stream_handle
*)hUrlCacheStream
;
3119 if (dwReserved
!= 0)
3121 ERR("dwReserved != 0\n");
3122 SetLastError(ERROR_INVALID_PARAMETER
);
3126 if (IsBadReadPtr(pStream
, sizeof(*pStream
)) || IsBadStringPtrA(pStream
->url
, INTERNET_MAX_URL_LENGTH
))
3128 SetLastError(ERROR_INVALID_HANDLE
);
3132 if (SetFilePointer(pStream
->file
, dwLocation
, NULL
, FILE_CURRENT
) == INVALID_SET_FILE_POINTER
)
3134 return ReadFile(pStream
->file
, lpBuffer
, *lpdwLen
, lpdwLen
, NULL
);
3137 /***********************************************************************
3138 * RetrieveUrlCacheEntryStreamA (WININET.@)
3141 HANDLE WINAPI
RetrieveUrlCacheEntryStreamA(LPCSTR lpszUrlName
,
3142 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
3143 LPDWORD lpdwCacheEntryInfoBufferSize
, BOOL fRandomRead
, DWORD dwReserved
)
3145 /* NOTE: this is not the same as the way that the native
3146 * version allocates 'stream' handles. I did it this way
3147 * as it is much easier and no applications should depend
3148 * on this behaviour. (Native version appears to allocate
3149 * indices into a table)
3151 stream_handle
*stream
;
3154 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName
), lpCacheEntryInfo
,
3155 lpdwCacheEntryInfoBufferSize
, fRandomRead
, dwReserved
);
3157 if(!RetrieveUrlCacheEntryFileA(lpszUrlName
, lpCacheEntryInfo
,
3158 lpdwCacheEntryInfoBufferSize
, dwReserved
))
3161 file
= CreateFileA(lpCacheEntryInfo
->lpszLocalFileName
, GENERIC_READ
, FILE_SHARE_READ
,
3162 NULL
, OPEN_EXISTING
, fRandomRead
? FILE_FLAG_RANDOM_ACCESS
: 0, NULL
);
3163 if(file
== INVALID_HANDLE_VALUE
) {
3164 UnlockUrlCacheEntryFileA(lpszUrlName
, 0);
3168 /* allocate handle storage space */
3169 stream
= heap_alloc(sizeof(stream_handle
) + strlen(lpszUrlName
) * sizeof(CHAR
));
3172 UnlockUrlCacheEntryFileA(lpszUrlName
, 0);
3173 SetLastError(ERROR_OUTOFMEMORY
);
3177 stream
->file
= file
;
3178 strcpy(stream
->url
, lpszUrlName
);
3182 /***********************************************************************
3183 * RetrieveUrlCacheEntryStreamW (WININET.@)
3186 HANDLE WINAPI
RetrieveUrlCacheEntryStreamW(LPCWSTR lpszUrlName
,
3187 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
3188 LPDWORD lpdwCacheEntryInfoBufferSize
, BOOL fRandomRead
, DWORD dwReserved
)
3191 /* NOTE: this is not the same as the way that the native
3192 * version allocates 'stream' handles. I did it this way
3193 * as it is much easier and no applications should depend
3194 * on this behaviour. (Native version appears to allocate
3195 * indices into a table)
3197 stream_handle
*stream
;
3200 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName
), lpCacheEntryInfo
,
3201 lpdwCacheEntryInfoBufferSize
, fRandomRead
, dwReserved
);
3203 if(!(len
= urlcache_encode_url(lpszUrlName
, NULL
, 0)))
3206 if(!RetrieveUrlCacheEntryFileW(lpszUrlName
, lpCacheEntryInfo
,
3207 lpdwCacheEntryInfoBufferSize
, dwReserved
))
3210 file
= CreateFileW(lpCacheEntryInfo
->lpszLocalFileName
, GENERIC_READ
, FILE_SHARE_READ
,
3211 NULL
, OPEN_EXISTING
, fRandomRead
? FILE_FLAG_RANDOM_ACCESS
: 0, NULL
);
3212 if(file
== INVALID_HANDLE_VALUE
) {
3213 UnlockUrlCacheEntryFileW(lpszUrlName
, 0);
3217 /* allocate handle storage space */
3218 stream
= heap_alloc(sizeof(stream_handle
) + len
*sizeof(WCHAR
));
3221 UnlockUrlCacheEntryFileW(lpszUrlName
, 0);
3222 SetLastError(ERROR_OUTOFMEMORY
);
3226 stream
->file
= file
;
3227 if(!urlcache_encode_url(lpszUrlName
, stream
->url
, len
)) {
3229 UnlockUrlCacheEntryFileW(lpszUrlName
, 0);
3236 /***********************************************************************
3237 * UnlockUrlCacheEntryStream (WININET.@)
3240 BOOL WINAPI
UnlockUrlCacheEntryStream(
3241 IN HANDLE hUrlCacheStream
,
3245 stream_handle
*pStream
= (stream_handle
*)hUrlCacheStream
;
3247 if (dwReserved
!= 0)
3249 ERR("dwReserved != 0\n");
3250 SetLastError(ERROR_INVALID_PARAMETER
);
3254 if (IsBadReadPtr(pStream
, sizeof(*pStream
)) || IsBadStringPtrA(pStream
->url
, INTERNET_MAX_URL_LENGTH
))
3256 SetLastError(ERROR_INVALID_HANDLE
);
3260 if (!UnlockUrlCacheEntryFileA(pStream
->url
, 0))
3263 CloseHandle(pStream
->file
);
3269 /***********************************************************************
3270 * DeleteUrlCacheEntryA (WININET.@)
3273 BOOL WINAPI
DeleteUrlCacheEntryA(LPCSTR lpszUrlName
)
3275 cache_container
*pContainer
;
3276 urlcache_header
*pHeader
;
3277 struct hash_entry
*pHashEntry
;
3281 TRACE("(%s)\n", debugstr_a(lpszUrlName
));
3283 error
= cache_containers_find(lpszUrlName
, &pContainer
);
3284 if (error
!= ERROR_SUCCESS
)
3286 SetLastError(error
);
3290 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
3291 if (error
!= ERROR_SUCCESS
)
3293 SetLastError(error
);
3297 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3300 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
3302 cache_container_unlock_index(pContainer
, pHeader
);
3303 TRACE("entry %s not found!\n", lpszUrlName
);
3304 SetLastError(ERROR_FILE_NOT_FOUND
);
3308 ret
= urlcache_entry_delete(pContainer
, pHeader
, pHashEntry
);
3310 cache_container_unlock_index(pContainer
, pHeader
);
3315 /***********************************************************************
3316 * DeleteUrlCacheEntryW (WININET.@)
3319 BOOL WINAPI
DeleteUrlCacheEntryW(LPCWSTR lpszUrlName
)
3324 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
3327 ret
= DeleteUrlCacheEntryA(url
);
3332 BOOL WINAPI
DeleteUrlCacheContainerA(DWORD d1
, DWORD d2
)
3334 FIXME("(0x%08x, 0x%08x) stub\n", d1
, d2
);
3338 BOOL WINAPI
DeleteUrlCacheContainerW(DWORD d1
, DWORD d2
)
3340 FIXME("(0x%08x, 0x%08x) stub\n", d1
, d2
);
3344 /***********************************************************************
3345 * CreateCacheContainerA (WININET.@)
3347 BOOL WINAPI
CreateUrlCacheContainerA(DWORD d1
, DWORD d2
, DWORD d3
, DWORD d4
,
3348 DWORD d5
, DWORD d6
, DWORD d7
, DWORD d8
)
3350 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3351 d1
, d2
, d3
, d4
, d5
, d6
, d7
, d8
);
3355 /***********************************************************************
3356 * CreateCacheContainerW (WININET.@)
3358 BOOL WINAPI
CreateUrlCacheContainerW(DWORD d1
, DWORD d2
, DWORD d3
, DWORD d4
,
3359 DWORD d5
, DWORD d6
, DWORD d7
, DWORD d8
)
3361 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3362 d1
, d2
, d3
, d4
, d5
, d6
, d7
, d8
);
3366 /***********************************************************************
3367 * FindFirstUrlCacheContainerA (WININET.@)
3369 HANDLE WINAPI
FindFirstUrlCacheContainerA( LPVOID p1
, LPVOID p2
, LPVOID p3
, DWORD d1
)
3371 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1
, p2
, p3
, d1
);
3375 /***********************************************************************
3376 * FindFirstUrlCacheContainerW (WININET.@)
3378 HANDLE WINAPI
FindFirstUrlCacheContainerW( LPVOID p1
, LPVOID p2
, LPVOID p3
, DWORD d1
)
3380 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1
, p2
, p3
, d1
);
3384 /***********************************************************************
3385 * FindNextUrlCacheContainerA (WININET.@)
3387 BOOL WINAPI
FindNextUrlCacheContainerA( HANDLE handle
, LPVOID p1
, LPVOID p2
)
3389 FIXME("(%p, %p, %p) stub\n", handle
, p1
, p2
);
3393 /***********************************************************************
3394 * FindNextUrlCacheContainerW (WININET.@)
3396 BOOL WINAPI
FindNextUrlCacheContainerW( HANDLE handle
, LPVOID p1
, LPVOID p2
)
3398 FIXME("(%p, %p, %p) stub\n", handle
, p1
, p2
);
3402 HANDLE WINAPI
FindFirstUrlCacheEntryExA(
3403 LPCSTR lpszUrlSearchPattern
,
3407 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
,
3408 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3410 LPDWORD pcbReserved2
,
3414 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern
),
3415 dwFlags
, dwFilter
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
, lpFirstCacheEntryInfo
,
3416 lpdwFirstCacheEntryInfoBufferSize
, lpReserved
, pcbReserved2
,lpReserved3
);
3417 SetLastError(ERROR_FILE_NOT_FOUND
);
3421 HANDLE WINAPI
FindFirstUrlCacheEntryExW(
3422 LPCWSTR lpszUrlSearchPattern
,
3426 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
,
3427 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3429 LPDWORD pcbReserved2
,
3433 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern
),
3434 dwFlags
, dwFilter
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
, lpFirstCacheEntryInfo
,
3435 lpdwFirstCacheEntryInfoBufferSize
, lpReserved
, pcbReserved2
,lpReserved3
);
3436 SetLastError(ERROR_FILE_NOT_FOUND
);
3440 /***********************************************************************
3441 * FindFirstUrlCacheEntryA (WININET.@)
3444 INTERNETAPI HANDLE WINAPI
FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern
,
3445 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
, LPDWORD lpdwFirstCacheEntryInfoBufferSize
)
3447 find_handle
*pEntryHandle
;
3449 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern
), lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
);
3451 pEntryHandle
= heap_alloc(sizeof(*pEntryHandle
));
3455 pEntryHandle
->magic
= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
;
3456 if (lpszUrlSearchPattern
)
3458 pEntryHandle
->url_search_pattern
= heap_strdupA(lpszUrlSearchPattern
);
3459 if (!pEntryHandle
->url_search_pattern
)
3461 heap_free(pEntryHandle
);
3466 pEntryHandle
->url_search_pattern
= NULL
;
3467 pEntryHandle
->container_idx
= 0;
3468 pEntryHandle
->hash_table_idx
= 0;
3469 pEntryHandle
->hash_entry_idx
= 0;
3471 if (!FindNextUrlCacheEntryA(pEntryHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
))
3473 heap_free(pEntryHandle
);
3476 return pEntryHandle
;
3479 /***********************************************************************
3480 * FindFirstUrlCacheEntryW (WININET.@)
3483 INTERNETAPI HANDLE WINAPI
FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern
,
3484 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
, LPDWORD lpdwFirstCacheEntryInfoBufferSize
)
3486 find_handle
*pEntryHandle
;
3488 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern
), lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
);
3490 pEntryHandle
= heap_alloc(sizeof(*pEntryHandle
));
3494 pEntryHandle
->magic
= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
;
3495 if (lpszUrlSearchPattern
)
3497 pEntryHandle
->url_search_pattern
= heap_strdupWtoA(lpszUrlSearchPattern
);
3498 if (!pEntryHandle
->url_search_pattern
)
3500 heap_free(pEntryHandle
);
3505 pEntryHandle
->url_search_pattern
= NULL
;
3506 pEntryHandle
->container_idx
= 0;
3507 pEntryHandle
->hash_table_idx
= 0;
3508 pEntryHandle
->hash_entry_idx
= 0;
3510 if (!FindNextUrlCacheEntryW(pEntryHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
))
3512 heap_free(pEntryHandle
);
3515 return pEntryHandle
;
3518 static BOOL
urlcache_find_next_entry(
3520 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo
,
3521 LPDWORD lpdwNextCacheEntryInfoBufferSize
,
3524 find_handle
*pEntryHandle
= (find_handle
*)hEnumHandle
;
3525 cache_container
*pContainer
;
3527 if (pEntryHandle
->magic
!= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
)
3529 SetLastError(ERROR_INVALID_HANDLE
);
3533 for (; cache_containers_enum(pEntryHandle
->url_search_pattern
, pEntryHandle
->container_idx
, &pContainer
);
3534 pEntryHandle
->container_idx
++, pEntryHandle
->hash_table_idx
= 0)
3536 urlcache_header
*pHeader
;
3537 entry_hash_table
*pHashTableEntry
;
3540 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
3541 if (error
!= ERROR_SUCCESS
)
3543 SetLastError(error
);
3547 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3550 for (; urlcache_enum_hash_tables(pHeader
, &pEntryHandle
->hash_table_idx
, &pHashTableEntry
);
3551 pEntryHandle
->hash_table_idx
++, pEntryHandle
->hash_entry_idx
= 0)
3553 const struct hash_entry
*pHashEntry
= NULL
;
3554 for (; urlcache_enum_hash_table_entries(pHeader
, pHashTableEntry
, &pEntryHandle
->hash_entry_idx
, &pHashEntry
);
3555 pEntryHandle
->hash_entry_idx
++)
3557 const entry_url
*pUrlEntry
;
3558 const entry_header
*pEntry
= (const entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
3560 if (pEntry
->signature
!= URL_SIGNATURE
)
3563 pUrlEntry
= (const entry_url
*)pEntry
;
3564 TRACE("Found URL: %s\n",
3565 debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->url_off
));
3566 TRACE("Header info: %s\n",
3567 debugstr_an((LPCSTR
)pUrlEntry
+ pUrlEntry
->header_info_off
,
3568 pUrlEntry
->header_info_size
));
3570 error
= urlcache_copy_entry(
3573 lpNextCacheEntryInfo
,
3574 lpdwNextCacheEntryInfoBufferSize
,
3577 if (error
!= ERROR_SUCCESS
)
3579 cache_container_unlock_index(pContainer
, pHeader
);
3580 SetLastError(error
);
3583 if(pUrlEntry
->local_name_off
)
3584 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->local_name_off
));
3586 /* increment the current index so that next time the function
3587 * is called the next entry is returned */
3588 pEntryHandle
->hash_entry_idx
++;
3589 cache_container_unlock_index(pContainer
, pHeader
);
3594 cache_container_unlock_index(pContainer
, pHeader
);
3597 SetLastError(ERROR_NO_MORE_ITEMS
);
3601 /***********************************************************************
3602 * FindNextUrlCacheEntryA (WININET.@)
3604 BOOL WINAPI
FindNextUrlCacheEntryA(
3606 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo
,
3607 LPDWORD lpdwNextCacheEntryInfoBufferSize
)
3609 TRACE("(%p, %p, %p)\n", hEnumHandle
, lpNextCacheEntryInfo
, lpdwNextCacheEntryInfoBufferSize
);
3611 return urlcache_find_next_entry(hEnumHandle
, lpNextCacheEntryInfo
,
3612 lpdwNextCacheEntryInfoBufferSize
, FALSE
/* not UNICODE */);
3615 /***********************************************************************
3616 * FindNextUrlCacheEntryW (WININET.@)
3618 BOOL WINAPI
FindNextUrlCacheEntryW(
3620 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo
,
3621 LPDWORD lpdwNextCacheEntryInfoBufferSize
3624 TRACE("(%p, %p, %p)\n", hEnumHandle
, lpNextCacheEntryInfo
, lpdwNextCacheEntryInfoBufferSize
);
3626 return urlcache_find_next_entry(hEnumHandle
,
3627 (LPINTERNET_CACHE_ENTRY_INFOA
)lpNextCacheEntryInfo
,
3628 lpdwNextCacheEntryInfoBufferSize
, TRUE
/* UNICODE */);
3631 /***********************************************************************
3632 * FindCloseUrlCache (WININET.@)
3634 BOOL WINAPI
FindCloseUrlCache(HANDLE hEnumHandle
)
3636 find_handle
*pEntryHandle
= (find_handle
*)hEnumHandle
;
3638 TRACE("(%p)\n", hEnumHandle
);
3640 if (!pEntryHandle
|| pEntryHandle
->magic
!= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
)
3642 SetLastError(ERROR_INVALID_HANDLE
);
3646 pEntryHandle
->magic
= 0;
3647 heap_free(pEntryHandle
->url_search_pattern
);
3648 heap_free(pEntryHandle
);
3652 HANDLE WINAPI
FindFirstUrlCacheGroup( DWORD dwFlags
, DWORD dwFilter
, LPVOID lpSearchCondition
,
3653 DWORD dwSearchCondition
, GROUPID
* lpGroupId
, LPVOID lpReserved
)
3655 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags
, dwFilter
, lpSearchCondition
,
3656 dwSearchCondition
, lpGroupId
, lpReserved
);
3660 BOOL WINAPI
FindNextUrlCacheEntryExA(
3662 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
,
3663 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3665 LPDWORD pcbReserved2
,
3669 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
,
3670 lpReserved
, pcbReserved2
, lpReserved3
);
3674 BOOL WINAPI
FindNextUrlCacheEntryExW(
3676 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
,
3677 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3679 LPDWORD pcbReserved2
,
3683 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
,
3684 lpReserved
, pcbReserved2
, lpReserved3
);
3688 BOOL WINAPI
FindNextUrlCacheGroup( HANDLE hFind
, GROUPID
* lpGroupId
, LPVOID lpReserved
)
3690 FIXME("(%p, %p, %p) stub\n", hFind
, lpGroupId
, lpReserved
);
3694 /***********************************************************************
3695 * CreateUrlCacheGroup (WININET.@)
3698 INTERNETAPI GROUPID WINAPI
CreateUrlCacheGroup(DWORD dwFlags
, LPVOID lpReserved
)
3700 FIXME("(0x%08x, %p): stub\n", dwFlags
, lpReserved
);
3704 /***********************************************************************
3705 * DeleteUrlCacheGroup (WININET.@)
3708 BOOL WINAPI
DeleteUrlCacheGroup(GROUPID GroupId
, DWORD dwFlags
, LPVOID lpReserved
)
3710 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3711 (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
, dwFlags
, lpReserved
);
3715 /***********************************************************************
3716 * DeleteWpadCacheForNetworks (WININET.@)
3717 * Undocumented, added in IE8
3719 BOOL WINAPI
DeleteWpadCacheForNetworks(DWORD unk1
)
3721 FIXME("(%d) stub\n", unk1
);
3725 /***********************************************************************
3726 * SetUrlCacheEntryGroupA (WININET.@)
3729 BOOL WINAPI
SetUrlCacheEntryGroupA(LPCSTR lpszUrlName
, DWORD dwFlags
,
3730 GROUPID GroupId
, LPBYTE pbGroupAttributes
, DWORD cbGroupAttributes
,
3733 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3734 debugstr_a(lpszUrlName
), dwFlags
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
,
3735 pbGroupAttributes
, cbGroupAttributes
, lpReserved
);
3736 SetLastError(ERROR_FILE_NOT_FOUND
);
3740 /***********************************************************************
3741 * SetUrlCacheEntryGroupW (WININET.@)
3744 BOOL WINAPI
SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName
, DWORD dwFlags
,
3745 GROUPID GroupId
, LPBYTE pbGroupAttributes
, DWORD cbGroupAttributes
,
3748 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3749 debugstr_w(lpszUrlName
), dwFlags
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
,
3750 pbGroupAttributes
, cbGroupAttributes
, lpReserved
);
3751 SetLastError(ERROR_FILE_NOT_FOUND
);
3755 /***********************************************************************
3756 * GetUrlCacheConfigInfoW (WININET.@)
3758 BOOL WINAPI
GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo
, LPDWORD size
, DWORD bitmask
)
3760 FIXME("(%p, %p, %x)\n", CacheInfo
, size
, bitmask
);
3761 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
3765 /***********************************************************************
3766 * GetUrlCacheConfigInfoA (WININET.@)
3768 BOOL WINAPI
GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo
, LPDWORD size
, DWORD bitmask
)
3770 FIXME("(%p, %p, %x)\n", CacheInfo
, size
, bitmask
);
3771 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
3775 BOOL WINAPI
GetUrlCacheGroupAttributeA( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3776 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo
,
3777 LPDWORD lpdwGroupInfo
, LPVOID lpReserved
)
3779 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3780 (ULONG
)(gid
>> 32), (ULONG
)gid
, dwFlags
, dwAttributes
, lpGroupInfo
,
3781 lpdwGroupInfo
, lpReserved
);
3785 BOOL WINAPI
GetUrlCacheGroupAttributeW( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3786 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo
,
3787 LPDWORD lpdwGroupInfo
, LPVOID lpReserved
)
3789 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3790 (ULONG
)(gid
>> 32), (ULONG
)gid
, dwFlags
, dwAttributes
, lpGroupInfo
,
3791 lpdwGroupInfo
, lpReserved
);
3795 BOOL WINAPI
SetUrlCacheGroupAttributeA( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3796 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo
, LPVOID lpReserved
)
3798 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3799 (ULONG
)(gid
>> 32), (ULONG
)gid
, dwFlags
, dwAttributes
, lpGroupInfo
, lpReserved
);
3803 BOOL WINAPI
SetUrlCacheGroupAttributeW( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3804 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo
, LPVOID lpReserved
)
3806 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3807 (ULONG
)(gid
>> 32), (ULONG
)gid
, dwFlags
, dwAttributes
, lpGroupInfo
, lpReserved
);
3811 BOOL WINAPI
SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo
, DWORD dwFieldControl
)
3813 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo
, dwFieldControl
);
3817 BOOL WINAPI
SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo
, DWORD dwFieldControl
)
3819 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo
, dwFieldControl
);
3823 /***********************************************************************
3824 * DeleteIE3Cache (WININET.@)
3826 * Deletes the files used by the IE3 URL caching system.
3829 * hWnd [I] A dummy window.
3830 * hInst [I] Instance of process calling the function.
3831 * lpszCmdLine [I] Options used by function.
3832 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3834 DWORD WINAPI
DeleteIE3Cache(HWND hWnd
, HINSTANCE hInst
, LPSTR lpszCmdLine
, int nCmdShow
)
3836 FIXME("(%p, %p, %s, %d)\n", hWnd
, hInst
, debugstr_a(lpszCmdLine
), nCmdShow
);
3840 static BOOL
urlcache_entry_is_expired(const entry_url
*pUrlEntry
,
3841 FILETIME
*pftLastModified
)
3844 FILETIME now
, expired
;
3846 *pftLastModified
= pUrlEntry
->modification_time
;
3847 GetSystemTimeAsFileTime(&now
);
3848 dos_date_time_to_file_time(pUrlEntry
->expire_date
,
3849 pUrlEntry
->expire_time
, &expired
);
3850 /* If the expired time is 0, it's interpreted as not expired */
3851 if (!expired
.dwLowDateTime
&& !expired
.dwHighDateTime
)
3854 ret
= CompareFileTime(&expired
, &now
) < 0;
3858 /***********************************************************************
3859 * IsUrlCacheEntryExpiredA (WININET.@)
3863 * dwFlags [I] Unknown
3864 * pftLastModified [O] Last modified time
3866 BOOL WINAPI
IsUrlCacheEntryExpiredA(LPCSTR url
, DWORD dwFlags
, FILETIME
* pftLastModified
)
3868 urlcache_header
*pHeader
;
3869 struct hash_entry
*pHashEntry
;
3870 const entry_header
*pEntry
;
3871 const entry_url
* pUrlEntry
;
3872 cache_container
*pContainer
;
3875 TRACE("(%s, %08x, %p)\n", debugstr_a(url
), dwFlags
, pftLastModified
);
3877 if (!url
|| !pftLastModified
)
3880 FIXME("unknown flags 0x%08x\n", dwFlags
);
3882 /* Any error implies that the URL is expired, i.e. not in the cache */
3883 if (cache_containers_find(url
, &pContainer
))
3885 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3889 if (cache_container_open_index(pContainer
, MIN_BLOCK_NO
))
3891 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3895 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3897 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3901 if (!urlcache_find_hash_entry(pHeader
, url
, &pHashEntry
))
3903 cache_container_unlock_index(pContainer
, pHeader
);
3904 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3905 TRACE("entry %s not found!\n", url
);
3909 pEntry
= (const entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
3910 if (pEntry
->signature
!= URL_SIGNATURE
)
3912 cache_container_unlock_index(pContainer
, pHeader
);
3913 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3914 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR
)&pEntry
->signature
, sizeof(DWORD
)));
3918 pUrlEntry
= (const entry_url
*)pEntry
;
3919 expired
= urlcache_entry_is_expired(pUrlEntry
, pftLastModified
);
3921 cache_container_unlock_index(pContainer
, pHeader
);
3926 /***********************************************************************
3927 * IsUrlCacheEntryExpiredW (WININET.@)
3931 * dwFlags [I] Unknown
3932 * pftLastModified [O] Last modified time
3934 BOOL WINAPI
IsUrlCacheEntryExpiredW(LPCWSTR url
, DWORD dwFlags
, FILETIME
* pftLastModified
)
3939 if(!urlcache_encode_url_alloc(url
, &encoded_url
))
3942 ret
= IsUrlCacheEntryExpiredA(encoded_url
, dwFlags
, pftLastModified
);
3943 heap_free(encoded_url
);
3947 /***********************************************************************
3948 * GetDiskInfoA (WININET.@)
3950 BOOL WINAPI
GetDiskInfoA(PCSTR path
, PDWORD cluster_size
, PDWORDLONG free
, PDWORDLONG total
)
3953 ULARGE_INTEGER bytes_free
, bytes_total
;
3955 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path
), cluster_size
, free
, total
);
3959 SetLastError(ERROR_INVALID_PARAMETER
);
3963 if ((ret
= GetDiskFreeSpaceExA(path
, NULL
, &bytes_total
, &bytes_free
)))
3965 if (cluster_size
) *cluster_size
= 1;
3966 if (free
) *free
= bytes_free
.QuadPart
;
3967 if (total
) *total
= bytes_total
.QuadPart
;
3972 /***********************************************************************
3973 * RegisterUrlCacheNotification (WININET.@)
3975 DWORD WINAPI
RegisterUrlCacheNotification(LPVOID a
, DWORD b
, DWORD c
, DWORD d
, DWORD e
, DWORD f
)
3977 FIXME("(%p %x %x %x %x %x)\n", a
, b
, c
, d
, e
, f
);
3981 /***********************************************************************
3982 * IncrementUrlCacheHeaderData (WININET.@)
3984 BOOL WINAPI
IncrementUrlCacheHeaderData(DWORD index
, LPDWORD data
)
3986 FIXME("(%u, %p)\n", index
, data
);
3990 /***********************************************************************
3991 * RunOnceUrlCache (WININET.@)
3994 DWORD WINAPI
RunOnceUrlCache(HWND hwnd
, HINSTANCE hinst
, LPSTR cmd
, int cmdshow
)
3996 FIXME("(%p, %p, %s, %d): stub\n", hwnd
, hinst
, debugstr_a(cmd
), cmdshow
);
4000 BOOL
init_urlcache(void)
4002 dll_unload_event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
4003 if(!dll_unload_event
)
4006 free_cache_running
= CreateSemaphoreW(NULL
, 1, 1, NULL
);
4007 if(!free_cache_running
) {
4008 CloseHandle(dll_unload_event
);
4012 cache_containers_init();
4016 void free_urlcache(void)
4018 SetEvent(dll_unload_event
);
4019 WaitForSingleObject(free_cache_running
, INFINITE
);
4020 ReleaseSemaphore(free_cache_running
, 1, NULL
);
4021 CloseHandle(free_cache_running
);
4022 CloseHandle(dll_unload_event
);
4024 cache_containers_free();
4027 /***********************************************************************
4028 * LoadUrlCacheContent (WININET.@)
4030 BOOL WINAPI
LoadUrlCacheContent(void)