2 * Wininet - Url Cache functions
4 * Copyright 2001,2002 CodeWeavers
5 * Copyright 2003-2008 Robert Shearman
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #define NONAMELESSUNION
48 #include "wine/unicode.h"
49 #include "wine/debug.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(wininet
);
53 static const char urlcache_ver_prefix
[] = "WINE URLCache Ver ";
54 static const char urlcache_ver
[] = "0.2012001";
57 #define CHAR_BIT (8 * sizeof(CHAR))
60 #define ENTRY_START_OFFSET 0x4000
62 #define MAX_DIR_NO 0x20
64 #define HASHTABLE_SIZE 448
65 #define HASHTABLE_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
66 #define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
67 #define ALLOCATION_TABLE_OFFSET 0x250
68 #define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
69 #define MIN_BLOCK_NO 0x80
70 #define MAX_BLOCK_NO (ALLOCATION_TABLE_SIZE * CHAR_BIT)
71 #define FILE_SIZE(blocks) ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
73 #define HASHTABLE_URL 0
74 #define HASHTABLE_DEL 1
75 #define HASHTABLE_LOCK 2
76 #define HASHTABLE_FREE 3
77 #define HASHTABLE_REDR 5
78 #define HASHTABLE_FLAG_BITS 6
80 #define PENDING_DELETE_CACHE_ENTRY 0x00400000
81 #define INSTALLED_CACHE_ENTRY 0x10000000
82 #define GET_INSTALLED_ENTRY 0x200
83 #define CACHE_CONTAINER_NO_SUBDIR 0xFE
85 #define CACHE_HEADER_DATA_ROOT_LEAK_OFFSET 0x16
87 #define FILETIME_SECOND 10000000
89 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
90 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
91 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
92 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
93 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
95 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
97 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
102 DWORD blocks_used
; /* number of 128byte blocks used by this entry */
108 FILETIME modification_time
;
109 FILETIME access_time
;
110 WORD expire_date
; /* expire date in dos format */
111 WORD expire_time
; /* expire time in dos format */
112 DWORD unk1
; /* usually zero */
113 ULARGE_INTEGER size
; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
114 DWORD unk2
; /* usually zero */
115 DWORD exempt_delta
; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
116 DWORD unk3
; /* usually 0x60 */
117 DWORD url_off
; /* offset of start of url from start of entry */
118 BYTE cache_dir
; /* index of cache directory this url is stored in */
119 BYTE unk4
; /* usually zero */
120 WORD unk5
; /* usually 0x1010 */
121 DWORD local_name_off
; /* offset of start of local filename from start of entry */
122 DWORD cache_entry_type
; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
123 DWORD header_info_off
; /* offset of start of header info from start of entry */
124 DWORD header_info_size
;
125 DWORD file_extension_off
; /* offset of start of file extension from start of entry */
126 WORD sync_date
; /* last sync date in dos format */
127 WORD sync_time
; /* last sync time in dos format */
128 DWORD hit_rate
; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
129 DWORD use_count
; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
132 DWORD unk7
; /* usually zero */
133 DWORD unk8
; /* usually zero */
134 /* packing to dword align start of next field */
135 /* CHAR szSourceUrlName[]; (url) */
136 /* packing to dword align start of next field */
137 /* CHAR szLocalFileName[]; (local file name excluding path) */
138 /* packing to dword align start of next field */
139 /* CHAR szHeaderInfo[]; (header info) */
153 struct hash_entry hash_table
[HASHTABLE_SIZE
];
160 DWORD hash_table_off
;
161 DWORD capacity_in_blocks
;
164 ULARGE_INTEGER cache_limit
;
165 ULARGE_INTEGER cache_usage
;
166 ULARGE_INTEGER exempt_usage
;
168 struct _directory_data
171 char name
[DIR_LENGTH
];
172 } directory_data
[MAX_DIR_NO
];
174 BYTE allocation_table
[ALLOCATION_TABLE_SIZE
];
185 struct list entry
; /* part of a list */
186 char *cache_prefix
; /* string that has to be prefixed for this container to be used */
187 LPWSTR path
; /* path to url container directory */
188 HANDLE mapping
; /* handle of file mapping */
189 DWORD file_size
; /* size of file when mapping was opened */
190 HANDLE mutex
; /* handle of mutex */
191 DWORD default_entry_type
;
197 char *url_search_pattern
;
199 DWORD hash_table_idx
;
200 DWORD hash_entry_idx
;
203 /* List of all containers available */
204 static struct list UrlContainers
= LIST_INIT(UrlContainers
);
206 BOOL bDefaultContainersAdded
= FALSE
;
208 static inline char *heap_strdupWtoUTF8(LPCWSTR str
)
213 DWORD size
= WideCharToMultiByte(CP_UTF8
, 0, str
, -1, NULL
, 0, NULL
, NULL
);
214 ret
= heap_alloc(size
);
216 WideCharToMultiByte(CP_UTF8
, 0, str
, -1, ret
, size
, NULL
, NULL
);
222 /***********************************************************************
223 * urlcache_block_is_free (Internal)
225 * Is the specified block number free?
232 static inline BYTE
urlcache_block_is_free(BYTE
*allocation_table
, DWORD block_number
)
234 BYTE mask
= 1 << (block_number
%CHAR_BIT
);
235 return (allocation_table
[block_number
/CHAR_BIT
] & mask
) == 0;
238 /***********************************************************************
239 * urlcache_block_free (Internal)
241 * Marks the specified block as free
244 * this function is not updating used blocks count
250 static inline void urlcache_block_free(BYTE
*allocation_table
, DWORD block_number
)
252 BYTE mask
= ~(1 << (block_number
%CHAR_BIT
));
253 allocation_table
[block_number
/CHAR_BIT
] &= mask
;
256 /***********************************************************************
257 * urlcache_block_alloc (Internal)
259 * Marks the specified block as allocated
262 * this function is not updating used blocks count
268 static inline void urlcache_block_alloc(BYTE
*allocation_table
, DWORD block_number
)
270 BYTE mask
= 1 << (block_number
%CHAR_BIT
);
271 allocation_table
[block_number
/CHAR_BIT
] |= mask
;
274 /***********************************************************************
275 * urlcache_entry_alloc (Internal)
277 * Finds and allocates the first block of free space big enough and
278 * sets entry to point to it.
281 * ERROR_SUCCESS when free memory block was found
282 * Any other Win32 error code if the entry could not be added
285 static DWORD
urlcache_entry_alloc(urlcache_header
*header
, DWORD blocks_needed
, entry_header
**entry
)
287 DWORD block
, block_size
;
289 for(block
=0; block
<header
->capacity_in_blocks
; block
+=block_size
+1)
292 while(block_size
<blocks_needed
&& block_size
+block
<header
->capacity_in_blocks
293 && urlcache_block_is_free(header
->allocation_table
, block
+block_size
))
296 if(block_size
== blocks_needed
)
300 TRACE("Found free blocks starting at no. %d (0x%x)\n", block
, ENTRY_START_OFFSET
+block
*BLOCKSIZE
);
302 for(index
=0; index
<blocks_needed
; index
++)
303 urlcache_block_alloc(header
->allocation_table
, block
+index
);
305 *entry
= (entry_header
*)((BYTE
*)header
+ENTRY_START_OFFSET
+block
*BLOCKSIZE
);
306 for(index
=0; index
<blocks_needed
*BLOCKSIZE
/sizeof(DWORD
); index
++)
307 ((DWORD
*)*entry
)[index
] = 0xdeadbeef;
308 (*entry
)->blocks_used
= blocks_needed
;
310 header
->blocks_in_use
+= blocks_needed
;
311 return ERROR_SUCCESS
;
315 return ERROR_HANDLE_DISK_FULL
;
318 /***********************************************************************
319 * urlcache_entry_free (Internal)
321 * Deletes the specified entry and frees the space allocated to it
324 * TRUE if it succeeded
328 static BOOL
urlcache_entry_free(urlcache_header
*header
, entry_header
*entry
)
330 DWORD start_block
, block
;
332 /* update allocation table */
333 start_block
= ((DWORD
)((BYTE
*)entry
- (BYTE
*)header
) - ENTRY_START_OFFSET
) / BLOCKSIZE
;
334 for(block
= start_block
; block
< start_block
+entry
->blocks_used
; block
++)
335 urlcache_block_free(header
->allocation_table
, block
);
337 header
->blocks_in_use
-= entry
->blocks_used
;
341 /***********************************************************************
342 * urlcache_create_hash_table (Internal)
344 * Creates a new hash table in free space and adds it to the chain of existing
348 * ERROR_SUCCESS if the hash table was created
349 * ERROR_DISK_FULL if the hash table could not be created
352 static DWORD
urlcache_create_hash_table(urlcache_header
*header
, entry_hash_table
*hash_table_prev
, entry_hash_table
**hash_table
)
354 DWORD dwOffset
, error
;
357 if((error
= urlcache_entry_alloc(header
, 0x20, (entry_header
**)hash_table
)) != ERROR_SUCCESS
)
360 dwOffset
= (BYTE
*)*hash_table
-(BYTE
*)header
;
363 hash_table_prev
->next
= dwOffset
;
365 header
->hash_table_off
= dwOffset
;
367 (*hash_table
)->header
.signature
= HASH_SIGNATURE
;
368 (*hash_table
)->next
= 0;
369 (*hash_table
)->id
= hash_table_prev
? hash_table_prev
->id
+1 : 0;
370 for(i
= 0; i
< HASHTABLE_SIZE
; i
++) {
371 (*hash_table
)->hash_table
[i
].offset
= HASHTABLE_FREE
;
372 (*hash_table
)->hash_table
[i
].key
= HASHTABLE_FREE
;
374 return ERROR_SUCCESS
;
377 /***********************************************************************
378 * cache_container_create_object_name (Internal)
380 * Converts a path to a name suitable for use as a Win32 object name.
381 * Replaces '\\' characters in-place with the specified character
382 * (usually '_' or '!')
388 static void cache_container_create_object_name(LPWSTR lpszPath
, WCHAR replace
)
390 for (; *lpszPath
; lpszPath
++)
392 if (*lpszPath
== '\\')
397 /* Caller must hold container lock */
398 static HANDLE
cache_container_map_index(HANDLE file
, const WCHAR
*path
, DWORD size
, BOOL
*validate
)
400 static const WCHAR mapping_name_format
[]
401 = {'%','s','i','n','d','e','x','.','d','a','t','_','%','l','u',0};
402 WCHAR mapping_name
[MAX_PATH
];
405 wsprintfW(mapping_name
, mapping_name_format
, path
, size
);
406 cache_container_create_object_name(mapping_name
, '_');
408 mapping
= OpenFileMappingW(FILE_MAP_WRITE
, FALSE
, mapping_name
);
410 if(validate
) *validate
= FALSE
;
414 if(validate
) *validate
= TRUE
;
415 return CreateFileMappingW(file
, NULL
, PAGE_READWRITE
, 0, 0, mapping_name
);
418 /* Caller must hold container lock */
419 static DWORD
cache_container_set_size(cache_container
*container
, HANDLE file
, DWORD blocks_no
)
421 static const WCHAR cache_content_key
[] = {'S','o','f','t','w','a','r','e','\\',
422 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
423 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
424 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s','\\',
425 'C','a','c','h','e','\\','C','o','n','t','e','n','t',0};
426 static const WCHAR cache_limit
[] = {'C','a','c','h','e','L','i','m','i','t',0};
428 DWORD file_size
= FILE_SIZE(blocks_no
);
429 WCHAR dir_path
[MAX_PATH
], *dir_name
;
430 entry_hash_table
*hashtable_entry
;
431 urlcache_header
*header
;
437 if(SetFilePointer(file
, file_size
, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
438 return GetLastError();
440 if(!SetEndOfFile(file
))
441 return GetLastError();
443 mapping
= cache_container_map_index(file
, container
->path
, file_size
, NULL
);
445 return GetLastError();
447 header
= MapViewOfFile(mapping
, FILE_MAP_WRITE
, 0, 0, 0);
449 CloseHandle(mapping
);
450 return GetLastError();
453 if(blocks_no
!= MIN_BLOCK_NO
) {
454 if(file_size
> header
->size
)
455 memset((char*)header
+header
->size
, 0, file_size
-header
->size
);
456 header
->size
= file_size
;
457 header
->capacity_in_blocks
= blocks_no
;
459 UnmapViewOfFile(header
);
460 CloseHandle(container
->mapping
);
461 container
->mapping
= mapping
;
462 container
->file_size
= file_size
;
463 return ERROR_SUCCESS
;
466 memset(header
, 0, file_size
);
467 /* First set some constants and defaults in the header */
468 memcpy(header
->signature
, urlcache_ver_prefix
, sizeof(urlcache_ver_prefix
)-1);
469 memcpy(header
->signature
+sizeof(urlcache_ver_prefix
)-1, urlcache_ver
, sizeof(urlcache_ver
)-1);
470 header
->size
= file_size
;
471 header
->capacity_in_blocks
= blocks_no
;
472 /* 127MB - taken from default for Windows 2000 */
473 header
->cache_limit
.QuadPart
= 0x07ff5400;
474 /* Copied from a Windows 2000 cache index */
475 header
->dirs_no
= container
->default_entry_type
==NORMAL_CACHE_ENTRY
? 4 : 0;
477 /* If the registry has a cache size set, use the registry value */
478 if(RegOpenKeyW(HKEY_CURRENT_USER
, cache_content_key
, &key
) == ERROR_SUCCESS
) {
479 DWORD dw
, len
= sizeof(dw
), keytype
;
481 if(RegQueryValueExW(key
, cache_limit
, NULL
, &keytype
, (BYTE
*)&dw
, &len
) == ERROR_SUCCESS
&&
482 keytype
== REG_DWORD
)
483 header
->cache_limit
.QuadPart
= (ULONGLONG
)dw
* 1024;
487 urlcache_create_hash_table(header
, NULL
, &hashtable_entry
);
489 /* Last step - create the directories */
490 strcpyW(dir_path
, container
->path
);
491 dir_name
= dir_path
+ strlenW(dir_path
);
494 GetSystemTimeAsFileTime(&ft
);
496 for(i
=0; i
<header
->dirs_no
; ++i
) {
497 header
->directory_data
[i
].files_no
= 0;
499 ULONGLONG n
= ft
.dwHighDateTime
;
502 /* Generate a file name to attempt to create.
503 * This algorithm will create what will appear
504 * to be random and unrelated directory names
505 * of up to 9 characters in length.
508 n
+= ft
.dwLowDateTime
;
509 n
^= ((ULONGLONG
) i
<< 56) | ((ULONGLONG
) j
<< 48);
511 for(k
= 0; k
< 8; ++k
) {
514 /* Dividing by a prime greater than 36 helps
515 * with the appearance of randomness
520 dir_name
[k
] = '0' + r
;
522 dir_name
[k
] = 'A' + (r
- 10);
525 if(CreateDirectoryW(dir_path
, 0)) {
526 /* The following is OK because we generated an
527 * 8 character directory name made from characters
528 * [A-Z0-9], which are equivalent for all code
529 * pages and for UTF-16
531 for (k
= 0; k
< 8; ++k
)
532 header
->directory_data
[i
].name
[k
] = dir_name
[k
];
535 /* Give up. The most likely cause of this
536 * is a full disk, but whatever the cause
537 * is, it should be more than apparent that
540 UnmapViewOfFile(header
);
541 CloseHandle(mapping
);
542 return GetLastError();
547 UnmapViewOfFile(header
);
548 CloseHandle(container
->mapping
);
549 container
->mapping
= mapping
;
550 container
->file_size
= file_size
;
551 return ERROR_SUCCESS
;
554 static BOOL
cache_container_is_valid(urlcache_header
*header
, DWORD file_size
)
556 DWORD allocation_size
, count_bits
, i
;
558 if(file_size
< FILE_SIZE(MIN_BLOCK_NO
))
561 if(file_size
!= header
->size
)
564 if (!memcmp(header
->signature
, urlcache_ver_prefix
, sizeof(urlcache_ver_prefix
)-1) &&
565 memcmp(header
->signature
+sizeof(urlcache_ver_prefix
)-1, urlcache_ver
, sizeof(urlcache_ver
)-1))
568 if(FILE_SIZE(header
->capacity_in_blocks
) != file_size
)
572 for(i
=0; i
<header
->capacity_in_blocks
/8; i
++) {
573 for(count_bits
= header
->allocation_table
[i
]; count_bits
!=0; count_bits
>>=1) {
578 if(allocation_size
!= header
->blocks_in_use
)
581 for(; i
<ALLOCATION_TABLE_SIZE
; i
++) {
582 if(header
->allocation_table
[i
])
589 /***********************************************************************
590 * cache_container_open_index (Internal)
592 * Opens the index file and saves mapping handle
595 * ERROR_SUCCESS if succeeded
596 * Any other Win32 error code if failed
599 static DWORD
cache_container_open_index(cache_container
*container
, DWORD blocks_no
)
601 static const WCHAR index_dat
[] = {'i','n','d','e','x','.','d','a','t',0};
604 WCHAR index_path
[MAX_PATH
];
608 WaitForSingleObject(container
->mutex
, INFINITE
);
610 if(container
->mapping
) {
611 ReleaseMutex(container
->mutex
);
612 return ERROR_SUCCESS
;
615 strcpyW(index_path
, container
->path
);
616 strcatW(index_path
, index_dat
);
618 file
= CreateFileW(index_path
, GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_ALWAYS
, 0, NULL
);
619 if(file
== INVALID_HANDLE_VALUE
) {
620 /* Maybe the directory wasn't there? Try to create it */
621 if(CreateDirectoryW(container
->path
, 0))
622 file
= CreateFileW(index_path
, GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_ALWAYS
, 0, NULL
);
624 if(file
== INVALID_HANDLE_VALUE
) {
625 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(index_path
));
626 ReleaseMutex(container
->mutex
);
627 return GetLastError();
630 file_size
= GetFileSize(file
, NULL
);
631 if(file_size
== INVALID_FILE_SIZE
) {
633 ReleaseMutex(container
->mutex
);
634 return GetLastError();
637 if(blocks_no
< MIN_BLOCK_NO
)
638 blocks_no
= MIN_BLOCK_NO
;
639 else if(blocks_no
> MAX_BLOCK_NO
)
640 blocks_no
= MAX_BLOCK_NO
;
642 if(file_size
< FILE_SIZE(blocks_no
)) {
643 DWORD ret
= cache_container_set_size(container
, file
, blocks_no
);
645 ReleaseMutex(container
->mutex
);
649 container
->file_size
= file_size
;
650 container
->mapping
= cache_container_map_index(file
, container
->path
, file_size
, &validate
);
652 if(container
->mapping
&& validate
) {
653 urlcache_header
*header
= MapViewOfFile(container
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
655 if(header
&& !cache_container_is_valid(header
, file_size
)) {
656 WARN("detected old or broken index.dat file\n");
657 UnmapViewOfFile(header
);
658 FreeUrlCacheSpaceW(container
->path
, 100, 0);
660 UnmapViewOfFile(header
);
662 CloseHandle(container
->mapping
);
663 container
->mapping
= NULL
;
667 if(!container
->mapping
)
669 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
670 ReleaseMutex(container
->mutex
);
671 return GetLastError();
674 ReleaseMutex(container
->mutex
);
675 return ERROR_SUCCESS
;
678 /***********************************************************************
679 * cache_container_close_index (Internal)
687 static void cache_container_close_index(cache_container
*pContainer
)
689 CloseHandle(pContainer
->mapping
);
690 pContainer
->mapping
= NULL
;
693 static BOOL
cache_containers_add(const char *cache_prefix
, LPCWSTR path
,
694 DWORD default_entry_type
, LPWSTR mutex_name
)
696 cache_container
*pContainer
= heap_alloc(sizeof(cache_container
));
697 int cache_prefix_len
= strlen(cache_prefix
);
704 pContainer
->mapping
= NULL
;
705 pContainer
->file_size
= 0;
706 pContainer
->default_entry_type
= default_entry_type
;
708 pContainer
->path
= heap_strdupW(path
);
709 if (!pContainer
->path
)
711 heap_free(pContainer
);
715 pContainer
->cache_prefix
= heap_alloc(cache_prefix_len
+1);
716 if (!pContainer
->cache_prefix
)
718 heap_free(pContainer
->path
);
719 heap_free(pContainer
);
723 memcpy(pContainer
->cache_prefix
, cache_prefix
, cache_prefix_len
+1);
725 CharLowerW(mutex_name
);
726 cache_container_create_object_name(mutex_name
, '!');
728 if ((pContainer
->mutex
= CreateMutexW(NULL
, FALSE
, mutex_name
)) == NULL
)
730 ERR("couldn't create mutex (error is %d)\n", GetLastError());
731 heap_free(pContainer
->path
);
732 heap_free(pContainer
);
736 list_add_head(&UrlContainers
, &pContainer
->entry
);
741 static void cache_container_delete_container(cache_container
*pContainer
)
743 list_remove(&pContainer
->entry
);
745 cache_container_close_index(pContainer
);
746 CloseHandle(pContainer
->mutex
);
747 heap_free(pContainer
->path
);
748 heap_free(pContainer
->cache_prefix
);
749 heap_free(pContainer
);
752 static void cache_containers_init(void)
754 static const WCHAR UrlSuffix
[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
755 static const WCHAR HistorySuffix
[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
756 static const WCHAR CookieSuffix
[] = {0};
758 static const WCHAR UserProfile
[] = {'U','S','E','R','P','R','O','F','I','L','E',0};
761 int nFolder
; /* CSIDL_* constant */
762 const WCHAR
*shpath_suffix
; /* suffix on path returned by SHGetSpecialFolderPath */
763 const char *cache_prefix
; /* prefix used to reference the container */
764 DWORD default_entry_type
;
765 } DefaultContainerData
[] =
767 { CSIDL_INTERNET_CACHE
, UrlSuffix
, "", NORMAL_CACHE_ENTRY
},
768 { CSIDL_HISTORY
, HistorySuffix
, "Visited:", URLHISTORY_CACHE_ENTRY
},
769 { CSIDL_COOKIES
, CookieSuffix
, "Cookie:", COOKIE_CACHE_ENTRY
},
774 if (GetEnvironmentVariableW(UserProfile
, NULL
, 0) == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND
)
776 ERR("Environment variable 'USERPROFILE' does not exist!\n");
780 for (i
= 0; i
< sizeof(DefaultContainerData
) / sizeof(DefaultContainerData
[0]); i
++)
782 WCHAR wszCachePath
[MAX_PATH
];
783 WCHAR wszMutexName
[MAX_PATH
];
784 int path_len
, suffix_len
;
787 if (!SHGetSpecialFolderPathW(NULL
, wszCachePath
, DefaultContainerData
[i
].nFolder
, TRUE
))
789 ERR("Couldn't get path for default container %u\n", i
);
792 path_len
= strlenW(wszCachePath
);
793 suffix_len
= strlenW(DefaultContainerData
[i
].shpath_suffix
);
795 if (path_len
+ suffix_len
+ 2 > MAX_PATH
)
797 ERR("Path too long\n");
801 wszCachePath
[path_len
] = '\\';
802 wszCachePath
[path_len
+1] = 0;
804 strcpyW(wszMutexName
, wszCachePath
);
808 memcpy(wszCachePath
+ path_len
+ 1, DefaultContainerData
[i
].shpath_suffix
, (suffix_len
+ 1) * sizeof(WCHAR
));
809 wszCachePath
[path_len
+ suffix_len
+ 1] = '\\';
810 wszCachePath
[path_len
+ suffix_len
+ 2] = '\0';
813 if (!WideCharToMultiByte(CP_ACP
, WC_NO_BEST_FIT_CHARS
, wszCachePath
, path_len
,
814 NULL
, 0, NULL
, &def_char
) || def_char
)
818 /* cannot convert path to ANSI code page */
819 if (!(path_len
= GetShortPathNameW(wszCachePath
, tmp
, MAX_PATH
)) ||
820 !WideCharToMultiByte(CP_ACP
, WC_NO_BEST_FIT_CHARS
, tmp
, path_len
,
821 NULL
, 0, NULL
, &def_char
) || def_char
)
822 ERR("Can't create container path accessible by ANSI functions\n");
824 memcpy(wszCachePath
, tmp
, (path_len
+1)*sizeof(WCHAR
));
827 cache_containers_add(DefaultContainerData
[i
].cache_prefix
, wszCachePath
,
828 DefaultContainerData
[i
].default_entry_type
, wszMutexName
);
832 bDefaultContainersAdded
= TRUE
;
836 static void cache_containers_free(void)
838 while(!list_empty(&UrlContainers
))
839 cache_container_delete_container(
840 LIST_ENTRY(list_head(&UrlContainers
), cache_container
, entry
)
844 static DWORD
cache_containers_find(const char *url
, cache_container
**ret
)
846 cache_container
*container
;
848 TRACE("searching for prefix for URL: %s\n", debugstr_a(url
));
851 return ERROR_INVALID_PARAMETER
;
855 if (!bDefaultContainersAdded
)
856 cache_containers_init();
859 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
861 int prefix_len
= strlen(container
->cache_prefix
);
863 if(!strncmp(container
->cache_prefix
, url
, prefix_len
)) {
864 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
866 return ERROR_SUCCESS
;
870 ERR("no container found\n");
871 return ERROR_FILE_NOT_FOUND
;
874 static BOOL
cache_containers_enum(char *search_pattern
, DWORD index
, cache_container
**ret
)
877 cache_container
*container
;
879 TRACE("searching for prefix: %s\n", debugstr_a(search_pattern
));
881 /* non-NULL search pattern only returns one container ever */
882 if (search_pattern
&& index
> 0)
887 if (!bDefaultContainersAdded
)
888 cache_containers_init();
891 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
895 if (!strcmp(container
->cache_prefix
, search_pattern
))
897 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
906 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
916 /***********************************************************************
917 * cache_container_lock_index (Internal)
919 * Locks the index for system-wide exclusive access.
922 * Cache file header if successful
923 * NULL if failed and calls SetLastError.
925 static urlcache_header
* cache_container_lock_index(cache_container
*pContainer
)
929 urlcache_header
* pHeader
;
933 WaitForSingleObject(pContainer
->mutex
, INFINITE
);
935 pIndexData
= MapViewOfFile(pContainer
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
939 ReleaseMutex(pContainer
->mutex
);
940 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
943 pHeader
= (urlcache_header
*)pIndexData
;
945 /* file has grown - we need to remap to prevent us getting
946 * access violations when we try and access beyond the end
947 * of the memory mapped file */
948 if (pHeader
->size
!= pContainer
->file_size
)
950 UnmapViewOfFile( pHeader
);
951 cache_container_close_index(pContainer
);
952 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
953 if (error
!= ERROR_SUCCESS
)
955 ReleaseMutex(pContainer
->mutex
);
959 pIndexData
= MapViewOfFile(pContainer
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
963 ReleaseMutex(pContainer
->mutex
);
964 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
967 pHeader
= (urlcache_header
*)pIndexData
;
970 TRACE("Signature: %s, file size: %d bytes\n", pHeader
->signature
, pHeader
->size
);
972 for (index
= 0; index
< pHeader
->dirs_no
; index
++)
974 TRACE("Directory[%d] = \"%.8s\"\n", index
, pHeader
->directory_data
[index
].name
);
980 /***********************************************************************
981 * cache_container_unlock_index (Internal)
984 static BOOL
cache_container_unlock_index(cache_container
*pContainer
, urlcache_header
*pHeader
)
987 ReleaseMutex(pContainer
->mutex
);
988 return UnmapViewOfFile(pHeader
);
991 /***********************************************************************
992 * urlcache_create_file_pathW (Internal)
994 * Copies the full path to the specified buffer given the local file
995 * name and the index of the directory it is in. Always sets value in
996 * lpBufferSize to the required buffer size (in bytes).
999 * TRUE if the buffer was big enough
1000 * FALSE if the buffer was too small
1003 static BOOL
urlcache_create_file_pathW(
1004 const cache_container
*pContainer
,
1005 const urlcache_header
*pHeader
,
1006 LPCSTR szLocalFileName
,
1009 LPLONG lpBufferSize
,
1013 int path_len
= strlenW(pContainer
->path
);
1014 int file_name_len
= MultiByteToWideChar(CP_ACP
, 0, szLocalFileName
, -1, NULL
, 0);
1015 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
&& Directory
>=pHeader
->dirs_no
)
1021 nRequired
= (path_len
+ file_name_len
) * sizeof(WCHAR
);
1022 if(Directory
!= CACHE_CONTAINER_NO_SUBDIR
)
1023 nRequired
+= (DIR_LENGTH
+ 1) * sizeof(WCHAR
);
1024 if (trunc_name
&& nRequired
>= *lpBufferSize
)
1025 nRequired
= *lpBufferSize
;
1026 if (nRequired
<= *lpBufferSize
)
1030 memcpy(wszPath
, pContainer
->path
, path_len
* sizeof(WCHAR
));
1031 if (Directory
!= CACHE_CONTAINER_NO_SUBDIR
)
1033 dir_len
= MultiByteToWideChar(CP_ACP
, 0, pHeader
->directory_data
[Directory
].name
, DIR_LENGTH
, wszPath
+ path_len
, DIR_LENGTH
);
1034 wszPath
[dir_len
+ path_len
] = '\\';
1041 MultiByteToWideChar(CP_ACP
, 0, szLocalFileName
, -1, wszPath
+ dir_len
+ path_len
,
1042 *lpBufferSize
/sizeof(WCHAR
)-dir_len
-path_len
);
1043 wszPath
[*lpBufferSize
/sizeof(WCHAR
)-1] = 0;
1044 *lpBufferSize
= nRequired
;
1047 *lpBufferSize
= nRequired
;
1051 /***********************************************************************
1052 * urlcache_create_file_pathA (Internal)
1054 * Copies the full path to the specified buffer given the local file
1055 * name and the index of the directory it is in. Always sets value in
1056 * lpBufferSize to the required buffer size.
1059 * TRUE if the buffer was big enough
1060 * FALSE if the buffer was too small
1063 static BOOL
urlcache_create_file_pathA(
1064 const cache_container
*pContainer
,
1065 const urlcache_header
*pHeader
,
1066 LPCSTR szLocalFileName
,
1069 LPLONG lpBufferSize
)
1072 int path_len
, file_name_len
, dir_len
;
1074 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
&& Directory
>=pHeader
->dirs_no
)
1080 path_len
= WideCharToMultiByte(CP_ACP
, 0, pContainer
->path
, -1, NULL
, 0, NULL
, NULL
) - 1;
1081 file_name_len
= strlen(szLocalFileName
) + 1 /* for nul-terminator */;
1082 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
)
1083 dir_len
= DIR_LENGTH
+1;
1087 nRequired
= (path_len
+ dir_len
+ file_name_len
) * sizeof(char);
1088 if (nRequired
<= *lpBufferSize
)
1090 WideCharToMultiByte(CP_ACP
, 0, pContainer
->path
, -1, szPath
, path_len
, NULL
, NULL
);
1092 memcpy(szPath
+path_len
, pHeader
->directory_data
[Directory
].name
, dir_len
-1);
1093 szPath
[path_len
+ dir_len
-1] = '\\';
1095 memcpy(szPath
+ path_len
+ dir_len
, szLocalFileName
, file_name_len
);
1096 *lpBufferSize
= nRequired
;
1099 *lpBufferSize
= nRequired
;
1103 /* Just like FileTimeToDosDateTime, except that it also maps the special
1104 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1106 static void file_time_to_dos_date_time(const FILETIME
*ft
, WORD
*fatdate
,
1109 if (!ft
->dwLowDateTime
&& !ft
->dwHighDateTime
)
1110 *fatdate
= *fattime
= 0;
1112 FileTimeToDosDateTime(ft
, fatdate
, fattime
);
1115 /***********************************************************************
1116 * urlcache_delete_file (Internal)
1118 static DWORD
urlcache_delete_file(const cache_container
*container
,
1119 urlcache_header
*header
, entry_url
*url_entry
)
1121 WIN32_FILE_ATTRIBUTE_DATA attr
;
1122 WCHAR path
[MAX_PATH
];
1123 LONG path_size
= sizeof(path
);
1127 if(!url_entry
->local_name_off
)
1130 if(!urlcache_create_file_pathW(container
, header
,
1131 (LPCSTR
)url_entry
+url_entry
->local_name_off
,
1132 url_entry
->cache_dir
, path
, &path_size
, FALSE
))
1135 if(!GetFileAttributesExW(path
, GetFileExInfoStandard
, &attr
))
1137 file_time_to_dos_date_time(&attr
.ftLastWriteTime
, &date
, &time
);
1138 if(date
!= url_entry
->write_date
|| time
!= url_entry
->write_time
)
1141 err
= (DeleteFileW(path
) ? ERROR_SUCCESS
: GetLastError());
1142 if(err
== ERROR_ACCESS_DENIED
|| err
== ERROR_SHARING_VIOLATION
)
1146 if (url_entry
->cache_dir
< header
->dirs_no
)
1148 if (header
->directory_data
[url_entry
->cache_dir
].files_no
)
1149 header
->directory_data
[url_entry
->cache_dir
].files_no
--;
1151 if (url_entry
->cache_entry_type
& STICKY_CACHE_ENTRY
)
1153 if (url_entry
->size
.QuadPart
< header
->exempt_usage
.QuadPart
)
1154 header
->exempt_usage
.QuadPart
-= url_entry
->size
.QuadPart
;
1156 header
->exempt_usage
.QuadPart
= 0;
1160 if (url_entry
->size
.QuadPart
< header
->cache_usage
.QuadPart
)
1161 header
->cache_usage
.QuadPart
-= url_entry
->size
.QuadPart
;
1163 header
->cache_usage
.QuadPart
= 0;
1166 return ERROR_SUCCESS
;
1169 static BOOL
urlcache_clean_leaked_entries(cache_container
*container
, urlcache_header
*header
)
1174 leak_off
= &header
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
];
1176 entry_url
*url_entry
= (entry_url
*)((LPBYTE
)header
+ *leak_off
);
1178 if(SUCCEEDED(urlcache_delete_file(container
, header
, url_entry
))) {
1179 *leak_off
= url_entry
->exempt_delta
;
1180 urlcache_entry_free(header
, &url_entry
->header
);
1183 leak_off
= &url_entry
->exempt_delta
;
1190 /***********************************************************************
1191 * cache_container_clean_index (Internal)
1193 * This function is meant to make place in index file by removing leaked
1194 * files entries and resizing the file.
1196 * CAUTION: file view may get mapped to new memory
1199 * ERROR_SUCCESS when new memory is available
1200 * error code otherwise
1202 static DWORD
cache_container_clean_index(cache_container
*container
, urlcache_header
**file_view
)
1204 urlcache_header
*header
= *file_view
;
1207 TRACE("(%s %s)\n", debugstr_a(container
->cache_prefix
), debugstr_w(container
->path
));
1209 if(urlcache_clean_leaked_entries(container
, header
))
1210 return ERROR_SUCCESS
;
1212 if(header
->size
>= ALLOCATION_TABLE_SIZE
*8*BLOCKSIZE
+ ENTRY_START_OFFSET
) {
1213 WARN("index file has maximal size\n");
1214 return ERROR_NOT_ENOUGH_MEMORY
;
1217 cache_container_close_index(container
);
1218 ret
= cache_container_open_index(container
, header
->capacity_in_blocks
*2);
1219 if(ret
!= ERROR_SUCCESS
)
1221 header
= MapViewOfFile(container
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
1223 return GetLastError();
1225 UnmapViewOfFile(*file_view
);
1226 *file_view
= header
;
1227 return ERROR_SUCCESS
;
1230 /* Just like DosDateTimeToFileTime, except that it also maps the special
1231 * case of a DOS date/time of (0,0) to a filetime of (0,0).
1233 static void dos_date_time_to_file_time(WORD fatdate
, WORD fattime
,
1236 if (!fatdate
&& !fattime
)
1237 ft
->dwLowDateTime
= ft
->dwHighDateTime
= 0;
1239 DosDateTimeToFileTime(fatdate
, fattime
, ft
);
1242 static int urlcache_decode_url(const char *url
, WCHAR
*decoded_url
, int decoded_len
)
1245 DWORD len
, part_len
;
1248 memset(&uc
, 0, sizeof(uc
));
1249 uc
.dwStructSize
= sizeof(uc
);
1250 uc
.dwHostNameLength
= 1;
1251 if(!InternetCrackUrlA(url
, 0, 0, &uc
))
1252 uc
.nScheme
= INTERNET_SCHEME_UNKNOWN
;
1254 if(uc
.nScheme
!=INTERNET_SCHEME_HTTP
&& uc
.nScheme
!=INTERNET_SCHEME_HTTPS
)
1255 return MultiByteToWideChar(CP_UTF8
, 0, url
, -1, decoded_url
, decoded_len
);
1260 len
= MultiByteToWideChar(CP_UTF8
, 0, url
, uc
.lpszHostName
-url
, decoded_url
, decoded_len
);
1266 host_name
= heap_alloc(uc
.dwHostNameLength
*sizeof(WCHAR
));
1269 if(!MultiByteToWideChar(CP_UTF8
, 0, uc
.lpszHostName
, uc
.dwHostNameLength
,
1270 host_name
, uc
.dwHostNameLength
)) {
1271 heap_free(host_name
);
1274 part_len
= IdnToUnicode(0, host_name
, uc
.dwHostNameLength
,
1275 decoded_url
? decoded_url
+len
: NULL
, decoded_len
);
1276 heap_free(host_name
);
1278 SetLastError(ERROR_INTERNET_INVALID_URL
);
1283 decoded_len
-= part_len
;
1285 part_len
= MultiByteToWideChar(CP_UTF8
, 0,
1286 uc
.lpszHostName
+uc
.dwHostNameLength
,
1287 -1, decoded_url
? decoded_url
+len
: NULL
, decoded_len
);
1295 /***********************************************************************
1296 * urlcache_copy_entry (Internal)
1298 * Copies an entry from the cache index file to the Win32 structure
1301 * ERROR_SUCCESS if the buffer was big enough
1302 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1305 static DWORD
urlcache_copy_entry(cache_container
*container
, const urlcache_header
*header
,
1306 INTERNET_CACHE_ENTRY_INFOA
*entry_info
, DWORD
*info_size
, const entry_url
*url_entry
, BOOL unicode
)
1309 DWORD size
= sizeof(*entry_info
);
1311 if(*info_size
>= size
) {
1312 entry_info
->lpHeaderInfo
= NULL
;
1313 entry_info
->lpszFileExtension
= NULL
;
1314 entry_info
->lpszLocalFileName
= NULL
;
1315 entry_info
->lpszSourceUrlName
= NULL
;
1316 entry_info
->CacheEntryType
= url_entry
->cache_entry_type
;
1317 entry_info
->u
.dwExemptDelta
= url_entry
->exempt_delta
;
1318 entry_info
->dwHeaderInfoSize
= url_entry
->header_info_size
;
1319 entry_info
->dwHitRate
= url_entry
->hit_rate
;
1320 entry_info
->dwSizeHigh
= url_entry
->size
.u
.HighPart
;
1321 entry_info
->dwSizeLow
= url_entry
->size
.u
.LowPart
;
1322 entry_info
->dwStructSize
= sizeof(*entry_info
);
1323 entry_info
->dwUseCount
= url_entry
->use_count
;
1324 dos_date_time_to_file_time(url_entry
->expire_date
, url_entry
->expire_time
, &entry_info
->ExpireTime
);
1325 entry_info
->LastAccessTime
= url_entry
->access_time
;
1326 entry_info
->LastModifiedTime
= url_entry
->modification_time
;
1327 dos_date_time_to_file_time(url_entry
->sync_date
, url_entry
->sync_time
, &entry_info
->LastSyncTime
);
1331 url_len
= urlcache_decode_url((const char*)url_entry
+url_entry
->url_off
, NULL
, 0);
1333 url_len
= strlen((LPCSTR
)url_entry
+url_entry
->url_off
) + 1;
1334 size
+= url_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1336 if(*info_size
>= size
) {
1337 DWORD url_size
= url_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1339 entry_info
->lpszSourceUrlName
= (LPSTR
)entry_info
+size
-url_size
;
1341 urlcache_decode_url((const char*)url_entry
+url_entry
->url_off
, (WCHAR
*)entry_info
->lpszSourceUrlName
, url_len
);
1343 memcpy(entry_info
->lpszSourceUrlName
, (LPCSTR
)url_entry
+url_entry
->url_off
, url_size
);
1346 if(size
%4 && size
<*info_size
)
1347 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1348 size
= DWORD_ALIGN(size
);
1350 if(url_entry
->local_name_off
) {
1351 LONG file_name_size
;
1353 file_name
= (LPSTR
)entry_info
+size
;
1354 file_name_size
= *info_size
-size
;
1355 if((unicode
&& urlcache_create_file_pathW(container
, header
, (LPCSTR
)url_entry
+url_entry
->local_name_off
,
1356 url_entry
->cache_dir
, (LPWSTR
)file_name
, &file_name_size
, FALSE
)) ||
1357 (!unicode
&& urlcache_create_file_pathA(container
, header
, (LPCSTR
)url_entry
+url_entry
->local_name_off
,
1358 url_entry
->cache_dir
, file_name
, &file_name_size
))) {
1359 entry_info
->lpszLocalFileName
= file_name
;
1361 size
+= file_name_size
;
1363 if(size
%4 && size
<*info_size
)
1364 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1365 size
= DWORD_ALIGN(size
);
1368 if(url_entry
->header_info_off
) {
1372 header_len
= MultiByteToWideChar(CP_UTF8
, 0, (const char*)url_entry
+url_entry
->header_info_off
,
1373 url_entry
->header_info_size
, NULL
, 0);
1375 header_len
= url_entry
->header_info_size
;
1376 size
+= header_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1378 if(*info_size
>= size
) {
1379 DWORD header_size
= header_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1380 entry_info
->lpHeaderInfo
= (LPBYTE
)entry_info
+size
-header_size
;
1382 MultiByteToWideChar(CP_UTF8
, 0, (const char*)url_entry
+url_entry
->header_info_off
,
1383 url_entry
->header_info_size
, (LPWSTR
)entry_info
->lpHeaderInfo
, header_len
);
1385 memcpy(entry_info
->lpHeaderInfo
, (LPCSTR
)url_entry
+url_entry
->header_info_off
, header_len
);
1387 if(size
%4 && size
<*info_size
)
1388 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1389 size
= DWORD_ALIGN(size
);
1392 if(url_entry
->file_extension_off
) {
1396 ext_len
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)url_entry
+url_entry
->file_extension_off
, -1, NULL
, 0);
1398 ext_len
= strlen((LPCSTR
)url_entry
+url_entry
->file_extension_off
) + 1;
1399 size
+= ext_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1401 if(*info_size
>= size
) {
1402 DWORD ext_size
= ext_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1403 entry_info
->lpszFileExtension
= (LPSTR
)entry_info
+size
-ext_size
;
1405 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)url_entry
+url_entry
->file_extension_off
, -1, (LPWSTR
)entry_info
->lpszFileExtension
, ext_len
);
1407 memcpy(entry_info
->lpszFileExtension
, (LPCSTR
)url_entry
+url_entry
->file_extension_off
, ext_len
*sizeof(CHAR
));
1410 if(size
%4 && size
<*info_size
)
1411 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1412 size
= DWORD_ALIGN(size
);
1415 if(size
> *info_size
) {
1417 return ERROR_INSUFFICIENT_BUFFER
;
1420 return ERROR_SUCCESS
;
1423 /***********************************************************************
1424 * urlcache_set_entry_info (Internal)
1426 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1427 * according to the flags set by field_control.
1430 * ERROR_SUCCESS if the buffer was big enough
1431 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1434 static DWORD
urlcache_set_entry_info(entry_url
*url_entry
, const INTERNET_CACHE_ENTRY_INFOA
*entry_info
, DWORD field_control
)
1436 if (field_control
& CACHE_ENTRY_ACCTIME_FC
)
1437 url_entry
->access_time
= entry_info
->LastAccessTime
;
1438 if (field_control
& CACHE_ENTRY_ATTRIBUTE_FC
)
1439 url_entry
->cache_entry_type
= entry_info
->CacheEntryType
;
1440 if (field_control
& CACHE_ENTRY_EXEMPT_DELTA_FC
)
1441 url_entry
->exempt_delta
= entry_info
->u
.dwExemptDelta
;
1442 if (field_control
& CACHE_ENTRY_EXPTIME_FC
)
1443 file_time_to_dos_date_time(&entry_info
->ExpireTime
, &url_entry
->expire_date
, &url_entry
->expire_time
);
1444 if (field_control
& CACHE_ENTRY_HEADERINFO_FC
)
1445 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1446 if (field_control
& CACHE_ENTRY_HITRATE_FC
)
1447 url_entry
->hit_rate
= entry_info
->dwHitRate
;
1448 if (field_control
& CACHE_ENTRY_MODTIME_FC
)
1449 url_entry
->modification_time
= entry_info
->LastModifiedTime
;
1450 if (field_control
& CACHE_ENTRY_SYNCTIME_FC
)
1451 file_time_to_dos_date_time(&entry_info
->LastAccessTime
, &url_entry
->sync_date
, &url_entry
->sync_time
);
1453 return ERROR_SUCCESS
;
1456 /***********************************************************************
1457 * urlcache_hash_key (Internal)
1459 * Returns the hash key for a given string
1462 * hash key for the string
1465 static DWORD
urlcache_hash_key(LPCSTR lpszKey
)
1467 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1468 * but the algorithm and result are not the same!
1470 static const unsigned char lookupTable
[256] =
1472 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1473 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1474 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1475 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1476 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1477 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1478 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1479 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1480 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1481 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1482 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1483 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1484 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1485 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1486 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1487 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1488 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1489 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1490 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1491 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1492 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1493 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1494 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1495 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1496 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1497 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1498 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1499 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1500 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1501 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1502 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1503 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1508 for (i
= 0; i
< sizeof(key
) / sizeof(key
[0]); i
++)
1509 key
[i
] = lookupTable
[(*lpszKey
+ i
) & 0xFF];
1511 for (lpszKey
++; *lpszKey
; lpszKey
++)
1513 for (i
= 0; i
< sizeof(key
) / sizeof(key
[0]); i
++)
1514 key
[i
] = lookupTable
[*lpszKey
^ key
[i
]];
1517 return *(DWORD
*)key
;
1520 static inline entry_hash_table
* urlcache_get_hash_table(const urlcache_header
*pHeader
, DWORD dwOffset
)
1524 return (entry_hash_table
*)((LPBYTE
)pHeader
+ dwOffset
);
1527 static BOOL
urlcache_find_hash_entry(const urlcache_header
*pHeader
, LPCSTR lpszUrl
, struct hash_entry
**ppHashEntry
)
1529 /* structure of hash table:
1530 * 448 entries divided into 64 blocks
1531 * each block therefore contains a chain of 7 key/offset pairs
1532 * how position in table is calculated:
1533 * 1. the url is hashed in helper function
1534 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1535 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1538 * there can be multiple hash tables in the file and the offset to
1539 * the next one is stored in the header of the hash table
1541 DWORD key
= urlcache_hash_key(lpszUrl
);
1542 DWORD offset
= (key
& (HASHTABLE_NUM_ENTRIES
-1)) * HASHTABLE_BLOCKSIZE
;
1543 entry_hash_table
* pHashEntry
;
1546 key
>>= HASHTABLE_FLAG_BITS
;
1548 for (pHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1549 pHashEntry
; pHashEntry
= urlcache_get_hash_table(pHeader
, pHashEntry
->next
))
1552 if (pHashEntry
->id
!= id
++)
1554 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry
->id
, id
);
1557 /* make sure that it is in fact a hash entry */
1558 if (pHashEntry
->header
.signature
!= HASH_SIGNATURE
)
1560 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&pHashEntry
->header
.signature
);
1564 for (i
= 0; i
< HASHTABLE_BLOCKSIZE
; i
++)
1566 struct hash_entry
*pHashElement
= &pHashEntry
->hash_table
[offset
+ i
];
1567 if (key
== pHashElement
->key
>>HASHTABLE_FLAG_BITS
)
1569 /* FIXME: we should make sure that this is the right element
1570 * before returning and claiming that it is. We can do this
1571 * by doing a simple compare between the URL we were given
1572 * and the URL stored in the entry. However, this assumes
1573 * we know the format of all the entries stored in the
1575 *ppHashEntry
= pHashElement
;
1583 /***********************************************************************
1584 * urlcache_hash_entry_set_flags (Internal)
1586 * Sets special bits in hash key
1592 static void urlcache_hash_entry_set_flags(struct hash_entry
*pHashEntry
, DWORD dwFlag
)
1594 pHashEntry
->key
= (pHashEntry
->key
>> HASHTABLE_FLAG_BITS
<< HASHTABLE_FLAG_BITS
) | dwFlag
;
1597 /***********************************************************************
1598 * urlcache_hash_entry_delete (Internal)
1600 * Searches all the hash tables in the index for the given URL and
1601 * then if found deletes the entry.
1604 * TRUE if the entry was found
1605 * FALSE if the entry could not be found
1608 static BOOL
urlcache_hash_entry_delete(struct hash_entry
*pHashEntry
)
1610 pHashEntry
->key
= HASHTABLE_DEL
;
1614 /***********************************************************************
1615 * urlcache_hash_entry_create (Internal)
1617 * Searches all the hash tables for a free slot based on the offset
1618 * generated from the hash key. If a free slot is found, the offset and
1619 * key are entered into the hash table.
1622 * ERROR_SUCCESS if the entry was added
1623 * Any other Win32 error code if the entry could not be added
1626 static DWORD
urlcache_hash_entry_create(urlcache_header
*pHeader
, LPCSTR lpszUrl
, DWORD dwOffsetEntry
, DWORD dwFieldType
)
1628 /* see urlcache_find_hash_entry for structure of hash tables */
1630 DWORD key
= urlcache_hash_key(lpszUrl
);
1631 DWORD offset
= (key
& (HASHTABLE_NUM_ENTRIES
-1)) * HASHTABLE_BLOCKSIZE
;
1632 entry_hash_table
* pHashEntry
, *pHashPrev
= NULL
;
1636 key
= ((key
>> HASHTABLE_FLAG_BITS
) << HASHTABLE_FLAG_BITS
) + dwFieldType
;
1638 for (pHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1639 pHashEntry
; pHashEntry
= urlcache_get_hash_table(pHeader
, pHashEntry
->next
))
1642 pHashPrev
= pHashEntry
;
1644 if (pHashEntry
->id
!= id
++)
1646 ERR("not right hash table number (%d) expected %d\n", pHashEntry
->id
, id
);
1649 /* make sure that it is in fact a hash entry */
1650 if (pHashEntry
->header
.signature
!= HASH_SIGNATURE
)
1652 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&pHashEntry
->header
.signature
);
1656 for (i
= 0; i
< HASHTABLE_BLOCKSIZE
; i
++)
1658 struct hash_entry
*pHashElement
= &pHashEntry
->hash_table
[offset
+ i
];
1659 if (pHashElement
->key
==HASHTABLE_FREE
|| pHashElement
->key
==HASHTABLE_DEL
) /* if the slot is free */
1661 pHashElement
->key
= key
;
1662 pHashElement
->offset
= dwOffsetEntry
;
1663 return ERROR_SUCCESS
;
1667 error
= urlcache_create_hash_table(pHeader
, pHashPrev
, &pHashEntry
);
1668 if (error
!= ERROR_SUCCESS
)
1671 pHashEntry
->hash_table
[offset
].key
= key
;
1672 pHashEntry
->hash_table
[offset
].offset
= dwOffsetEntry
;
1673 return ERROR_SUCCESS
;
1676 /***********************************************************************
1677 * urlcache_enum_hash_tables (Internal)
1679 * Enumerates the hash tables in a container.
1682 * TRUE if an entry was found
1683 * FALSE if there are no more tables to enumerate.
1686 static BOOL
urlcache_enum_hash_tables(const urlcache_header
*pHeader
, DWORD
*id
, entry_hash_table
**ppHashEntry
)
1688 for (*ppHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1689 *ppHashEntry
; *ppHashEntry
= urlcache_get_hash_table(pHeader
, (*ppHashEntry
)->next
))
1691 TRACE("looking at hash table number %d\n", (*ppHashEntry
)->id
);
1692 if ((*ppHashEntry
)->id
!= *id
)
1694 /* make sure that it is in fact a hash entry */
1695 if ((*ppHashEntry
)->header
.signature
!= HASH_SIGNATURE
)
1697 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&(*ppHashEntry
)->header
.signature
);
1702 TRACE("hash table number %d found\n", *id
);
1708 /***********************************************************************
1709 * urlcache_enum_hash_table_entries (Internal)
1711 * Enumerates entries in a hash table and returns the next non-free entry.
1714 * TRUE if an entry was found
1715 * FALSE if the hash table is empty or there are no more entries to
1719 static BOOL
urlcache_enum_hash_table_entries(const urlcache_header
*pHeader
, const entry_hash_table
*pHashEntry
,
1720 DWORD
* index
, const struct hash_entry
**ppHashEntry
)
1722 for (; *index
< HASHTABLE_SIZE
; (*index
)++)
1724 if (pHashEntry
->hash_table
[*index
].key
==HASHTABLE_FREE
|| pHashEntry
->hash_table
[*index
].key
==HASHTABLE_DEL
)
1727 *ppHashEntry
= &pHashEntry
->hash_table
[*index
];
1728 TRACE("entry found %d\n", *index
);
1731 TRACE("no more entries (%d)\n", *index
);
1735 /***********************************************************************
1736 * cache_container_delete_dir (Internal)
1738 * Erase a directory containing an URL cache.
1741 * TRUE success, FALSE failure/aborted.
1744 static BOOL
cache_container_delete_dir(LPCWSTR lpszPath
)
1747 WCHAR path
[MAX_PATH
+ 1];
1748 SHFILEOPSTRUCTW shfos
;
1751 path_len
= strlenW(lpszPath
);
1752 if (path_len
>= MAX_PATH
)
1754 strcpyW(path
, lpszPath
);
1755 path
[path_len
+ 1] = 0; /* double-NUL-terminate path */
1758 shfos
.wFunc
= FO_DELETE
;
1761 shfos
.fFlags
= FOF_NOCONFIRMATION
;
1762 shfos
.fAnyOperationsAborted
= FALSE
;
1763 ret
= SHFileOperationW(&shfos
);
1765 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path
), ret
);
1766 return !(ret
|| shfos
.fAnyOperationsAborted
);
1769 /***********************************************************************
1770 * urlcache_hash_entry_is_locked (Internal)
1772 * Checks if entry is locked. Unlocks it if possible.
1774 static BOOL
urlcache_hash_entry_is_locked(struct hash_entry
*hash_entry
, entry_url
*url_entry
)
1777 ULARGE_INTEGER acc_time
, time
;
1779 if ((hash_entry
->key
& ((1<<HASHTABLE_FLAG_BITS
)-1)) != HASHTABLE_LOCK
)
1782 GetSystemTimeAsFileTime(&cur_time
);
1783 time
.u
.LowPart
= cur_time
.dwLowDateTime
;
1784 time
.u
.HighPart
= cur_time
.dwHighDateTime
;
1786 acc_time
.u
.LowPart
= url_entry
->access_time
.dwLowDateTime
;
1787 acc_time
.u
.HighPart
= url_entry
->access_time
.dwHighDateTime
;
1789 time
.QuadPart
-= acc_time
.QuadPart
;
1791 /* check if entry was locked for at least a day */
1792 if(time
.QuadPart
> (ULONGLONG
)24*60*60*FILETIME_SECOND
) {
1793 urlcache_hash_entry_set_flags(hash_entry
, HASHTABLE_URL
);
1794 url_entry
->use_count
= 0;
1801 static BOOL
urlcache_get_entry_info(const char *url
, void *entry_info
,
1802 DWORD
*size
, DWORD flags
, BOOL unicode
)
1804 urlcache_header
*header
;
1805 struct hash_entry
*hash_entry
;
1806 const entry_url
*url_entry
;
1807 cache_container
*container
;
1810 TRACE("(%s, %p, %p, %x, %x)\n", debugstr_a(url
), entry_info
, size
, flags
, unicode
);
1812 if(flags
& ~GET_INSTALLED_ENTRY
)
1813 FIXME("ignoring unsupported flags: %x\n", flags
);
1815 error
= cache_containers_find(url
, &container
);
1816 if(error
!= ERROR_SUCCESS
) {
1817 SetLastError(error
);
1821 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
1822 if(error
!= ERROR_SUCCESS
) {
1823 SetLastError(error
);
1827 if(!(header
= cache_container_lock_index(container
)))
1830 if(!urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
1831 cache_container_unlock_index(container
, header
);
1832 WARN("entry %s not found!\n", debugstr_a(url
));
1833 SetLastError(ERROR_FILE_NOT_FOUND
);
1837 url_entry
= (const entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
1838 if(url_entry
->header
.signature
!= URL_SIGNATURE
) {
1839 cache_container_unlock_index(container
, header
);
1840 FIXME("Trying to retrieve entry of unknown format %s\n",
1841 debugstr_an((LPCSTR
)&url_entry
->header
.signature
, sizeof(DWORD
)));
1842 SetLastError(ERROR_FILE_NOT_FOUND
);
1846 TRACE("Found URL: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->url_off
));
1847 TRACE("Header info: %s\n", debugstr_an((LPCSTR
)url_entry
+
1848 url_entry
->header_info_off
, url_entry
->header_info_size
));
1850 if((flags
& GET_INSTALLED_ENTRY
) && !(url_entry
->cache_entry_type
& INSTALLED_CACHE_ENTRY
)) {
1851 cache_container_unlock_index(container
, header
);
1852 SetLastError(ERROR_FILE_NOT_FOUND
);
1860 error
= urlcache_copy_entry(container
, header
, entry_info
, size
, url_entry
, unicode
);
1861 if(error
!= ERROR_SUCCESS
) {
1862 cache_container_unlock_index(container
, header
);
1863 SetLastError(error
);
1866 if(url_entry
->local_name_off
)
1867 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->local_name_off
));
1870 cache_container_unlock_index(container
, header
);
1874 /***********************************************************************
1875 * GetUrlCacheEntryInfoExA (WININET.@)
1878 BOOL WINAPI
GetUrlCacheEntryInfoExA(LPCSTR lpszUrl
,
1879 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1880 LPDWORD lpdwCacheEntryInfoBufSize
, LPSTR lpszReserved
,
1881 LPDWORD lpdwReserved
, LPVOID lpReserved
, DWORD dwFlags
)
1883 if(lpszReserved
!=NULL
|| lpdwReserved
!=NULL
|| lpReserved
!=NULL
) {
1884 ERR("Reserved value was not 0\n");
1885 SetLastError(ERROR_INVALID_PARAMETER
);
1889 return urlcache_get_entry_info(lpszUrl
, lpCacheEntryInfo
,
1890 lpdwCacheEntryInfoBufSize
, dwFlags
, FALSE
);
1893 /***********************************************************************
1894 * GetUrlCacheEntryInfoA (WININET.@)
1897 BOOL WINAPI
GetUrlCacheEntryInfoA(LPCSTR lpszUrlName
,
1898 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1899 LPDWORD lpdwCacheEntryInfoBufferSize
)
1901 return GetUrlCacheEntryInfoExA(lpszUrlName
, lpCacheEntryInfo
,
1902 lpdwCacheEntryInfoBufferSize
, NULL
, NULL
, NULL
, 0);
1905 static int urlcache_encode_url(const WCHAR
*url
, char *encoded_url
, int encoded_len
)
1908 DWORD len
, part_len
;
1911 TRACE("%s\n", debugstr_w(url
));
1913 memset(&uc
, 0, sizeof(uc
));
1914 uc
.dwStructSize
= sizeof(uc
);
1915 uc
.dwHostNameLength
= 1;
1916 if(!InternetCrackUrlW(url
, 0, 0, &uc
))
1917 uc
.nScheme
= INTERNET_SCHEME_UNKNOWN
;
1919 if(uc
.nScheme
!=INTERNET_SCHEME_HTTP
&& uc
.nScheme
!=INTERNET_SCHEME_HTTPS
)
1920 return WideCharToMultiByte(CP_UTF8
, 0, url
, -1, encoded_url
, encoded_len
, NULL
, NULL
);
1922 len
= WideCharToMultiByte(CP_UTF8
, 0, url
, uc
.lpszHostName
-url
,
1923 encoded_url
, encoded_len
, NULL
, NULL
);
1929 part_len
= IdnToAscii(0, uc
.lpszHostName
, uc
.dwHostNameLength
, NULL
, 0);
1931 SetLastError(ERROR_INTERNET_INVALID_URL
);
1935 punycode
= heap_alloc(part_len
*sizeof(WCHAR
));
1939 part_len
= IdnToAscii(0, uc
.lpszHostName
, uc
.dwHostNameLength
, punycode
, part_len
);
1941 heap_free(punycode
);
1945 part_len
= WideCharToMultiByte(CP_UTF8
, 0, punycode
, part_len
,
1946 encoded_url
? encoded_url
+len
: NULL
, encoded_len
, NULL
, NULL
);
1947 heap_free(punycode
);
1951 encoded_len
-= part_len
;
1954 part_len
= WideCharToMultiByte(CP_UTF8
, 0, uc
.lpszHostName
+uc
.dwHostNameLength
,
1955 -1, encoded_url
? encoded_url
+len
: NULL
, encoded_len
, NULL
, NULL
);
1960 TRACE("got (%d)%s\n", len
, debugstr_a(encoded_url
));
1964 static BOOL
urlcache_encode_url_alloc(const WCHAR
*url
, char **encoded_url
)
1969 encoded_len
= urlcache_encode_url(url
, NULL
, 0);
1973 ret
= heap_alloc(encoded_len
*sizeof(WCHAR
));
1977 encoded_len
= urlcache_encode_url(url
, ret
, encoded_len
);
1987 /***********************************************************************
1988 * GetUrlCacheEntryInfoExW (WININET.@)
1991 BOOL WINAPI
GetUrlCacheEntryInfoExW(LPCWSTR lpszUrl
,
1992 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
1993 LPDWORD lpdwCacheEntryInfoBufSize
, LPWSTR lpszReserved
,
1994 LPDWORD lpdwReserved
, LPVOID lpReserved
, DWORD dwFlags
)
1999 if(lpszReserved
!=NULL
|| lpdwReserved
!=NULL
|| lpReserved
!=NULL
) {
2000 ERR("Reserved value was not 0\n");
2001 SetLastError(ERROR_INVALID_PARAMETER
);
2005 /* Ignore GET_INSTALLED_ENTRY flag in unicode version of function */
2006 dwFlags
&= ~GET_INSTALLED_ENTRY
;
2008 if(!urlcache_encode_url_alloc(lpszUrl
, &url
))
2011 ret
= urlcache_get_entry_info(url
, lpCacheEntryInfo
,
2012 lpdwCacheEntryInfoBufSize
, dwFlags
, TRUE
);
2017 /***********************************************************************
2018 * GetUrlCacheEntryInfoW (WININET.@)
2021 BOOL WINAPI
GetUrlCacheEntryInfoW(LPCWSTR lpszUrl
,
2022 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
2023 LPDWORD lpdwCacheEntryInfoBufferSize
)
2025 return GetUrlCacheEntryInfoExW(lpszUrl
, lpCacheEntryInfo
,
2026 lpdwCacheEntryInfoBufferSize
, NULL
, NULL
, NULL
, 0);
2029 /***********************************************************************
2030 * SetUrlCacheEntryInfoA (WININET.@)
2032 BOOL WINAPI
SetUrlCacheEntryInfoA(LPCSTR lpszUrlName
,
2033 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
2034 DWORD dwFieldControl
)
2036 urlcache_header
*pHeader
;
2037 struct hash_entry
*pHashEntry
;
2038 entry_header
*pEntry
;
2039 cache_container
*pContainer
;
2042 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName
), lpCacheEntryInfo
, dwFieldControl
);
2044 error
= cache_containers_find(lpszUrlName
, &pContainer
);
2045 if (error
!= ERROR_SUCCESS
)
2047 SetLastError(error
);
2051 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
2052 if (error
!= ERROR_SUCCESS
)
2054 SetLastError(error
);
2058 if (!(pHeader
= cache_container_lock_index(pContainer
)))
2061 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
2063 cache_container_unlock_index(pContainer
, pHeader
);
2064 WARN("entry %s not found!\n", debugstr_a(lpszUrlName
));
2065 SetLastError(ERROR_FILE_NOT_FOUND
);
2069 pEntry
= (entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2070 if (pEntry
->signature
!= URL_SIGNATURE
)
2072 cache_container_unlock_index(pContainer
, pHeader
);
2073 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR
)&pEntry
->signature
, sizeof(DWORD
)));
2074 SetLastError(ERROR_FILE_NOT_FOUND
);
2078 urlcache_set_entry_info((entry_url
*)pEntry
, lpCacheEntryInfo
, dwFieldControl
);
2080 cache_container_unlock_index(pContainer
, pHeader
);
2085 /***********************************************************************
2086 * SetUrlCacheEntryInfoW (WININET.@)
2088 BOOL WINAPI
SetUrlCacheEntryInfoW(LPCWSTR lpszUrl
,
2089 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
2090 DWORD dwFieldControl
)
2095 if(!urlcache_encode_url_alloc(lpszUrl
, &url
))
2098 ret
= SetUrlCacheEntryInfoA(url
, (INTERNET_CACHE_ENTRY_INFOA
*)lpCacheEntryInfo
, dwFieldControl
);
2103 static BOOL
urlcache_entry_get_file(const char *url
, void *entry_info
, DWORD
*size
, BOOL unicode
)
2105 urlcache_header
*header
;
2106 struct hash_entry
*hash_entry
;
2107 entry_url
*url_entry
;
2108 cache_container
*container
;
2111 TRACE("(%s, %p, %p, %x)\n", debugstr_a(url
), entry_info
, size
, unicode
);
2113 if(!url
|| !size
|| (!entry_info
&& *size
)) {
2114 SetLastError(ERROR_INVALID_PARAMETER
);
2118 error
= cache_containers_find(url
, &container
);
2119 if(error
!= ERROR_SUCCESS
) {
2120 SetLastError(error
);
2124 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2125 if (error
!= ERROR_SUCCESS
) {
2126 SetLastError(error
);
2130 if (!(header
= cache_container_lock_index(container
)))
2133 if (!urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
2134 cache_container_unlock_index(container
, header
);
2135 TRACE("entry %s not found!\n", debugstr_a(url
));
2136 SetLastError(ERROR_FILE_NOT_FOUND
);
2140 url_entry
= (entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
2141 if(url_entry
->header
.signature
!= URL_SIGNATURE
) {
2142 cache_container_unlock_index(container
, header
);
2143 FIXME("Trying to retrieve entry of unknown format %s\n",
2144 debugstr_an((LPSTR
)&url_entry
->header
.signature
, sizeof(DWORD
)));
2145 SetLastError(ERROR_FILE_NOT_FOUND
);
2149 if(!url_entry
->local_name_off
) {
2150 cache_container_unlock_index(container
, header
);
2151 SetLastError(ERROR_INVALID_DATA
);
2155 TRACE("Found URL: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->url_off
));
2156 TRACE("Header info: %s\n", debugstr_an((LPCSTR
)url_entry
+ url_entry
->header_info_off
,
2157 url_entry
->header_info_size
));
2159 error
= urlcache_copy_entry(container
, header
, entry_info
,
2160 size
, url_entry
, unicode
);
2161 if(error
!= ERROR_SUCCESS
) {
2162 cache_container_unlock_index(container
, header
);
2163 SetLastError(error
);
2166 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->local_name_off
));
2168 url_entry
->hit_rate
++;
2169 url_entry
->use_count
++;
2170 urlcache_hash_entry_set_flags(hash_entry
, HASHTABLE_LOCK
);
2171 GetSystemTimeAsFileTime(&url_entry
->access_time
);
2173 cache_container_unlock_index(container
, header
);
2178 /***********************************************************************
2179 * RetrieveUrlCacheEntryFileA (WININET.@)
2182 BOOL WINAPI
RetrieveUrlCacheEntryFileA(LPCSTR lpszUrlName
,
2183 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
2184 LPDWORD lpdwCacheEntryInfoBufferSize
, DWORD dwReserved
)
2186 return urlcache_entry_get_file(lpszUrlName
, lpCacheEntryInfo
,
2187 lpdwCacheEntryInfoBufferSize
, FALSE
);
2190 /***********************************************************************
2191 * RetrieveUrlCacheEntryFileW (WININET.@)
2194 BOOL WINAPI
RetrieveUrlCacheEntryFileW(LPCWSTR lpszUrlName
,
2195 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
2196 LPDWORD lpdwCacheEntryInfoBufferSize
, DWORD dwReserved
)
2201 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
2204 ret
= urlcache_entry_get_file(url
, lpCacheEntryInfo
,
2205 lpdwCacheEntryInfoBufferSize
, TRUE
);
2210 static BOOL
urlcache_entry_delete(const cache_container
*pContainer
,
2211 urlcache_header
*pHeader
, struct hash_entry
*pHashEntry
)
2213 entry_header
*pEntry
;
2214 entry_url
* pUrlEntry
;
2216 pEntry
= (entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2217 if (pEntry
->signature
!= URL_SIGNATURE
)
2219 FIXME("Trying to delete entry of unknown format %s\n",
2220 debugstr_an((LPCSTR
)&pEntry
->signature
, sizeof(DWORD
)));
2221 SetLastError(ERROR_FILE_NOT_FOUND
);
2225 pUrlEntry
= (entry_url
*)pEntry
;
2226 if(urlcache_hash_entry_is_locked(pHashEntry
, pUrlEntry
))
2228 TRACE("Trying to delete locked entry\n");
2229 pUrlEntry
->cache_entry_type
|= PENDING_DELETE_CACHE_ENTRY
;
2230 SetLastError(ERROR_SHARING_VIOLATION
);
2234 if(!urlcache_delete_file(pContainer
, pHeader
, pUrlEntry
))
2236 urlcache_entry_free(pHeader
, pEntry
);
2240 /* Add entry to leaked files list */
2241 pUrlEntry
->header
.signature
= LEAK_SIGNATURE
;
2242 pUrlEntry
->exempt_delta
= pHeader
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
];
2243 pHeader
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
] = pHashEntry
->offset
;
2246 urlcache_hash_entry_delete(pHashEntry
);
2250 static HANDLE free_cache_running
;
2251 static HANDLE dll_unload_event
;
2252 static DWORD WINAPI
handle_full_cache_worker(void *param
)
2254 FreeUrlCacheSpaceW(NULL
, 20, 0);
2255 ReleaseSemaphore(free_cache_running
, 1, NULL
);
2259 static void handle_full_cache(void)
2261 if(WaitForSingleObject(free_cache_running
, 0) == WAIT_OBJECT_0
) {
2262 if(!QueueUserWorkItem(handle_full_cache_worker
, NULL
, 0))
2263 ReleaseSemaphore(free_cache_running
, 1, NULL
);
2267 /* Enumerates entries in cache, allows cache unlocking between calls. */
2268 static BOOL
urlcache_next_entry(urlcache_header
*header
, DWORD
*hash_table_off
, DWORD
*hash_table_entry
,
2269 struct hash_entry
**hash_entry
, entry_header
**entry
)
2271 entry_hash_table
*hashtable_entry
;
2276 if(!*hash_table_off
) {
2277 *hash_table_off
= header
->hash_table_off
;
2278 *hash_table_entry
= 0;
2280 hashtable_entry
= urlcache_get_hash_table(header
, *hash_table_off
);
2282 if(*hash_table_off
>= header
->size
) {
2283 *hash_table_off
= 0;
2287 hashtable_entry
= urlcache_get_hash_table(header
, *hash_table_off
);
2290 if(hashtable_entry
->header
.signature
!= HASH_SIGNATURE
) {
2291 *hash_table_off
= 0;
2296 if(*hash_table_entry
>= HASHTABLE_SIZE
) {
2297 *hash_table_off
= hashtable_entry
->next
;
2298 if(!*hash_table_off
) {
2299 *hash_table_off
= 0;
2303 hashtable_entry
= urlcache_get_hash_table(header
, *hash_table_off
);
2304 *hash_table_entry
= 0;
2307 if(hashtable_entry
->hash_table
[*hash_table_entry
].key
!= HASHTABLE_DEL
&&
2308 hashtable_entry
->hash_table
[*hash_table_entry
].key
!= HASHTABLE_FREE
) {
2309 *hash_entry
= &hashtable_entry
->hash_table
[*hash_table_entry
];
2310 *entry
= (entry_header
*)((LPBYTE
)header
+ hashtable_entry
->hash_table
[*hash_table_entry
].offset
);
2311 (*hash_table_entry
)++;
2315 (*hash_table_entry
)++;
2318 *hash_table_off
= 0;
2322 /* Rates an urlcache entry to determine if it can be deleted.
2324 * Score 0 means that entry can safely be removed, the bigger rating
2325 * the smaller chance of entry being removed.
2326 * DWORD_MAX means that entry can't be deleted at all.
2328 * Rating system is currently not fully compatible with native implementation.
2330 static DWORD
urlcache_rate_entry(entry_url
*url_entry
, FILETIME
*cur_time
)
2332 ULARGE_INTEGER time
, access_time
;
2335 access_time
.u
.LowPart
= url_entry
->access_time
.dwLowDateTime
;
2336 access_time
.u
.HighPart
= url_entry
->access_time
.dwHighDateTime
;
2338 time
.u
.LowPart
= cur_time
->dwLowDateTime
;
2339 time
.u
.HighPart
= cur_time
->dwHighDateTime
;
2341 /* Don't touch entries that were added less than 10 minutes ago */
2342 if(time
.QuadPart
< access_time
.QuadPart
+ (ULONGLONG
)10*60*FILETIME_SECOND
)
2345 if(url_entry
->cache_entry_type
& STICKY_CACHE_ENTRY
)
2346 if(time
.QuadPart
< access_time
.QuadPart
+ (ULONGLONG
)url_entry
->exempt_delta
*FILETIME_SECOND
)
2349 time
.QuadPart
= (time
.QuadPart
-access_time
.QuadPart
)/FILETIME_SECOND
;
2350 rating
= 400*60*60*24/(60*60*24+time
.QuadPart
);
2352 if(url_entry
->hit_rate
> 100)
2355 rating
+= url_entry
->hit_rate
;
2360 static int dword_cmp(const void *p1
, const void *p2
)
2362 return *(const DWORD
*)p1
- *(const DWORD
*)p2
;
2365 /***********************************************************************
2366 * FreeUrlCacheSpaceW (WININET.@)
2368 * Frees up some cache.
2371 * cache_path [I] Which volume to free up from, or NULL if you don't care.
2372 * size [I] Percentage of the cache that should be free.
2373 * filter [I] Which entries can't be deleted (CacheEntryType)
2376 * TRUE success. FALSE failure.
2379 * This implementation just retrieves the path of the cache directory, and
2380 * deletes its contents from the filesystem. The correct approach would
2381 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
2383 BOOL WINAPI
FreeUrlCacheSpaceW(LPCWSTR cache_path
, DWORD size
, DWORD filter
)
2385 cache_container
*container
;
2386 DWORD path_len
, err
;
2388 TRACE("(%s, %x, %x)\n", debugstr_w(cache_path
), size
, filter
);
2390 if(size
<1 || size
>100) {
2391 SetLastError(ERROR_INVALID_PARAMETER
);
2396 path_len
= strlenW(cache_path
);
2397 if(cache_path
[path_len
-1] == '\\')
2403 if(size
==100 && !filter
) {
2404 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
2406 /* When cache_path==NULL only clean Temporary Internet Files */
2407 if((!path_len
&& container
->cache_prefix
[0]==0) ||
2408 (path_len
&& !strncmpiW(container
->path
, cache_path
, path_len
) &&
2409 (container
->path
[path_len
]=='\0' || container
->path
[path_len
]=='\\')))
2413 WaitForSingleObject(container
->mutex
, INFINITE
);
2415 /* unlock, delete, recreate and lock cache */
2416 cache_container_close_index(container
);
2417 ret_del
= cache_container_delete_dir(container
->path
);
2418 err
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2420 ReleaseMutex(container
->mutex
);
2421 if(!ret_del
|| (err
!= ERROR_SUCCESS
))
2429 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
2431 urlcache_header
*header
;
2432 struct hash_entry
*hash_entry
;
2433 entry_header
*entry
;
2434 entry_url
*url_entry
;
2435 ULONGLONG desired_size
, cur_size
;
2436 DWORD delete_factor
, hash_table_off
, hash_table_entry
;
2437 DWORD rate
[100], rate_no
;
2440 if((path_len
|| container
->cache_prefix
[0]!=0) &&
2441 (!path_len
|| strncmpiW(container
->path
, cache_path
, path_len
) ||
2442 (container
->path
[path_len
]!='\0' && container
->path
[path_len
]!='\\')))
2445 err
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2446 if(err
!= ERROR_SUCCESS
)
2449 header
= cache_container_lock_index(container
);
2453 urlcache_clean_leaked_entries(container
, header
);
2455 desired_size
= header
->cache_limit
.QuadPart
*(100-size
)/100;
2456 cur_size
= header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
;
2457 if(cur_size
<= desired_size
)
2460 delete_factor
= (cur_size
-desired_size
)*100/cur_size
;
2462 if(!delete_factor
) {
2463 cache_container_unlock_index(container
, header
);
2468 hash_table_entry
= 0;
2470 GetSystemTimeAsFileTime(&cur_time
);
2471 while(rate_no
<sizeof(rate
)/sizeof(*rate
) &&
2472 urlcache_next_entry(header
, &hash_table_off
, &hash_table_entry
, &hash_entry
, &entry
)) {
2473 if(entry
->signature
!= URL_SIGNATURE
) {
2474 WARN("only url entries are currently supported\n");
2478 url_entry
= (entry_url
*)entry
;
2479 if(url_entry
->cache_entry_type
& filter
)
2482 rate
[rate_no
] = urlcache_rate_entry(url_entry
, &cur_time
);
2483 if(rate
[rate_no
] != -1)
2488 TRACE("nothing to delete\n");
2489 cache_container_unlock_index(container
, header
);
2493 qsort(rate
, rate_no
, sizeof(DWORD
), dword_cmp
);
2495 delete_factor
= delete_factor
*rate_no
/100;
2496 delete_factor
= rate
[delete_factor
];
2497 TRACE("deleting files with rating %d or less\n", delete_factor
);
2500 while(urlcache_next_entry(header
, &hash_table_off
, &hash_table_entry
, &hash_entry
, &entry
)) {
2501 if(entry
->signature
!= URL_SIGNATURE
)
2504 url_entry
= (entry_url
*)entry
;
2505 if(url_entry
->cache_entry_type
& filter
)
2508 if(urlcache_rate_entry(url_entry
, &cur_time
) <= delete_factor
) {
2509 TRACE("deleting file: %s\n", debugstr_a((char*)url_entry
+url_entry
->local_name_off
));
2510 urlcache_entry_delete(container
, header
, hash_entry
);
2512 if(header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
<= desired_size
)
2515 /* Allow other threads to use cache while cleaning */
2516 cache_container_unlock_index(container
, header
);
2517 if(WaitForSingleObject(dll_unload_event
, 0) == WAIT_OBJECT_0
) {
2518 TRACE("got dll_unload_event - finishing\n");
2522 header
= cache_container_lock_index(container
);
2526 TRACE("cache size after cleaning 0x%s/0x%s\n",
2527 wine_dbgstr_longlong(header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
),
2528 wine_dbgstr_longlong(header
->cache_limit
.QuadPart
));
2529 cache_container_unlock_index(container
, header
);
2535 /***********************************************************************
2536 * FreeUrlCacheSpaceA (WININET.@)
2538 * See FreeUrlCacheSpaceW.
2540 BOOL WINAPI
FreeUrlCacheSpaceA(LPCSTR lpszCachePath
, DWORD dwSize
, DWORD dwFilter
)
2543 LPWSTR path
= heap_strdupAtoW(lpszCachePath
);
2544 if (lpszCachePath
== NULL
|| path
!= NULL
)
2545 ret
= FreeUrlCacheSpaceW(path
, dwSize
, dwFilter
);
2550 /***********************************************************************
2551 * UnlockUrlCacheEntryFileA (WININET.@)
2554 BOOL WINAPI
UnlockUrlCacheEntryFileA(LPCSTR lpszUrlName
, DWORD dwReserved
)
2556 urlcache_header
*pHeader
;
2557 struct hash_entry
*pHashEntry
;
2558 entry_header
*pEntry
;
2559 entry_url
* pUrlEntry
;
2560 cache_container
*pContainer
;
2563 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName
), dwReserved
);
2567 ERR("dwReserved != 0\n");
2568 SetLastError(ERROR_INVALID_PARAMETER
);
2572 error
= cache_containers_find(lpszUrlName
, &pContainer
);
2573 if (error
!= ERROR_SUCCESS
)
2575 SetLastError(error
);
2579 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
2580 if (error
!= ERROR_SUCCESS
)
2582 SetLastError(error
);
2586 if (!(pHeader
= cache_container_lock_index(pContainer
)))
2589 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
2591 cache_container_unlock_index(pContainer
, pHeader
);
2592 TRACE("entry %s not found!\n", debugstr_a(lpszUrlName
));
2593 SetLastError(ERROR_FILE_NOT_FOUND
);
2597 pEntry
= (entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2598 if (pEntry
->signature
!= URL_SIGNATURE
)
2600 cache_container_unlock_index(pContainer
, pHeader
);
2601 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR
)&pEntry
->signature
, sizeof(DWORD
)));
2602 SetLastError(ERROR_FILE_NOT_FOUND
);
2606 pUrlEntry
= (entry_url
*)pEntry
;
2608 if (pUrlEntry
->use_count
== 0)
2610 cache_container_unlock_index(pContainer
, pHeader
);
2613 pUrlEntry
->use_count
--;
2614 if (!pUrlEntry
->use_count
)
2616 urlcache_hash_entry_set_flags(pHashEntry
, HASHTABLE_URL
);
2617 if (pUrlEntry
->cache_entry_type
& PENDING_DELETE_CACHE_ENTRY
)
2618 urlcache_entry_delete(pContainer
, pHeader
, pHashEntry
);
2621 cache_container_unlock_index(pContainer
, pHeader
);
2626 /***********************************************************************
2627 * UnlockUrlCacheEntryFileW (WININET.@)
2630 BOOL WINAPI
UnlockUrlCacheEntryFileW(LPCWSTR lpszUrlName
, DWORD dwReserved
)
2635 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
2638 ret
= UnlockUrlCacheEntryFileA(url
, dwReserved
);
2643 static BOOL
urlcache_entry_create(const char *url
, const char *ext
, WCHAR
*full_path
)
2645 cache_container
*container
;
2646 urlcache_header
*header
;
2647 char file_name
[MAX_PATH
];
2648 WCHAR extW
[MAX_PATH
];
2650 LONG full_path_len
, ext_len
= 0;
2651 BOOL generate_name
= FALSE
;
2658 TRACE("(%s, %s, %p)\n", debugstr_a(url
), debugstr_a(ext
), full_path
);
2660 memset(&uc
, 0, sizeof(uc
));
2661 uc
.dwStructSize
= sizeof(uc
);
2662 uc
.dwUrlPathLength
= 1;
2663 uc
.dwExtraInfoLength
= 1;
2664 if(!InternetCrackUrlA(url
, 0, 0, &uc
))
2665 uc
.dwUrlPathLength
= 0;
2667 if(!uc
.dwUrlPathLength
) {
2672 p
= e
= uc
.lpszUrlPath
+uc
.dwUrlPathLength
;
2673 while(p
>uc
.lpszUrlPath
&& *(p
-1)!='/' && *(p
-1)!='\\' && *(p
-1)!='.')
2675 if(p
>uc
.lpszUrlPath
&& *(p
-1)=='.') {
2677 while(p
>uc
.lpszUrlPath
&& *(p
-1)!='/' && *(p
-1)!='\\')
2683 memcpy(file_name
, p
, e
-p
);
2686 for(p
=file_name
; *p
; p
++) {
2699 generate_name
= TRUE
;
2701 error
= cache_containers_find(url
, &container
);
2702 if(error
!= ERROR_SUCCESS
) {
2703 SetLastError(error
);
2707 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2708 if(error
!= ERROR_SUCCESS
) {
2709 SetLastError(error
);
2713 if(!(header
= cache_container_lock_index(container
)))
2717 cache_dir
= (BYTE
)(rand() % header
->dirs_no
);
2719 cache_dir
= CACHE_CONTAINER_NO_SUBDIR
;
2721 full_path_len
= MAX_PATH
* sizeof(WCHAR
);
2722 if(!urlcache_create_file_pathW(container
, header
, file_name
, cache_dir
, full_path
, &full_path_len
, TRUE
)) {
2723 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2724 debugstr_a(file_name
), full_path_len
);
2725 cache_container_unlock_index(container
, header
);
2728 full_path_len
= full_path_len
/sizeof(WCHAR
) - 1;
2730 cache_container_unlock_index(container
, header
);
2736 ext_len
= MultiByteToWideChar(CP_ACP
, 0, ext
, -1, extW
+1, MAX_PATH
-1);
2738 for(p
=extW
; *p
; p
++) {
2748 if(p
[-1]==' ' || p
[-1]=='.')
2754 if(!generate_name
&& full_path_len
+5+ext_len
>=MAX_PATH
) { /* strlen("[255]") = 5 */
2755 full_path_len
= MAX_PATH
-5-ext_len
-1;
2758 for(i
=0; i
<255 && !generate_name
; i
++) {
2759 static const WCHAR format
[] = {'[','%','u',']','%','s',0};
2761 wsprintfW(full_path
+full_path_len
, format
, i
, extW
);
2763 TRACE("Trying: %s\n", debugstr_w(full_path
));
2764 file
= CreateFileW(full_path
, GENERIC_READ
, 0, NULL
, CREATE_NEW
, 0, NULL
);
2765 if(file
!= INVALID_HANDLE_VALUE
) {
2771 if(full_path_len
+8+ext_len
>= MAX_PATH
)
2772 full_path_len
= MAX_PATH
-8-ext_len
-1;
2774 /* Try to generate random name */
2775 GetSystemTimeAsFileTime(&ft
);
2776 strcpyW(full_path
+full_path_len
+8, extW
);
2778 for(i
=0; i
<255; i
++) {
2780 ULONGLONG n
= ft
.dwHighDateTime
;
2782 n
+= ft
.dwLowDateTime
;
2783 n
^= (ULONGLONG
)i
<<48;
2785 for(j
=0; j
<8; j
++) {
2788 full_path
[full_path_len
+j
] = (r
< 10 ? '0' + r
: 'A' + r
- 10);
2791 TRACE("Trying: %s\n", debugstr_w(full_path
));
2792 file
= CreateFileW(full_path
, GENERIC_READ
, 0, NULL
, CREATE_NEW
, 0, NULL
);
2793 if(file
!= INVALID_HANDLE_VALUE
) {
2799 WARN("Could not find a unique filename\n");
2803 /***********************************************************************
2804 * CreateUrlCacheEntryA (WININET.@)
2807 BOOL WINAPI
CreateUrlCacheEntryA(LPCSTR lpszUrlName
, DWORD dwExpectedFileSize
,
2808 LPCSTR lpszFileExtension
, LPSTR lpszFileName
, DWORD dwReserved
)
2810 WCHAR file_name
[MAX_PATH
];
2813 FIXME("dwReserved 0x%08x\n", dwReserved
);
2815 if(!urlcache_entry_create(lpszUrlName
, lpszFileExtension
, file_name
))
2818 if(!WideCharToMultiByte(CP_ACP
, 0, file_name
, -1, lpszFileName
, MAX_PATH
, NULL
, NULL
))
2822 /***********************************************************************
2823 * CreateUrlCacheEntryW (WININET.@)
2826 BOOL WINAPI
CreateUrlCacheEntryW(LPCWSTR lpszUrlName
, DWORD dwExpectedFileSize
,
2827 LPCWSTR lpszFileExtension
, LPWSTR lpszFileName
, DWORD dwReserved
)
2829 char *url
, *ext
= NULL
;
2833 FIXME("dwReserved 0x%08x\n", dwReserved
);
2835 if(lpszFileExtension
) {
2836 ext
= heap_strdupWtoUTF8(lpszFileExtension
);
2841 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
)) {
2846 ret
= urlcache_entry_create(url
, ext
, lpszFileName
);
2852 static BOOL
urlcache_entry_commit(const char *url
, const WCHAR
*file_name
,
2853 FILETIME expire_time
, FILETIME modify_time
, DWORD entry_type
,
2854 BYTE
*header_info
, DWORD header_size
, const char *file_ext
,
2855 const char *original_url
)
2857 cache_container
*container
;
2858 urlcache_header
*header
;
2859 struct hash_entry
*hash_entry
;
2860 entry_header
*entry
;
2861 entry_url
*url_entry
;
2862 DWORD url_entry_offset
;
2863 DWORD size
= DWORD_ALIGN(sizeof(*url_entry
));
2864 DWORD file_name_off
= 0;
2865 DWORD header_info_off
= 0;
2866 DWORD file_ext_off
= 0;
2867 WIN32_FILE_ATTRIBUTE_DATA file_attr
;
2868 LARGE_INTEGER file_size
;
2870 char file_name_no_container
[MAX_PATH
];
2871 char *local_file_name
= 0;
2873 DWORD exempt_delta
= 0;
2876 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n", debugstr_a(url
), debugstr_w(file_name
),
2877 entry_type
, header_info
, header_size
, debugstr_a(file_ext
), debugstr_a(original_url
));
2879 if(entry_type
& STICKY_CACHE_ENTRY
&& !file_name
) {
2880 SetLastError(ERROR_INVALID_PARAMETER
);
2884 WARN(": original_url ignored\n");
2886 memset(&file_attr
, 0, sizeof(file_attr
));
2888 if(!GetFileAttributesExW(file_name
, GetFileExInfoStandard
, &file_attr
))
2891 file_size
.u
.LowPart
= file_attr
.nFileSizeLow
;
2892 file_size
.u
.HighPart
= file_attr
.nFileSizeHigh
;
2894 error
= cache_containers_find(url
, &container
);
2895 if(error
!= ERROR_SUCCESS
) {
2896 SetLastError(error
);
2900 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2901 if(error
!= ERROR_SUCCESS
) {
2902 SetLastError(error
);
2906 if(!(header
= cache_container_lock_index(container
)))
2909 if(urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
2910 entry_url
*url_entry
= (entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
2912 if(urlcache_hash_entry_is_locked(hash_entry
, url_entry
)) {
2913 TRACE("Trying to overwrite locked entry\n");
2914 cache_container_unlock_index(container
, header
);
2915 SetLastError(ERROR_SHARING_VIOLATION
);
2919 hit_rate
= url_entry
->hit_rate
;
2920 exempt_delta
= url_entry
->exempt_delta
;
2921 urlcache_entry_delete(container
, header
, hash_entry
);
2927 dir_id
= CACHE_CONTAINER_NO_SUBDIR
;
2930 BOOL bFound
= FALSE
;
2932 if(strncmpW(file_name
, container
->path
, lstrlenW(container
->path
))) {
2933 ERR("path %s must begin with cache content path %s\n", debugstr_w(file_name
), debugstr_w(container
->path
));
2934 cache_container_unlock_index(container
, header
);
2935 SetLastError(ERROR_INVALID_PARAMETER
);
2939 /* skip container path prefix */
2940 file_name
+= lstrlenW(container
->path
);
2942 WideCharToMultiByte(CP_ACP
, 0, file_name
, -1, file_name_no_container
, MAX_PATH
, NULL
, NULL
);
2943 local_file_name
= file_name_no_container
;
2945 if(header
->dirs_no
) {
2946 for(dir_id
= 0; dir_id
< header
->dirs_no
; dir_id
++) {
2947 if(!strncmp(header
->directory_data
[dir_id
].name
, local_file_name
, DIR_LENGTH
)) {
2954 ERR("cache directory not found in path %s\n", debugstr_w(file_name
));
2955 cache_container_unlock_index(container
, header
);
2956 SetLastError(ERROR_INVALID_PARAMETER
);
2960 file_name
+= DIR_LENGTH
+ 1;
2961 local_file_name
+= DIR_LENGTH
+ 1;
2965 size
= DWORD_ALIGN(size
+ strlen(url
) + 1);
2967 file_name_off
= size
;
2968 size
= DWORD_ALIGN(size
+ strlen(local_file_name
) + 1);
2970 if(header_info
&& header_size
) {
2971 header_info_off
= size
;
2972 size
= DWORD_ALIGN(size
+ header_size
);
2974 if(file_ext
&& (file_ext_off
= strlen(file_ext
))) {
2975 DWORD len
= file_ext_off
;
2977 file_ext_off
= size
;
2978 size
= DWORD_ALIGN(size
+ len
+ 1);
2981 /* round up to next block */
2982 if(size
% BLOCKSIZE
) {
2983 size
-= size
% BLOCKSIZE
;
2987 error
= urlcache_entry_alloc(header
, size
/ BLOCKSIZE
, &entry
);
2988 while(error
== ERROR_HANDLE_DISK_FULL
) {
2989 error
= cache_container_clean_index(container
, &header
);
2990 if(error
== ERROR_SUCCESS
)
2991 error
= urlcache_entry_alloc(header
, size
/ BLOCKSIZE
, &entry
);
2993 if(error
!= ERROR_SUCCESS
) {
2994 cache_container_unlock_index(container
, header
);
2995 SetLastError(error
);
2999 /* FindFirstFreeEntry fills in blocks used */
3000 url_entry
= (entry_url
*)entry
;
3001 url_entry_offset
= (LPBYTE
)url_entry
- (LPBYTE
)header
;
3002 url_entry
->header
.signature
= URL_SIGNATURE
;
3003 url_entry
->cache_dir
= dir_id
;
3004 url_entry
->cache_entry_type
= entry_type
| container
->default_entry_type
;
3005 url_entry
->header_info_size
= header_size
;
3006 if((entry_type
& STICKY_CACHE_ENTRY
) && !exempt_delta
) {
3007 /* Sticky entries have a default exempt time of one day */
3008 exempt_delta
= 86400;
3010 url_entry
->exempt_delta
= exempt_delta
;
3011 url_entry
->hit_rate
= hit_rate
+1;
3012 url_entry
->file_extension_off
= file_ext_off
;
3013 url_entry
->header_info_off
= header_info_off
;
3014 url_entry
->local_name_off
= file_name_off
;
3015 url_entry
->url_off
= DWORD_ALIGN(sizeof(*url_entry
));
3016 url_entry
->size
.QuadPart
= file_size
.QuadPart
;
3017 url_entry
->use_count
= 0;
3018 GetSystemTimeAsFileTime(&url_entry
->access_time
);
3019 url_entry
->modification_time
= modify_time
;
3020 file_time_to_dos_date_time(&url_entry
->access_time
, &url_entry
->sync_date
, &url_entry
->sync_time
);
3021 file_time_to_dos_date_time(&expire_time
, &url_entry
->expire_date
, &url_entry
->expire_time
);
3022 file_time_to_dos_date_time(&file_attr
.ftLastWriteTime
, &url_entry
->write_date
, &url_entry
->write_time
);
3025 url_entry
->unk1
= 0;
3026 url_entry
->unk2
= 0;
3027 url_entry
->unk3
= 0x60;
3028 url_entry
->unk4
= 0;
3029 url_entry
->unk5
= 0x1010;
3030 url_entry
->unk7
= 0;
3031 url_entry
->unk8
= 0;
3034 strcpy((LPSTR
)url_entry
+ url_entry
->url_off
, url
);
3036 strcpy((LPSTR
)((LPBYTE
)url_entry
+ file_name_off
), local_file_name
);
3038 memcpy((LPBYTE
)url_entry
+ header_info_off
, header_info
, header_size
);
3040 strcpy((LPSTR
)((LPBYTE
)url_entry
+ file_ext_off
), file_ext
);
3042 error
= urlcache_hash_entry_create(header
, url
, url_entry_offset
, HASHTABLE_URL
);
3043 while(error
== ERROR_HANDLE_DISK_FULL
) {
3044 error
= cache_container_clean_index(container
, &header
);
3045 if(error
== ERROR_SUCCESS
) {
3046 url_entry
= (entry_url
*)((LPBYTE
)header
+ url_entry_offset
);
3047 error
= urlcache_hash_entry_create(header
, url
,
3048 url_entry_offset
, HASHTABLE_URL
);
3051 if(error
!= ERROR_SUCCESS
) {
3052 urlcache_entry_free(header
, &url_entry
->header
);
3053 cache_container_unlock_index(container
, header
);
3054 SetLastError(error
);
3058 if(url_entry
->cache_dir
< header
->dirs_no
)
3059 header
->directory_data
[url_entry
->cache_dir
].files_no
++;
3060 if(entry_type
& STICKY_CACHE_ENTRY
)
3061 header
->exempt_usage
.QuadPart
+= file_size
.QuadPart
;
3063 header
->cache_usage
.QuadPart
+= file_size
.QuadPart
;
3064 if(header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
> header
->cache_limit
.QuadPart
)
3065 handle_full_cache();
3067 cache_container_unlock_index(container
, header
);
3071 /***********************************************************************
3072 * CommitUrlCacheEntryA (WININET.@)
3074 BOOL WINAPI
CommitUrlCacheEntryA(LPCSTR lpszUrlName
, LPCSTR lpszLocalFileName
,
3075 FILETIME ExpireTime
, FILETIME LastModifiedTime
, DWORD CacheEntryType
,
3076 LPBYTE lpHeaderInfo
, DWORD dwHeaderSize
, LPCSTR lpszFileExtension
, LPCSTR lpszOriginalUrl
)
3078 WCHAR
*file_name
= NULL
;
3081 if(lpszLocalFileName
) {
3082 file_name
= heap_strdupAtoW(lpszLocalFileName
);
3087 ret
= urlcache_entry_commit(lpszUrlName
, file_name
, ExpireTime
, LastModifiedTime
,
3088 CacheEntryType
, lpHeaderInfo
, dwHeaderSize
, lpszFileExtension
, lpszOriginalUrl
);
3089 heap_free(file_name
);
3093 /***********************************************************************
3094 * CommitUrlCacheEntryW (WININET.@)
3096 BOOL WINAPI
CommitUrlCacheEntryW(LPCWSTR lpszUrlName
, LPCWSTR lpszLocalFileName
,
3097 FILETIME ExpireTime
, FILETIME LastModifiedTime
, DWORD CacheEntryType
,
3098 LPWSTR lpHeaderInfo
, DWORD dwHeaderSize
, LPCWSTR lpszFileExtension
, LPCWSTR lpszOriginalUrl
)
3100 char *url
, *original_url
=NULL
, *file_ext
=NULL
, *header_info
=NULL
;
3103 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
3107 header_info
= heap_strdupWtoUTF8(lpHeaderInfo
);
3112 dwHeaderSize
= strlen(header_info
);
3115 if(lpszFileExtension
) {
3116 file_ext
= heap_strdupWtoA(lpszFileExtension
);
3119 heap_free(header_info
);
3124 if(lpszOriginalUrl
&& !urlcache_encode_url_alloc(lpszOriginalUrl
, &original_url
)) {
3126 heap_free(header_info
);
3127 heap_free(file_ext
);
3131 ret
= urlcache_entry_commit(url
, lpszLocalFileName
, ExpireTime
, LastModifiedTime
,
3132 CacheEntryType
, (BYTE
*)header_info
, dwHeaderSize
, file_ext
, original_url
);
3134 heap_free(header_info
);
3135 heap_free(file_ext
);
3136 heap_free(original_url
);
3140 /***********************************************************************
3141 * ReadUrlCacheEntryStream (WININET.@)
3144 BOOL WINAPI
ReadUrlCacheEntryStream(
3145 IN HANDLE hUrlCacheStream
,
3146 IN DWORD dwLocation
,
3147 IN OUT LPVOID lpBuffer
,
3148 IN OUT LPDWORD lpdwLen
,
3152 /* Get handle to file from 'stream' */
3153 stream_handle
*pStream
= (stream_handle
*)hUrlCacheStream
;
3155 if (dwReserved
!= 0)
3157 ERR("dwReserved != 0\n");
3158 SetLastError(ERROR_INVALID_PARAMETER
);
3162 if (IsBadReadPtr(pStream
, sizeof(*pStream
)) || IsBadStringPtrA(pStream
->url
, INTERNET_MAX_URL_LENGTH
))
3164 SetLastError(ERROR_INVALID_HANDLE
);
3168 if (SetFilePointer(pStream
->file
, dwLocation
, NULL
, FILE_CURRENT
) == INVALID_SET_FILE_POINTER
)
3170 return ReadFile(pStream
->file
, lpBuffer
, *lpdwLen
, lpdwLen
, NULL
);
3173 /***********************************************************************
3174 * RetrieveUrlCacheEntryStreamA (WININET.@)
3177 HANDLE WINAPI
RetrieveUrlCacheEntryStreamA(LPCSTR lpszUrlName
,
3178 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
3179 LPDWORD lpdwCacheEntryInfoBufferSize
, BOOL fRandomRead
, DWORD dwReserved
)
3181 /* NOTE: this is not the same as the way that the native
3182 * version allocates 'stream' handles. I did it this way
3183 * as it is much easier and no applications should depend
3184 * on this behaviour. (Native version appears to allocate
3185 * indices into a table)
3187 stream_handle
*stream
;
3190 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName
), lpCacheEntryInfo
,
3191 lpdwCacheEntryInfoBufferSize
, fRandomRead
, dwReserved
);
3193 if(!RetrieveUrlCacheEntryFileA(lpszUrlName
, lpCacheEntryInfo
,
3194 lpdwCacheEntryInfoBufferSize
, dwReserved
))
3197 file
= CreateFileA(lpCacheEntryInfo
->lpszLocalFileName
, GENERIC_READ
, FILE_SHARE_READ
,
3198 NULL
, OPEN_EXISTING
, fRandomRead
? FILE_FLAG_RANDOM_ACCESS
: 0, NULL
);
3199 if(file
== INVALID_HANDLE_VALUE
) {
3200 UnlockUrlCacheEntryFileA(lpszUrlName
, 0);
3204 /* allocate handle storage space */
3205 stream
= heap_alloc(sizeof(stream_handle
) + strlen(lpszUrlName
) * sizeof(CHAR
));
3208 UnlockUrlCacheEntryFileA(lpszUrlName
, 0);
3209 SetLastError(ERROR_OUTOFMEMORY
);
3213 stream
->file
= file
;
3214 strcpy(stream
->url
, lpszUrlName
);
3218 /***********************************************************************
3219 * RetrieveUrlCacheEntryStreamW (WININET.@)
3222 HANDLE WINAPI
RetrieveUrlCacheEntryStreamW(LPCWSTR lpszUrlName
,
3223 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
3224 LPDWORD lpdwCacheEntryInfoBufferSize
, BOOL fRandomRead
, DWORD dwReserved
)
3227 /* NOTE: this is not the same as the way that the native
3228 * version allocates 'stream' handles. I did it this way
3229 * as it is much easier and no applications should depend
3230 * on this behaviour. (Native version appears to allocate
3231 * indices into a table)
3233 stream_handle
*stream
;
3236 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName
), lpCacheEntryInfo
,
3237 lpdwCacheEntryInfoBufferSize
, fRandomRead
, dwReserved
);
3239 if(!(len
= urlcache_encode_url(lpszUrlName
, NULL
, 0)))
3242 if(!RetrieveUrlCacheEntryFileW(lpszUrlName
, lpCacheEntryInfo
,
3243 lpdwCacheEntryInfoBufferSize
, dwReserved
))
3246 file
= CreateFileW(lpCacheEntryInfo
->lpszLocalFileName
, GENERIC_READ
, FILE_SHARE_READ
,
3247 NULL
, OPEN_EXISTING
, fRandomRead
? FILE_FLAG_RANDOM_ACCESS
: 0, NULL
);
3248 if(file
== INVALID_HANDLE_VALUE
) {
3249 UnlockUrlCacheEntryFileW(lpszUrlName
, 0);
3253 /* allocate handle storage space */
3254 stream
= heap_alloc(sizeof(stream_handle
) + len
*sizeof(WCHAR
));
3257 UnlockUrlCacheEntryFileW(lpszUrlName
, 0);
3258 SetLastError(ERROR_OUTOFMEMORY
);
3262 stream
->file
= file
;
3263 if(!urlcache_encode_url(lpszUrlName
, stream
->url
, len
)) {
3265 UnlockUrlCacheEntryFileW(lpszUrlName
, 0);
3272 /***********************************************************************
3273 * UnlockUrlCacheEntryStream (WININET.@)
3276 BOOL WINAPI
UnlockUrlCacheEntryStream(
3277 IN HANDLE hUrlCacheStream
,
3281 stream_handle
*pStream
= (stream_handle
*)hUrlCacheStream
;
3283 if (dwReserved
!= 0)
3285 ERR("dwReserved != 0\n");
3286 SetLastError(ERROR_INVALID_PARAMETER
);
3290 if (IsBadReadPtr(pStream
, sizeof(*pStream
)) || IsBadStringPtrA(pStream
->url
, INTERNET_MAX_URL_LENGTH
))
3292 SetLastError(ERROR_INVALID_HANDLE
);
3296 if (!UnlockUrlCacheEntryFileA(pStream
->url
, 0))
3299 CloseHandle(pStream
->file
);
3305 /***********************************************************************
3306 * DeleteUrlCacheEntryA (WININET.@)
3309 BOOL WINAPI
DeleteUrlCacheEntryA(LPCSTR lpszUrlName
)
3311 cache_container
*pContainer
;
3312 urlcache_header
*pHeader
;
3313 struct hash_entry
*pHashEntry
;
3317 TRACE("(%s)\n", debugstr_a(lpszUrlName
));
3319 error
= cache_containers_find(lpszUrlName
, &pContainer
);
3320 if (error
!= ERROR_SUCCESS
)
3322 SetLastError(error
);
3326 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
3327 if (error
!= ERROR_SUCCESS
)
3329 SetLastError(error
);
3333 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3336 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
3338 cache_container_unlock_index(pContainer
, pHeader
);
3339 TRACE("entry %s not found!\n", debugstr_a(lpszUrlName
));
3340 SetLastError(ERROR_FILE_NOT_FOUND
);
3344 ret
= urlcache_entry_delete(pContainer
, pHeader
, pHashEntry
);
3346 cache_container_unlock_index(pContainer
, pHeader
);
3351 /***********************************************************************
3352 * DeleteUrlCacheEntryW (WININET.@)
3355 BOOL WINAPI
DeleteUrlCacheEntryW(LPCWSTR lpszUrlName
)
3360 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
3363 ret
= DeleteUrlCacheEntryA(url
);
3368 BOOL WINAPI
DeleteUrlCacheContainerA(DWORD d1
, DWORD d2
)
3370 FIXME("(0x%08x, 0x%08x) stub\n", d1
, d2
);
3374 BOOL WINAPI
DeleteUrlCacheContainerW(DWORD d1
, DWORD d2
)
3376 FIXME("(0x%08x, 0x%08x) stub\n", d1
, d2
);
3380 /***********************************************************************
3381 * CreateCacheContainerA (WININET.@)
3383 BOOL WINAPI
CreateUrlCacheContainerA(DWORD d1
, DWORD d2
, DWORD d3
, DWORD d4
,
3384 DWORD d5
, DWORD d6
, DWORD d7
, DWORD d8
)
3386 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3387 d1
, d2
, d3
, d4
, d5
, d6
, d7
, d8
);
3391 /***********************************************************************
3392 * CreateCacheContainerW (WININET.@)
3394 BOOL WINAPI
CreateUrlCacheContainerW(DWORD d1
, DWORD d2
, DWORD d3
, DWORD d4
,
3395 DWORD d5
, DWORD d6
, DWORD d7
, DWORD d8
)
3397 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3398 d1
, d2
, d3
, d4
, d5
, d6
, d7
, d8
);
3402 /***********************************************************************
3403 * FindFirstUrlCacheContainerA (WININET.@)
3405 HANDLE WINAPI
FindFirstUrlCacheContainerA( LPVOID p1
, LPVOID p2
, LPVOID p3
, DWORD d1
)
3407 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1
, p2
, p3
, d1
);
3411 /***********************************************************************
3412 * FindFirstUrlCacheContainerW (WININET.@)
3414 HANDLE WINAPI
FindFirstUrlCacheContainerW( LPVOID p1
, LPVOID p2
, LPVOID p3
, DWORD d1
)
3416 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1
, p2
, p3
, d1
);
3420 /***********************************************************************
3421 * FindNextUrlCacheContainerA (WININET.@)
3423 BOOL WINAPI
FindNextUrlCacheContainerA( HANDLE handle
, LPVOID p1
, LPVOID p2
)
3425 FIXME("(%p, %p, %p) stub\n", handle
, p1
, p2
);
3429 /***********************************************************************
3430 * FindNextUrlCacheContainerW (WININET.@)
3432 BOOL WINAPI
FindNextUrlCacheContainerW( HANDLE handle
, LPVOID p1
, LPVOID p2
)
3434 FIXME("(%p, %p, %p) stub\n", handle
, p1
, p2
);
3438 HANDLE WINAPI
FindFirstUrlCacheEntryExA(
3439 LPCSTR lpszUrlSearchPattern
,
3443 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
,
3444 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3446 LPDWORD pcbReserved2
,
3450 FIXME("(%s, 0x%08x, 0x%08x, 0x%s, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern
),
3451 dwFlags
, dwFilter
, wine_dbgstr_longlong(GroupId
), lpFirstCacheEntryInfo
,
3452 lpdwFirstCacheEntryInfoBufferSize
, lpReserved
, pcbReserved2
,lpReserved3
);
3453 SetLastError(ERROR_FILE_NOT_FOUND
);
3457 HANDLE WINAPI
FindFirstUrlCacheEntryExW(
3458 LPCWSTR lpszUrlSearchPattern
,
3462 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
,
3463 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3465 LPDWORD pcbReserved2
,
3469 FIXME("(%s, 0x%08x, 0x%08x, 0x%s, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern
),
3470 dwFlags
, dwFilter
, wine_dbgstr_longlong(GroupId
), lpFirstCacheEntryInfo
,
3471 lpdwFirstCacheEntryInfoBufferSize
, lpReserved
, pcbReserved2
,lpReserved3
);
3472 SetLastError(ERROR_FILE_NOT_FOUND
);
3476 /***********************************************************************
3477 * FindFirstUrlCacheEntryA (WININET.@)
3480 INTERNETAPI HANDLE WINAPI
FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern
,
3481 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
, LPDWORD lpdwFirstCacheEntryInfoBufferSize
)
3483 find_handle
*pEntryHandle
;
3485 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern
), lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
);
3487 pEntryHandle
= heap_alloc(sizeof(*pEntryHandle
));
3491 pEntryHandle
->magic
= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
;
3492 if (lpszUrlSearchPattern
)
3494 pEntryHandle
->url_search_pattern
= heap_strdupA(lpszUrlSearchPattern
);
3495 if (!pEntryHandle
->url_search_pattern
)
3497 heap_free(pEntryHandle
);
3502 pEntryHandle
->url_search_pattern
= NULL
;
3503 pEntryHandle
->container_idx
= 0;
3504 pEntryHandle
->hash_table_idx
= 0;
3505 pEntryHandle
->hash_entry_idx
= 0;
3507 if (!FindNextUrlCacheEntryA(pEntryHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
))
3509 heap_free(pEntryHandle
);
3512 return pEntryHandle
;
3515 /***********************************************************************
3516 * FindFirstUrlCacheEntryW (WININET.@)
3519 INTERNETAPI HANDLE WINAPI
FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern
,
3520 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
, LPDWORD lpdwFirstCacheEntryInfoBufferSize
)
3522 find_handle
*pEntryHandle
;
3524 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern
), lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
);
3526 pEntryHandle
= heap_alloc(sizeof(*pEntryHandle
));
3530 pEntryHandle
->magic
= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
;
3531 if (lpszUrlSearchPattern
)
3533 pEntryHandle
->url_search_pattern
= heap_strdupWtoA(lpszUrlSearchPattern
);
3534 if (!pEntryHandle
->url_search_pattern
)
3536 heap_free(pEntryHandle
);
3541 pEntryHandle
->url_search_pattern
= NULL
;
3542 pEntryHandle
->container_idx
= 0;
3543 pEntryHandle
->hash_table_idx
= 0;
3544 pEntryHandle
->hash_entry_idx
= 0;
3546 if (!FindNextUrlCacheEntryW(pEntryHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
))
3548 heap_free(pEntryHandle
);
3551 return pEntryHandle
;
3554 static BOOL
urlcache_find_next_entry(
3556 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo
,
3557 LPDWORD lpdwNextCacheEntryInfoBufferSize
,
3560 find_handle
*pEntryHandle
= (find_handle
*)hEnumHandle
;
3561 cache_container
*pContainer
;
3563 if (pEntryHandle
->magic
!= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
)
3565 SetLastError(ERROR_INVALID_HANDLE
);
3569 for (; cache_containers_enum(pEntryHandle
->url_search_pattern
, pEntryHandle
->container_idx
, &pContainer
);
3570 pEntryHandle
->container_idx
++, pEntryHandle
->hash_table_idx
= 0)
3572 urlcache_header
*pHeader
;
3573 entry_hash_table
*pHashTableEntry
;
3576 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
3577 if (error
!= ERROR_SUCCESS
)
3579 SetLastError(error
);
3583 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3586 for (; urlcache_enum_hash_tables(pHeader
, &pEntryHandle
->hash_table_idx
, &pHashTableEntry
);
3587 pEntryHandle
->hash_table_idx
++, pEntryHandle
->hash_entry_idx
= 0)
3589 const struct hash_entry
*pHashEntry
= NULL
;
3590 for (; urlcache_enum_hash_table_entries(pHeader
, pHashTableEntry
, &pEntryHandle
->hash_entry_idx
, &pHashEntry
);
3591 pEntryHandle
->hash_entry_idx
++)
3593 const entry_url
*pUrlEntry
;
3594 const entry_header
*pEntry
= (const entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
3596 if (pEntry
->signature
!= URL_SIGNATURE
)
3599 pUrlEntry
= (const entry_url
*)pEntry
;
3600 TRACE("Found URL: %s\n",
3601 debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->url_off
));
3602 TRACE("Header info: %s\n",
3603 debugstr_an((LPCSTR
)pUrlEntry
+ pUrlEntry
->header_info_off
,
3604 pUrlEntry
->header_info_size
));
3606 error
= urlcache_copy_entry(
3609 lpNextCacheEntryInfo
,
3610 lpdwNextCacheEntryInfoBufferSize
,
3613 if (error
!= ERROR_SUCCESS
)
3615 cache_container_unlock_index(pContainer
, pHeader
);
3616 SetLastError(error
);
3619 if(pUrlEntry
->local_name_off
)
3620 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->local_name_off
));
3622 /* increment the current index so that next time the function
3623 * is called the next entry is returned */
3624 pEntryHandle
->hash_entry_idx
++;
3625 cache_container_unlock_index(pContainer
, pHeader
);
3630 cache_container_unlock_index(pContainer
, pHeader
);
3633 SetLastError(ERROR_NO_MORE_ITEMS
);
3637 /***********************************************************************
3638 * FindNextUrlCacheEntryA (WININET.@)
3640 BOOL WINAPI
FindNextUrlCacheEntryA(
3642 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo
,
3643 LPDWORD lpdwNextCacheEntryInfoBufferSize
)
3645 TRACE("(%p, %p, %p)\n", hEnumHandle
, lpNextCacheEntryInfo
, lpdwNextCacheEntryInfoBufferSize
);
3647 return urlcache_find_next_entry(hEnumHandle
, lpNextCacheEntryInfo
,
3648 lpdwNextCacheEntryInfoBufferSize
, FALSE
/* not UNICODE */);
3651 /***********************************************************************
3652 * FindNextUrlCacheEntryW (WININET.@)
3654 BOOL WINAPI
FindNextUrlCacheEntryW(
3656 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo
,
3657 LPDWORD lpdwNextCacheEntryInfoBufferSize
3660 TRACE("(%p, %p, %p)\n", hEnumHandle
, lpNextCacheEntryInfo
, lpdwNextCacheEntryInfoBufferSize
);
3662 return urlcache_find_next_entry(hEnumHandle
,
3663 (LPINTERNET_CACHE_ENTRY_INFOA
)lpNextCacheEntryInfo
,
3664 lpdwNextCacheEntryInfoBufferSize
, TRUE
/* UNICODE */);
3667 /***********************************************************************
3668 * FindCloseUrlCache (WININET.@)
3670 BOOL WINAPI
FindCloseUrlCache(HANDLE hEnumHandle
)
3672 find_handle
*pEntryHandle
= (find_handle
*)hEnumHandle
;
3674 TRACE("(%p)\n", hEnumHandle
);
3676 if (!pEntryHandle
|| pEntryHandle
->magic
!= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
)
3678 SetLastError(ERROR_INVALID_HANDLE
);
3682 pEntryHandle
->magic
= 0;
3683 heap_free(pEntryHandle
->url_search_pattern
);
3684 heap_free(pEntryHandle
);
3688 HANDLE WINAPI
FindFirstUrlCacheGroup( DWORD dwFlags
, DWORD dwFilter
, LPVOID lpSearchCondition
,
3689 DWORD dwSearchCondition
, GROUPID
* lpGroupId
, LPVOID lpReserved
)
3691 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags
, dwFilter
, lpSearchCondition
,
3692 dwSearchCondition
, lpGroupId
, lpReserved
);
3696 BOOL WINAPI
FindNextUrlCacheEntryExA(
3698 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
,
3699 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3701 LPDWORD pcbReserved2
,
3705 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
,
3706 lpReserved
, pcbReserved2
, lpReserved3
);
3710 BOOL WINAPI
FindNextUrlCacheEntryExW(
3712 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
,
3713 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3715 LPDWORD pcbReserved2
,
3719 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
,
3720 lpReserved
, pcbReserved2
, lpReserved3
);
3724 BOOL WINAPI
FindNextUrlCacheGroup( HANDLE hFind
, GROUPID
* lpGroupId
, LPVOID lpReserved
)
3726 FIXME("(%p, %p, %p) stub\n", hFind
, lpGroupId
, lpReserved
);
3730 /***********************************************************************
3731 * CreateUrlCacheGroup (WININET.@)
3734 INTERNETAPI GROUPID WINAPI
CreateUrlCacheGroup(DWORD dwFlags
, LPVOID lpReserved
)
3736 FIXME("(0x%08x, %p): stub\n", dwFlags
, lpReserved
);
3740 /***********************************************************************
3741 * DeleteUrlCacheGroup (WININET.@)
3744 BOOL WINAPI
DeleteUrlCacheGroup(GROUPID GroupId
, DWORD dwFlags
, LPVOID lpReserved
)
3746 FIXME("(0x%s, 0x%08x, %p) stub\n",
3747 wine_dbgstr_longlong(GroupId
), dwFlags
, lpReserved
);
3751 /***********************************************************************
3752 * DeleteWpadCacheForNetworks (WININET.@)
3753 * Undocumented, added in IE8
3755 BOOL WINAPI
DeleteWpadCacheForNetworks(DWORD unk1
)
3757 FIXME("(%d) stub\n", unk1
);
3761 /***********************************************************************
3762 * SetUrlCacheEntryGroupA (WININET.@)
3765 BOOL WINAPI
SetUrlCacheEntryGroupA(LPCSTR lpszUrlName
, DWORD dwFlags
,
3766 GROUPID GroupId
, LPBYTE pbGroupAttributes
, DWORD cbGroupAttributes
,
3769 FIXME("(%s, 0x%08x, 0x%s, %p, 0x%08x, %p) stub\n",
3770 debugstr_a(lpszUrlName
), dwFlags
, wine_dbgstr_longlong(GroupId
),
3771 pbGroupAttributes
, cbGroupAttributes
, lpReserved
);
3772 SetLastError(ERROR_FILE_NOT_FOUND
);
3776 /***********************************************************************
3777 * SetUrlCacheEntryGroupW (WININET.@)
3780 BOOL WINAPI
SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName
, DWORD dwFlags
,
3781 GROUPID GroupId
, LPBYTE pbGroupAttributes
, DWORD cbGroupAttributes
,
3784 FIXME("(%s, 0x%08x, 0x%s, %p, 0x%08x, %p) stub\n",
3785 debugstr_w(lpszUrlName
), dwFlags
, wine_dbgstr_longlong(GroupId
),
3786 pbGroupAttributes
, cbGroupAttributes
, lpReserved
);
3787 SetLastError(ERROR_FILE_NOT_FOUND
);
3791 /***********************************************************************
3792 * GetUrlCacheConfigInfoW (WININET.@)
3794 BOOL WINAPI
GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo
, LPDWORD size
, DWORD bitmask
)
3796 FIXME("(%p, %p, %x)\n", CacheInfo
, size
, bitmask
);
3797 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
3801 /***********************************************************************
3802 * GetUrlCacheConfigInfoA (WININET.@)
3804 BOOL WINAPI
GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo
, LPDWORD size
, DWORD bitmask
)
3806 FIXME("(%p, %p, %x)\n", CacheInfo
, size
, bitmask
);
3807 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
3811 BOOL WINAPI
GetUrlCacheGroupAttributeA( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3812 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo
,
3813 LPDWORD lpdwGroupInfo
, LPVOID lpReserved
)
3815 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3816 wine_dbgstr_longlong(gid
), dwFlags
, dwAttributes
, lpGroupInfo
,
3817 lpdwGroupInfo
, lpReserved
);
3821 BOOL WINAPI
GetUrlCacheGroupAttributeW( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3822 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo
,
3823 LPDWORD lpdwGroupInfo
, LPVOID lpReserved
)
3825 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3826 wine_dbgstr_longlong(gid
), dwFlags
, dwAttributes
, lpGroupInfo
,
3827 lpdwGroupInfo
, lpReserved
);
3831 BOOL WINAPI
SetUrlCacheGroupAttributeA( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3832 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo
, LPVOID lpReserved
)
3834 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p) stub\n",
3835 wine_dbgstr_longlong(gid
), dwFlags
, dwAttributes
, lpGroupInfo
, lpReserved
);
3839 BOOL WINAPI
SetUrlCacheGroupAttributeW( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3840 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo
, LPVOID lpReserved
)
3842 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p) stub\n",
3843 wine_dbgstr_longlong(gid
), dwFlags
, dwAttributes
, lpGroupInfo
, lpReserved
);
3847 BOOL WINAPI
SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo
, DWORD dwFieldControl
)
3849 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo
, dwFieldControl
);
3853 BOOL WINAPI
SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo
, DWORD dwFieldControl
)
3855 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo
, dwFieldControl
);
3859 /***********************************************************************
3860 * DeleteIE3Cache (WININET.@)
3862 * Deletes the files used by the IE3 URL caching system.
3865 * hWnd [I] A dummy window.
3866 * hInst [I] Instance of process calling the function.
3867 * lpszCmdLine [I] Options used by function.
3868 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3870 DWORD WINAPI
DeleteIE3Cache(HWND hWnd
, HINSTANCE hInst
, LPSTR lpszCmdLine
, int nCmdShow
)
3872 FIXME("(%p, %p, %s, %d)\n", hWnd
, hInst
, debugstr_a(lpszCmdLine
), nCmdShow
);
3876 static BOOL
urlcache_entry_is_expired(const entry_url
*pUrlEntry
,
3877 FILETIME
*pftLastModified
)
3880 FILETIME now
, expired
;
3882 *pftLastModified
= pUrlEntry
->modification_time
;
3883 GetSystemTimeAsFileTime(&now
);
3884 dos_date_time_to_file_time(pUrlEntry
->expire_date
,
3885 pUrlEntry
->expire_time
, &expired
);
3886 /* If the expired time is 0, it's interpreted as not expired */
3887 if (!expired
.dwLowDateTime
&& !expired
.dwHighDateTime
)
3890 ret
= CompareFileTime(&expired
, &now
) < 0;
3894 /***********************************************************************
3895 * IsUrlCacheEntryExpiredA (WININET.@)
3899 * dwFlags [I] Unknown
3900 * pftLastModified [O] Last modified time
3902 BOOL WINAPI
IsUrlCacheEntryExpiredA(LPCSTR url
, DWORD dwFlags
, FILETIME
* pftLastModified
)
3904 urlcache_header
*pHeader
;
3905 struct hash_entry
*pHashEntry
;
3906 const entry_header
*pEntry
;
3907 const entry_url
* pUrlEntry
;
3908 cache_container
*pContainer
;
3911 TRACE("(%s, %08x, %p)\n", debugstr_a(url
), dwFlags
, pftLastModified
);
3913 if (!url
|| !pftLastModified
)
3916 FIXME("unknown flags 0x%08x\n", dwFlags
);
3918 /* Any error implies that the URL is expired, i.e. not in the cache */
3919 if (cache_containers_find(url
, &pContainer
))
3921 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3925 if (cache_container_open_index(pContainer
, MIN_BLOCK_NO
))
3927 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3931 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3933 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3937 if (!urlcache_find_hash_entry(pHeader
, url
, &pHashEntry
))
3939 cache_container_unlock_index(pContainer
, pHeader
);
3940 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3941 TRACE("entry %s not found!\n", url
);
3945 pEntry
= (const entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
3946 if (pEntry
->signature
!= URL_SIGNATURE
)
3948 cache_container_unlock_index(pContainer
, pHeader
);
3949 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3950 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR
)&pEntry
->signature
, sizeof(DWORD
)));
3954 pUrlEntry
= (const entry_url
*)pEntry
;
3955 expired
= urlcache_entry_is_expired(pUrlEntry
, pftLastModified
);
3957 cache_container_unlock_index(pContainer
, pHeader
);
3962 /***********************************************************************
3963 * IsUrlCacheEntryExpiredW (WININET.@)
3967 * dwFlags [I] Unknown
3968 * pftLastModified [O] Last modified time
3970 BOOL WINAPI
IsUrlCacheEntryExpiredW(LPCWSTR url
, DWORD dwFlags
, FILETIME
* pftLastModified
)
3975 if(!urlcache_encode_url_alloc(url
, &encoded_url
))
3978 ret
= IsUrlCacheEntryExpiredA(encoded_url
, dwFlags
, pftLastModified
);
3979 heap_free(encoded_url
);
3983 /***********************************************************************
3984 * GetDiskInfoA (WININET.@)
3986 BOOL WINAPI
GetDiskInfoA(PCSTR path
, PDWORD cluster_size
, PDWORDLONG free
, PDWORDLONG total
)
3989 ULARGE_INTEGER bytes_free
, bytes_total
;
3991 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path
), cluster_size
, free
, total
);
3995 SetLastError(ERROR_INVALID_PARAMETER
);
3999 if ((ret
= GetDiskFreeSpaceExA(path
, NULL
, &bytes_total
, &bytes_free
)))
4001 if (cluster_size
) *cluster_size
= 1;
4002 if (free
) *free
= bytes_free
.QuadPart
;
4003 if (total
) *total
= bytes_total
.QuadPart
;
4008 /***********************************************************************
4009 * RegisterUrlCacheNotification (WININET.@)
4011 DWORD WINAPI
RegisterUrlCacheNotification(LPVOID a
, DWORD b
, DWORD c
, DWORD d
, DWORD e
, DWORD f
)
4013 FIXME("(%p %x %x %x %x %x)\n", a
, b
, c
, d
, e
, f
);
4017 /***********************************************************************
4018 * IncrementUrlCacheHeaderData (WININET.@)
4020 BOOL WINAPI
IncrementUrlCacheHeaderData(DWORD index
, LPDWORD data
)
4022 FIXME("(%u, %p)\n", index
, data
);
4026 /***********************************************************************
4027 * RunOnceUrlCache (WININET.@)
4030 DWORD WINAPI
RunOnceUrlCache(HWND hwnd
, HINSTANCE hinst
, LPSTR cmd
, int cmdshow
)
4032 FIXME("(%p, %p, %s, %d): stub\n", hwnd
, hinst
, debugstr_a(cmd
), cmdshow
);
4036 BOOL
init_urlcache(void)
4038 dll_unload_event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
4039 if(!dll_unload_event
)
4042 free_cache_running
= CreateSemaphoreW(NULL
, 1, 1, NULL
);
4043 if(!free_cache_running
) {
4044 CloseHandle(dll_unload_event
);
4049 cache_containers_init();
4054 void free_urlcache(void)
4056 SetEvent(dll_unload_event
);
4057 WaitForSingleObject(free_cache_running
, INFINITE
);
4058 ReleaseSemaphore(free_cache_running
, 1, NULL
);
4059 CloseHandle(free_cache_running
);
4060 CloseHandle(dll_unload_event
);
4062 cache_containers_free();
4065 /***********************************************************************
4066 * LoadUrlCacheContent (WININET.@)
4068 BOOL WINAPI
LoadUrlCacheContent(void)