2 * Wininet - Url Cache functions
4 * Copyright 2001,2002 CodeWeavers
5 * Copyright 2003-2008 Robert Shearman
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 static const char urlcache_ver_prefix
[] = "WINE URLCache Ver ";
31 static const char urlcache_ver
[] = "0.2012001";
34 #define CHAR_BIT (8 * sizeof(CHAR))
37 #define ENTRY_START_OFFSET 0x4000
39 #define MAX_DIR_NO 0x20
41 #define HASHTABLE_SIZE 448
42 #define HASHTABLE_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
43 #define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
44 #define ALLOCATION_TABLE_OFFSET 0x250
45 #define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
46 #define MIN_BLOCK_NO 0x80
47 #define MAX_BLOCK_NO (ALLOCATION_TABLE_SIZE * CHAR_BIT)
48 #define FILE_SIZE(blocks) ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
50 #define HASHTABLE_URL 0
51 #define HASHTABLE_DEL 1
52 #define HASHTABLE_LOCK 2
53 #define HASHTABLE_FREE 3
54 #define HASHTABLE_REDR 5
55 #define HASHTABLE_FLAG_BITS 6
57 #define PENDING_DELETE_CACHE_ENTRY 0x00400000
58 #define INSTALLED_CACHE_ENTRY 0x10000000
59 #define GET_INSTALLED_ENTRY 0x200
60 #define CACHE_CONTAINER_NO_SUBDIR 0xFE
62 #define CACHE_HEADER_DATA_ROOT_LEAK_OFFSET 0x16
64 #define FILETIME_SECOND 10000000
66 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
67 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
68 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
69 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
70 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
72 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
74 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
79 DWORD blocks_used
; /* number of 128byte blocks used by this entry */
85 FILETIME modification_time
;
87 WORD expire_date
; /* expire date in dos format */
88 WORD expire_time
; /* expire time in dos format */
89 DWORD unk1
; /* usually zero */
90 ULARGE_INTEGER size
; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
91 DWORD unk2
; /* usually zero */
92 DWORD exempt_delta
; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
93 DWORD unk3
; /* usually 0x60 */
94 DWORD url_off
; /* offset of start of url from start of entry */
95 BYTE cache_dir
; /* index of cache directory this url is stored in */
96 BYTE unk4
; /* usually zero */
97 WORD unk5
; /* usually 0x1010 */
98 DWORD local_name_off
; /* offset of start of local filename from start of entry */
99 DWORD cache_entry_type
; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
100 DWORD header_info_off
; /* offset of start of header info from start of entry */
101 DWORD header_info_size
;
102 DWORD file_extension_off
; /* offset of start of file extension from start of entry */
103 WORD sync_date
; /* last sync date in dos format */
104 WORD sync_time
; /* last sync time in dos format */
105 DWORD hit_rate
; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
106 DWORD use_count
; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
109 DWORD unk7
; /* usually zero */
110 DWORD unk8
; /* usually zero */
111 /* packing to dword align start of next field */
112 /* CHAR szSourceUrlName[]; (url) */
113 /* packing to dword align start of next field */
114 /* CHAR szLocalFileName[]; (local file name excluding path) */
115 /* packing to dword align start of next field */
116 /* CHAR szHeaderInfo[]; (header info) */
130 struct hash_entry hash_table
[HASHTABLE_SIZE
];
137 DWORD hash_table_off
;
138 DWORD capacity_in_blocks
;
141 ULARGE_INTEGER cache_limit
;
142 ULARGE_INTEGER cache_usage
;
143 ULARGE_INTEGER exempt_usage
;
145 struct _directory_data
148 char name
[DIR_LENGTH
];
149 } directory_data
[MAX_DIR_NO
];
151 BYTE allocation_table
[ALLOCATION_TABLE_SIZE
];
162 struct list entry
; /* part of a list */
163 char *cache_prefix
; /* string that has to be prefixed for this container to be used */
164 LPWSTR path
; /* path to url container directory */
165 HANDLE mapping
; /* handle of file mapping */
166 DWORD file_size
; /* size of file when mapping was opened */
167 HANDLE mutex
; /* handle of mutex */
168 DWORD default_entry_type
;
174 char *url_search_pattern
;
176 DWORD hash_table_idx
;
177 DWORD hash_entry_idx
;
180 /* List of all containers available */
181 static struct list UrlContainers
= LIST_INIT(UrlContainers
);
183 BOOL bDefaultContainersAdded
= FALSE
;
185 static inline char *heap_strdupWtoUTF8(LPCWSTR str
)
190 DWORD size
= WideCharToMultiByte(CP_UTF8
, 0, str
, -1, NULL
, 0, NULL
, NULL
);
191 ret
= heap_alloc(size
);
193 WideCharToMultiByte(CP_UTF8
, 0, str
, -1, ret
, size
, NULL
, NULL
);
199 /***********************************************************************
200 * urlcache_block_is_free (Internal)
202 * Is the specified block number free?
209 static inline BYTE
urlcache_block_is_free(BYTE
*allocation_table
, DWORD block_number
)
211 BYTE mask
= 1 << (block_number
%CHAR_BIT
);
212 return (allocation_table
[block_number
/CHAR_BIT
] & mask
) == 0;
215 /***********************************************************************
216 * urlcache_block_free (Internal)
218 * Marks the specified block as free
221 * this function is not updating used blocks count
227 static inline void urlcache_block_free(BYTE
*allocation_table
, DWORD block_number
)
229 BYTE mask
= ~(1 << (block_number
%CHAR_BIT
));
230 allocation_table
[block_number
/CHAR_BIT
] &= mask
;
233 /***********************************************************************
234 * urlcache_block_alloc (Internal)
236 * Marks the specified block as allocated
239 * this function is not updating used blocks count
245 static inline void urlcache_block_alloc(BYTE
*allocation_table
, DWORD block_number
)
247 BYTE mask
= 1 << (block_number
%CHAR_BIT
);
248 allocation_table
[block_number
/CHAR_BIT
] |= mask
;
251 /***********************************************************************
252 * urlcache_entry_alloc (Internal)
254 * Finds and allocates the first block of free space big enough and
255 * sets entry to point to it.
258 * ERROR_SUCCESS when free memory block was found
259 * Any other Win32 error code if the entry could not be added
262 static DWORD
urlcache_entry_alloc(urlcache_header
*header
, DWORD blocks_needed
, entry_header
**entry
)
264 DWORD block
, block_size
;
266 for(block
=0; block
<header
->capacity_in_blocks
; block
+=block_size
+1)
269 while(block_size
<blocks_needed
&& block_size
+block
<header
->capacity_in_blocks
270 && urlcache_block_is_free(header
->allocation_table
, block
+block_size
))
273 if(block_size
== blocks_needed
)
277 TRACE("Found free blocks starting at no. %d (0x%x)\n", block
, ENTRY_START_OFFSET
+block
*BLOCKSIZE
);
279 for(index
=0; index
<blocks_needed
; index
++)
280 urlcache_block_alloc(header
->allocation_table
, block
+index
);
282 *entry
= (entry_header
*)((BYTE
*)header
+ENTRY_START_OFFSET
+block
*BLOCKSIZE
);
283 for(index
=0; index
<blocks_needed
*BLOCKSIZE
/sizeof(DWORD
); index
++)
284 ((DWORD
*)*entry
)[index
] = 0xdeadbeef;
285 (*entry
)->blocks_used
= blocks_needed
;
287 header
->blocks_in_use
+= blocks_needed
;
288 return ERROR_SUCCESS
;
292 return ERROR_HANDLE_DISK_FULL
;
295 /***********************************************************************
296 * urlcache_entry_free (Internal)
298 * Deletes the specified entry and frees the space allocated to it
301 * TRUE if it succeeded
305 static BOOL
urlcache_entry_free(urlcache_header
*header
, entry_header
*entry
)
307 DWORD start_block
, block
;
309 /* update allocation table */
310 start_block
= ((DWORD
)((BYTE
*)entry
- (BYTE
*)header
) - ENTRY_START_OFFSET
) / BLOCKSIZE
;
311 for(block
= start_block
; block
< start_block
+entry
->blocks_used
; block
++)
312 urlcache_block_free(header
->allocation_table
, block
);
314 header
->blocks_in_use
-= entry
->blocks_used
;
318 /***********************************************************************
319 * urlcache_create_hash_table (Internal)
321 * Creates a new hash table in free space and adds it to the chain of existing
325 * ERROR_SUCCESS if the hash table was created
326 * ERROR_DISK_FULL if the hash table could not be created
329 static DWORD
urlcache_create_hash_table(urlcache_header
*header
, entry_hash_table
*hash_table_prev
, entry_hash_table
**hash_table
)
331 DWORD dwOffset
, error
;
334 if((error
= urlcache_entry_alloc(header
, 0x20, (entry_header
**)hash_table
)) != ERROR_SUCCESS
)
337 dwOffset
= (BYTE
*)*hash_table
-(BYTE
*)header
;
340 hash_table_prev
->next
= dwOffset
;
342 header
->hash_table_off
= dwOffset
;
344 (*hash_table
)->header
.signature
= HASH_SIGNATURE
;
345 (*hash_table
)->next
= 0;
346 (*hash_table
)->id
= hash_table_prev
? hash_table_prev
->id
+1 : 0;
347 for(i
= 0; i
< HASHTABLE_SIZE
; i
++) {
348 (*hash_table
)->hash_table
[i
].offset
= HASHTABLE_FREE
;
349 (*hash_table
)->hash_table
[i
].key
= HASHTABLE_FREE
;
351 return ERROR_SUCCESS
;
354 /***********************************************************************
355 * cache_container_create_object_name (Internal)
357 * Converts a path to a name suitable for use as a Win32 object name.
358 * Replaces '\\' characters in-place with the specified character
359 * (usually '_' or '!')
365 static void cache_container_create_object_name(LPWSTR lpszPath
, WCHAR replace
)
367 for (; *lpszPath
; lpszPath
++)
369 if (*lpszPath
== '\\')
374 /* Caller must hold container lock */
375 static HANDLE
cache_container_map_index(HANDLE file
, const WCHAR
*path
, DWORD size
, BOOL
*validate
)
377 static const WCHAR mapping_name_format
[]
378 = {'%','s','i','n','d','e','x','.','d','a','t','_','%','l','u',0};
379 WCHAR mapping_name
[MAX_PATH
];
382 wsprintfW(mapping_name
, mapping_name_format
, path
, size
);
383 cache_container_create_object_name(mapping_name
, '_');
385 mapping
= OpenFileMappingW(FILE_MAP_WRITE
, FALSE
, mapping_name
);
387 if(validate
) *validate
= FALSE
;
391 if(validate
) *validate
= TRUE
;
392 return CreateFileMappingW(file
, NULL
, PAGE_READWRITE
, 0, 0, mapping_name
);
395 /* Caller must hold container lock */
396 static DWORD
cache_container_set_size(cache_container
*container
, HANDLE file
, DWORD blocks_no
)
398 static const WCHAR cache_content_key
[] = {'S','o','f','t','w','a','r','e','\\',
399 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
400 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
401 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s','\\',
402 'C','a','c','h','e','\\','C','o','n','t','e','n','t',0};
403 static const WCHAR cache_limit
[] = {'C','a','c','h','e','L','i','m','i','t',0};
405 DWORD file_size
= FILE_SIZE(blocks_no
);
406 WCHAR dir_path
[MAX_PATH
], *dir_name
;
407 entry_hash_table
*hashtable_entry
;
408 urlcache_header
*header
;
414 if(SetFilePointer(file
, file_size
, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
415 return GetLastError();
417 if(!SetEndOfFile(file
))
418 return GetLastError();
420 mapping
= cache_container_map_index(file
, container
->path
, file_size
, NULL
);
422 return GetLastError();
424 header
= MapViewOfFile(mapping
, FILE_MAP_WRITE
, 0, 0, 0);
426 CloseHandle(mapping
);
427 return GetLastError();
430 if(blocks_no
!= MIN_BLOCK_NO
) {
431 if(file_size
> header
->size
)
432 memset((char*)header
+header
->size
, 0, file_size
-header
->size
);
433 header
->size
= file_size
;
434 header
->capacity_in_blocks
= blocks_no
;
436 UnmapViewOfFile(header
);
437 CloseHandle(container
->mapping
);
438 container
->mapping
= mapping
;
439 container
->file_size
= file_size
;
440 return ERROR_SUCCESS
;
443 memset(header
, 0, file_size
);
444 /* First set some constants and defaults in the header */
445 memcpy(header
->signature
, urlcache_ver_prefix
, sizeof(urlcache_ver_prefix
)-1);
446 memcpy(header
->signature
+sizeof(urlcache_ver_prefix
)-1, urlcache_ver
, sizeof(urlcache_ver
)-1);
447 header
->size
= file_size
;
448 header
->capacity_in_blocks
= blocks_no
;
449 /* 127MB - taken from default for Windows 2000 */
450 header
->cache_limit
.QuadPart
= 0x07ff5400;
451 /* Copied from a Windows 2000 cache index */
452 header
->dirs_no
= container
->default_entry_type
==NORMAL_CACHE_ENTRY
? 4 : 0;
454 /* If the registry has a cache size set, use the registry value */
455 if(RegOpenKeyW(HKEY_CURRENT_USER
, cache_content_key
, &key
) == ERROR_SUCCESS
) {
456 DWORD dw
, len
= sizeof(dw
), keytype
;
458 if(RegQueryValueExW(key
, cache_limit
, NULL
, &keytype
, (BYTE
*)&dw
, &len
) == ERROR_SUCCESS
&&
459 keytype
== REG_DWORD
)
460 header
->cache_limit
.QuadPart
= (ULONGLONG
)dw
* 1024;
464 urlcache_create_hash_table(header
, NULL
, &hashtable_entry
);
466 /* Last step - create the directories */
467 strcpyW(dir_path
, container
->path
);
468 dir_name
= dir_path
+ strlenW(dir_path
);
471 GetSystemTimeAsFileTime(&ft
);
473 for(i
=0; i
<header
->dirs_no
; ++i
) {
474 header
->directory_data
[i
].files_no
= 0;
476 ULONGLONG n
= ft
.dwHighDateTime
;
479 /* Generate a file name to attempt to create.
480 * This algorithm will create what will appear
481 * to be random and unrelated directory names
482 * of up to 9 characters in length.
485 n
+= ft
.dwLowDateTime
;
486 n
^= ((ULONGLONG
) i
<< 56) | ((ULONGLONG
) j
<< 48);
488 for(k
= 0; k
< 8; ++k
) {
491 /* Dividing by a prime greater than 36 helps
492 * with the appearance of randomness
497 dir_name
[k
] = '0' + r
;
499 dir_name
[k
] = 'A' + (r
- 10);
502 if(CreateDirectoryW(dir_path
, 0)) {
503 /* The following is OK because we generated an
504 * 8 character directory name made from characters
505 * [A-Z0-9], which are equivalent for all code
506 * pages and for UTF-16
508 for (k
= 0; k
< 8; ++k
)
509 header
->directory_data
[i
].name
[k
] = dir_name
[k
];
512 /* Give up. The most likely cause of this
513 * is a full disk, but whatever the cause
514 * is, it should be more than apparent that
517 UnmapViewOfFile(header
);
518 CloseHandle(mapping
);
519 return GetLastError();
524 UnmapViewOfFile(header
);
525 CloseHandle(container
->mapping
);
526 container
->mapping
= mapping
;
527 container
->file_size
= file_size
;
528 return ERROR_SUCCESS
;
531 static BOOL
cache_container_is_valid(urlcache_header
*header
, DWORD file_size
)
533 DWORD allocation_size
, count_bits
, i
;
535 if(file_size
< FILE_SIZE(MIN_BLOCK_NO
))
538 if(file_size
!= header
->size
)
541 if (!memcmp(header
->signature
, urlcache_ver_prefix
, sizeof(urlcache_ver_prefix
)-1) &&
542 memcmp(header
->signature
+sizeof(urlcache_ver_prefix
)-1, urlcache_ver
, sizeof(urlcache_ver
)-1))
545 if(FILE_SIZE(header
->capacity_in_blocks
) != file_size
)
549 for(i
=0; i
<header
->capacity_in_blocks
/8; i
++) {
550 for(count_bits
= header
->allocation_table
[i
]; count_bits
!=0; count_bits
>>=1) {
555 if(allocation_size
!= header
->blocks_in_use
)
558 for(; i
<ALLOCATION_TABLE_SIZE
; i
++) {
559 if(header
->allocation_table
[i
])
566 /***********************************************************************
567 * cache_container_open_index (Internal)
569 * Opens the index file and saves mapping handle
572 * ERROR_SUCCESS if succeeded
573 * Any other Win32 error code if failed
576 static DWORD
cache_container_open_index(cache_container
*container
, DWORD blocks_no
)
578 static const WCHAR index_dat
[] = {'i','n','d','e','x','.','d','a','t',0};
581 WCHAR index_path
[MAX_PATH
];
585 WaitForSingleObject(container
->mutex
, INFINITE
);
587 if(container
->mapping
) {
588 ReleaseMutex(container
->mutex
);
589 return ERROR_SUCCESS
;
592 strcpyW(index_path
, container
->path
);
593 strcatW(index_path
, index_dat
);
595 file
= CreateFileW(index_path
, GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_ALWAYS
, 0, NULL
);
596 if(file
== INVALID_HANDLE_VALUE
) {
597 /* Maybe the directory wasn't there? Try to create it */
598 if(CreateDirectoryW(container
->path
, 0))
599 file
= CreateFileW(index_path
, GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_ALWAYS
, 0, NULL
);
601 if(file
== INVALID_HANDLE_VALUE
) {
602 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(index_path
));
603 ReleaseMutex(container
->mutex
);
604 return GetLastError();
607 file_size
= GetFileSize(file
, NULL
);
608 if(file_size
== INVALID_FILE_SIZE
) {
610 ReleaseMutex(container
->mutex
);
611 return GetLastError();
614 if(blocks_no
< MIN_BLOCK_NO
)
615 blocks_no
= MIN_BLOCK_NO
;
616 else if(blocks_no
> MAX_BLOCK_NO
)
617 blocks_no
= MAX_BLOCK_NO
;
619 if(file_size
< FILE_SIZE(blocks_no
)) {
620 DWORD ret
= cache_container_set_size(container
, file
, blocks_no
);
622 ReleaseMutex(container
->mutex
);
626 container
->file_size
= file_size
;
627 container
->mapping
= cache_container_map_index(file
, container
->path
, file_size
, &validate
);
629 if(container
->mapping
&& validate
) {
630 urlcache_header
*header
= MapViewOfFile(container
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
632 if(header
&& !cache_container_is_valid(header
, file_size
)) {
633 WARN("detected old or broken index.dat file\n");
634 UnmapViewOfFile(header
);
635 FreeUrlCacheSpaceW(container
->path
, 100, 0);
637 UnmapViewOfFile(header
);
639 CloseHandle(container
->mapping
);
640 container
->mapping
= NULL
;
644 if(!container
->mapping
)
646 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
647 ReleaseMutex(container
->mutex
);
648 return GetLastError();
651 ReleaseMutex(container
->mutex
);
652 return ERROR_SUCCESS
;
655 /***********************************************************************
656 * cache_container_close_index (Internal)
664 static void cache_container_close_index(cache_container
*pContainer
)
666 CloseHandle(pContainer
->mapping
);
667 pContainer
->mapping
= NULL
;
670 static BOOL
cache_containers_add(const char *cache_prefix
, LPCWSTR path
,
671 DWORD default_entry_type
, LPWSTR mutex_name
)
673 cache_container
*pContainer
= heap_alloc(sizeof(cache_container
));
674 int cache_prefix_len
= strlen(cache_prefix
);
681 pContainer
->mapping
= NULL
;
682 pContainer
->file_size
= 0;
683 pContainer
->default_entry_type
= default_entry_type
;
685 pContainer
->path
= heap_strdupW(path
);
686 if (!pContainer
->path
)
688 heap_free(pContainer
);
692 pContainer
->cache_prefix
= heap_alloc(cache_prefix_len
+1);
693 if (!pContainer
->cache_prefix
)
695 heap_free(pContainer
->path
);
696 heap_free(pContainer
);
700 memcpy(pContainer
->cache_prefix
, cache_prefix
, cache_prefix_len
+1);
702 CharLowerW(mutex_name
);
703 cache_container_create_object_name(mutex_name
, '!');
705 if ((pContainer
->mutex
= CreateMutexW(NULL
, FALSE
, mutex_name
)) == NULL
)
707 ERR("couldn't create mutex (error is %d)\n", GetLastError());
708 heap_free(pContainer
->path
);
709 heap_free(pContainer
);
713 list_add_head(&UrlContainers
, &pContainer
->entry
);
718 static void cache_container_delete_container(cache_container
*pContainer
)
720 list_remove(&pContainer
->entry
);
722 cache_container_close_index(pContainer
);
723 CloseHandle(pContainer
->mutex
);
724 heap_free(pContainer
->path
);
725 heap_free(pContainer
->cache_prefix
);
726 heap_free(pContainer
);
729 static void cache_containers_init(void)
731 static const WCHAR UrlSuffix
[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
732 static const WCHAR HistorySuffix
[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
733 static const WCHAR CookieSuffix
[] = {0};
735 static const WCHAR UserProfile
[] = {'U','S','E','R','P','R','O','F','I','L','E',0};
738 int nFolder
; /* CSIDL_* constant */
739 const WCHAR
*shpath_suffix
; /* suffix on path returned by SHGetSpecialFolderPath */
740 const char *cache_prefix
; /* prefix used to reference the container */
741 DWORD default_entry_type
;
742 } DefaultContainerData
[] =
744 { CSIDL_INTERNET_CACHE
, UrlSuffix
, "", NORMAL_CACHE_ENTRY
},
745 { CSIDL_HISTORY
, HistorySuffix
, "Visited:", URLHISTORY_CACHE_ENTRY
},
746 { CSIDL_COOKIES
, CookieSuffix
, "Cookie:", COOKIE_CACHE_ENTRY
},
751 if (GetEnvironmentVariableW(UserProfile
, NULL
, 0) == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND
)
753 ERR("Environment variable 'USERPROFILE' does not exist!\n");
757 for (i
= 0; i
< sizeof(DefaultContainerData
) / sizeof(DefaultContainerData
[0]); i
++)
759 WCHAR wszCachePath
[MAX_PATH
];
760 WCHAR wszMutexName
[MAX_PATH
];
761 int path_len
, suffix_len
;
764 if (!SHGetSpecialFolderPathW(NULL
, wszCachePath
, DefaultContainerData
[i
].nFolder
, TRUE
))
766 ERR("Couldn't get path for default container %u\n", i
);
769 path_len
= strlenW(wszCachePath
);
770 suffix_len
= strlenW(DefaultContainerData
[i
].shpath_suffix
);
772 if (path_len
+ suffix_len
+ 2 > MAX_PATH
)
774 ERR("Path too long\n");
778 wszCachePath
[path_len
] = '\\';
779 wszCachePath
[path_len
+1] = 0;
781 strcpyW(wszMutexName
, wszCachePath
);
785 memcpy(wszCachePath
+ path_len
+ 1, DefaultContainerData
[i
].shpath_suffix
, (suffix_len
+ 1) * sizeof(WCHAR
));
786 wszCachePath
[path_len
+ suffix_len
+ 1] = '\\';
787 wszCachePath
[path_len
+ suffix_len
+ 2] = '\0';
790 if (!WideCharToMultiByte(CP_ACP
, WC_NO_BEST_FIT_CHARS
, wszCachePath
, path_len
,
791 NULL
, 0, NULL
, &def_char
) || def_char
)
795 /* cannot convert path to ANSI code page */
796 if (!(path_len
= GetShortPathNameW(wszCachePath
, tmp
, MAX_PATH
)) ||
797 !WideCharToMultiByte(CP_ACP
, WC_NO_BEST_FIT_CHARS
, tmp
, path_len
,
798 NULL
, 0, NULL
, &def_char
) || def_char
)
799 ERR("Can't create container path accessible by ANSI functions\n");
801 memcpy(wszCachePath
, tmp
, (path_len
+1)*sizeof(WCHAR
));
804 cache_containers_add(DefaultContainerData
[i
].cache_prefix
, wszCachePath
,
805 DefaultContainerData
[i
].default_entry_type
, wszMutexName
);
809 bDefaultContainersAdded
= TRUE
;
813 static void cache_containers_free(void)
815 while(!list_empty(&UrlContainers
))
816 cache_container_delete_container(
817 LIST_ENTRY(list_head(&UrlContainers
), cache_container
, entry
)
821 static DWORD
cache_containers_find(const char *url
, cache_container
**ret
)
823 cache_container
*container
;
825 TRACE("searching for prefix for URL: %s\n", debugstr_a(url
));
828 return ERROR_INVALID_PARAMETER
;
832 if (!bDefaultContainersAdded
)
833 cache_containers_init();
836 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
838 int prefix_len
= strlen(container
->cache_prefix
);
840 if(!strncmp(container
->cache_prefix
, url
, prefix_len
)) {
841 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
843 return ERROR_SUCCESS
;
847 ERR("no container found\n");
848 return ERROR_FILE_NOT_FOUND
;
851 static BOOL
cache_containers_enum(char *search_pattern
, DWORD index
, cache_container
**ret
)
854 cache_container
*container
;
856 TRACE("searching for prefix: %s\n", debugstr_a(search_pattern
));
858 /* non-NULL search pattern only returns one container ever */
859 if (search_pattern
&& index
> 0)
864 if (!bDefaultContainersAdded
)
865 cache_containers_init();
868 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
872 if (!strcmp(container
->cache_prefix
, search_pattern
))
874 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
883 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
893 /***********************************************************************
894 * cache_container_lock_index (Internal)
896 * Locks the index for system-wide exclusive access.
899 * Cache file header if successful
900 * NULL if failed and calls SetLastError.
902 static urlcache_header
* cache_container_lock_index(cache_container
*pContainer
)
906 urlcache_header
* pHeader
;
910 WaitForSingleObject(pContainer
->mutex
, INFINITE
);
912 pIndexData
= MapViewOfFile(pContainer
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
916 ReleaseMutex(pContainer
->mutex
);
917 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
920 pHeader
= (urlcache_header
*)pIndexData
;
922 /* file has grown - we need to remap to prevent us getting
923 * access violations when we try and access beyond the end
924 * of the memory mapped file */
925 if (pHeader
->size
!= pContainer
->file_size
)
927 UnmapViewOfFile( pHeader
);
928 cache_container_close_index(pContainer
);
929 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
930 if (error
!= ERROR_SUCCESS
)
932 ReleaseMutex(pContainer
->mutex
);
936 pIndexData
= MapViewOfFile(pContainer
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
940 ReleaseMutex(pContainer
->mutex
);
941 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
944 pHeader
= (urlcache_header
*)pIndexData
;
947 TRACE("Signature: %s, file size: %d bytes\n", pHeader
->signature
, pHeader
->size
);
949 for (index
= 0; index
< pHeader
->dirs_no
; index
++)
951 TRACE("Directory[%d] = \"%.8s\"\n", index
, pHeader
->directory_data
[index
].name
);
957 /***********************************************************************
958 * cache_container_unlock_index (Internal)
961 static BOOL
cache_container_unlock_index(cache_container
*pContainer
, urlcache_header
*pHeader
)
964 ReleaseMutex(pContainer
->mutex
);
965 return UnmapViewOfFile(pHeader
);
968 /***********************************************************************
969 * urlcache_create_file_pathW (Internal)
971 * Copies the full path to the specified buffer given the local file
972 * name and the index of the directory it is in. Always sets value in
973 * lpBufferSize to the required buffer size (in bytes).
976 * TRUE if the buffer was big enough
977 * FALSE if the buffer was too small
980 static BOOL
urlcache_create_file_pathW(
981 const cache_container
*pContainer
,
982 const urlcache_header
*pHeader
,
983 LPCSTR szLocalFileName
,
990 int path_len
= strlenW(pContainer
->path
);
991 int file_name_len
= MultiByteToWideChar(CP_ACP
, 0, szLocalFileName
, -1, NULL
, 0);
992 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
&& Directory
>=pHeader
->dirs_no
)
998 nRequired
= (path_len
+ file_name_len
) * sizeof(WCHAR
);
999 if(Directory
!= CACHE_CONTAINER_NO_SUBDIR
)
1000 nRequired
+= (DIR_LENGTH
+ 1) * sizeof(WCHAR
);
1001 if (trunc_name
&& nRequired
>= *lpBufferSize
)
1002 nRequired
= *lpBufferSize
;
1003 if (nRequired
<= *lpBufferSize
)
1007 memcpy(wszPath
, pContainer
->path
, path_len
* sizeof(WCHAR
));
1008 if (Directory
!= CACHE_CONTAINER_NO_SUBDIR
)
1010 dir_len
= MultiByteToWideChar(CP_ACP
, 0, pHeader
->directory_data
[Directory
].name
, DIR_LENGTH
, wszPath
+ path_len
, DIR_LENGTH
);
1011 wszPath
[dir_len
+ path_len
] = '\\';
1018 MultiByteToWideChar(CP_ACP
, 0, szLocalFileName
, -1, wszPath
+ dir_len
+ path_len
,
1019 *lpBufferSize
/sizeof(WCHAR
)-dir_len
-path_len
);
1020 wszPath
[*lpBufferSize
/sizeof(WCHAR
)-1] = 0;
1021 *lpBufferSize
= nRequired
;
1024 *lpBufferSize
= nRequired
;
1028 /***********************************************************************
1029 * urlcache_create_file_pathA (Internal)
1031 * Copies the full path to the specified buffer given the local file
1032 * name and the index of the directory it is in. Always sets value in
1033 * lpBufferSize to the required buffer size.
1036 * TRUE if the buffer was big enough
1037 * FALSE if the buffer was too small
1040 static BOOL
urlcache_create_file_pathA(
1041 const cache_container
*pContainer
,
1042 const urlcache_header
*pHeader
,
1043 LPCSTR szLocalFileName
,
1046 LPLONG lpBufferSize
)
1049 int path_len
, file_name_len
, dir_len
;
1051 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
&& Directory
>=pHeader
->dirs_no
)
1057 path_len
= WideCharToMultiByte(CP_ACP
, 0, pContainer
->path
, -1, NULL
, 0, NULL
, NULL
) - 1;
1058 file_name_len
= strlen(szLocalFileName
) + 1 /* for nul-terminator */;
1059 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
)
1060 dir_len
= DIR_LENGTH
+1;
1064 nRequired
= (path_len
+ dir_len
+ file_name_len
) * sizeof(char);
1065 if (nRequired
<= *lpBufferSize
)
1067 WideCharToMultiByte(CP_ACP
, 0, pContainer
->path
, -1, szPath
, path_len
, NULL
, NULL
);
1069 memcpy(szPath
+path_len
, pHeader
->directory_data
[Directory
].name
, dir_len
-1);
1070 szPath
[path_len
+ dir_len
-1] = '\\';
1072 memcpy(szPath
+ path_len
+ dir_len
, szLocalFileName
, file_name_len
);
1073 *lpBufferSize
= nRequired
;
1076 *lpBufferSize
= nRequired
;
1080 /* Just like FileTimeToDosDateTime, except that it also maps the special
1081 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1083 static void file_time_to_dos_date_time(const FILETIME
*ft
, WORD
*fatdate
,
1086 if (!ft
->dwLowDateTime
&& !ft
->dwHighDateTime
)
1087 *fatdate
= *fattime
= 0;
1089 FileTimeToDosDateTime(ft
, fatdate
, fattime
);
1092 /***********************************************************************
1093 * urlcache_delete_file (Internal)
1095 static DWORD
urlcache_delete_file(const cache_container
*container
,
1096 urlcache_header
*header
, entry_url
*url_entry
)
1098 WIN32_FILE_ATTRIBUTE_DATA attr
;
1099 WCHAR path
[MAX_PATH
];
1100 LONG path_size
= sizeof(path
);
1104 if(!url_entry
->local_name_off
)
1107 if(!urlcache_create_file_pathW(container
, header
,
1108 (LPCSTR
)url_entry
+url_entry
->local_name_off
,
1109 url_entry
->cache_dir
, path
, &path_size
, FALSE
))
1112 if(!GetFileAttributesExW(path
, GetFileExInfoStandard
, &attr
))
1114 file_time_to_dos_date_time(&attr
.ftLastWriteTime
, &date
, &time
);
1115 if(date
!= url_entry
->write_date
|| time
!= url_entry
->write_time
)
1118 err
= (DeleteFileW(path
) ? ERROR_SUCCESS
: GetLastError());
1119 if(err
== ERROR_ACCESS_DENIED
|| err
== ERROR_SHARING_VIOLATION
)
1123 if (url_entry
->cache_dir
< header
->dirs_no
)
1125 if (header
->directory_data
[url_entry
->cache_dir
].files_no
)
1126 header
->directory_data
[url_entry
->cache_dir
].files_no
--;
1128 if (url_entry
->cache_entry_type
& STICKY_CACHE_ENTRY
)
1130 if (url_entry
->size
.QuadPart
< header
->exempt_usage
.QuadPart
)
1131 header
->exempt_usage
.QuadPart
-= url_entry
->size
.QuadPart
;
1133 header
->exempt_usage
.QuadPart
= 0;
1137 if (url_entry
->size
.QuadPart
< header
->cache_usage
.QuadPart
)
1138 header
->cache_usage
.QuadPart
-= url_entry
->size
.QuadPart
;
1140 header
->cache_usage
.QuadPart
= 0;
1143 return ERROR_SUCCESS
;
1146 static BOOL
urlcache_clean_leaked_entries(cache_container
*container
, urlcache_header
*header
)
1151 leak_off
= &header
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
];
1153 entry_url
*url_entry
= (entry_url
*)((LPBYTE
)header
+ *leak_off
);
1155 if(SUCCEEDED(urlcache_delete_file(container
, header
, url_entry
))) {
1156 *leak_off
= url_entry
->exempt_delta
;
1157 urlcache_entry_free(header
, &url_entry
->header
);
1160 leak_off
= &url_entry
->exempt_delta
;
1167 /***********************************************************************
1168 * cache_container_clean_index (Internal)
1170 * This function is meant to make place in index file by removing leaked
1171 * files entries and resizing the file.
1173 * CAUTION: file view may get mapped to new memory
1176 * ERROR_SUCCESS when new memory is available
1177 * error code otherwise
1179 static DWORD
cache_container_clean_index(cache_container
*container
, urlcache_header
**file_view
)
1181 urlcache_header
*header
= *file_view
;
1184 TRACE("(%s %s)\n", debugstr_a(container
->cache_prefix
), debugstr_w(container
->path
));
1186 if(urlcache_clean_leaked_entries(container
, header
))
1187 return ERROR_SUCCESS
;
1189 if(header
->size
>= ALLOCATION_TABLE_SIZE
*8*BLOCKSIZE
+ ENTRY_START_OFFSET
) {
1190 WARN("index file has maximal size\n");
1191 return ERROR_NOT_ENOUGH_MEMORY
;
1194 cache_container_close_index(container
);
1195 ret
= cache_container_open_index(container
, header
->capacity_in_blocks
*2);
1196 if(ret
!= ERROR_SUCCESS
)
1198 header
= MapViewOfFile(container
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
1200 return GetLastError();
1202 UnmapViewOfFile(*file_view
);
1203 *file_view
= header
;
1204 return ERROR_SUCCESS
;
1207 /* Just like DosDateTimeToFileTime, except that it also maps the special
1208 * case of a DOS date/time of (0,0) to a filetime of (0,0).
1210 static void dos_date_time_to_file_time(WORD fatdate
, WORD fattime
,
1213 if (!fatdate
&& !fattime
)
1214 ft
->dwLowDateTime
= ft
->dwHighDateTime
= 0;
1216 DosDateTimeToFileTime(fatdate
, fattime
, ft
);
1219 static int urlcache_decode_url(const char *url
, WCHAR
*decoded_url
, int decoded_len
)
1222 DWORD len
, part_len
;
1225 memset(&uc
, 0, sizeof(uc
));
1226 uc
.dwStructSize
= sizeof(uc
);
1227 uc
.dwHostNameLength
= 1;
1228 if(!InternetCrackUrlA(url
, 0, 0, &uc
))
1229 uc
.nScheme
= INTERNET_SCHEME_UNKNOWN
;
1231 if(uc
.nScheme
!=INTERNET_SCHEME_HTTP
&& uc
.nScheme
!=INTERNET_SCHEME_HTTPS
)
1232 return MultiByteToWideChar(CP_UTF8
, 0, url
, -1, decoded_url
, decoded_len
);
1237 len
= MultiByteToWideChar(CP_UTF8
, 0, url
, uc
.lpszHostName
-url
, decoded_url
, decoded_len
);
1243 host_name
= heap_alloc(uc
.dwHostNameLength
*sizeof(WCHAR
));
1246 if(!MultiByteToWideChar(CP_UTF8
, 0, uc
.lpszHostName
, uc
.dwHostNameLength
,
1247 host_name
, uc
.dwHostNameLength
)) {
1248 heap_free(host_name
);
1251 part_len
= IdnToUnicode(0, host_name
, uc
.dwHostNameLength
,
1252 decoded_url
? decoded_url
+len
: NULL
, decoded_len
);
1253 heap_free(host_name
);
1255 SetLastError(ERROR_INTERNET_INVALID_URL
);
1260 decoded_len
-= part_len
;
1262 part_len
= MultiByteToWideChar(CP_UTF8
, 0,
1263 uc
.lpszHostName
+uc
.dwHostNameLength
,
1264 -1, decoded_url
? decoded_url
+len
: NULL
, decoded_len
);
1272 /***********************************************************************
1273 * urlcache_copy_entry (Internal)
1275 * Copies an entry from the cache index file to the Win32 structure
1278 * ERROR_SUCCESS if the buffer was big enough
1279 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1282 static DWORD
urlcache_copy_entry(cache_container
*container
, const urlcache_header
*header
,
1283 INTERNET_CACHE_ENTRY_INFOA
*entry_info
, DWORD
*info_size
, const entry_url
*url_entry
, BOOL unicode
)
1286 DWORD size
= sizeof(*entry_info
);
1288 if(*info_size
>= size
) {
1289 entry_info
->lpHeaderInfo
= NULL
;
1290 entry_info
->lpszFileExtension
= NULL
;
1291 entry_info
->lpszLocalFileName
= NULL
;
1292 entry_info
->lpszSourceUrlName
= NULL
;
1293 entry_info
->CacheEntryType
= url_entry
->cache_entry_type
;
1294 entry_info
->u
.dwExemptDelta
= url_entry
->exempt_delta
;
1295 entry_info
->dwHeaderInfoSize
= url_entry
->header_info_size
;
1296 entry_info
->dwHitRate
= url_entry
->hit_rate
;
1297 entry_info
->dwSizeHigh
= url_entry
->size
.u
.HighPart
;
1298 entry_info
->dwSizeLow
= url_entry
->size
.u
.LowPart
;
1299 entry_info
->dwStructSize
= sizeof(*entry_info
);
1300 entry_info
->dwUseCount
= url_entry
->use_count
;
1301 dos_date_time_to_file_time(url_entry
->expire_date
, url_entry
->expire_time
, &entry_info
->ExpireTime
);
1302 entry_info
->LastAccessTime
= url_entry
->access_time
;
1303 entry_info
->LastModifiedTime
= url_entry
->modification_time
;
1304 dos_date_time_to_file_time(url_entry
->sync_date
, url_entry
->sync_time
, &entry_info
->LastSyncTime
);
1308 url_len
= urlcache_decode_url((const char*)url_entry
+url_entry
->url_off
, NULL
, 0);
1310 url_len
= strlen((LPCSTR
)url_entry
+url_entry
->url_off
) + 1;
1311 size
+= url_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1313 if(*info_size
>= size
) {
1314 DWORD url_size
= url_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1316 entry_info
->lpszSourceUrlName
= (LPSTR
)entry_info
+size
-url_size
;
1318 urlcache_decode_url((const char*)url_entry
+url_entry
->url_off
, (WCHAR
*)entry_info
->lpszSourceUrlName
, url_len
);
1320 memcpy(entry_info
->lpszSourceUrlName
, (LPCSTR
)url_entry
+url_entry
->url_off
, url_size
);
1323 if(size
%4 && size
<*info_size
)
1324 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1325 size
= DWORD_ALIGN(size
);
1327 if(url_entry
->local_name_off
) {
1328 LONG file_name_size
;
1330 file_name
= (LPSTR
)entry_info
+size
;
1331 file_name_size
= *info_size
-size
;
1332 if((unicode
&& urlcache_create_file_pathW(container
, header
, (LPCSTR
)url_entry
+url_entry
->local_name_off
,
1333 url_entry
->cache_dir
, (LPWSTR
)file_name
, &file_name_size
, FALSE
)) ||
1334 (!unicode
&& urlcache_create_file_pathA(container
, header
, (LPCSTR
)url_entry
+url_entry
->local_name_off
,
1335 url_entry
->cache_dir
, file_name
, &file_name_size
))) {
1336 entry_info
->lpszLocalFileName
= file_name
;
1338 size
+= file_name_size
;
1340 if(size
%4 && size
<*info_size
)
1341 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1342 size
= DWORD_ALIGN(size
);
1345 if(url_entry
->header_info_off
) {
1349 header_len
= MultiByteToWideChar(CP_UTF8
, 0, (const char*)url_entry
+url_entry
->header_info_off
,
1350 url_entry
->header_info_size
, NULL
, 0);
1352 header_len
= url_entry
->header_info_size
;
1353 size
+= header_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1355 if(*info_size
>= size
) {
1356 DWORD header_size
= header_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1357 entry_info
->lpHeaderInfo
= (LPBYTE
)entry_info
+size
-header_size
;
1359 MultiByteToWideChar(CP_UTF8
, 0, (const char*)url_entry
+url_entry
->header_info_off
,
1360 url_entry
->header_info_size
, (LPWSTR
)entry_info
->lpHeaderInfo
, header_len
);
1362 memcpy(entry_info
->lpHeaderInfo
, (LPCSTR
)url_entry
+url_entry
->header_info_off
, header_len
);
1364 if(size
%4 && size
<*info_size
)
1365 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1366 size
= DWORD_ALIGN(size
);
1369 if(url_entry
->file_extension_off
) {
1373 ext_len
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)url_entry
+url_entry
->file_extension_off
, -1, NULL
, 0);
1375 ext_len
= strlen((LPCSTR
)url_entry
+url_entry
->file_extension_off
) + 1;
1376 size
+= ext_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1378 if(*info_size
>= size
) {
1379 DWORD ext_size
= ext_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1380 entry_info
->lpszFileExtension
= (LPSTR
)entry_info
+size
-ext_size
;
1382 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)url_entry
+url_entry
->file_extension_off
, -1, (LPWSTR
)entry_info
->lpszFileExtension
, ext_len
);
1384 memcpy(entry_info
->lpszFileExtension
, (LPCSTR
)url_entry
+url_entry
->file_extension_off
, ext_len
*sizeof(CHAR
));
1387 if(size
%4 && size
<*info_size
)
1388 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1389 size
= DWORD_ALIGN(size
);
1392 if(size
> *info_size
) {
1394 return ERROR_INSUFFICIENT_BUFFER
;
1397 return ERROR_SUCCESS
;
1400 /***********************************************************************
1401 * urlcache_set_entry_info (Internal)
1403 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1404 * according to the flags set by field_control.
1407 * ERROR_SUCCESS if the buffer was big enough
1408 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1411 static DWORD
urlcache_set_entry_info(entry_url
*url_entry
, const INTERNET_CACHE_ENTRY_INFOA
*entry_info
, DWORD field_control
)
1413 if (field_control
& CACHE_ENTRY_ACCTIME_FC
)
1414 url_entry
->access_time
= entry_info
->LastAccessTime
;
1415 if (field_control
& CACHE_ENTRY_ATTRIBUTE_FC
)
1416 url_entry
->cache_entry_type
= entry_info
->CacheEntryType
;
1417 if (field_control
& CACHE_ENTRY_EXEMPT_DELTA_FC
)
1418 url_entry
->exempt_delta
= entry_info
->u
.dwExemptDelta
;
1419 if (field_control
& CACHE_ENTRY_EXPTIME_FC
)
1420 file_time_to_dos_date_time(&entry_info
->ExpireTime
, &url_entry
->expire_date
, &url_entry
->expire_time
);
1421 if (field_control
& CACHE_ENTRY_HEADERINFO_FC
)
1422 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1423 if (field_control
& CACHE_ENTRY_HITRATE_FC
)
1424 url_entry
->hit_rate
= entry_info
->dwHitRate
;
1425 if (field_control
& CACHE_ENTRY_MODTIME_FC
)
1426 url_entry
->modification_time
= entry_info
->LastModifiedTime
;
1427 if (field_control
& CACHE_ENTRY_SYNCTIME_FC
)
1428 file_time_to_dos_date_time(&entry_info
->LastAccessTime
, &url_entry
->sync_date
, &url_entry
->sync_time
);
1430 return ERROR_SUCCESS
;
1433 /***********************************************************************
1434 * urlcache_hash_key (Internal)
1436 * Returns the hash key for a given string
1439 * hash key for the string
1442 static DWORD
urlcache_hash_key(LPCSTR lpszKey
)
1444 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1445 * but the algorithm and result are not the same!
1447 static const unsigned char lookupTable
[256] =
1449 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1450 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1451 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1452 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1453 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1454 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1455 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1456 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1457 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1458 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1459 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1460 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1461 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1462 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1463 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1464 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1465 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1466 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1467 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1468 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1469 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1470 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1471 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1472 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1473 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1474 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1475 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1476 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1477 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1478 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1479 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1480 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1485 for (i
= 0; i
< sizeof(key
) / sizeof(key
[0]); i
++)
1486 key
[i
] = lookupTable
[(*lpszKey
+ i
) & 0xFF];
1488 for (lpszKey
++; *lpszKey
; lpszKey
++)
1490 for (i
= 0; i
< sizeof(key
) / sizeof(key
[0]); i
++)
1491 key
[i
] = lookupTable
[*lpszKey
^ key
[i
]];
1494 return *(DWORD
*)key
;
1497 static inline entry_hash_table
* urlcache_get_hash_table(const urlcache_header
*pHeader
, DWORD dwOffset
)
1501 return (entry_hash_table
*)((LPBYTE
)pHeader
+ dwOffset
);
1504 static BOOL
urlcache_find_hash_entry(const urlcache_header
*pHeader
, LPCSTR lpszUrl
, struct hash_entry
**ppHashEntry
)
1506 /* structure of hash table:
1507 * 448 entries divided into 64 blocks
1508 * each block therefore contains a chain of 7 key/offset pairs
1509 * how position in table is calculated:
1510 * 1. the url is hashed in helper function
1511 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1512 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1515 * there can be multiple hash tables in the file and the offset to
1516 * the next one is stored in the header of the hash table
1518 DWORD key
= urlcache_hash_key(lpszUrl
);
1519 DWORD offset
= (key
& (HASHTABLE_NUM_ENTRIES
-1)) * HASHTABLE_BLOCKSIZE
;
1520 entry_hash_table
* pHashEntry
;
1523 key
>>= HASHTABLE_FLAG_BITS
;
1525 for (pHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1526 pHashEntry
; pHashEntry
= urlcache_get_hash_table(pHeader
, pHashEntry
->next
))
1529 if (pHashEntry
->id
!= id
++)
1531 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry
->id
, id
);
1534 /* make sure that it is in fact a hash entry */
1535 if (pHashEntry
->header
.signature
!= HASH_SIGNATURE
)
1537 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&pHashEntry
->header
.signature
);
1541 for (i
= 0; i
< HASHTABLE_BLOCKSIZE
; i
++)
1543 struct hash_entry
*pHashElement
= &pHashEntry
->hash_table
[offset
+ i
];
1544 if (key
== pHashElement
->key
>>HASHTABLE_FLAG_BITS
)
1546 /* FIXME: we should make sure that this is the right element
1547 * before returning and claiming that it is. We can do this
1548 * by doing a simple compare between the URL we were given
1549 * and the URL stored in the entry. However, this assumes
1550 * we know the format of all the entries stored in the
1552 *ppHashEntry
= pHashElement
;
1560 /***********************************************************************
1561 * urlcache_hash_entry_set_flags (Internal)
1563 * Sets special bits in hash key
1569 static void urlcache_hash_entry_set_flags(struct hash_entry
*pHashEntry
, DWORD dwFlag
)
1571 pHashEntry
->key
= (pHashEntry
->key
>> HASHTABLE_FLAG_BITS
<< HASHTABLE_FLAG_BITS
) | dwFlag
;
1574 /***********************************************************************
1575 * urlcache_hash_entry_delete (Internal)
1577 * Searches all the hash tables in the index for the given URL and
1578 * then if found deletes the entry.
1581 * TRUE if the entry was found
1582 * FALSE if the entry could not be found
1585 static BOOL
urlcache_hash_entry_delete(struct hash_entry
*pHashEntry
)
1587 pHashEntry
->key
= HASHTABLE_DEL
;
1591 /***********************************************************************
1592 * urlcache_hash_entry_create (Internal)
1594 * Searches all the hash tables for a free slot based on the offset
1595 * generated from the hash key. If a free slot is found, the offset and
1596 * key are entered into the hash table.
1599 * ERROR_SUCCESS if the entry was added
1600 * Any other Win32 error code if the entry could not be added
1603 static DWORD
urlcache_hash_entry_create(urlcache_header
*pHeader
, LPCSTR lpszUrl
, DWORD dwOffsetEntry
, DWORD dwFieldType
)
1605 /* see urlcache_find_hash_entry for structure of hash tables */
1607 DWORD key
= urlcache_hash_key(lpszUrl
);
1608 DWORD offset
= (key
& (HASHTABLE_NUM_ENTRIES
-1)) * HASHTABLE_BLOCKSIZE
;
1609 entry_hash_table
* pHashEntry
, *pHashPrev
= NULL
;
1613 key
= ((key
>> HASHTABLE_FLAG_BITS
) << HASHTABLE_FLAG_BITS
) + dwFieldType
;
1615 for (pHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1616 pHashEntry
; pHashEntry
= urlcache_get_hash_table(pHeader
, pHashEntry
->next
))
1619 pHashPrev
= pHashEntry
;
1621 if (pHashEntry
->id
!= id
++)
1623 ERR("not right hash table number (%d) expected %d\n", pHashEntry
->id
, id
);
1626 /* make sure that it is in fact a hash entry */
1627 if (pHashEntry
->header
.signature
!= HASH_SIGNATURE
)
1629 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&pHashEntry
->header
.signature
);
1633 for (i
= 0; i
< HASHTABLE_BLOCKSIZE
; i
++)
1635 struct hash_entry
*pHashElement
= &pHashEntry
->hash_table
[offset
+ i
];
1636 if (pHashElement
->key
==HASHTABLE_FREE
|| pHashElement
->key
==HASHTABLE_DEL
) /* if the slot is free */
1638 pHashElement
->key
= key
;
1639 pHashElement
->offset
= dwOffsetEntry
;
1640 return ERROR_SUCCESS
;
1644 error
= urlcache_create_hash_table(pHeader
, pHashPrev
, &pHashEntry
);
1645 if (error
!= ERROR_SUCCESS
)
1648 pHashEntry
->hash_table
[offset
].key
= key
;
1649 pHashEntry
->hash_table
[offset
].offset
= dwOffsetEntry
;
1650 return ERROR_SUCCESS
;
1653 /***********************************************************************
1654 * urlcache_enum_hash_tables (Internal)
1656 * Enumerates the hash tables in a container.
1659 * TRUE if an entry was found
1660 * FALSE if there are no more tables to enumerate.
1663 static BOOL
urlcache_enum_hash_tables(const urlcache_header
*pHeader
, DWORD
*id
, entry_hash_table
**ppHashEntry
)
1665 for (*ppHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1666 *ppHashEntry
; *ppHashEntry
= urlcache_get_hash_table(pHeader
, (*ppHashEntry
)->next
))
1668 TRACE("looking at hash table number %d\n", (*ppHashEntry
)->id
);
1669 if ((*ppHashEntry
)->id
!= *id
)
1671 /* make sure that it is in fact a hash entry */
1672 if ((*ppHashEntry
)->header
.signature
!= HASH_SIGNATURE
)
1674 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&(*ppHashEntry
)->header
.signature
);
1679 TRACE("hash table number %d found\n", *id
);
1685 /***********************************************************************
1686 * urlcache_enum_hash_table_entries (Internal)
1688 * Enumerates entries in a hash table and returns the next non-free entry.
1691 * TRUE if an entry was found
1692 * FALSE if the hash table is empty or there are no more entries to
1696 static BOOL
urlcache_enum_hash_table_entries(const urlcache_header
*pHeader
, const entry_hash_table
*pHashEntry
,
1697 DWORD
* index
, const struct hash_entry
**ppHashEntry
)
1699 for (; *index
< HASHTABLE_SIZE
; (*index
)++)
1701 if (pHashEntry
->hash_table
[*index
].key
==HASHTABLE_FREE
|| pHashEntry
->hash_table
[*index
].key
==HASHTABLE_DEL
)
1704 *ppHashEntry
= &pHashEntry
->hash_table
[*index
];
1705 TRACE("entry found %d\n", *index
);
1708 TRACE("no more entries (%d)\n", *index
);
1712 /***********************************************************************
1713 * cache_container_delete_dir (Internal)
1715 * Erase a directory containing an URL cache.
1718 * TRUE success, FALSE failure/aborted.
1721 static BOOL
cache_container_delete_dir(LPCWSTR lpszPath
)
1724 WCHAR path
[MAX_PATH
+ 1];
1725 SHFILEOPSTRUCTW shfos
;
1728 path_len
= strlenW(lpszPath
);
1729 if (path_len
>= MAX_PATH
)
1731 strcpyW(path
, lpszPath
);
1732 path
[path_len
+ 1] = 0; /* double-NUL-terminate path */
1735 shfos
.wFunc
= FO_DELETE
;
1738 shfos
.fFlags
= FOF_NOCONFIRMATION
;
1739 shfos
.fAnyOperationsAborted
= FALSE
;
1740 ret
= SHFileOperationW(&shfos
);
1742 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path
), ret
);
1743 return !(ret
|| shfos
.fAnyOperationsAborted
);
1746 /***********************************************************************
1747 * urlcache_hash_entry_is_locked (Internal)
1749 * Checks if entry is locked. Unlocks it if possible.
1751 static BOOL
urlcache_hash_entry_is_locked(struct hash_entry
*hash_entry
, entry_url
*url_entry
)
1754 ULARGE_INTEGER acc_time
, time
;
1756 if ((hash_entry
->key
& ((1<<HASHTABLE_FLAG_BITS
)-1)) != HASHTABLE_LOCK
)
1759 GetSystemTimeAsFileTime(&cur_time
);
1760 time
.u
.LowPart
= cur_time
.dwLowDateTime
;
1761 time
.u
.HighPart
= cur_time
.dwHighDateTime
;
1763 acc_time
.u
.LowPart
= url_entry
->access_time
.dwLowDateTime
;
1764 acc_time
.u
.HighPart
= url_entry
->access_time
.dwHighDateTime
;
1766 time
.QuadPart
-= acc_time
.QuadPart
;
1768 /* check if entry was locked for at least a day */
1769 if(time
.QuadPart
> (ULONGLONG
)24*60*60*FILETIME_SECOND
) {
1770 urlcache_hash_entry_set_flags(hash_entry
, HASHTABLE_URL
);
1771 url_entry
->use_count
= 0;
1778 static BOOL
urlcache_get_entry_info(const char *url
, void *entry_info
,
1779 DWORD
*size
, DWORD flags
, BOOL unicode
)
1781 urlcache_header
*header
;
1782 struct hash_entry
*hash_entry
;
1783 const entry_url
*url_entry
;
1784 cache_container
*container
;
1787 TRACE("(%s, %p, %p, %x, %x)\n", debugstr_a(url
), entry_info
, size
, flags
, unicode
);
1789 if(flags
& ~GET_INSTALLED_ENTRY
)
1790 FIXME("ignoring unsupported flags: %x\n", flags
);
1792 error
= cache_containers_find(url
, &container
);
1793 if(error
!= ERROR_SUCCESS
) {
1794 SetLastError(error
);
1798 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
1799 if(error
!= ERROR_SUCCESS
) {
1800 SetLastError(error
);
1804 if(!(header
= cache_container_lock_index(container
)))
1807 if(!urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
1808 cache_container_unlock_index(container
, header
);
1809 WARN("entry %s not found!\n", debugstr_a(url
));
1810 SetLastError(ERROR_FILE_NOT_FOUND
);
1814 url_entry
= (const entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
1815 if(url_entry
->header
.signature
!= URL_SIGNATURE
) {
1816 cache_container_unlock_index(container
, header
);
1817 FIXME("Trying to retrieve entry of unknown format %s\n",
1818 debugstr_an((LPCSTR
)&url_entry
->header
.signature
, sizeof(DWORD
)));
1819 SetLastError(ERROR_FILE_NOT_FOUND
);
1823 TRACE("Found URL: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->url_off
));
1824 TRACE("Header info: %s\n", debugstr_an((LPCSTR
)url_entry
+
1825 url_entry
->header_info_off
, url_entry
->header_info_size
));
1827 if((flags
& GET_INSTALLED_ENTRY
) && !(url_entry
->cache_entry_type
& INSTALLED_CACHE_ENTRY
)) {
1828 cache_container_unlock_index(container
, header
);
1829 SetLastError(ERROR_FILE_NOT_FOUND
);
1837 error
= urlcache_copy_entry(container
, header
, entry_info
, size
, url_entry
, unicode
);
1838 if(error
!= ERROR_SUCCESS
) {
1839 cache_container_unlock_index(container
, header
);
1840 SetLastError(error
);
1843 if(url_entry
->local_name_off
)
1844 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->local_name_off
));
1847 cache_container_unlock_index(container
, header
);
1851 /***********************************************************************
1852 * GetUrlCacheEntryInfoExA (WININET.@)
1855 BOOL WINAPI
GetUrlCacheEntryInfoExA(LPCSTR lpszUrl
,
1856 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1857 LPDWORD lpdwCacheEntryInfoBufSize
, LPSTR lpszReserved
,
1858 LPDWORD lpdwReserved
, LPVOID lpReserved
, DWORD dwFlags
)
1860 if(lpszReserved
!=NULL
|| lpdwReserved
!=NULL
|| lpReserved
!=NULL
) {
1861 ERR("Reserved value was not 0\n");
1862 SetLastError(ERROR_INVALID_PARAMETER
);
1866 return urlcache_get_entry_info(lpszUrl
, lpCacheEntryInfo
,
1867 lpdwCacheEntryInfoBufSize
, dwFlags
, FALSE
);
1870 /***********************************************************************
1871 * GetUrlCacheEntryInfoA (WININET.@)
1874 BOOL WINAPI
GetUrlCacheEntryInfoA(LPCSTR lpszUrlName
,
1875 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1876 LPDWORD lpdwCacheEntryInfoBufferSize
)
1878 return GetUrlCacheEntryInfoExA(lpszUrlName
, lpCacheEntryInfo
,
1879 lpdwCacheEntryInfoBufferSize
, NULL
, NULL
, NULL
, 0);
1882 static int urlcache_encode_url(const WCHAR
*url
, char *encoded_url
, int encoded_len
)
1885 DWORD len
, part_len
;
1888 TRACE("%s\n", debugstr_w(url
));
1890 memset(&uc
, 0, sizeof(uc
));
1891 uc
.dwStructSize
= sizeof(uc
);
1892 uc
.dwHostNameLength
= 1;
1893 if(!InternetCrackUrlW(url
, 0, 0, &uc
))
1894 uc
.nScheme
= INTERNET_SCHEME_UNKNOWN
;
1896 if(uc
.nScheme
!=INTERNET_SCHEME_HTTP
&& uc
.nScheme
!=INTERNET_SCHEME_HTTPS
)
1897 return WideCharToMultiByte(CP_UTF8
, 0, url
, -1, encoded_url
, encoded_len
, NULL
, NULL
);
1899 len
= WideCharToMultiByte(CP_UTF8
, 0, url
, uc
.lpszHostName
-url
,
1900 encoded_url
, encoded_len
, NULL
, NULL
);
1906 part_len
= IdnToAscii(0, uc
.lpszHostName
, uc
.dwHostNameLength
, NULL
, 0);
1908 SetLastError(ERROR_INTERNET_INVALID_URL
);
1912 punycode
= heap_alloc(part_len
*sizeof(WCHAR
));
1916 part_len
= IdnToAscii(0, uc
.lpszHostName
, uc
.dwHostNameLength
, punycode
, part_len
);
1918 heap_free(punycode
);
1922 part_len
= WideCharToMultiByte(CP_UTF8
, 0, punycode
, part_len
,
1923 encoded_url
? encoded_url
+len
: NULL
, encoded_len
, NULL
, NULL
);
1924 heap_free(punycode
);
1928 encoded_len
-= part_len
;
1931 part_len
= WideCharToMultiByte(CP_UTF8
, 0, uc
.lpszHostName
+uc
.dwHostNameLength
,
1932 -1, encoded_url
? encoded_url
+len
: NULL
, encoded_len
, NULL
, NULL
);
1937 TRACE("got (%d)%s\n", len
, debugstr_a(encoded_url
));
1941 static BOOL
urlcache_encode_url_alloc(const WCHAR
*url
, char **encoded_url
)
1946 encoded_len
= urlcache_encode_url(url
, NULL
, 0);
1950 ret
= heap_alloc(encoded_len
*sizeof(WCHAR
));
1954 encoded_len
= urlcache_encode_url(url
, ret
, encoded_len
);
1964 /***********************************************************************
1965 * GetUrlCacheEntryInfoExW (WININET.@)
1968 BOOL WINAPI
GetUrlCacheEntryInfoExW(LPCWSTR lpszUrl
,
1969 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
1970 LPDWORD lpdwCacheEntryInfoBufSize
, LPWSTR lpszReserved
,
1971 LPDWORD lpdwReserved
, LPVOID lpReserved
, DWORD dwFlags
)
1976 if(lpszReserved
!=NULL
|| lpdwReserved
!=NULL
|| lpReserved
!=NULL
) {
1977 ERR("Reserved value was not 0\n");
1978 SetLastError(ERROR_INVALID_PARAMETER
);
1982 /* Ignore GET_INSTALLED_ENTRY flag in unicode version of function */
1983 dwFlags
&= ~GET_INSTALLED_ENTRY
;
1985 if(!urlcache_encode_url_alloc(lpszUrl
, &url
))
1988 ret
= urlcache_get_entry_info(url
, lpCacheEntryInfo
,
1989 lpdwCacheEntryInfoBufSize
, dwFlags
, TRUE
);
1994 /***********************************************************************
1995 * GetUrlCacheEntryInfoW (WININET.@)
1998 BOOL WINAPI
GetUrlCacheEntryInfoW(LPCWSTR lpszUrl
,
1999 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
2000 LPDWORD lpdwCacheEntryInfoBufferSize
)
2002 return GetUrlCacheEntryInfoExW(lpszUrl
, lpCacheEntryInfo
,
2003 lpdwCacheEntryInfoBufferSize
, NULL
, NULL
, NULL
, 0);
2006 /***********************************************************************
2007 * SetUrlCacheEntryInfoA (WININET.@)
2009 BOOL WINAPI
SetUrlCacheEntryInfoA(LPCSTR lpszUrlName
,
2010 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
2011 DWORD dwFieldControl
)
2013 urlcache_header
*pHeader
;
2014 struct hash_entry
*pHashEntry
;
2015 entry_header
*pEntry
;
2016 cache_container
*pContainer
;
2019 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName
), lpCacheEntryInfo
, dwFieldControl
);
2021 error
= cache_containers_find(lpszUrlName
, &pContainer
);
2022 if (error
!= ERROR_SUCCESS
)
2024 SetLastError(error
);
2028 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
2029 if (error
!= ERROR_SUCCESS
)
2031 SetLastError(error
);
2035 if (!(pHeader
= cache_container_lock_index(pContainer
)))
2038 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
2040 cache_container_unlock_index(pContainer
, pHeader
);
2041 WARN("entry %s not found!\n", debugstr_a(lpszUrlName
));
2042 SetLastError(ERROR_FILE_NOT_FOUND
);
2046 pEntry
= (entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2047 if (pEntry
->signature
!= URL_SIGNATURE
)
2049 cache_container_unlock_index(pContainer
, pHeader
);
2050 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR
)&pEntry
->signature
, sizeof(DWORD
)));
2051 SetLastError(ERROR_FILE_NOT_FOUND
);
2055 urlcache_set_entry_info((entry_url
*)pEntry
, lpCacheEntryInfo
, dwFieldControl
);
2057 cache_container_unlock_index(pContainer
, pHeader
);
2062 /***********************************************************************
2063 * SetUrlCacheEntryInfoW (WININET.@)
2065 BOOL WINAPI
SetUrlCacheEntryInfoW(LPCWSTR lpszUrl
,
2066 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
2067 DWORD dwFieldControl
)
2072 if(!urlcache_encode_url_alloc(lpszUrl
, &url
))
2075 ret
= SetUrlCacheEntryInfoA(url
, (INTERNET_CACHE_ENTRY_INFOA
*)lpCacheEntryInfo
, dwFieldControl
);
2080 static BOOL
urlcache_entry_get_file(const char *url
, void *entry_info
, DWORD
*size
, BOOL unicode
)
2082 urlcache_header
*header
;
2083 struct hash_entry
*hash_entry
;
2084 entry_url
*url_entry
;
2085 cache_container
*container
;
2088 TRACE("(%s, %p, %p, %x)\n", debugstr_a(url
), entry_info
, size
, unicode
);
2090 if(!url
|| !size
|| (!entry_info
&& *size
)) {
2091 SetLastError(ERROR_INVALID_PARAMETER
);
2095 error
= cache_containers_find(url
, &container
);
2096 if(error
!= ERROR_SUCCESS
) {
2097 SetLastError(error
);
2101 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2102 if (error
!= ERROR_SUCCESS
) {
2103 SetLastError(error
);
2107 if (!(header
= cache_container_lock_index(container
)))
2110 if (!urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
2111 cache_container_unlock_index(container
, header
);
2112 TRACE("entry %s not found!\n", url
);
2113 SetLastError(ERROR_FILE_NOT_FOUND
);
2117 url_entry
= (entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
2118 if(url_entry
->header
.signature
!= URL_SIGNATURE
) {
2119 cache_container_unlock_index(container
, header
);
2120 FIXME("Trying to retrieve entry of unknown format %s\n",
2121 debugstr_an((LPSTR
)&url_entry
->header
.signature
, sizeof(DWORD
)));
2122 SetLastError(ERROR_FILE_NOT_FOUND
);
2126 if(!url_entry
->local_name_off
) {
2127 cache_container_unlock_index(container
, header
);
2128 SetLastError(ERROR_INVALID_DATA
);
2132 TRACE("Found URL: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->url_off
));
2133 TRACE("Header info: %s\n", debugstr_an((LPCSTR
)url_entry
+ url_entry
->header_info_off
,
2134 url_entry
->header_info_size
));
2136 error
= urlcache_copy_entry(container
, header
, entry_info
,
2137 size
, url_entry
, unicode
);
2138 if(error
!= ERROR_SUCCESS
) {
2139 cache_container_unlock_index(container
, header
);
2140 SetLastError(error
);
2143 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->local_name_off
));
2145 url_entry
->hit_rate
++;
2146 url_entry
->use_count
++;
2147 urlcache_hash_entry_set_flags(hash_entry
, HASHTABLE_LOCK
);
2148 GetSystemTimeAsFileTime(&url_entry
->access_time
);
2150 cache_container_unlock_index(container
, header
);
2155 /***********************************************************************
2156 * RetrieveUrlCacheEntryFileA (WININET.@)
2159 BOOL WINAPI
RetrieveUrlCacheEntryFileA(LPCSTR lpszUrlName
,
2160 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
2161 LPDWORD lpdwCacheEntryInfoBufferSize
, DWORD dwReserved
)
2163 return urlcache_entry_get_file(lpszUrlName
, lpCacheEntryInfo
,
2164 lpdwCacheEntryInfoBufferSize
, FALSE
);
2167 /***********************************************************************
2168 * RetrieveUrlCacheEntryFileW (WININET.@)
2171 BOOL WINAPI
RetrieveUrlCacheEntryFileW(LPCWSTR lpszUrlName
,
2172 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
2173 LPDWORD lpdwCacheEntryInfoBufferSize
, DWORD dwReserved
)
2178 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
2181 ret
= urlcache_entry_get_file(url
, lpCacheEntryInfo
,
2182 lpdwCacheEntryInfoBufferSize
, TRUE
);
2187 static BOOL
urlcache_entry_delete(const cache_container
*pContainer
,
2188 urlcache_header
*pHeader
, struct hash_entry
*pHashEntry
)
2190 entry_header
*pEntry
;
2191 entry_url
* pUrlEntry
;
2193 pEntry
= (entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2194 if (pEntry
->signature
!= URL_SIGNATURE
)
2196 FIXME("Trying to delete entry of unknown format %s\n",
2197 debugstr_an((LPCSTR
)&pEntry
->signature
, sizeof(DWORD
)));
2198 SetLastError(ERROR_FILE_NOT_FOUND
);
2202 pUrlEntry
= (entry_url
*)pEntry
;
2203 if(urlcache_hash_entry_is_locked(pHashEntry
, pUrlEntry
))
2205 TRACE("Trying to delete locked entry\n");
2206 pUrlEntry
->cache_entry_type
|= PENDING_DELETE_CACHE_ENTRY
;
2207 SetLastError(ERROR_SHARING_VIOLATION
);
2211 if(!urlcache_delete_file(pContainer
, pHeader
, pUrlEntry
))
2213 urlcache_entry_free(pHeader
, pEntry
);
2217 /* Add entry to leaked files list */
2218 pUrlEntry
->header
.signature
= LEAK_SIGNATURE
;
2219 pUrlEntry
->exempt_delta
= pHeader
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
];
2220 pHeader
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
] = pHashEntry
->offset
;
2223 urlcache_hash_entry_delete(pHashEntry
);
2227 static HANDLE free_cache_running
;
2228 static HANDLE dll_unload_event
;
2229 static DWORD WINAPI
handle_full_cache_worker(void *param
)
2231 FreeUrlCacheSpaceW(NULL
, 20, 0);
2232 ReleaseSemaphore(free_cache_running
, 1, NULL
);
2236 static void handle_full_cache(void)
2238 if(WaitForSingleObject(free_cache_running
, 0) == WAIT_OBJECT_0
) {
2239 if(!QueueUserWorkItem(handle_full_cache_worker
, NULL
, 0))
2240 ReleaseSemaphore(free_cache_running
, 1, NULL
);
2244 /* Enumerates entries in cache, allows cache unlocking between calls. */
2245 static BOOL
urlcache_next_entry(urlcache_header
*header
, DWORD
*hash_table_off
, DWORD
*hash_table_entry
,
2246 struct hash_entry
**hash_entry
, entry_header
**entry
)
2248 entry_hash_table
*hashtable_entry
;
2253 if(!*hash_table_off
) {
2254 *hash_table_off
= header
->hash_table_off
;
2255 *hash_table_entry
= 0;
2257 hashtable_entry
= urlcache_get_hash_table(header
, *hash_table_off
);
2259 if(*hash_table_off
>= header
->size
) {
2260 *hash_table_off
= 0;
2264 hashtable_entry
= urlcache_get_hash_table(header
, *hash_table_off
);
2267 if(hashtable_entry
->header
.signature
!= HASH_SIGNATURE
) {
2268 *hash_table_off
= 0;
2273 if(*hash_table_entry
>= HASHTABLE_SIZE
) {
2274 *hash_table_off
= hashtable_entry
->next
;
2275 if(!*hash_table_off
) {
2276 *hash_table_off
= 0;
2280 hashtable_entry
= urlcache_get_hash_table(header
, *hash_table_off
);
2281 *hash_table_entry
= 0;
2284 if(hashtable_entry
->hash_table
[*hash_table_entry
].key
!= HASHTABLE_DEL
&&
2285 hashtable_entry
->hash_table
[*hash_table_entry
].key
!= HASHTABLE_FREE
) {
2286 *hash_entry
= &hashtable_entry
->hash_table
[*hash_table_entry
];
2287 *entry
= (entry_header
*)((LPBYTE
)header
+ hashtable_entry
->hash_table
[*hash_table_entry
].offset
);
2288 (*hash_table_entry
)++;
2292 (*hash_table_entry
)++;
2295 *hash_table_off
= 0;
2299 /* Rates an urlcache entry to determine if it can be deleted.
2301 * Score 0 means that entry can safely be removed, the bigger rating
2302 * the smaller chance of entry being removed.
2303 * DWORD_MAX means that entry can't be deleted at all.
2305 * Rating system is currently not fully compatible with native implementation.
2307 static DWORD
urlcache_rate_entry(entry_url
*url_entry
, FILETIME
*cur_time
)
2309 ULARGE_INTEGER time
, access_time
;
2312 access_time
.u
.LowPart
= url_entry
->access_time
.dwLowDateTime
;
2313 access_time
.u
.HighPart
= url_entry
->access_time
.dwHighDateTime
;
2315 time
.u
.LowPart
= cur_time
->dwLowDateTime
;
2316 time
.u
.HighPart
= cur_time
->dwHighDateTime
;
2318 /* Don't touch entries that were added less than 10 minutes ago */
2319 if(time
.QuadPart
< access_time
.QuadPart
+ (ULONGLONG
)10*60*FILETIME_SECOND
)
2322 if(url_entry
->cache_entry_type
& STICKY_CACHE_ENTRY
)
2323 if(time
.QuadPart
< access_time
.QuadPart
+ (ULONGLONG
)url_entry
->exempt_delta
*FILETIME_SECOND
)
2326 time
.QuadPart
= (time
.QuadPart
-access_time
.QuadPart
)/FILETIME_SECOND
;
2327 rating
= 400*60*60*24/(60*60*24+time
.QuadPart
);
2329 if(url_entry
->hit_rate
> 100)
2332 rating
+= url_entry
->hit_rate
;
2337 static int dword_cmp(const void *p1
, const void *p2
)
2339 return *(const DWORD
*)p1
- *(const DWORD
*)p2
;
2342 /***********************************************************************
2343 * FreeUrlCacheSpaceW (WININET.@)
2345 * Frees up some cache.
2348 * cache_path [I] Which volume to free up from, or NULL if you don't care.
2349 * size [I] Percentage of the cache that should be free.
2350 * filter [I] Which entries can't be deleted (CacheEntryType)
2353 * TRUE success. FALSE failure.
2356 * This implementation just retrieves the path of the cache directory, and
2357 * deletes its contents from the filesystem. The correct approach would
2358 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
2360 BOOL WINAPI
FreeUrlCacheSpaceW(LPCWSTR cache_path
, DWORD size
, DWORD filter
)
2362 cache_container
*container
;
2363 DWORD path_len
, err
;
2365 TRACE("(%s, %x, %x)\n", debugstr_w(cache_path
), size
, filter
);
2367 if(size
<1 || size
>100) {
2368 SetLastError(ERROR_INVALID_PARAMETER
);
2373 path_len
= strlenW(cache_path
);
2374 if(cache_path
[path_len
-1] == '\\')
2380 if(size
==100 && !filter
) {
2381 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
2383 /* When cache_path==NULL only clean Temporary Internet Files */
2384 if((!path_len
&& container
->cache_prefix
[0]==0) ||
2385 (path_len
&& !strncmpiW(container
->path
, cache_path
, path_len
) &&
2386 (container
->path
[path_len
]=='\0' || container
->path
[path_len
]=='\\')))
2390 WaitForSingleObject(container
->mutex
, INFINITE
);
2392 /* unlock, delete, recreate and lock cache */
2393 cache_container_close_index(container
);
2394 ret_del
= cache_container_delete_dir(container
->path
);
2395 err
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2397 ReleaseMutex(container
->mutex
);
2398 if(!ret_del
|| (err
!= ERROR_SUCCESS
))
2406 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
2408 urlcache_header
*header
;
2409 struct hash_entry
*hash_entry
;
2410 entry_header
*entry
;
2411 entry_url
*url_entry
;
2412 ULONGLONG desired_size
, cur_size
;
2413 DWORD delete_factor
, hash_table_off
, hash_table_entry
;
2414 DWORD rate
[100], rate_no
;
2417 if((path_len
|| container
->cache_prefix
[0]!=0) &&
2418 (!path_len
|| strncmpiW(container
->path
, cache_path
, path_len
) ||
2419 (container
->path
[path_len
]!='\0' && container
->path
[path_len
]!='\\')))
2422 err
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2423 if(err
!= ERROR_SUCCESS
)
2426 header
= cache_container_lock_index(container
);
2430 urlcache_clean_leaked_entries(container
, header
);
2432 desired_size
= header
->cache_limit
.QuadPart
*(100-size
)/100;
2433 cur_size
= header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
;
2434 if(cur_size
<= desired_size
)
2437 delete_factor
= (cur_size
-desired_size
)*100/cur_size
;
2439 if(!delete_factor
) {
2440 cache_container_unlock_index(container
, header
);
2445 hash_table_entry
= 0;
2447 GetSystemTimeAsFileTime(&cur_time
);
2448 while(rate_no
<sizeof(rate
)/sizeof(*rate
) &&
2449 urlcache_next_entry(header
, &hash_table_off
, &hash_table_entry
, &hash_entry
, &entry
)) {
2450 if(entry
->signature
!= URL_SIGNATURE
) {
2451 WARN("only url entries are currently supported\n");
2455 url_entry
= (entry_url
*)entry
;
2456 if(url_entry
->cache_entry_type
& filter
)
2459 rate
[rate_no
] = urlcache_rate_entry(url_entry
, &cur_time
);
2460 if(rate
[rate_no
] != -1)
2465 TRACE("nothing to delete\n");
2466 cache_container_unlock_index(container
, header
);
2470 qsort(rate
, rate_no
, sizeof(DWORD
), dword_cmp
);
2472 delete_factor
= delete_factor
*rate_no
/100;
2473 delete_factor
= rate
[delete_factor
];
2474 TRACE("deleting files with rating %d or less\n", delete_factor
);
2477 while(urlcache_next_entry(header
, &hash_table_off
, &hash_table_entry
, &hash_entry
, &entry
)) {
2478 if(entry
->signature
!= URL_SIGNATURE
)
2481 url_entry
= (entry_url
*)entry
;
2482 if(url_entry
->cache_entry_type
& filter
)
2485 if(urlcache_rate_entry(url_entry
, &cur_time
) <= delete_factor
) {
2486 TRACE("deleting file: %s\n", debugstr_a((char*)url_entry
+url_entry
->local_name_off
));
2487 urlcache_entry_delete(container
, header
, hash_entry
);
2489 if(header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
<= desired_size
)
2492 /* Allow other threads to use cache while cleaning */
2493 cache_container_unlock_index(container
, header
);
2494 if(WaitForSingleObject(dll_unload_event
, 0) == WAIT_OBJECT_0
) {
2495 TRACE("got dll_unload_event - finishing\n");
2499 header
= cache_container_lock_index(container
);
2503 TRACE("cache size after cleaning 0x%s/0x%s\n",
2504 wine_dbgstr_longlong(header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
),
2505 wine_dbgstr_longlong(header
->cache_limit
.QuadPart
));
2506 cache_container_unlock_index(container
, header
);
2512 /***********************************************************************
2513 * FreeUrlCacheSpaceA (WININET.@)
2515 * See FreeUrlCacheSpaceW.
2517 BOOL WINAPI
FreeUrlCacheSpaceA(LPCSTR lpszCachePath
, DWORD dwSize
, DWORD dwFilter
)
2520 LPWSTR path
= heap_strdupAtoW(lpszCachePath
);
2521 if (lpszCachePath
== NULL
|| path
!= NULL
)
2522 ret
= FreeUrlCacheSpaceW(path
, dwSize
, dwFilter
);
2527 /***********************************************************************
2528 * UnlockUrlCacheEntryFileA (WININET.@)
2531 BOOL WINAPI
UnlockUrlCacheEntryFileA(LPCSTR lpszUrlName
, DWORD dwReserved
)
2533 urlcache_header
*pHeader
;
2534 struct hash_entry
*pHashEntry
;
2535 entry_header
*pEntry
;
2536 entry_url
* pUrlEntry
;
2537 cache_container
*pContainer
;
2540 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName
), dwReserved
);
2544 ERR("dwReserved != 0\n");
2545 SetLastError(ERROR_INVALID_PARAMETER
);
2549 error
= cache_containers_find(lpszUrlName
, &pContainer
);
2550 if (error
!= ERROR_SUCCESS
)
2552 SetLastError(error
);
2556 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
2557 if (error
!= ERROR_SUCCESS
)
2559 SetLastError(error
);
2563 if (!(pHeader
= cache_container_lock_index(pContainer
)))
2566 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
2568 cache_container_unlock_index(pContainer
, pHeader
);
2569 TRACE("entry %s not found!\n", lpszUrlName
);
2570 SetLastError(ERROR_FILE_NOT_FOUND
);
2574 pEntry
= (entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2575 if (pEntry
->signature
!= URL_SIGNATURE
)
2577 cache_container_unlock_index(pContainer
, pHeader
);
2578 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR
)&pEntry
->signature
, sizeof(DWORD
)));
2579 SetLastError(ERROR_FILE_NOT_FOUND
);
2583 pUrlEntry
= (entry_url
*)pEntry
;
2585 if (pUrlEntry
->use_count
== 0)
2587 cache_container_unlock_index(pContainer
, pHeader
);
2590 pUrlEntry
->use_count
--;
2591 if (!pUrlEntry
->use_count
)
2593 urlcache_hash_entry_set_flags(pHashEntry
, HASHTABLE_URL
);
2594 if (pUrlEntry
->cache_entry_type
& PENDING_DELETE_CACHE_ENTRY
)
2595 urlcache_entry_delete(pContainer
, pHeader
, pHashEntry
);
2598 cache_container_unlock_index(pContainer
, pHeader
);
2603 /***********************************************************************
2604 * UnlockUrlCacheEntryFileW (WININET.@)
2607 BOOL WINAPI
UnlockUrlCacheEntryFileW(LPCWSTR lpszUrlName
, DWORD dwReserved
)
2612 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
2615 ret
= UnlockUrlCacheEntryFileA(url
, dwReserved
);
2620 static BOOL
urlcache_entry_create(const char *url
, const char *ext
, WCHAR
*full_path
)
2622 cache_container
*container
;
2623 urlcache_header
*header
;
2624 char file_name
[MAX_PATH
];
2625 WCHAR extW
[MAX_PATH
];
2627 LONG full_path_len
, ext_len
= 0;
2628 BOOL generate_name
= FALSE
;
2635 TRACE("(%s, %s, %p)\n", debugstr_a(url
), debugstr_a(ext
), full_path
);
2637 memset(&uc
, 0, sizeof(uc
));
2638 uc
.dwStructSize
= sizeof(uc
);
2639 uc
.dwUrlPathLength
= 1;
2640 uc
.dwExtraInfoLength
= 1;
2641 if(!InternetCrackUrlA(url
, 0, 0, &uc
))
2642 uc
.dwUrlPathLength
= 0;
2644 if(!uc
.dwUrlPathLength
) {
2649 p
= e
= uc
.lpszUrlPath
+uc
.dwUrlPathLength
;
2650 while(p
>uc
.lpszUrlPath
&& *(p
-1)!='/' && *(p
-1)!='\\' && *(p
-1)!='.')
2652 if(p
>uc
.lpszUrlPath
&& *(p
-1)=='.') {
2654 while(p
>uc
.lpszUrlPath
&& *(p
-1)!='/' && *(p
-1)!='\\')
2660 memcpy(file_name
, p
, e
-p
);
2663 for(p
=file_name
; *p
; p
++) {
2676 generate_name
= TRUE
;
2678 error
= cache_containers_find(url
, &container
);
2679 if(error
!= ERROR_SUCCESS
) {
2680 SetLastError(error
);
2684 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2685 if(error
!= ERROR_SUCCESS
) {
2686 SetLastError(error
);
2690 if(!(header
= cache_container_lock_index(container
)))
2694 cache_dir
= (BYTE
)(rand() % header
->dirs_no
);
2696 cache_dir
= CACHE_CONTAINER_NO_SUBDIR
;
2698 full_path_len
= MAX_PATH
* sizeof(WCHAR
);
2699 if(!urlcache_create_file_pathW(container
, header
, file_name
, cache_dir
, full_path
, &full_path_len
, TRUE
)) {
2700 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2701 debugstr_a(file_name
), full_path_len
);
2702 cache_container_unlock_index(container
, header
);
2705 full_path_len
= full_path_len
/sizeof(WCHAR
) - 1;
2707 cache_container_unlock_index(container
, header
);
2713 ext_len
= MultiByteToWideChar(CP_ACP
, 0, ext
, -1, extW
+1, MAX_PATH
-1);
2715 for(p
=extW
; *p
; p
++) {
2725 if(p
[-1]==' ' || p
[-1]=='.')
2731 if(!generate_name
&& full_path_len
+5+ext_len
>=MAX_PATH
) { /* strlen("[255]") = 5 */
2732 full_path_len
= MAX_PATH
-5-ext_len
-1;
2735 for(i
=0; i
<255 && !generate_name
; i
++) {
2736 static const WCHAR format
[] = {'[','%','u',']','%','s',0};
2738 wsprintfW(full_path
+full_path_len
, format
, i
, extW
);
2740 TRACE("Trying: %s\n", debugstr_w(full_path
));
2741 file
= CreateFileW(full_path
, GENERIC_READ
, 0, NULL
, CREATE_NEW
, 0, NULL
);
2742 if(file
!= INVALID_HANDLE_VALUE
) {
2748 if(full_path_len
+8+ext_len
>= MAX_PATH
)
2749 full_path_len
= MAX_PATH
-8-ext_len
-1;
2751 /* Try to generate random name */
2752 GetSystemTimeAsFileTime(&ft
);
2753 strcpyW(full_path
+full_path_len
+8, extW
);
2755 for(i
=0; i
<255; i
++) {
2757 ULONGLONG n
= ft
.dwHighDateTime
;
2759 n
+= ft
.dwLowDateTime
;
2760 n
^= (ULONGLONG
)i
<<48;
2762 for(j
=0; j
<8; j
++) {
2765 full_path
[full_path_len
+j
] = (r
< 10 ? '0' + r
: 'A' + r
- 10);
2768 TRACE("Trying: %s\n", debugstr_w(full_path
));
2769 file
= CreateFileW(full_path
, GENERIC_READ
, 0, NULL
, CREATE_NEW
, 0, NULL
);
2770 if(file
!= INVALID_HANDLE_VALUE
) {
2776 WARN("Could not find a unique filename\n");
2780 /***********************************************************************
2781 * CreateUrlCacheEntryA (WININET.@)
2784 BOOL WINAPI
CreateUrlCacheEntryA(LPCSTR lpszUrlName
, DWORD dwExpectedFileSize
,
2785 LPCSTR lpszFileExtension
, LPSTR lpszFileName
, DWORD dwReserved
)
2787 WCHAR file_name
[MAX_PATH
];
2790 FIXME("dwReserved 0x%08x\n", dwReserved
);
2792 if(!urlcache_entry_create(lpszUrlName
, lpszFileExtension
, file_name
))
2795 if(!WideCharToMultiByte(CP_ACP
, 0, file_name
, -1, lpszFileName
, MAX_PATH
, NULL
, NULL
))
2799 /***********************************************************************
2800 * CreateUrlCacheEntryW (WININET.@)
2803 BOOL WINAPI
CreateUrlCacheEntryW(LPCWSTR lpszUrlName
, DWORD dwExpectedFileSize
,
2804 LPCWSTR lpszFileExtension
, LPWSTR lpszFileName
, DWORD dwReserved
)
2806 char *url
, *ext
= NULL
;
2810 FIXME("dwReserved 0x%08x\n", dwReserved
);
2812 if(lpszFileExtension
) {
2813 ext
= heap_strdupWtoUTF8(lpszFileExtension
);
2818 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
)) {
2823 ret
= urlcache_entry_create(url
, ext
, lpszFileName
);
2829 static BOOL
urlcache_entry_commit(const char *url
, const WCHAR
*file_name
,
2830 FILETIME expire_time
, FILETIME modify_time
, DWORD entry_type
,
2831 BYTE
*header_info
, DWORD header_size
, const char *file_ext
,
2832 const char *original_url
)
2834 cache_container
*container
;
2835 urlcache_header
*header
;
2836 struct hash_entry
*hash_entry
;
2837 entry_header
*entry
;
2838 entry_url
*url_entry
;
2839 DWORD url_entry_offset
;
2840 DWORD size
= DWORD_ALIGN(sizeof(*url_entry
));
2841 DWORD file_name_off
= 0;
2842 DWORD header_info_off
= 0;
2843 DWORD file_ext_off
= 0;
2844 WIN32_FILE_ATTRIBUTE_DATA file_attr
;
2845 LARGE_INTEGER file_size
;
2847 char file_name_no_container
[MAX_PATH
];
2848 char *local_file_name
= 0;
2850 DWORD exempt_delta
= 0;
2853 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n", debugstr_a(url
), debugstr_w(file_name
),
2854 entry_type
, header_info
, header_size
, debugstr_a(file_ext
), debugstr_a(original_url
));
2856 if(entry_type
& STICKY_CACHE_ENTRY
&& !file_name
) {
2857 SetLastError(ERROR_INVALID_PARAMETER
);
2861 WARN(": original_url ignored\n");
2863 memset(&file_attr
, 0, sizeof(file_attr
));
2865 if(!GetFileAttributesExW(file_name
, GetFileExInfoStandard
, &file_attr
))
2868 file_size
.u
.LowPart
= file_attr
.nFileSizeLow
;
2869 file_size
.u
.HighPart
= file_attr
.nFileSizeHigh
;
2871 error
= cache_containers_find(url
, &container
);
2872 if(error
!= ERROR_SUCCESS
) {
2873 SetLastError(error
);
2877 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2878 if(error
!= ERROR_SUCCESS
) {
2879 SetLastError(error
);
2883 if(!(header
= cache_container_lock_index(container
)))
2886 if(urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
2887 entry_url
*url_entry
= (entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
2889 if(urlcache_hash_entry_is_locked(hash_entry
, url_entry
)) {
2890 TRACE("Trying to overwrite locked entry\n");
2891 cache_container_unlock_index(container
, header
);
2892 SetLastError(ERROR_SHARING_VIOLATION
);
2896 hit_rate
= url_entry
->hit_rate
;
2897 exempt_delta
= url_entry
->exempt_delta
;
2898 urlcache_entry_delete(container
, header
, hash_entry
);
2904 dir_id
= CACHE_CONTAINER_NO_SUBDIR
;
2907 BOOL bFound
= FALSE
;
2909 if(strncmpW(file_name
, container
->path
, lstrlenW(container
->path
))) {
2910 ERR("path %s must begin with cache content path %s\n", debugstr_w(file_name
), debugstr_w(container
->path
));
2911 cache_container_unlock_index(container
, header
);
2912 SetLastError(ERROR_INVALID_PARAMETER
);
2916 /* skip container path prefix */
2917 file_name
+= lstrlenW(container
->path
);
2919 WideCharToMultiByte(CP_ACP
, 0, file_name
, -1, file_name_no_container
, MAX_PATH
, NULL
, NULL
);
2920 local_file_name
= file_name_no_container
;
2922 if(header
->dirs_no
) {
2923 for(dir_id
= 0; dir_id
< header
->dirs_no
; dir_id
++) {
2924 if(!strncmp(header
->directory_data
[dir_id
].name
, local_file_name
, DIR_LENGTH
)) {
2931 ERR("cache directory not found in path %s\n", debugstr_w(file_name
));
2932 cache_container_unlock_index(container
, header
);
2933 SetLastError(ERROR_INVALID_PARAMETER
);
2937 file_name
+= DIR_LENGTH
+ 1;
2938 local_file_name
+= DIR_LENGTH
+ 1;
2942 size
= DWORD_ALIGN(size
+ strlen(url
) + 1);
2944 file_name_off
= size
;
2945 size
= DWORD_ALIGN(size
+ strlen(local_file_name
) + 1);
2947 if(header_info
&& header_size
) {
2948 header_info_off
= size
;
2949 size
= DWORD_ALIGN(size
+ header_size
);
2951 if(file_ext
&& (file_ext_off
= strlen(file_ext
))) {
2952 DWORD len
= file_ext_off
;
2954 file_ext_off
= size
;
2955 size
= DWORD_ALIGN(size
+ len
+ 1);
2958 /* round up to next block */
2959 if(size
% BLOCKSIZE
) {
2960 size
-= size
% BLOCKSIZE
;
2964 error
= urlcache_entry_alloc(header
, size
/ BLOCKSIZE
, &entry
);
2965 while(error
== ERROR_HANDLE_DISK_FULL
) {
2966 error
= cache_container_clean_index(container
, &header
);
2967 if(error
== ERROR_SUCCESS
)
2968 error
= urlcache_entry_alloc(header
, size
/ BLOCKSIZE
, &entry
);
2970 if(error
!= ERROR_SUCCESS
) {
2971 cache_container_unlock_index(container
, header
);
2972 SetLastError(error
);
2976 /* FindFirstFreeEntry fills in blocks used */
2977 url_entry
= (entry_url
*)entry
;
2978 url_entry_offset
= (LPBYTE
)url_entry
- (LPBYTE
)header
;
2979 url_entry
->header
.signature
= URL_SIGNATURE
;
2980 url_entry
->cache_dir
= dir_id
;
2981 url_entry
->cache_entry_type
= entry_type
| container
->default_entry_type
;
2982 url_entry
->header_info_size
= header_size
;
2983 if((entry_type
& STICKY_CACHE_ENTRY
) && !exempt_delta
) {
2984 /* Sticky entries have a default exempt time of one day */
2985 exempt_delta
= 86400;
2987 url_entry
->exempt_delta
= exempt_delta
;
2988 url_entry
->hit_rate
= hit_rate
+1;
2989 url_entry
->file_extension_off
= file_ext_off
;
2990 url_entry
->header_info_off
= header_info_off
;
2991 url_entry
->local_name_off
= file_name_off
;
2992 url_entry
->url_off
= DWORD_ALIGN(sizeof(*url_entry
));
2993 url_entry
->size
.QuadPart
= file_size
.QuadPart
;
2994 url_entry
->use_count
= 0;
2995 GetSystemTimeAsFileTime(&url_entry
->access_time
);
2996 url_entry
->modification_time
= modify_time
;
2997 file_time_to_dos_date_time(&url_entry
->access_time
, &url_entry
->sync_date
, &url_entry
->sync_time
);
2998 file_time_to_dos_date_time(&expire_time
, &url_entry
->expire_date
, &url_entry
->expire_time
);
2999 file_time_to_dos_date_time(&file_attr
.ftLastWriteTime
, &url_entry
->write_date
, &url_entry
->write_time
);
3002 url_entry
->unk1
= 0;
3003 url_entry
->unk2
= 0;
3004 url_entry
->unk3
= 0x60;
3005 url_entry
->unk4
= 0;
3006 url_entry
->unk5
= 0x1010;
3007 url_entry
->unk7
= 0;
3008 url_entry
->unk8
= 0;
3011 strcpy((LPSTR
)url_entry
+ url_entry
->url_off
, url
);
3013 strcpy((LPSTR
)((LPBYTE
)url_entry
+ file_name_off
), local_file_name
);
3015 memcpy((LPBYTE
)url_entry
+ header_info_off
, header_info
, header_size
);
3017 strcpy((LPSTR
)((LPBYTE
)url_entry
+ file_ext_off
), file_ext
);
3019 error
= urlcache_hash_entry_create(header
, url
, url_entry_offset
, HASHTABLE_URL
);
3020 while(error
== ERROR_HANDLE_DISK_FULL
) {
3021 error
= cache_container_clean_index(container
, &header
);
3022 if(error
== ERROR_SUCCESS
) {
3023 url_entry
= (entry_url
*)((LPBYTE
)header
+ url_entry_offset
);
3024 error
= urlcache_hash_entry_create(header
, url
,
3025 url_entry_offset
, HASHTABLE_URL
);
3028 if(error
!= ERROR_SUCCESS
) {
3029 urlcache_entry_free(header
, &url_entry
->header
);
3030 cache_container_unlock_index(container
, header
);
3031 SetLastError(error
);
3035 if(url_entry
->cache_dir
< header
->dirs_no
)
3036 header
->directory_data
[url_entry
->cache_dir
].files_no
++;
3037 if(entry_type
& STICKY_CACHE_ENTRY
)
3038 header
->exempt_usage
.QuadPart
+= file_size
.QuadPart
;
3040 header
->cache_usage
.QuadPart
+= file_size
.QuadPart
;
3041 if(header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
> header
->cache_limit
.QuadPart
)
3042 handle_full_cache();
3044 cache_container_unlock_index(container
, header
);
3048 /***********************************************************************
3049 * CommitUrlCacheEntryA (WININET.@)
3051 BOOL WINAPI
CommitUrlCacheEntryA(LPCSTR lpszUrlName
, LPCSTR lpszLocalFileName
,
3052 FILETIME ExpireTime
, FILETIME LastModifiedTime
, DWORD CacheEntryType
,
3053 LPBYTE lpHeaderInfo
, DWORD dwHeaderSize
, LPCSTR lpszFileExtension
, LPCSTR lpszOriginalUrl
)
3055 WCHAR
*file_name
= NULL
;
3058 if(lpszLocalFileName
) {
3059 file_name
= heap_strdupAtoW(lpszLocalFileName
);
3064 ret
= urlcache_entry_commit(lpszUrlName
, file_name
, ExpireTime
, LastModifiedTime
,
3065 CacheEntryType
, lpHeaderInfo
, dwHeaderSize
, lpszFileExtension
, lpszOriginalUrl
);
3066 heap_free(file_name
);
3070 /***********************************************************************
3071 * CommitUrlCacheEntryW (WININET.@)
3073 BOOL WINAPI
CommitUrlCacheEntryW(LPCWSTR lpszUrlName
, LPCWSTR lpszLocalFileName
,
3074 FILETIME ExpireTime
, FILETIME LastModifiedTime
, DWORD CacheEntryType
,
3075 LPWSTR lpHeaderInfo
, DWORD dwHeaderSize
, LPCWSTR lpszFileExtension
, LPCWSTR lpszOriginalUrl
)
3077 char *url
, *original_url
=NULL
, *file_ext
=NULL
, *header_info
=NULL
;
3080 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
3084 header_info
= heap_strdupWtoUTF8(lpHeaderInfo
);
3089 dwHeaderSize
= strlen(header_info
);
3092 if(lpszFileExtension
) {
3093 file_ext
= heap_strdupWtoA(lpszFileExtension
);
3096 heap_free(header_info
);
3101 if(lpszOriginalUrl
&& !urlcache_encode_url_alloc(lpszOriginalUrl
, &original_url
)) {
3103 heap_free(header_info
);
3104 heap_free(file_ext
);
3108 ret
= urlcache_entry_commit(url
, lpszLocalFileName
, ExpireTime
, LastModifiedTime
,
3109 CacheEntryType
, (BYTE
*)header_info
, dwHeaderSize
, file_ext
, original_url
);
3111 heap_free(header_info
);
3112 heap_free(file_ext
);
3113 heap_free(original_url
);
3117 /***********************************************************************
3118 * ReadUrlCacheEntryStream (WININET.@)
3121 BOOL WINAPI
ReadUrlCacheEntryStream(
3122 IN HANDLE hUrlCacheStream
,
3123 IN DWORD dwLocation
,
3124 IN OUT LPVOID lpBuffer
,
3125 IN OUT LPDWORD lpdwLen
,
3129 /* Get handle to file from 'stream' */
3130 stream_handle
*pStream
= (stream_handle
*)hUrlCacheStream
;
3132 if (dwReserved
!= 0)
3134 ERR("dwReserved != 0\n");
3135 SetLastError(ERROR_INVALID_PARAMETER
);
3139 if (IsBadReadPtr(pStream
, sizeof(*pStream
)) || IsBadStringPtrA(pStream
->url
, INTERNET_MAX_URL_LENGTH
))
3141 SetLastError(ERROR_INVALID_HANDLE
);
3145 if (SetFilePointer(pStream
->file
, dwLocation
, NULL
, FILE_CURRENT
) == INVALID_SET_FILE_POINTER
)
3147 return ReadFile(pStream
->file
, lpBuffer
, *lpdwLen
, lpdwLen
, NULL
);
3150 /***********************************************************************
3151 * RetrieveUrlCacheEntryStreamA (WININET.@)
3154 HANDLE WINAPI
RetrieveUrlCacheEntryStreamA(LPCSTR lpszUrlName
,
3155 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
3156 LPDWORD lpdwCacheEntryInfoBufferSize
, BOOL fRandomRead
, DWORD dwReserved
)
3158 /* NOTE: this is not the same as the way that the native
3159 * version allocates 'stream' handles. I did it this way
3160 * as it is much easier and no applications should depend
3161 * on this behaviour. (Native version appears to allocate
3162 * indices into a table)
3164 stream_handle
*stream
;
3167 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName
), lpCacheEntryInfo
,
3168 lpdwCacheEntryInfoBufferSize
, fRandomRead
, dwReserved
);
3170 if(!RetrieveUrlCacheEntryFileA(lpszUrlName
, lpCacheEntryInfo
,
3171 lpdwCacheEntryInfoBufferSize
, dwReserved
))
3174 file
= CreateFileA(lpCacheEntryInfo
->lpszLocalFileName
, GENERIC_READ
, FILE_SHARE_READ
,
3175 NULL
, OPEN_EXISTING
, fRandomRead
? FILE_FLAG_RANDOM_ACCESS
: 0, NULL
);
3176 if(file
== INVALID_HANDLE_VALUE
) {
3177 UnlockUrlCacheEntryFileA(lpszUrlName
, 0);
3181 /* allocate handle storage space */
3182 stream
= heap_alloc(sizeof(stream_handle
) + strlen(lpszUrlName
) * sizeof(CHAR
));
3185 UnlockUrlCacheEntryFileA(lpszUrlName
, 0);
3186 SetLastError(ERROR_OUTOFMEMORY
);
3190 stream
->file
= file
;
3191 strcpy(stream
->url
, lpszUrlName
);
3195 /***********************************************************************
3196 * RetrieveUrlCacheEntryStreamW (WININET.@)
3199 HANDLE WINAPI
RetrieveUrlCacheEntryStreamW(LPCWSTR lpszUrlName
,
3200 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
3201 LPDWORD lpdwCacheEntryInfoBufferSize
, BOOL fRandomRead
, DWORD dwReserved
)
3204 /* NOTE: this is not the same as the way that the native
3205 * version allocates 'stream' handles. I did it this way
3206 * as it is much easier and no applications should depend
3207 * on this behaviour. (Native version appears to allocate
3208 * indices into a table)
3210 stream_handle
*stream
;
3213 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName
), lpCacheEntryInfo
,
3214 lpdwCacheEntryInfoBufferSize
, fRandomRead
, dwReserved
);
3216 if(!(len
= urlcache_encode_url(lpszUrlName
, NULL
, 0)))
3219 if(!RetrieveUrlCacheEntryFileW(lpszUrlName
, lpCacheEntryInfo
,
3220 lpdwCacheEntryInfoBufferSize
, dwReserved
))
3223 file
= CreateFileW(lpCacheEntryInfo
->lpszLocalFileName
, GENERIC_READ
, FILE_SHARE_READ
,
3224 NULL
, OPEN_EXISTING
, fRandomRead
? FILE_FLAG_RANDOM_ACCESS
: 0, NULL
);
3225 if(file
== INVALID_HANDLE_VALUE
) {
3226 UnlockUrlCacheEntryFileW(lpszUrlName
, 0);
3230 /* allocate handle storage space */
3231 stream
= heap_alloc(sizeof(stream_handle
) + len
*sizeof(WCHAR
));
3234 UnlockUrlCacheEntryFileW(lpszUrlName
, 0);
3235 SetLastError(ERROR_OUTOFMEMORY
);
3239 stream
->file
= file
;
3240 if(!urlcache_encode_url(lpszUrlName
, stream
->url
, len
)) {
3242 UnlockUrlCacheEntryFileW(lpszUrlName
, 0);
3249 /***********************************************************************
3250 * UnlockUrlCacheEntryStream (WININET.@)
3253 BOOL WINAPI
UnlockUrlCacheEntryStream(
3254 IN HANDLE hUrlCacheStream
,
3258 stream_handle
*pStream
= (stream_handle
*)hUrlCacheStream
;
3260 if (dwReserved
!= 0)
3262 ERR("dwReserved != 0\n");
3263 SetLastError(ERROR_INVALID_PARAMETER
);
3267 if (IsBadReadPtr(pStream
, sizeof(*pStream
)) || IsBadStringPtrA(pStream
->url
, INTERNET_MAX_URL_LENGTH
))
3269 SetLastError(ERROR_INVALID_HANDLE
);
3273 if (!UnlockUrlCacheEntryFileA(pStream
->url
, 0))
3276 CloseHandle(pStream
->file
);
3282 /***********************************************************************
3283 * DeleteUrlCacheEntryA (WININET.@)
3286 BOOL WINAPI
DeleteUrlCacheEntryA(LPCSTR lpszUrlName
)
3288 cache_container
*pContainer
;
3289 urlcache_header
*pHeader
;
3290 struct hash_entry
*pHashEntry
;
3294 TRACE("(%s)\n", debugstr_a(lpszUrlName
));
3296 error
= cache_containers_find(lpszUrlName
, &pContainer
);
3297 if (error
!= ERROR_SUCCESS
)
3299 SetLastError(error
);
3303 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
3304 if (error
!= ERROR_SUCCESS
)
3306 SetLastError(error
);
3310 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3313 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
3315 cache_container_unlock_index(pContainer
, pHeader
);
3316 TRACE("entry %s not found!\n", lpszUrlName
);
3317 SetLastError(ERROR_FILE_NOT_FOUND
);
3321 ret
= urlcache_entry_delete(pContainer
, pHeader
, pHashEntry
);
3323 cache_container_unlock_index(pContainer
, pHeader
);
3328 /***********************************************************************
3329 * DeleteUrlCacheEntryW (WININET.@)
3332 BOOL WINAPI
DeleteUrlCacheEntryW(LPCWSTR lpszUrlName
)
3337 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
3340 ret
= DeleteUrlCacheEntryA(url
);
3345 BOOL WINAPI
DeleteUrlCacheContainerA(DWORD d1
, DWORD d2
)
3347 FIXME("(0x%08x, 0x%08x) stub\n", d1
, d2
);
3351 BOOL WINAPI
DeleteUrlCacheContainerW(DWORD d1
, DWORD d2
)
3353 FIXME("(0x%08x, 0x%08x) stub\n", d1
, d2
);
3357 /***********************************************************************
3358 * CreateCacheContainerA (WININET.@)
3360 BOOL WINAPI
CreateUrlCacheContainerA(DWORD d1
, DWORD d2
, DWORD d3
, DWORD d4
,
3361 DWORD d5
, DWORD d6
, DWORD d7
, DWORD d8
)
3363 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3364 d1
, d2
, d3
, d4
, d5
, d6
, d7
, d8
);
3368 /***********************************************************************
3369 * CreateCacheContainerW (WININET.@)
3371 BOOL WINAPI
CreateUrlCacheContainerW(DWORD d1
, DWORD d2
, DWORD d3
, DWORD d4
,
3372 DWORD d5
, DWORD d6
, DWORD d7
, DWORD d8
)
3374 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3375 d1
, d2
, d3
, d4
, d5
, d6
, d7
, d8
);
3379 /***********************************************************************
3380 * FindFirstUrlCacheContainerA (WININET.@)
3382 HANDLE WINAPI
FindFirstUrlCacheContainerA( LPVOID p1
, LPVOID p2
, LPVOID p3
, DWORD d1
)
3384 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1
, p2
, p3
, d1
);
3388 /***********************************************************************
3389 * FindFirstUrlCacheContainerW (WININET.@)
3391 HANDLE WINAPI
FindFirstUrlCacheContainerW( LPVOID p1
, LPVOID p2
, LPVOID p3
, DWORD d1
)
3393 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1
, p2
, p3
, d1
);
3397 /***********************************************************************
3398 * FindNextUrlCacheContainerA (WININET.@)
3400 BOOL WINAPI
FindNextUrlCacheContainerA( HANDLE handle
, LPVOID p1
, LPVOID p2
)
3402 FIXME("(%p, %p, %p) stub\n", handle
, p1
, p2
);
3406 /***********************************************************************
3407 * FindNextUrlCacheContainerW (WININET.@)
3409 BOOL WINAPI
FindNextUrlCacheContainerW( HANDLE handle
, LPVOID p1
, LPVOID p2
)
3411 FIXME("(%p, %p, %p) stub\n", handle
, p1
, p2
);
3415 HANDLE WINAPI
FindFirstUrlCacheEntryExA(
3416 LPCSTR lpszUrlSearchPattern
,
3420 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
,
3421 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3423 LPDWORD pcbReserved2
,
3427 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern
),
3428 dwFlags
, dwFilter
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
, lpFirstCacheEntryInfo
,
3429 lpdwFirstCacheEntryInfoBufferSize
, lpReserved
, pcbReserved2
,lpReserved3
);
3430 SetLastError(ERROR_FILE_NOT_FOUND
);
3434 HANDLE WINAPI
FindFirstUrlCacheEntryExW(
3435 LPCWSTR lpszUrlSearchPattern
,
3439 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
,
3440 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3442 LPDWORD pcbReserved2
,
3446 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern
),
3447 dwFlags
, dwFilter
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
, lpFirstCacheEntryInfo
,
3448 lpdwFirstCacheEntryInfoBufferSize
, lpReserved
, pcbReserved2
,lpReserved3
);
3449 SetLastError(ERROR_FILE_NOT_FOUND
);
3453 /***********************************************************************
3454 * FindFirstUrlCacheEntryA (WININET.@)
3457 INTERNETAPI HANDLE WINAPI
FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern
,
3458 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
, LPDWORD lpdwFirstCacheEntryInfoBufferSize
)
3460 find_handle
*pEntryHandle
;
3462 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern
), lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
);
3464 pEntryHandle
= heap_alloc(sizeof(*pEntryHandle
));
3468 pEntryHandle
->magic
= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
;
3469 if (lpszUrlSearchPattern
)
3471 pEntryHandle
->url_search_pattern
= heap_strdupA(lpszUrlSearchPattern
);
3472 if (!pEntryHandle
->url_search_pattern
)
3474 heap_free(pEntryHandle
);
3479 pEntryHandle
->url_search_pattern
= NULL
;
3480 pEntryHandle
->container_idx
= 0;
3481 pEntryHandle
->hash_table_idx
= 0;
3482 pEntryHandle
->hash_entry_idx
= 0;
3484 if (!FindNextUrlCacheEntryA(pEntryHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
))
3486 heap_free(pEntryHandle
);
3489 return pEntryHandle
;
3492 /***********************************************************************
3493 * FindFirstUrlCacheEntryW (WININET.@)
3496 INTERNETAPI HANDLE WINAPI
FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern
,
3497 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
, LPDWORD lpdwFirstCacheEntryInfoBufferSize
)
3499 find_handle
*pEntryHandle
;
3501 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern
), lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
);
3503 pEntryHandle
= heap_alloc(sizeof(*pEntryHandle
));
3507 pEntryHandle
->magic
= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
;
3508 if (lpszUrlSearchPattern
)
3510 pEntryHandle
->url_search_pattern
= heap_strdupWtoA(lpszUrlSearchPattern
);
3511 if (!pEntryHandle
->url_search_pattern
)
3513 heap_free(pEntryHandle
);
3518 pEntryHandle
->url_search_pattern
= NULL
;
3519 pEntryHandle
->container_idx
= 0;
3520 pEntryHandle
->hash_table_idx
= 0;
3521 pEntryHandle
->hash_entry_idx
= 0;
3523 if (!FindNextUrlCacheEntryW(pEntryHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
))
3525 heap_free(pEntryHandle
);
3528 return pEntryHandle
;
3531 static BOOL
urlcache_find_next_entry(
3533 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo
,
3534 LPDWORD lpdwNextCacheEntryInfoBufferSize
,
3537 find_handle
*pEntryHandle
= (find_handle
*)hEnumHandle
;
3538 cache_container
*pContainer
;
3540 if (pEntryHandle
->magic
!= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
)
3542 SetLastError(ERROR_INVALID_HANDLE
);
3546 for (; cache_containers_enum(pEntryHandle
->url_search_pattern
, pEntryHandle
->container_idx
, &pContainer
);
3547 pEntryHandle
->container_idx
++, pEntryHandle
->hash_table_idx
= 0)
3549 urlcache_header
*pHeader
;
3550 entry_hash_table
*pHashTableEntry
;
3553 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
3554 if (error
!= ERROR_SUCCESS
)
3556 SetLastError(error
);
3560 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3563 for (; urlcache_enum_hash_tables(pHeader
, &pEntryHandle
->hash_table_idx
, &pHashTableEntry
);
3564 pEntryHandle
->hash_table_idx
++, pEntryHandle
->hash_entry_idx
= 0)
3566 const struct hash_entry
*pHashEntry
= NULL
;
3567 for (; urlcache_enum_hash_table_entries(pHeader
, pHashTableEntry
, &pEntryHandle
->hash_entry_idx
, &pHashEntry
);
3568 pEntryHandle
->hash_entry_idx
++)
3570 const entry_url
*pUrlEntry
;
3571 const entry_header
*pEntry
= (const entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
3573 if (pEntry
->signature
!= URL_SIGNATURE
)
3576 pUrlEntry
= (const entry_url
*)pEntry
;
3577 TRACE("Found URL: %s\n",
3578 debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->url_off
));
3579 TRACE("Header info: %s\n",
3580 debugstr_an((LPCSTR
)pUrlEntry
+ pUrlEntry
->header_info_off
,
3581 pUrlEntry
->header_info_size
));
3583 error
= urlcache_copy_entry(
3586 lpNextCacheEntryInfo
,
3587 lpdwNextCacheEntryInfoBufferSize
,
3590 if (error
!= ERROR_SUCCESS
)
3592 cache_container_unlock_index(pContainer
, pHeader
);
3593 SetLastError(error
);
3596 if(pUrlEntry
->local_name_off
)
3597 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->local_name_off
));
3599 /* increment the current index so that next time the function
3600 * is called the next entry is returned */
3601 pEntryHandle
->hash_entry_idx
++;
3602 cache_container_unlock_index(pContainer
, pHeader
);
3607 cache_container_unlock_index(pContainer
, pHeader
);
3610 SetLastError(ERROR_NO_MORE_ITEMS
);
3614 /***********************************************************************
3615 * FindNextUrlCacheEntryA (WININET.@)
3617 BOOL WINAPI
FindNextUrlCacheEntryA(
3619 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo
,
3620 LPDWORD lpdwNextCacheEntryInfoBufferSize
)
3622 TRACE("(%p, %p, %p)\n", hEnumHandle
, lpNextCacheEntryInfo
, lpdwNextCacheEntryInfoBufferSize
);
3624 return urlcache_find_next_entry(hEnumHandle
, lpNextCacheEntryInfo
,
3625 lpdwNextCacheEntryInfoBufferSize
, FALSE
/* not UNICODE */);
3628 /***********************************************************************
3629 * FindNextUrlCacheEntryW (WININET.@)
3631 BOOL WINAPI
FindNextUrlCacheEntryW(
3633 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo
,
3634 LPDWORD lpdwNextCacheEntryInfoBufferSize
3637 TRACE("(%p, %p, %p)\n", hEnumHandle
, lpNextCacheEntryInfo
, lpdwNextCacheEntryInfoBufferSize
);
3639 return urlcache_find_next_entry(hEnumHandle
,
3640 (LPINTERNET_CACHE_ENTRY_INFOA
)lpNextCacheEntryInfo
,
3641 lpdwNextCacheEntryInfoBufferSize
, TRUE
/* UNICODE */);
3644 /***********************************************************************
3645 * FindCloseUrlCache (WININET.@)
3647 BOOL WINAPI
FindCloseUrlCache(HANDLE hEnumHandle
)
3649 find_handle
*pEntryHandle
= (find_handle
*)hEnumHandle
;
3651 TRACE("(%p)\n", hEnumHandle
);
3653 if (!pEntryHandle
|| pEntryHandle
->magic
!= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
)
3655 SetLastError(ERROR_INVALID_HANDLE
);
3659 pEntryHandle
->magic
= 0;
3660 heap_free(pEntryHandle
->url_search_pattern
);
3661 heap_free(pEntryHandle
);
3665 HANDLE WINAPI
FindFirstUrlCacheGroup( DWORD dwFlags
, DWORD dwFilter
, LPVOID lpSearchCondition
,
3666 DWORD dwSearchCondition
, GROUPID
* lpGroupId
, LPVOID lpReserved
)
3668 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags
, dwFilter
, lpSearchCondition
,
3669 dwSearchCondition
, lpGroupId
, lpReserved
);
3673 BOOL WINAPI
FindNextUrlCacheEntryExA(
3675 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
,
3676 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3678 LPDWORD pcbReserved2
,
3682 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
,
3683 lpReserved
, pcbReserved2
, lpReserved3
);
3687 BOOL WINAPI
FindNextUrlCacheEntryExW(
3689 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
,
3690 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3692 LPDWORD pcbReserved2
,
3696 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
,
3697 lpReserved
, pcbReserved2
, lpReserved3
);
3701 BOOL WINAPI
FindNextUrlCacheGroup( HANDLE hFind
, GROUPID
* lpGroupId
, LPVOID lpReserved
)
3703 FIXME("(%p, %p, %p) stub\n", hFind
, lpGroupId
, lpReserved
);
3707 /***********************************************************************
3708 * CreateUrlCacheGroup (WININET.@)
3711 INTERNETAPI GROUPID WINAPI
CreateUrlCacheGroup(DWORD dwFlags
, LPVOID lpReserved
)
3713 FIXME("(0x%08x, %p): stub\n", dwFlags
, lpReserved
);
3717 /***********************************************************************
3718 * DeleteUrlCacheGroup (WININET.@)
3721 BOOL WINAPI
DeleteUrlCacheGroup(GROUPID GroupId
, DWORD dwFlags
, LPVOID lpReserved
)
3723 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3724 (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
, dwFlags
, lpReserved
);
3728 /***********************************************************************
3729 * DeleteWpadCacheForNetworks (WININET.@)
3730 * Undocumented, added in IE8
3732 BOOL WINAPI
DeleteWpadCacheForNetworks(DWORD unk1
)
3734 FIXME("(%d) stub\n", unk1
);
3738 /***********************************************************************
3739 * SetUrlCacheEntryGroupA (WININET.@)
3742 BOOL WINAPI
SetUrlCacheEntryGroupA(LPCSTR lpszUrlName
, DWORD dwFlags
,
3743 GROUPID GroupId
, LPBYTE pbGroupAttributes
, DWORD cbGroupAttributes
,
3746 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3747 debugstr_a(lpszUrlName
), dwFlags
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
,
3748 pbGroupAttributes
, cbGroupAttributes
, lpReserved
);
3749 SetLastError(ERROR_FILE_NOT_FOUND
);
3753 /***********************************************************************
3754 * SetUrlCacheEntryGroupW (WININET.@)
3757 BOOL WINAPI
SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName
, DWORD dwFlags
,
3758 GROUPID GroupId
, LPBYTE pbGroupAttributes
, DWORD cbGroupAttributes
,
3761 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3762 debugstr_w(lpszUrlName
), dwFlags
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
,
3763 pbGroupAttributes
, cbGroupAttributes
, lpReserved
);
3764 SetLastError(ERROR_FILE_NOT_FOUND
);
3768 /***********************************************************************
3769 * GetUrlCacheConfigInfoW (WININET.@)
3771 BOOL WINAPI
GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo
, LPDWORD size
, DWORD bitmask
)
3773 FIXME("(%p, %p, %x)\n", CacheInfo
, size
, bitmask
);
3774 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
3778 /***********************************************************************
3779 * GetUrlCacheConfigInfoA (WININET.@)
3781 BOOL WINAPI
GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo
, LPDWORD size
, DWORD bitmask
)
3783 FIXME("(%p, %p, %x)\n", CacheInfo
, size
, bitmask
);
3784 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
3788 BOOL WINAPI
GetUrlCacheGroupAttributeA( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3789 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo
,
3790 LPDWORD lpdwGroupInfo
, LPVOID lpReserved
)
3792 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3793 (ULONG
)(gid
>> 32), (ULONG
)gid
, dwFlags
, dwAttributes
, lpGroupInfo
,
3794 lpdwGroupInfo
, lpReserved
);
3798 BOOL WINAPI
GetUrlCacheGroupAttributeW( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3799 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo
,
3800 LPDWORD lpdwGroupInfo
, LPVOID lpReserved
)
3802 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3803 (ULONG
)(gid
>> 32), (ULONG
)gid
, dwFlags
, dwAttributes
, lpGroupInfo
,
3804 lpdwGroupInfo
, lpReserved
);
3808 BOOL WINAPI
SetUrlCacheGroupAttributeA( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3809 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo
, LPVOID lpReserved
)
3811 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3812 (ULONG
)(gid
>> 32), (ULONG
)gid
, dwFlags
, dwAttributes
, lpGroupInfo
, lpReserved
);
3816 BOOL WINAPI
SetUrlCacheGroupAttributeW( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3817 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo
, LPVOID lpReserved
)
3819 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3820 (ULONG
)(gid
>> 32), (ULONG
)gid
, dwFlags
, dwAttributes
, lpGroupInfo
, lpReserved
);
3824 BOOL WINAPI
SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo
, DWORD dwFieldControl
)
3826 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo
, dwFieldControl
);
3830 BOOL WINAPI
SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo
, DWORD dwFieldControl
)
3832 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo
, dwFieldControl
);
3836 /***********************************************************************
3837 * DeleteIE3Cache (WININET.@)
3839 * Deletes the files used by the IE3 URL caching system.
3842 * hWnd [I] A dummy window.
3843 * hInst [I] Instance of process calling the function.
3844 * lpszCmdLine [I] Options used by function.
3845 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3847 DWORD WINAPI
DeleteIE3Cache(HWND hWnd
, HINSTANCE hInst
, LPSTR lpszCmdLine
, int nCmdShow
)
3849 FIXME("(%p, %p, %s, %d)\n", hWnd
, hInst
, debugstr_a(lpszCmdLine
), nCmdShow
);
3853 static BOOL
urlcache_entry_is_expired(const entry_url
*pUrlEntry
,
3854 FILETIME
*pftLastModified
)
3857 FILETIME now
, expired
;
3859 *pftLastModified
= pUrlEntry
->modification_time
;
3860 GetSystemTimeAsFileTime(&now
);
3861 dos_date_time_to_file_time(pUrlEntry
->expire_date
,
3862 pUrlEntry
->expire_time
, &expired
);
3863 /* If the expired time is 0, it's interpreted as not expired */
3864 if (!expired
.dwLowDateTime
&& !expired
.dwHighDateTime
)
3867 ret
= CompareFileTime(&expired
, &now
) < 0;
3871 /***********************************************************************
3872 * IsUrlCacheEntryExpiredA (WININET.@)
3876 * dwFlags [I] Unknown
3877 * pftLastModified [O] Last modified time
3879 BOOL WINAPI
IsUrlCacheEntryExpiredA(LPCSTR url
, DWORD dwFlags
, FILETIME
* pftLastModified
)
3881 urlcache_header
*pHeader
;
3882 struct hash_entry
*pHashEntry
;
3883 const entry_header
*pEntry
;
3884 const entry_url
* pUrlEntry
;
3885 cache_container
*pContainer
;
3888 TRACE("(%s, %08x, %p)\n", debugstr_a(url
), dwFlags
, pftLastModified
);
3890 if (!url
|| !pftLastModified
)
3893 FIXME("unknown flags 0x%08x\n", dwFlags
);
3895 /* Any error implies that the URL is expired, i.e. not in the cache */
3896 if (cache_containers_find(url
, &pContainer
))
3898 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3902 if (cache_container_open_index(pContainer
, MIN_BLOCK_NO
))
3904 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3908 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3910 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3914 if (!urlcache_find_hash_entry(pHeader
, url
, &pHashEntry
))
3916 cache_container_unlock_index(pContainer
, pHeader
);
3917 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3918 TRACE("entry %s not found!\n", url
);
3922 pEntry
= (const entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
3923 if (pEntry
->signature
!= URL_SIGNATURE
)
3925 cache_container_unlock_index(pContainer
, pHeader
);
3926 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3927 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR
)&pEntry
->signature
, sizeof(DWORD
)));
3931 pUrlEntry
= (const entry_url
*)pEntry
;
3932 expired
= urlcache_entry_is_expired(pUrlEntry
, pftLastModified
);
3934 cache_container_unlock_index(pContainer
, pHeader
);
3939 /***********************************************************************
3940 * IsUrlCacheEntryExpiredW (WININET.@)
3944 * dwFlags [I] Unknown
3945 * pftLastModified [O] Last modified time
3947 BOOL WINAPI
IsUrlCacheEntryExpiredW(LPCWSTR url
, DWORD dwFlags
, FILETIME
* pftLastModified
)
3952 if(!urlcache_encode_url_alloc(url
, &encoded_url
))
3955 ret
= IsUrlCacheEntryExpiredA(encoded_url
, dwFlags
, pftLastModified
);
3956 heap_free(encoded_url
);
3960 /***********************************************************************
3961 * GetDiskInfoA (WININET.@)
3963 BOOL WINAPI
GetDiskInfoA(PCSTR path
, PDWORD cluster_size
, PDWORDLONG free
, PDWORDLONG total
)
3966 ULARGE_INTEGER bytes_free
, bytes_total
;
3968 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path
), cluster_size
, free
, total
);
3972 SetLastError(ERROR_INVALID_PARAMETER
);
3976 if ((ret
= GetDiskFreeSpaceExA(path
, NULL
, &bytes_total
, &bytes_free
)))
3978 if (cluster_size
) *cluster_size
= 1;
3979 if (free
) *free
= bytes_free
.QuadPart
;
3980 if (total
) *total
= bytes_total
.QuadPart
;
3985 /***********************************************************************
3986 * RegisterUrlCacheNotification (WININET.@)
3988 DWORD WINAPI
RegisterUrlCacheNotification(LPVOID a
, DWORD b
, DWORD c
, DWORD d
, DWORD e
, DWORD f
)
3990 FIXME("(%p %x %x %x %x %x)\n", a
, b
, c
, d
, e
, f
);
3994 /***********************************************************************
3995 * IncrementUrlCacheHeaderData (WININET.@)
3997 BOOL WINAPI
IncrementUrlCacheHeaderData(DWORD index
, LPDWORD data
)
3999 FIXME("(%u, %p)\n", index
, data
);
4003 /***********************************************************************
4004 * RunOnceUrlCache (WININET.@)
4007 DWORD WINAPI
RunOnceUrlCache(HWND hwnd
, HINSTANCE hinst
, LPSTR cmd
, int cmdshow
)
4009 FIXME("(%p, %p, %s, %d): stub\n", hwnd
, hinst
, debugstr_a(cmd
), cmdshow
);
4013 BOOL
init_urlcache(void)
4015 dll_unload_event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
4016 if(!dll_unload_event
)
4019 free_cache_running
= CreateSemaphoreW(NULL
, 1, 1, NULL
);
4020 if(!free_cache_running
) {
4021 CloseHandle(dll_unload_event
);
4026 cache_containers_init();
4031 void free_urlcache(void)
4033 SetEvent(dll_unload_event
);
4034 WaitForSingleObject(free_cache_running
, INFINITE
);
4035 ReleaseSemaphore(free_cache_running
, 1, NULL
);
4036 CloseHandle(free_cache_running
);
4037 CloseHandle(dll_unload_event
);
4039 cache_containers_free();
4042 /***********************************************************************
4043 * LoadUrlCacheContent (WININET.@)
4045 BOOL WINAPI
LoadUrlCacheContent(void)