2 * Wininet - Url Cache functions
4 * Copyright 2001,2002 CodeWeavers
5 * Copyright 2003-2008 Robert Shearman
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
30 static const char urlcache_ver_prefix
[] = "WINE URLCache Ver ";
31 static const char urlcache_ver
[] = "0.2012001";
34 #define CHAR_BIT (8 * sizeof(CHAR))
37 #define ENTRY_START_OFFSET 0x4000
39 #define MAX_DIR_NO 0x20
41 #define HASHTABLE_SIZE 448
42 #define HASHTABLE_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
43 #define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
44 #define ALLOCATION_TABLE_OFFSET 0x250
45 #define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
46 #define MIN_BLOCK_NO 0x80
47 #define MAX_BLOCK_NO (ALLOCATION_TABLE_SIZE * CHAR_BIT)
48 #define FILE_SIZE(blocks) ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
50 #define HASHTABLE_URL 0
51 #define HASHTABLE_DEL 1
52 #define HASHTABLE_LOCK 2
53 #define HASHTABLE_FREE 3
54 #define HASHTABLE_REDR 5
55 #define HASHTABLE_FLAG_BITS 6
57 #define PENDING_DELETE_CACHE_ENTRY 0x00400000
58 #define INSTALLED_CACHE_ENTRY 0x10000000
59 #define GET_INSTALLED_ENTRY 0x200
60 #define CACHE_CONTAINER_NO_SUBDIR 0xFE
62 #define CACHE_HEADER_DATA_ROOT_LEAK_OFFSET 0x16
64 #define FILETIME_SECOND 10000000
66 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
67 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
68 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
69 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
70 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
72 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
74 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
79 DWORD blocks_used
; /* number of 128byte blocks used by this entry */
85 FILETIME modification_time
;
87 WORD expire_date
; /* expire date in dos format */
88 WORD expire_time
; /* expire time in dos format */
89 DWORD unk1
; /* usually zero */
90 ULARGE_INTEGER size
; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
91 DWORD unk2
; /* usually zero */
92 DWORD exempt_delta
; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
93 DWORD unk3
; /* usually 0x60 */
94 DWORD url_off
; /* offset of start of url from start of entry */
95 BYTE cache_dir
; /* index of cache directory this url is stored in */
96 BYTE unk4
; /* usually zero */
97 WORD unk5
; /* usually 0x1010 */
98 DWORD local_name_off
; /* offset of start of local filename from start of entry */
99 DWORD cache_entry_type
; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
100 DWORD header_info_off
; /* offset of start of header info from start of entry */
101 DWORD header_info_size
;
102 DWORD file_extension_off
; /* offset of start of file extension from start of entry */
103 WORD sync_date
; /* last sync date in dos format */
104 WORD sync_time
; /* last sync time in dos format */
105 DWORD hit_rate
; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
106 DWORD use_count
; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
109 DWORD unk7
; /* usually zero */
110 DWORD unk8
; /* usually zero */
111 /* packing to dword align start of next field */
112 /* CHAR szSourceUrlName[]; (url) */
113 /* packing to dword align start of next field */
114 /* CHAR szLocalFileName[]; (local file name excluding path) */
115 /* packing to dword align start of next field */
116 /* CHAR szHeaderInfo[]; (header info) */
130 struct hash_entry hash_table
[HASHTABLE_SIZE
];
137 DWORD hash_table_off
;
138 DWORD capacity_in_blocks
;
141 ULARGE_INTEGER cache_limit
;
142 ULARGE_INTEGER cache_usage
;
143 ULARGE_INTEGER exempt_usage
;
145 struct _directory_data
148 char name
[DIR_LENGTH
];
149 } directory_data
[MAX_DIR_NO
];
151 BYTE allocation_table
[ALLOCATION_TABLE_SIZE
];
162 struct list entry
; /* part of a list */
163 char *cache_prefix
; /* string that has to be prefixed for this container to be used */
164 LPWSTR path
; /* path to url container directory */
165 HANDLE mapping
; /* handle of file mapping */
166 DWORD file_size
; /* size of file when mapping was opened */
167 HANDLE mutex
; /* handle of mutex */
168 DWORD default_entry_type
;
174 char *url_search_pattern
;
176 DWORD hash_table_idx
;
177 DWORD hash_entry_idx
;
180 /* List of all containers available */
181 static struct list UrlContainers
= LIST_INIT(UrlContainers
);
183 BOOL bDefaultContainersAdded
= FALSE
;
185 static inline char *heap_strdupWtoUTF8(LPCWSTR str
)
190 DWORD size
= WideCharToMultiByte(CP_UTF8
, 0, str
, -1, NULL
, 0, NULL
, NULL
);
191 ret
= heap_alloc(size
);
193 WideCharToMultiByte(CP_UTF8
, 0, str
, -1, ret
, size
, NULL
, NULL
);
199 /***********************************************************************
200 * urlcache_block_is_free (Internal)
202 * Is the specified block number free?
209 static inline BYTE
urlcache_block_is_free(BYTE
*allocation_table
, DWORD block_number
)
211 BYTE mask
= 1 << (block_number
%CHAR_BIT
);
212 return (allocation_table
[block_number
/CHAR_BIT
] & mask
) == 0;
215 /***********************************************************************
216 * urlcache_block_free (Internal)
218 * Marks the specified block as free
221 * this function is not updating used blocks count
227 static inline void urlcache_block_free(BYTE
*allocation_table
, DWORD block_number
)
229 BYTE mask
= ~(1 << (block_number
%CHAR_BIT
));
230 allocation_table
[block_number
/CHAR_BIT
] &= mask
;
233 /***********************************************************************
234 * urlcache_block_alloc (Internal)
236 * Marks the specified block as allocated
239 * this function is not updating used blocks count
245 static inline void urlcache_block_alloc(BYTE
*allocation_table
, DWORD block_number
)
247 BYTE mask
= 1 << (block_number
%CHAR_BIT
);
248 allocation_table
[block_number
/CHAR_BIT
] |= mask
;
251 /***********************************************************************
252 * urlcache_entry_alloc (Internal)
254 * Finds and allocates the first block of free space big enough and
255 * sets entry to point to it.
258 * ERROR_SUCCESS when free memory block was found
259 * Any other Win32 error code if the entry could not be added
262 static DWORD
urlcache_entry_alloc(urlcache_header
*header
, DWORD blocks_needed
, entry_header
**entry
)
264 DWORD block
, block_size
;
266 for(block
=0; block
<header
->capacity_in_blocks
; block
+=block_size
+1)
269 while(block_size
<blocks_needed
&& block_size
+block
<header
->capacity_in_blocks
270 && urlcache_block_is_free(header
->allocation_table
, block
+block_size
))
273 if(block_size
== blocks_needed
)
277 TRACE("Found free blocks starting at no. %d (0x%x)\n", block
, ENTRY_START_OFFSET
+block
*BLOCKSIZE
);
279 for(index
=0; index
<blocks_needed
; index
++)
280 urlcache_block_alloc(header
->allocation_table
, block
+index
);
282 *entry
= (entry_header
*)((BYTE
*)header
+ENTRY_START_OFFSET
+block
*BLOCKSIZE
);
283 for(index
=0; index
<blocks_needed
*BLOCKSIZE
/sizeof(DWORD
); index
++)
284 ((DWORD
*)*entry
)[index
] = 0xdeadbeef;
285 (*entry
)->blocks_used
= blocks_needed
;
287 header
->blocks_in_use
+= blocks_needed
;
288 return ERROR_SUCCESS
;
292 return ERROR_HANDLE_DISK_FULL
;
295 /***********************************************************************
296 * urlcache_entry_free (Internal)
298 * Deletes the specified entry and frees the space allocated to it
301 * TRUE if it succeeded
305 static BOOL
urlcache_entry_free(urlcache_header
*header
, entry_header
*entry
)
307 DWORD start_block
, block
;
309 /* update allocation table */
310 start_block
= ((DWORD
)((BYTE
*)entry
- (BYTE
*)header
) - ENTRY_START_OFFSET
) / BLOCKSIZE
;
311 for(block
= start_block
; block
< start_block
+entry
->blocks_used
; block
++)
312 urlcache_block_free(header
->allocation_table
, block
);
314 header
->blocks_in_use
-= entry
->blocks_used
;
318 /***********************************************************************
319 * urlcache_create_hash_table (Internal)
321 * Creates a new hash table in free space and adds it to the chain of existing
325 * ERROR_SUCCESS if the hash table was created
326 * ERROR_DISK_FULL if the hash table could not be created
329 static DWORD
urlcache_create_hash_table(urlcache_header
*header
, entry_hash_table
*hash_table_prev
, entry_hash_table
**hash_table
)
331 DWORD dwOffset
, error
;
334 if((error
= urlcache_entry_alloc(header
, 0x20, (entry_header
**)hash_table
)) != ERROR_SUCCESS
)
337 dwOffset
= (BYTE
*)*hash_table
-(BYTE
*)header
;
340 hash_table_prev
->next
= dwOffset
;
342 header
->hash_table_off
= dwOffset
;
344 (*hash_table
)->header
.signature
= HASH_SIGNATURE
;
345 (*hash_table
)->next
= 0;
346 (*hash_table
)->id
= hash_table_prev
? hash_table_prev
->id
+1 : 0;
347 for(i
= 0; i
< HASHTABLE_SIZE
; i
++) {
348 (*hash_table
)->hash_table
[i
].offset
= HASHTABLE_FREE
;
349 (*hash_table
)->hash_table
[i
].key
= HASHTABLE_FREE
;
351 return ERROR_SUCCESS
;
354 /***********************************************************************
355 * cache_container_create_object_name (Internal)
357 * Converts a path to a name suitable for use as a Win32 object name.
358 * Replaces '\\' characters in-place with the specified character
359 * (usually '_' or '!')
365 static void cache_container_create_object_name(LPWSTR lpszPath
, WCHAR replace
)
367 for (; *lpszPath
; lpszPath
++)
369 if (*lpszPath
== '\\')
374 /* Caller must hold container lock */
375 static HANDLE
cache_container_map_index(HANDLE file
, const WCHAR
*path
, DWORD size
, BOOL
*validate
)
377 static const WCHAR mapping_name_format
[]
378 = {'%','s','i','n','d','e','x','.','d','a','t','_','%','l','u',0};
379 WCHAR mapping_name
[MAX_PATH
];
382 wsprintfW(mapping_name
, mapping_name_format
, path
, size
);
383 cache_container_create_object_name(mapping_name
, '_');
385 mapping
= OpenFileMappingW(FILE_MAP_WRITE
, FALSE
, mapping_name
);
387 if(validate
) *validate
= FALSE
;
391 if(validate
) *validate
= TRUE
;
392 return CreateFileMappingW(file
, NULL
, PAGE_READWRITE
, 0, 0, mapping_name
);
395 /* Caller must hold container lock */
396 static DWORD
cache_container_set_size(cache_container
*container
, HANDLE file
, DWORD blocks_no
)
398 static const WCHAR cache_content_key
[] = {'S','o','f','t','w','a','r','e','\\',
399 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
400 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
401 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s','\\',
402 'C','a','c','h','e','\\','C','o','n','t','e','n','t',0};
403 static const WCHAR cache_limit
[] = {'C','a','c','h','e','L','i','m','i','t',0};
405 DWORD file_size
= FILE_SIZE(blocks_no
);
406 WCHAR dir_path
[MAX_PATH
], *dir_name
;
407 entry_hash_table
*hashtable_entry
;
408 urlcache_header
*header
;
414 if(SetFilePointer(file
, file_size
, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
415 return GetLastError();
417 if(!SetEndOfFile(file
))
418 return GetLastError();
420 mapping
= cache_container_map_index(file
, container
->path
, file_size
, NULL
);
422 return GetLastError();
424 header
= MapViewOfFile(mapping
, FILE_MAP_WRITE
, 0, 0, 0);
426 CloseHandle(mapping
);
427 return GetLastError();
430 if(blocks_no
!= MIN_BLOCK_NO
) {
431 if(file_size
> header
->size
)
432 memset((char*)header
+header
->size
, 0, file_size
-header
->size
);
433 header
->size
= file_size
;
434 header
->capacity_in_blocks
= blocks_no
;
436 UnmapViewOfFile(header
);
437 CloseHandle(container
->mapping
);
438 container
->mapping
= mapping
;
439 container
->file_size
= file_size
;
440 return ERROR_SUCCESS
;
443 memset(header
, 0, file_size
);
444 /* First set some constants and defaults in the header */
445 memcpy(header
->signature
, urlcache_ver_prefix
, sizeof(urlcache_ver_prefix
)-1);
446 memcpy(header
->signature
+sizeof(urlcache_ver_prefix
)-1, urlcache_ver
, sizeof(urlcache_ver
)-1);
447 header
->size
= file_size
;
448 header
->capacity_in_blocks
= blocks_no
;
449 /* 127MB - taken from default for Windows 2000 */
450 header
->cache_limit
.QuadPart
= 0x07ff5400;
451 /* Copied from a Windows 2000 cache index */
452 header
->dirs_no
= container
->default_entry_type
==NORMAL_CACHE_ENTRY
? 4 : 0;
454 /* If the registry has a cache size set, use the registry value */
455 if(RegOpenKeyW(HKEY_CURRENT_USER
, cache_content_key
, &key
) == ERROR_SUCCESS
) {
456 DWORD dw
, len
= sizeof(dw
), keytype
;
458 if(RegQueryValueExW(key
, cache_limit
, NULL
, &keytype
, (BYTE
*)&dw
, &len
) == ERROR_SUCCESS
&&
459 keytype
== REG_DWORD
)
460 header
->cache_limit
.QuadPart
= (ULONGLONG
)dw
* 1024;
464 urlcache_create_hash_table(header
, NULL
, &hashtable_entry
);
466 /* Last step - create the directories */
467 strcpyW(dir_path
, container
->path
);
468 dir_name
= dir_path
+ strlenW(dir_path
);
471 GetSystemTimeAsFileTime(&ft
);
473 for(i
=0; i
<header
->dirs_no
; ++i
) {
474 header
->directory_data
[i
].files_no
= 0;
476 ULONGLONG n
= ft
.dwHighDateTime
;
479 /* Generate a file name to attempt to create.
480 * This algorithm will create what will appear
481 * to be random and unrelated directory names
482 * of up to 9 characters in length.
485 n
+= ft
.dwLowDateTime
;
486 n
^= ((ULONGLONG
) i
<< 56) | ((ULONGLONG
) j
<< 48);
488 for(k
= 0; k
< 8; ++k
) {
491 /* Dividing by a prime greater than 36 helps
492 * with the appearance of randomness
497 dir_name
[k
] = '0' + r
;
499 dir_name
[k
] = 'A' + (r
- 10);
502 if(CreateDirectoryW(dir_path
, 0)) {
503 /* The following is OK because we generated an
504 * 8 character directory name made from characters
505 * [A-Z0-9], which are equivalent for all code
506 * pages and for UTF-16
508 for (k
= 0; k
< 8; ++k
)
509 header
->directory_data
[i
].name
[k
] = dir_name
[k
];
512 /* Give up. The most likely cause of this
513 * is a full disk, but whatever the cause
514 * is, it should be more than apparent that
517 UnmapViewOfFile(header
);
518 CloseHandle(mapping
);
519 return GetLastError();
524 UnmapViewOfFile(header
);
525 CloseHandle(container
->mapping
);
526 container
->mapping
= mapping
;
527 container
->file_size
= file_size
;
528 return ERROR_SUCCESS
;
531 static BOOL
cache_container_is_valid(urlcache_header
*header
, DWORD file_size
)
533 DWORD allocation_size
, count_bits
, i
;
535 if(file_size
< FILE_SIZE(MIN_BLOCK_NO
))
538 if(file_size
!= header
->size
)
541 if (!memcmp(header
->signature
, urlcache_ver_prefix
, sizeof(urlcache_ver_prefix
)-1) &&
542 memcmp(header
->signature
+sizeof(urlcache_ver_prefix
)-1, urlcache_ver
, sizeof(urlcache_ver
)-1))
545 if(FILE_SIZE(header
->capacity_in_blocks
) != file_size
)
549 for(i
=0; i
<header
->capacity_in_blocks
/8; i
++) {
550 for(count_bits
= header
->allocation_table
[i
]; count_bits
!=0; count_bits
>>=1) {
555 if(allocation_size
!= header
->blocks_in_use
)
558 for(; i
<ALLOCATION_TABLE_SIZE
; i
++) {
559 if(header
->allocation_table
[i
])
566 /***********************************************************************
567 * cache_container_open_index (Internal)
569 * Opens the index file and saves mapping handle
572 * ERROR_SUCCESS if succeeded
573 * Any other Win32 error code if failed
576 static DWORD
cache_container_open_index(cache_container
*container
, DWORD blocks_no
)
578 static const WCHAR index_dat
[] = {'i','n','d','e','x','.','d','a','t',0};
581 WCHAR index_path
[MAX_PATH
];
585 WaitForSingleObject(container
->mutex
, INFINITE
);
587 if(container
->mapping
) {
588 ReleaseMutex(container
->mutex
);
589 return ERROR_SUCCESS
;
592 strcpyW(index_path
, container
->path
);
593 strcatW(index_path
, index_dat
);
595 file
= CreateFileW(index_path
, GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_ALWAYS
, 0, NULL
);
596 if(file
== INVALID_HANDLE_VALUE
) {
597 /* Maybe the directory wasn't there? Try to create it */
598 if(CreateDirectoryW(container
->path
, 0))
599 file
= CreateFileW(index_path
, GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_ALWAYS
, 0, NULL
);
601 if(file
== INVALID_HANDLE_VALUE
) {
602 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(index_path
));
603 ReleaseMutex(container
->mutex
);
604 return GetLastError();
607 file_size
= GetFileSize(file
, NULL
);
608 if(file_size
== INVALID_FILE_SIZE
) {
610 ReleaseMutex(container
->mutex
);
611 return GetLastError();
614 if(blocks_no
< MIN_BLOCK_NO
)
615 blocks_no
= MIN_BLOCK_NO
;
616 else if(blocks_no
> MAX_BLOCK_NO
)
617 blocks_no
= MAX_BLOCK_NO
;
619 if(file_size
< FILE_SIZE(blocks_no
)) {
620 DWORD ret
= cache_container_set_size(container
, file
, blocks_no
);
622 ReleaseMutex(container
->mutex
);
626 container
->file_size
= file_size
;
627 container
->mapping
= cache_container_map_index(file
, container
->path
, file_size
, &validate
);
629 if(container
->mapping
&& validate
) {
630 urlcache_header
*header
= MapViewOfFile(container
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
632 if(header
&& !cache_container_is_valid(header
, file_size
)) {
633 WARN("detected old or broken index.dat file\n");
634 UnmapViewOfFile(header
);
635 FreeUrlCacheSpaceW(container
->path
, 100, 0);
637 UnmapViewOfFile(header
);
639 CloseHandle(container
->mapping
);
640 container
->mapping
= NULL
;
644 if(!container
->mapping
)
646 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
647 ReleaseMutex(container
->mutex
);
648 return GetLastError();
651 ReleaseMutex(container
->mutex
);
652 return ERROR_SUCCESS
;
655 /***********************************************************************
656 * cache_container_close_index (Internal)
664 static void cache_container_close_index(cache_container
*pContainer
)
666 CloseHandle(pContainer
->mapping
);
667 pContainer
->mapping
= NULL
;
670 static BOOL
cache_containers_add(const char *cache_prefix
, LPCWSTR path
,
671 DWORD default_entry_type
, LPWSTR mutex_name
)
673 cache_container
*pContainer
= heap_alloc(sizeof(cache_container
));
674 int cache_prefix_len
= strlen(cache_prefix
);
681 pContainer
->mapping
= NULL
;
682 pContainer
->file_size
= 0;
683 pContainer
->default_entry_type
= default_entry_type
;
685 pContainer
->path
= heap_strdupW(path
);
686 if (!pContainer
->path
)
688 heap_free(pContainer
);
692 pContainer
->cache_prefix
= heap_alloc(cache_prefix_len
+1);
693 if (!pContainer
->cache_prefix
)
695 heap_free(pContainer
->path
);
696 heap_free(pContainer
);
700 memcpy(pContainer
->cache_prefix
, cache_prefix
, cache_prefix_len
+1);
702 CharLowerW(mutex_name
);
703 cache_container_create_object_name(mutex_name
, '!');
705 if ((pContainer
->mutex
= CreateMutexW(NULL
, FALSE
, mutex_name
)) == NULL
)
707 ERR("couldn't create mutex (error is %d)\n", GetLastError());
708 heap_free(pContainer
->path
);
709 heap_free(pContainer
);
713 list_add_head(&UrlContainers
, &pContainer
->entry
);
718 static void cache_container_delete_container(cache_container
*pContainer
)
720 list_remove(&pContainer
->entry
);
722 cache_container_close_index(pContainer
);
723 CloseHandle(pContainer
->mutex
);
724 heap_free(pContainer
->path
);
725 heap_free(pContainer
->cache_prefix
);
726 heap_free(pContainer
);
729 static void cache_containers_init(void)
731 static const WCHAR UrlSuffix
[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
732 static const WCHAR HistorySuffix
[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
733 static const WCHAR CookieSuffix
[] = {0};
735 static const WCHAR UserProfile
[] = {'U','S','E','R','P','R','O','F','I','L','E',0};
738 int nFolder
; /* CSIDL_* constant */
739 const WCHAR
*shpath_suffix
; /* suffix on path returned by SHGetSpecialFolderPath */
740 const char *cache_prefix
; /* prefix used to reference the container */
741 DWORD default_entry_type
;
742 } DefaultContainerData
[] =
744 { CSIDL_INTERNET_CACHE
, UrlSuffix
, "", NORMAL_CACHE_ENTRY
},
745 { CSIDL_HISTORY
, HistorySuffix
, "Visited:", URLHISTORY_CACHE_ENTRY
},
746 { CSIDL_COOKIES
, CookieSuffix
, "Cookie:", COOKIE_CACHE_ENTRY
},
751 if (GetEnvironmentVariableW(UserProfile
, NULL
, 0) == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND
)
753 TRACE("Environment variable 'USERPROFILE' does not exist!\n");
757 for (i
= 0; i
< sizeof(DefaultContainerData
) / sizeof(DefaultContainerData
[0]); i
++)
759 WCHAR wszCachePath
[MAX_PATH
];
760 WCHAR wszMutexName
[MAX_PATH
];
761 int path_len
, suffix_len
;
764 if (!SHGetSpecialFolderPathW(NULL
, wszCachePath
, DefaultContainerData
[i
].nFolder
, TRUE
))
766 ERR("Couldn't get path for default container %u\n", i
);
769 path_len
= strlenW(wszCachePath
);
770 suffix_len
= strlenW(DefaultContainerData
[i
].shpath_suffix
);
772 if (path_len
+ suffix_len
+ 2 > MAX_PATH
)
774 ERR("Path too long\n");
778 wszCachePath
[path_len
] = '\\';
779 wszCachePath
[path_len
+1] = 0;
781 strcpyW(wszMutexName
, wszCachePath
);
785 memcpy(wszCachePath
+ path_len
+ 1, DefaultContainerData
[i
].shpath_suffix
, (suffix_len
+ 1) * sizeof(WCHAR
));
786 wszCachePath
[path_len
+ suffix_len
+ 1] = '\\';
787 wszCachePath
[path_len
+ suffix_len
+ 2] = '\0';
790 if (!WideCharToMultiByte(CP_ACP
, WC_NO_BEST_FIT_CHARS
, wszCachePath
, path_len
,
791 NULL
, 0, NULL
, &def_char
) || def_char
)
795 /* cannot convert path to ANSI code page */
796 if (!(path_len
= GetShortPathNameW(wszCachePath
, tmp
, MAX_PATH
)) ||
797 !WideCharToMultiByte(CP_ACP
, WC_NO_BEST_FIT_CHARS
, tmp
, path_len
,
798 NULL
, 0, NULL
, &def_char
) || def_char
)
799 ERR("Can't create container path accessible by ANSI functions\n");
801 memcpy(wszCachePath
, tmp
, (path_len
+1)*sizeof(WCHAR
));
804 cache_containers_add(DefaultContainerData
[i
].cache_prefix
, wszCachePath
,
805 DefaultContainerData
[i
].default_entry_type
, wszMutexName
);
809 static void cache_containers_free(void)
811 while(!list_empty(&UrlContainers
))
812 cache_container_delete_container(
813 LIST_ENTRY(list_head(&UrlContainers
), cache_container
, entry
)
817 static DWORD
cache_containers_find(const char *url
, cache_container
**ret
)
819 cache_container
*container
;
821 TRACE("searching for prefix for URL: %s\n", debugstr_a(url
));
824 return ERROR_INVALID_PARAMETER
;
827 if (!bDefaultContainersAdded
)
828 cache_containers_init();
830 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
832 int prefix_len
= strlen(container
->cache_prefix
);
834 if(!strncmp(container
->cache_prefix
, url
, prefix_len
)) {
835 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
837 return ERROR_SUCCESS
;
841 ERR("no container found\n");
842 return ERROR_FILE_NOT_FOUND
;
845 static BOOL
cache_containers_enum(char *search_pattern
, DWORD index
, cache_container
**ret
)
848 cache_container
*container
;
850 TRACE("searching for prefix: %s\n", debugstr_a(search_pattern
));
852 /* non-NULL search pattern only returns one container ever */
853 if (search_pattern
&& index
> 0)
857 if (!bDefaultContainersAdded
)
858 cache_containers_init();
860 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
864 if (!strcmp(container
->cache_prefix
, search_pattern
))
866 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
875 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
885 /***********************************************************************
886 * cache_container_lock_index (Internal)
888 * Locks the index for system-wide exclusive access.
891 * Cache file header if successful
892 * NULL if failed and calls SetLastError.
894 static urlcache_header
* cache_container_lock_index(cache_container
*pContainer
)
898 urlcache_header
* pHeader
;
902 WaitForSingleObject(pContainer
->mutex
, INFINITE
);
904 pIndexData
= MapViewOfFile(pContainer
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
908 ReleaseMutex(pContainer
->mutex
);
909 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
912 pHeader
= (urlcache_header
*)pIndexData
;
914 /* file has grown - we need to remap to prevent us getting
915 * access violations when we try and access beyond the end
916 * of the memory mapped file */
917 if (pHeader
->size
!= pContainer
->file_size
)
919 UnmapViewOfFile( pHeader
);
920 cache_container_close_index(pContainer
);
921 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
922 if (error
!= ERROR_SUCCESS
)
924 ReleaseMutex(pContainer
->mutex
);
928 pIndexData
= MapViewOfFile(pContainer
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
932 ReleaseMutex(pContainer
->mutex
);
933 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
936 pHeader
= (urlcache_header
*)pIndexData
;
939 TRACE("Signature: %s, file size: %d bytes\n", pHeader
->signature
, pHeader
->size
);
941 for (index
= 0; index
< pHeader
->dirs_no
; index
++)
943 TRACE("Directory[%d] = \"%.8s\"\n", index
, pHeader
->directory_data
[index
].name
);
949 /***********************************************************************
950 * cache_container_unlock_index (Internal)
953 static BOOL
cache_container_unlock_index(cache_container
*pContainer
, urlcache_header
*pHeader
)
956 ReleaseMutex(pContainer
->mutex
);
957 return UnmapViewOfFile(pHeader
);
960 /***********************************************************************
961 * urlcache_create_file_pathW (Internal)
963 * Copies the full path to the specified buffer given the local file
964 * name and the index of the directory it is in. Always sets value in
965 * lpBufferSize to the required buffer size (in bytes).
968 * TRUE if the buffer was big enough
969 * FALSE if the buffer was too small
972 static BOOL
urlcache_create_file_pathW(
973 const cache_container
*pContainer
,
974 const urlcache_header
*pHeader
,
975 LPCSTR szLocalFileName
,
982 int path_len
= strlenW(pContainer
->path
);
983 int file_name_len
= MultiByteToWideChar(CP_ACP
, 0, szLocalFileName
, -1, NULL
, 0);
984 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
&& Directory
>=pHeader
->dirs_no
)
990 nRequired
= (path_len
+ file_name_len
) * sizeof(WCHAR
);
991 if(Directory
!= CACHE_CONTAINER_NO_SUBDIR
)
992 nRequired
+= (DIR_LENGTH
+ 1) * sizeof(WCHAR
);
993 if (trunc_name
&& nRequired
>= *lpBufferSize
)
994 nRequired
= *lpBufferSize
;
995 if (nRequired
<= *lpBufferSize
)
999 memcpy(wszPath
, pContainer
->path
, path_len
* sizeof(WCHAR
));
1000 if (Directory
!= CACHE_CONTAINER_NO_SUBDIR
)
1002 dir_len
= MultiByteToWideChar(CP_ACP
, 0, pHeader
->directory_data
[Directory
].name
, DIR_LENGTH
, wszPath
+ path_len
, DIR_LENGTH
);
1003 wszPath
[dir_len
+ path_len
] = '\\';
1010 MultiByteToWideChar(CP_ACP
, 0, szLocalFileName
, -1, wszPath
+ dir_len
+ path_len
,
1011 *lpBufferSize
/sizeof(WCHAR
)-dir_len
-path_len
);
1012 wszPath
[*lpBufferSize
/sizeof(WCHAR
)-1] = 0;
1013 *lpBufferSize
= nRequired
;
1016 *lpBufferSize
= nRequired
;
1020 /***********************************************************************
1021 * urlcache_create_file_pathA (Internal)
1023 * Copies the full path to the specified buffer given the local file
1024 * name and the index of the directory it is in. Always sets value in
1025 * lpBufferSize to the required buffer size.
1028 * TRUE if the buffer was big enough
1029 * FALSE if the buffer was too small
1032 static BOOL
urlcache_create_file_pathA(
1033 const cache_container
*pContainer
,
1034 const urlcache_header
*pHeader
,
1035 LPCSTR szLocalFileName
,
1038 LPLONG lpBufferSize
)
1041 int path_len
, file_name_len
, dir_len
;
1043 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
&& Directory
>=pHeader
->dirs_no
)
1049 path_len
= WideCharToMultiByte(CP_ACP
, 0, pContainer
->path
, -1, NULL
, 0, NULL
, NULL
) - 1;
1050 file_name_len
= strlen(szLocalFileName
) + 1 /* for nul-terminator */;
1051 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
)
1052 dir_len
= DIR_LENGTH
+1;
1056 nRequired
= (path_len
+ dir_len
+ file_name_len
) * sizeof(char);
1057 if (nRequired
<= *lpBufferSize
)
1059 WideCharToMultiByte(CP_ACP
, 0, pContainer
->path
, -1, szPath
, path_len
, NULL
, NULL
);
1061 memcpy(szPath
+path_len
, pHeader
->directory_data
[Directory
].name
, dir_len
-1);
1062 szPath
[path_len
+ dir_len
-1] = '\\';
1064 memcpy(szPath
+ path_len
+ dir_len
, szLocalFileName
, file_name_len
);
1065 *lpBufferSize
= nRequired
;
1068 *lpBufferSize
= nRequired
;
1072 /* Just like FileTimeToDosDateTime, except that it also maps the special
1073 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1075 static void file_time_to_dos_date_time(const FILETIME
*ft
, WORD
*fatdate
,
1078 if (!ft
->dwLowDateTime
&& !ft
->dwHighDateTime
)
1079 *fatdate
= *fattime
= 0;
1081 FileTimeToDosDateTime(ft
, fatdate
, fattime
);
1084 /***********************************************************************
1085 * urlcache_delete_file (Internal)
1087 static DWORD
urlcache_delete_file(const cache_container
*container
,
1088 urlcache_header
*header
, entry_url
*url_entry
)
1090 WIN32_FILE_ATTRIBUTE_DATA attr
;
1091 WCHAR path
[MAX_PATH
];
1092 LONG path_size
= sizeof(path
);
1096 if(!url_entry
->local_name_off
)
1099 if(!urlcache_create_file_pathW(container
, header
,
1100 (LPCSTR
)url_entry
+url_entry
->local_name_off
,
1101 url_entry
->cache_dir
, path
, &path_size
, FALSE
))
1104 if(!GetFileAttributesExW(path
, GetFileExInfoStandard
, &attr
))
1106 file_time_to_dos_date_time(&attr
.ftLastWriteTime
, &date
, &time
);
1107 if(date
!= url_entry
->write_date
|| time
!= url_entry
->write_time
)
1110 err
= (DeleteFileW(path
) ? ERROR_SUCCESS
: GetLastError());
1111 if(err
== ERROR_ACCESS_DENIED
|| err
== ERROR_SHARING_VIOLATION
)
1115 if (url_entry
->cache_dir
< header
->dirs_no
)
1117 if (header
->directory_data
[url_entry
->cache_dir
].files_no
)
1118 header
->directory_data
[url_entry
->cache_dir
].files_no
--;
1120 if (url_entry
->cache_entry_type
& STICKY_CACHE_ENTRY
)
1122 if (url_entry
->size
.QuadPart
< header
->exempt_usage
.QuadPart
)
1123 header
->exempt_usage
.QuadPart
-= url_entry
->size
.QuadPart
;
1125 header
->exempt_usage
.QuadPart
= 0;
1129 if (url_entry
->size
.QuadPart
< header
->cache_usage
.QuadPart
)
1130 header
->cache_usage
.QuadPart
-= url_entry
->size
.QuadPart
;
1132 header
->cache_usage
.QuadPart
= 0;
1135 return ERROR_SUCCESS
;
1138 static BOOL
urlcache_clean_leaked_entries(cache_container
*container
, urlcache_header
*header
)
1143 leak_off
= &header
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
];
1145 entry_url
*url_entry
= (entry_url
*)((LPBYTE
)header
+ *leak_off
);
1147 if(SUCCEEDED(urlcache_delete_file(container
, header
, url_entry
))) {
1148 *leak_off
= url_entry
->exempt_delta
;
1149 urlcache_entry_free(header
, &url_entry
->header
);
1152 leak_off
= &url_entry
->exempt_delta
;
1159 /***********************************************************************
1160 * cache_container_clean_index (Internal)
1162 * This function is meant to make place in index file by removing leaked
1163 * files entries and resizing the file.
1165 * CAUTION: file view may get mapped to new memory
1168 * ERROR_SUCCESS when new memory is available
1169 * error code otherwise
1171 static DWORD
cache_container_clean_index(cache_container
*container
, urlcache_header
**file_view
)
1173 urlcache_header
*header
= *file_view
;
1176 TRACE("(%s %s)\n", debugstr_a(container
->cache_prefix
), debugstr_w(container
->path
));
1178 if(urlcache_clean_leaked_entries(container
, header
))
1179 return ERROR_SUCCESS
;
1181 if(header
->size
>= ALLOCATION_TABLE_SIZE
*8*BLOCKSIZE
+ ENTRY_START_OFFSET
) {
1182 WARN("index file has maximal size\n");
1183 return ERROR_NOT_ENOUGH_MEMORY
;
1186 cache_container_close_index(container
);
1187 ret
= cache_container_open_index(container
, header
->capacity_in_blocks
*2);
1188 if(ret
!= ERROR_SUCCESS
)
1190 header
= MapViewOfFile(container
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
1192 return GetLastError();
1194 UnmapViewOfFile(*file_view
);
1195 *file_view
= header
;
1196 return ERROR_SUCCESS
;
1199 /* Just like DosDateTimeToFileTime, except that it also maps the special
1200 * case of a DOS date/time of (0,0) to a filetime of (0,0).
1202 static void dos_date_time_to_file_time(WORD fatdate
, WORD fattime
,
1205 if (!fatdate
&& !fattime
)
1206 ft
->dwLowDateTime
= ft
->dwHighDateTime
= 0;
1208 DosDateTimeToFileTime(fatdate
, fattime
, ft
);
1211 static int urlcache_decode_url(const char *url
, WCHAR
*decoded_url
, int decoded_len
)
1214 DWORD len
, part_len
;
1217 memset(&uc
, 0, sizeof(uc
));
1218 uc
.dwStructSize
= sizeof(uc
);
1219 uc
.dwHostNameLength
= 1;
1220 if(!InternetCrackUrlA(url
, 0, 0, &uc
))
1221 uc
.nScheme
= INTERNET_SCHEME_UNKNOWN
;
1223 if(uc
.nScheme
!=INTERNET_SCHEME_HTTP
&& uc
.nScheme
!=INTERNET_SCHEME_HTTPS
)
1224 return MultiByteToWideChar(CP_UTF8
, 0, url
, -1, decoded_url
, decoded_len
);
1229 len
= MultiByteToWideChar(CP_UTF8
, 0, url
, uc
.lpszHostName
-url
, decoded_url
, decoded_len
);
1235 host_name
= heap_alloc(uc
.dwHostNameLength
*sizeof(WCHAR
));
1238 if(!MultiByteToWideChar(CP_UTF8
, 0, uc
.lpszHostName
, uc
.dwHostNameLength
,
1239 host_name
, uc
.dwHostNameLength
)) {
1240 heap_free(host_name
);
1243 part_len
= IdnToUnicode(0, host_name
, uc
.dwHostNameLength
,
1244 decoded_url
? decoded_url
+len
: NULL
, decoded_len
);
1245 heap_free(host_name
);
1247 SetLastError(ERROR_INTERNET_INVALID_URL
);
1252 decoded_len
-= part_len
;
1254 part_len
= MultiByteToWideChar(CP_UTF8
, 0,
1255 uc
.lpszHostName
+uc
.dwHostNameLength
,
1256 -1, decoded_url
? decoded_url
+len
: NULL
, decoded_len
);
1264 /***********************************************************************
1265 * urlcache_copy_entry (Internal)
1267 * Copies an entry from the cache index file to the Win32 structure
1270 * ERROR_SUCCESS if the buffer was big enough
1271 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1274 static DWORD
urlcache_copy_entry(cache_container
*container
, const urlcache_header
*header
,
1275 INTERNET_CACHE_ENTRY_INFOA
*entry_info
, DWORD
*info_size
, const entry_url
*url_entry
, BOOL unicode
)
1278 DWORD size
= sizeof(*entry_info
);
1280 if(*info_size
>= size
) {
1281 entry_info
->lpHeaderInfo
= NULL
;
1282 entry_info
->lpszFileExtension
= NULL
;
1283 entry_info
->lpszLocalFileName
= NULL
;
1284 entry_info
->lpszSourceUrlName
= NULL
;
1285 entry_info
->CacheEntryType
= url_entry
->cache_entry_type
;
1286 entry_info
->u
.dwExemptDelta
= url_entry
->exempt_delta
;
1287 entry_info
->dwHeaderInfoSize
= url_entry
->header_info_size
;
1288 entry_info
->dwHitRate
= url_entry
->hit_rate
;
1289 entry_info
->dwSizeHigh
= url_entry
->size
.u
.HighPart
;
1290 entry_info
->dwSizeLow
= url_entry
->size
.u
.LowPart
;
1291 entry_info
->dwStructSize
= sizeof(*entry_info
);
1292 entry_info
->dwUseCount
= url_entry
->use_count
;
1293 dos_date_time_to_file_time(url_entry
->expire_date
, url_entry
->expire_time
, &entry_info
->ExpireTime
);
1294 entry_info
->LastAccessTime
= url_entry
->access_time
;
1295 entry_info
->LastModifiedTime
= url_entry
->modification_time
;
1296 dos_date_time_to_file_time(url_entry
->sync_date
, url_entry
->sync_time
, &entry_info
->LastSyncTime
);
1300 url_len
= urlcache_decode_url((const char*)url_entry
+url_entry
->url_off
, NULL
, 0);
1302 url_len
= strlen((LPCSTR
)url_entry
+url_entry
->url_off
) + 1;
1303 size
+= url_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1305 if(*info_size
>= size
) {
1306 DWORD url_size
= url_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1308 entry_info
->lpszSourceUrlName
= (LPSTR
)entry_info
+size
-url_size
;
1310 urlcache_decode_url((const char*)url_entry
+url_entry
->url_off
, (WCHAR
*)entry_info
->lpszSourceUrlName
, url_len
);
1312 memcpy(entry_info
->lpszSourceUrlName
, (LPCSTR
)url_entry
+url_entry
->url_off
, url_size
);
1315 if(size
%4 && size
<*info_size
)
1316 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1317 size
= DWORD_ALIGN(size
);
1319 if(url_entry
->local_name_off
) {
1320 LONG file_name_size
;
1322 file_name
= (LPSTR
)entry_info
+size
;
1323 file_name_size
= *info_size
-size
;
1324 if((unicode
&& urlcache_create_file_pathW(container
, header
, (LPCSTR
)url_entry
+url_entry
->local_name_off
,
1325 url_entry
->cache_dir
, (LPWSTR
)file_name
, &file_name_size
, FALSE
)) ||
1326 (!unicode
&& urlcache_create_file_pathA(container
, header
, (LPCSTR
)url_entry
+url_entry
->local_name_off
,
1327 url_entry
->cache_dir
, file_name
, &file_name_size
))) {
1328 entry_info
->lpszLocalFileName
= file_name
;
1330 size
+= file_name_size
;
1332 if(size
%4 && size
<*info_size
)
1333 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1334 size
= DWORD_ALIGN(size
);
1337 if(url_entry
->header_info_off
) {
1341 header_len
= MultiByteToWideChar(CP_UTF8
, 0, (const char*)url_entry
+url_entry
->header_info_off
,
1342 url_entry
->header_info_size
, NULL
, 0);
1344 header_len
= url_entry
->header_info_size
;
1345 size
+= header_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1347 if(*info_size
>= size
) {
1348 DWORD header_size
= header_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1349 entry_info
->lpHeaderInfo
= (LPBYTE
)entry_info
+size
-header_size
;
1351 MultiByteToWideChar(CP_UTF8
, 0, (const char*)url_entry
+url_entry
->header_info_off
,
1352 url_entry
->header_info_size
, (LPWSTR
)entry_info
->lpHeaderInfo
, header_len
);
1354 memcpy(entry_info
->lpHeaderInfo
, (LPCSTR
)url_entry
+url_entry
->header_info_off
, header_len
);
1356 if(size
%4 && size
<*info_size
)
1357 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1358 size
= DWORD_ALIGN(size
);
1361 if(url_entry
->file_extension_off
) {
1365 ext_len
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)url_entry
+url_entry
->file_extension_off
, -1, NULL
, 0);
1367 ext_len
= strlen((LPCSTR
)url_entry
+url_entry
->file_extension_off
) + 1;
1368 size
+= ext_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1370 if(*info_size
>= size
) {
1371 DWORD ext_size
= ext_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1372 entry_info
->lpszFileExtension
= (LPSTR
)entry_info
+size
-ext_size
;
1374 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)url_entry
+url_entry
->file_extension_off
, -1, (LPWSTR
)entry_info
->lpszFileExtension
, ext_len
);
1376 memcpy(entry_info
->lpszFileExtension
, (LPCSTR
)url_entry
+url_entry
->file_extension_off
, ext_len
*sizeof(CHAR
));
1379 if(size
%4 && size
<*info_size
)
1380 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1381 size
= DWORD_ALIGN(size
);
1384 if(size
> *info_size
) {
1386 return ERROR_INSUFFICIENT_BUFFER
;
1389 return ERROR_SUCCESS
;
1392 /***********************************************************************
1393 * urlcache_set_entry_info (Internal)
1395 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1396 * according to the flags set by field_control.
1399 * ERROR_SUCCESS if the buffer was big enough
1400 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1403 static DWORD
urlcache_set_entry_info(entry_url
*url_entry
, const INTERNET_CACHE_ENTRY_INFOA
*entry_info
, DWORD field_control
)
1405 if (field_control
& CACHE_ENTRY_ACCTIME_FC
)
1406 url_entry
->access_time
= entry_info
->LastAccessTime
;
1407 if (field_control
& CACHE_ENTRY_ATTRIBUTE_FC
)
1408 url_entry
->cache_entry_type
= entry_info
->CacheEntryType
;
1409 if (field_control
& CACHE_ENTRY_EXEMPT_DELTA_FC
)
1410 url_entry
->exempt_delta
= entry_info
->u
.dwExemptDelta
;
1411 if (field_control
& CACHE_ENTRY_EXPTIME_FC
)
1412 file_time_to_dos_date_time(&entry_info
->ExpireTime
, &url_entry
->expire_date
, &url_entry
->expire_time
);
1413 if (field_control
& CACHE_ENTRY_HEADERINFO_FC
)
1414 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1415 if (field_control
& CACHE_ENTRY_HITRATE_FC
)
1416 url_entry
->hit_rate
= entry_info
->dwHitRate
;
1417 if (field_control
& CACHE_ENTRY_MODTIME_FC
)
1418 url_entry
->modification_time
= entry_info
->LastModifiedTime
;
1419 if (field_control
& CACHE_ENTRY_SYNCTIME_FC
)
1420 file_time_to_dos_date_time(&entry_info
->LastAccessTime
, &url_entry
->sync_date
, &url_entry
->sync_time
);
1422 return ERROR_SUCCESS
;
1425 /***********************************************************************
1426 * urlcache_hash_key (Internal)
1428 * Returns the hash key for a given string
1431 * hash key for the string
1434 static DWORD
urlcache_hash_key(LPCSTR lpszKey
)
1436 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1437 * but the algorithm and result are not the same!
1439 static const unsigned char lookupTable
[256] =
1441 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1442 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1443 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1444 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1445 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1446 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1447 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1448 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1449 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1450 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1451 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1452 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1453 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1454 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1455 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1456 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1457 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1458 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1459 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1460 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1461 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1462 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1463 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1464 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1465 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1466 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1467 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1468 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1469 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1470 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1471 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1472 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1477 for (i
= 0; i
< sizeof(key
) / sizeof(key
[0]); i
++)
1478 key
[i
] = lookupTable
[(*lpszKey
+ i
) & 0xFF];
1480 for (lpszKey
++; *lpszKey
; lpszKey
++)
1482 for (i
= 0; i
< sizeof(key
) / sizeof(key
[0]); i
++)
1483 key
[i
] = lookupTable
[*lpszKey
^ key
[i
]];
1486 return *(DWORD
*)key
;
1489 static inline entry_hash_table
* urlcache_get_hash_table(const urlcache_header
*pHeader
, DWORD dwOffset
)
1493 return (entry_hash_table
*)((LPBYTE
)pHeader
+ dwOffset
);
1496 static BOOL
urlcache_find_hash_entry(const urlcache_header
*pHeader
, LPCSTR lpszUrl
, struct hash_entry
**ppHashEntry
)
1498 /* structure of hash table:
1499 * 448 entries divided into 64 blocks
1500 * each block therefore contains a chain of 7 key/offset pairs
1501 * how position in table is calculated:
1502 * 1. the url is hashed in helper function
1503 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1504 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1507 * there can be multiple hash tables in the file and the offset to
1508 * the next one is stored in the header of the hash table
1510 DWORD key
= urlcache_hash_key(lpszUrl
);
1511 DWORD offset
= (key
& (HASHTABLE_NUM_ENTRIES
-1)) * HASHTABLE_BLOCKSIZE
;
1512 entry_hash_table
* pHashEntry
;
1515 key
>>= HASHTABLE_FLAG_BITS
;
1517 for (pHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1518 pHashEntry
; pHashEntry
= urlcache_get_hash_table(pHeader
, pHashEntry
->next
))
1521 if (pHashEntry
->id
!= id
++)
1523 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry
->id
, id
);
1526 /* make sure that it is in fact a hash entry */
1527 if (pHashEntry
->header
.signature
!= HASH_SIGNATURE
)
1529 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&pHashEntry
->header
.signature
);
1533 for (i
= 0; i
< HASHTABLE_BLOCKSIZE
; i
++)
1535 struct hash_entry
*pHashElement
= &pHashEntry
->hash_table
[offset
+ i
];
1536 if (key
== pHashElement
->key
>>HASHTABLE_FLAG_BITS
)
1538 /* FIXME: we should make sure that this is the right element
1539 * before returning and claiming that it is. We can do this
1540 * by doing a simple compare between the URL we were given
1541 * and the URL stored in the entry. However, this assumes
1542 * we know the format of all the entries stored in the
1544 *ppHashEntry
= pHashElement
;
1552 /***********************************************************************
1553 * urlcache_hash_entry_set_flags (Internal)
1555 * Sets special bits in hash key
1561 static void urlcache_hash_entry_set_flags(struct hash_entry
*pHashEntry
, DWORD dwFlag
)
1563 pHashEntry
->key
= (pHashEntry
->key
>> HASHTABLE_FLAG_BITS
<< HASHTABLE_FLAG_BITS
) | dwFlag
;
1566 /***********************************************************************
1567 * urlcache_hash_entry_delete (Internal)
1569 * Searches all the hash tables in the index for the given URL and
1570 * then if found deletes the entry.
1573 * TRUE if the entry was found
1574 * FALSE if the entry could not be found
1577 static BOOL
urlcache_hash_entry_delete(struct hash_entry
*pHashEntry
)
1579 pHashEntry
->key
= HASHTABLE_DEL
;
1583 /***********************************************************************
1584 * urlcache_hash_entry_create (Internal)
1586 * Searches all the hash tables for a free slot based on the offset
1587 * generated from the hash key. If a free slot is found, the offset and
1588 * key are entered into the hash table.
1591 * ERROR_SUCCESS if the entry was added
1592 * Any other Win32 error code if the entry could not be added
1595 static DWORD
urlcache_hash_entry_create(urlcache_header
*pHeader
, LPCSTR lpszUrl
, DWORD dwOffsetEntry
, DWORD dwFieldType
)
1597 /* see urlcache_find_hash_entry for structure of hash tables */
1599 DWORD key
= urlcache_hash_key(lpszUrl
);
1600 DWORD offset
= (key
& (HASHTABLE_NUM_ENTRIES
-1)) * HASHTABLE_BLOCKSIZE
;
1601 entry_hash_table
* pHashEntry
, *pHashPrev
= NULL
;
1605 key
= ((key
>> HASHTABLE_FLAG_BITS
) << HASHTABLE_FLAG_BITS
) + dwFieldType
;
1607 for (pHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1608 pHashEntry
; pHashEntry
= urlcache_get_hash_table(pHeader
, pHashEntry
->next
))
1611 pHashPrev
= pHashEntry
;
1613 if (pHashEntry
->id
!= id
++)
1615 ERR("not right hash table number (%d) expected %d\n", pHashEntry
->id
, id
);
1618 /* make sure that it is in fact a hash entry */
1619 if (pHashEntry
->header
.signature
!= HASH_SIGNATURE
)
1621 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&pHashEntry
->header
.signature
);
1625 for (i
= 0; i
< HASHTABLE_BLOCKSIZE
; i
++)
1627 struct hash_entry
*pHashElement
= &pHashEntry
->hash_table
[offset
+ i
];
1628 if (pHashElement
->key
==HASHTABLE_FREE
|| pHashElement
->key
==HASHTABLE_DEL
) /* if the slot is free */
1630 pHashElement
->key
= key
;
1631 pHashElement
->offset
= dwOffsetEntry
;
1632 return ERROR_SUCCESS
;
1636 error
= urlcache_create_hash_table(pHeader
, pHashPrev
, &pHashEntry
);
1637 if (error
!= ERROR_SUCCESS
)
1640 pHashEntry
->hash_table
[offset
].key
= key
;
1641 pHashEntry
->hash_table
[offset
].offset
= dwOffsetEntry
;
1642 return ERROR_SUCCESS
;
1645 /***********************************************************************
1646 * urlcache_enum_hash_tables (Internal)
1648 * Enumerates the hash tables in a container.
1651 * TRUE if an entry was found
1652 * FALSE if there are no more tables to enumerate.
1655 static BOOL
urlcache_enum_hash_tables(const urlcache_header
*pHeader
, DWORD
*id
, entry_hash_table
**ppHashEntry
)
1657 for (*ppHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1658 *ppHashEntry
; *ppHashEntry
= urlcache_get_hash_table(pHeader
, (*ppHashEntry
)->next
))
1660 TRACE("looking at hash table number %d\n", (*ppHashEntry
)->id
);
1661 if ((*ppHashEntry
)->id
!= *id
)
1663 /* make sure that it is in fact a hash entry */
1664 if ((*ppHashEntry
)->header
.signature
!= HASH_SIGNATURE
)
1666 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&(*ppHashEntry
)->header
.signature
);
1671 TRACE("hash table number %d found\n", *id
);
1677 /***********************************************************************
1678 * urlcache_enum_hash_table_entries (Internal)
1680 * Enumerates entries in a hash table and returns the next non-free entry.
1683 * TRUE if an entry was found
1684 * FALSE if the hash table is empty or there are no more entries to
1688 static BOOL
urlcache_enum_hash_table_entries(const urlcache_header
*pHeader
, const entry_hash_table
*pHashEntry
,
1689 DWORD
* index
, const struct hash_entry
**ppHashEntry
)
1691 for (; *index
< HASHTABLE_SIZE
; (*index
)++)
1693 if (pHashEntry
->hash_table
[*index
].key
==HASHTABLE_FREE
|| pHashEntry
->hash_table
[*index
].key
==HASHTABLE_DEL
)
1696 *ppHashEntry
= &pHashEntry
->hash_table
[*index
];
1697 TRACE("entry found %d\n", *index
);
1700 TRACE("no more entries (%d)\n", *index
);
1704 /***********************************************************************
1705 * cache_container_delete_dir (Internal)
1707 * Erase a directory containing an URL cache.
1710 * TRUE success, FALSE failure/aborted.
1713 static BOOL
cache_container_delete_dir(LPCWSTR lpszPath
)
1716 WCHAR path
[MAX_PATH
+ 1];
1717 SHFILEOPSTRUCTW shfos
;
1720 path_len
= strlenW(lpszPath
);
1721 if (path_len
>= MAX_PATH
)
1723 strcpyW(path
, lpszPath
);
1724 path
[path_len
+ 1] = 0; /* double-NUL-terminate path */
1727 shfos
.wFunc
= FO_DELETE
;
1730 shfos
.fFlags
= FOF_NOCONFIRMATION
;
1731 shfos
.fAnyOperationsAborted
= FALSE
;
1732 ret
= SHFileOperationW(&shfos
);
1734 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path
), ret
);
1735 return !(ret
|| shfos
.fAnyOperationsAborted
);
1738 /***********************************************************************
1739 * urlcache_hash_entry_is_locked (Internal)
1741 * Checks if entry is locked. Unlocks it if possible.
1743 static BOOL
urlcache_hash_entry_is_locked(struct hash_entry
*hash_entry
, entry_url
*url_entry
)
1746 ULARGE_INTEGER acc_time
, time
;
1748 if ((hash_entry
->key
& ((1<<HASHTABLE_FLAG_BITS
)-1)) != HASHTABLE_LOCK
)
1751 GetSystemTimeAsFileTime(&cur_time
);
1752 time
.u
.LowPart
= cur_time
.dwLowDateTime
;
1753 time
.u
.HighPart
= cur_time
.dwHighDateTime
;
1755 acc_time
.u
.LowPart
= url_entry
->access_time
.dwLowDateTime
;
1756 acc_time
.u
.HighPart
= url_entry
->access_time
.dwHighDateTime
;
1758 time
.QuadPart
-= acc_time
.QuadPart
;
1760 /* check if entry was locked for at least a day */
1761 if(time
.QuadPart
> (ULONGLONG
)24*60*60*FILETIME_SECOND
) {
1762 urlcache_hash_entry_set_flags(hash_entry
, HASHTABLE_URL
);
1763 url_entry
->use_count
= 0;
1770 static BOOL
urlcache_get_entry_info(const char *url
, void *entry_info
,
1771 DWORD
*size
, DWORD flags
, BOOL unicode
)
1773 urlcache_header
*header
;
1774 struct hash_entry
*hash_entry
;
1775 const entry_url
*url_entry
;
1776 cache_container
*container
;
1779 TRACE("(%s, %p, %p, %x, %x)\n", debugstr_a(url
), entry_info
, size
, flags
, unicode
);
1781 if(flags
& ~GET_INSTALLED_ENTRY
)
1782 FIXME("ignoring unsupported flags: %x\n", flags
);
1784 error
= cache_containers_find(url
, &container
);
1785 if(error
!= ERROR_SUCCESS
) {
1786 SetLastError(error
);
1790 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
1791 if(error
!= ERROR_SUCCESS
) {
1792 SetLastError(error
);
1796 if(!(header
= cache_container_lock_index(container
)))
1799 if(!urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
1800 cache_container_unlock_index(container
, header
);
1801 WARN("entry %s not found!\n", debugstr_a(url
));
1802 SetLastError(ERROR_FILE_NOT_FOUND
);
1806 url_entry
= (const entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
1807 if(url_entry
->header
.signature
!= URL_SIGNATURE
) {
1808 cache_container_unlock_index(container
, header
);
1809 FIXME("Trying to retrieve entry of unknown format %s\n",
1810 debugstr_an((LPCSTR
)&url_entry
->header
.signature
, sizeof(DWORD
)));
1811 SetLastError(ERROR_FILE_NOT_FOUND
);
1815 TRACE("Found URL: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->url_off
));
1816 TRACE("Header info: %s\n", debugstr_an((LPCSTR
)url_entry
+
1817 url_entry
->header_info_off
, url_entry
->header_info_size
));
1819 if((flags
& GET_INSTALLED_ENTRY
) && !(url_entry
->cache_entry_type
& INSTALLED_CACHE_ENTRY
)) {
1820 cache_container_unlock_index(container
, header
);
1821 SetLastError(ERROR_FILE_NOT_FOUND
);
1829 error
= urlcache_copy_entry(container
, header
, entry_info
, size
, url_entry
, unicode
);
1830 if(error
!= ERROR_SUCCESS
) {
1831 cache_container_unlock_index(container
, header
);
1832 SetLastError(error
);
1835 if(url_entry
->local_name_off
)
1836 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->local_name_off
));
1839 cache_container_unlock_index(container
, header
);
1843 /***********************************************************************
1844 * GetUrlCacheEntryInfoExA (WININET.@)
1847 BOOL WINAPI
GetUrlCacheEntryInfoExA(LPCSTR lpszUrl
,
1848 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1849 LPDWORD lpdwCacheEntryInfoBufSize
, LPSTR lpszReserved
,
1850 LPDWORD lpdwReserved
, LPVOID lpReserved
, DWORD dwFlags
)
1852 if(lpszReserved
!=NULL
|| lpdwReserved
!=NULL
|| lpReserved
!=NULL
) {
1853 ERR("Reserved value was not 0\n");
1854 SetLastError(ERROR_INVALID_PARAMETER
);
1858 return urlcache_get_entry_info(lpszUrl
, lpCacheEntryInfo
,
1859 lpdwCacheEntryInfoBufSize
, dwFlags
, FALSE
);
1862 /***********************************************************************
1863 * GetUrlCacheEntryInfoA (WININET.@)
1866 BOOL WINAPI
GetUrlCacheEntryInfoA(LPCSTR lpszUrlName
,
1867 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1868 LPDWORD lpdwCacheEntryInfoBufferSize
)
1870 return GetUrlCacheEntryInfoExA(lpszUrlName
, lpCacheEntryInfo
,
1871 lpdwCacheEntryInfoBufferSize
, NULL
, NULL
, NULL
, 0);
1874 static int urlcache_encode_url(const WCHAR
*url
, char *encoded_url
, int encoded_len
)
1877 DWORD len
, part_len
;
1880 TRACE("%s\n", debugstr_w(url
));
1882 memset(&uc
, 0, sizeof(uc
));
1883 uc
.dwStructSize
= sizeof(uc
);
1884 uc
.dwHostNameLength
= 1;
1885 if(!InternetCrackUrlW(url
, 0, 0, &uc
))
1886 uc
.nScheme
= INTERNET_SCHEME_UNKNOWN
;
1888 if(uc
.nScheme
!=INTERNET_SCHEME_HTTP
&& uc
.nScheme
!=INTERNET_SCHEME_HTTPS
)
1889 return WideCharToMultiByte(CP_UTF8
, 0, url
, -1, encoded_url
, encoded_len
, NULL
, NULL
);
1891 len
= WideCharToMultiByte(CP_UTF8
, 0, url
, uc
.lpszHostName
-url
,
1892 encoded_url
, encoded_len
, NULL
, NULL
);
1898 part_len
= IdnToAscii(0, uc
.lpszHostName
, uc
.dwHostNameLength
, NULL
, 0);
1900 SetLastError(ERROR_INTERNET_INVALID_URL
);
1904 punycode
= heap_alloc(part_len
*sizeof(WCHAR
));
1908 part_len
= IdnToAscii(0, uc
.lpszHostName
, uc
.dwHostNameLength
, punycode
, part_len
);
1910 heap_free(punycode
);
1914 part_len
= WideCharToMultiByte(CP_UTF8
, 0, punycode
, part_len
,
1915 encoded_url
? encoded_url
+len
: NULL
, encoded_len
, NULL
, NULL
);
1916 heap_free(punycode
);
1920 encoded_len
-= part_len
;
1923 part_len
= WideCharToMultiByte(CP_UTF8
, 0, uc
.lpszHostName
+uc
.dwHostNameLength
,
1924 -1, encoded_url
? encoded_url
+len
: NULL
, encoded_len
, NULL
, NULL
);
1929 TRACE("got (%d)%s\n", len
, debugstr_a(encoded_url
));
1933 static BOOL
urlcache_encode_url_alloc(const WCHAR
*url
, char **encoded_url
)
1938 encoded_len
= urlcache_encode_url(url
, NULL
, 0);
1942 ret
= heap_alloc(encoded_len
*sizeof(WCHAR
));
1946 encoded_len
= urlcache_encode_url(url
, ret
, encoded_len
);
1956 /***********************************************************************
1957 * GetUrlCacheEntryInfoExW (WININET.@)
1960 BOOL WINAPI
GetUrlCacheEntryInfoExW(LPCWSTR lpszUrl
,
1961 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
1962 LPDWORD lpdwCacheEntryInfoBufSize
, LPWSTR lpszReserved
,
1963 LPDWORD lpdwReserved
, LPVOID lpReserved
, DWORD dwFlags
)
1968 if(lpszReserved
!=NULL
|| lpdwReserved
!=NULL
|| lpReserved
!=NULL
) {
1969 ERR("Reserved value was not 0\n");
1970 SetLastError(ERROR_INVALID_PARAMETER
);
1974 /* Ignore GET_INSTALLED_ENTRY flag in unicode version of function */
1975 dwFlags
&= ~GET_INSTALLED_ENTRY
;
1977 if(!urlcache_encode_url_alloc(lpszUrl
, &url
))
1980 ret
= urlcache_get_entry_info(url
, lpCacheEntryInfo
,
1981 lpdwCacheEntryInfoBufSize
, dwFlags
, TRUE
);
1986 /***********************************************************************
1987 * GetUrlCacheEntryInfoW (WININET.@)
1990 BOOL WINAPI
GetUrlCacheEntryInfoW(LPCWSTR lpszUrl
,
1991 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
1992 LPDWORD lpdwCacheEntryInfoBufferSize
)
1994 return GetUrlCacheEntryInfoExW(lpszUrl
, lpCacheEntryInfo
,
1995 lpdwCacheEntryInfoBufferSize
, NULL
, NULL
, NULL
, 0);
1998 /***********************************************************************
1999 * SetUrlCacheEntryInfoA (WININET.@)
2001 BOOL WINAPI
SetUrlCacheEntryInfoA(LPCSTR lpszUrlName
,
2002 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
2003 DWORD dwFieldControl
)
2005 urlcache_header
*pHeader
;
2006 struct hash_entry
*pHashEntry
;
2007 entry_header
*pEntry
;
2008 cache_container
*pContainer
;
2011 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName
), lpCacheEntryInfo
, dwFieldControl
);
2013 error
= cache_containers_find(lpszUrlName
, &pContainer
);
2014 if (error
!= ERROR_SUCCESS
)
2016 SetLastError(error
);
2020 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
2021 if (error
!= ERROR_SUCCESS
)
2023 SetLastError(error
);
2027 if (!(pHeader
= cache_container_lock_index(pContainer
)))
2030 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
2032 cache_container_unlock_index(pContainer
, pHeader
);
2033 WARN("entry %s not found!\n", debugstr_a(lpszUrlName
));
2034 SetLastError(ERROR_FILE_NOT_FOUND
);
2038 pEntry
= (entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2039 if (pEntry
->signature
!= URL_SIGNATURE
)
2041 cache_container_unlock_index(pContainer
, pHeader
);
2042 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR
)&pEntry
->signature
, sizeof(DWORD
)));
2043 SetLastError(ERROR_FILE_NOT_FOUND
);
2047 urlcache_set_entry_info((entry_url
*)pEntry
, lpCacheEntryInfo
, dwFieldControl
);
2049 cache_container_unlock_index(pContainer
, pHeader
);
2054 /***********************************************************************
2055 * SetUrlCacheEntryInfoW (WININET.@)
2057 BOOL WINAPI
SetUrlCacheEntryInfoW(LPCWSTR lpszUrl
,
2058 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
2059 DWORD dwFieldControl
)
2064 if(!urlcache_encode_url_alloc(lpszUrl
, &url
))
2067 ret
= SetUrlCacheEntryInfoA(url
, (INTERNET_CACHE_ENTRY_INFOA
*)lpCacheEntryInfo
, dwFieldControl
);
2072 static BOOL
urlcache_entry_get_file(const char *url
, void *entry_info
, DWORD
*size
, BOOL unicode
)
2074 urlcache_header
*header
;
2075 struct hash_entry
*hash_entry
;
2076 entry_url
*url_entry
;
2077 cache_container
*container
;
2080 TRACE("(%s, %p, %p, %x)\n", debugstr_a(url
), entry_info
, size
, unicode
);
2082 if(!url
|| !size
|| (!entry_info
&& *size
)) {
2083 SetLastError(ERROR_INVALID_PARAMETER
);
2087 error
= cache_containers_find(url
, &container
);
2088 if(error
!= ERROR_SUCCESS
) {
2089 SetLastError(error
);
2093 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2094 if (error
!= ERROR_SUCCESS
) {
2095 SetLastError(error
);
2099 if (!(header
= cache_container_lock_index(container
)))
2102 if (!urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
2103 cache_container_unlock_index(container
, header
);
2104 TRACE("entry %s not found!\n", url
);
2105 SetLastError(ERROR_FILE_NOT_FOUND
);
2109 url_entry
= (entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
2110 if(url_entry
->header
.signature
!= URL_SIGNATURE
) {
2111 cache_container_unlock_index(container
, header
);
2112 FIXME("Trying to retrieve entry of unknown format %s\n",
2113 debugstr_an((LPSTR
)&url_entry
->header
.signature
, sizeof(DWORD
)));
2114 SetLastError(ERROR_FILE_NOT_FOUND
);
2118 if(!url_entry
->local_name_off
) {
2119 cache_container_unlock_index(container
, header
);
2120 SetLastError(ERROR_INVALID_DATA
);
2124 TRACE("Found URL: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->url_off
));
2125 TRACE("Header info: %s\n", debugstr_an((LPCSTR
)url_entry
+ url_entry
->header_info_off
,
2126 url_entry
->header_info_size
));
2128 error
= urlcache_copy_entry(container
, header
, entry_info
,
2129 size
, url_entry
, unicode
);
2130 if(error
!= ERROR_SUCCESS
) {
2131 cache_container_unlock_index(container
, header
);
2132 SetLastError(error
);
2135 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->local_name_off
));
2137 url_entry
->hit_rate
++;
2138 url_entry
->use_count
++;
2139 urlcache_hash_entry_set_flags(hash_entry
, HASHTABLE_LOCK
);
2140 GetSystemTimeAsFileTime(&url_entry
->access_time
);
2142 cache_container_unlock_index(container
, header
);
2147 /***********************************************************************
2148 * RetrieveUrlCacheEntryFileA (WININET.@)
2151 BOOL WINAPI
RetrieveUrlCacheEntryFileA(LPCSTR lpszUrlName
,
2152 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
2153 LPDWORD lpdwCacheEntryInfoBufferSize
, DWORD dwReserved
)
2155 return urlcache_entry_get_file(lpszUrlName
, lpCacheEntryInfo
,
2156 lpdwCacheEntryInfoBufferSize
, FALSE
);
2159 /***********************************************************************
2160 * RetrieveUrlCacheEntryFileW (WININET.@)
2163 BOOL WINAPI
RetrieveUrlCacheEntryFileW(LPCWSTR lpszUrlName
,
2164 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
2165 LPDWORD lpdwCacheEntryInfoBufferSize
, DWORD dwReserved
)
2170 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
2173 ret
= urlcache_entry_get_file(url
, lpCacheEntryInfo
,
2174 lpdwCacheEntryInfoBufferSize
, TRUE
);
2179 static BOOL
urlcache_entry_delete(const cache_container
*pContainer
,
2180 urlcache_header
*pHeader
, struct hash_entry
*pHashEntry
)
2182 entry_header
*pEntry
;
2183 entry_url
* pUrlEntry
;
2185 pEntry
= (entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2186 if (pEntry
->signature
!= URL_SIGNATURE
)
2188 FIXME("Trying to delete entry of unknown format %s\n",
2189 debugstr_an((LPCSTR
)&pEntry
->signature
, sizeof(DWORD
)));
2190 SetLastError(ERROR_FILE_NOT_FOUND
);
2194 pUrlEntry
= (entry_url
*)pEntry
;
2195 if(urlcache_hash_entry_is_locked(pHashEntry
, pUrlEntry
))
2197 TRACE("Trying to delete locked entry\n");
2198 pUrlEntry
->cache_entry_type
|= PENDING_DELETE_CACHE_ENTRY
;
2199 SetLastError(ERROR_SHARING_VIOLATION
);
2203 if(!urlcache_delete_file(pContainer
, pHeader
, pUrlEntry
))
2205 urlcache_entry_free(pHeader
, pEntry
);
2209 /* Add entry to leaked files list */
2210 pUrlEntry
->header
.signature
= LEAK_SIGNATURE
;
2211 pUrlEntry
->exempt_delta
= pHeader
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
];
2212 pHeader
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
] = pHashEntry
->offset
;
2215 urlcache_hash_entry_delete(pHashEntry
);
2219 static HANDLE free_cache_running
;
2220 static HANDLE dll_unload_event
;
2221 static DWORD WINAPI
handle_full_cache_worker(void *param
)
2223 FreeUrlCacheSpaceW(NULL
, 20, 0);
2224 ReleaseSemaphore(free_cache_running
, 1, NULL
);
2228 static void handle_full_cache(void)
2230 if(WaitForSingleObject(free_cache_running
, 0) == WAIT_OBJECT_0
) {
2231 if(!QueueUserWorkItem(handle_full_cache_worker
, NULL
, 0))
2232 ReleaseSemaphore(free_cache_running
, 1, NULL
);
2236 /* Enumerates entries in cache, allows cache unlocking between calls. */
2237 static BOOL
urlcache_next_entry(urlcache_header
*header
, DWORD
*hash_table_off
, DWORD
*hash_table_entry
,
2238 struct hash_entry
**hash_entry
, entry_header
**entry
)
2240 entry_hash_table
*hashtable_entry
;
2245 if(!*hash_table_off
) {
2246 *hash_table_off
= header
->hash_table_off
;
2247 *hash_table_entry
= 0;
2249 hashtable_entry
= urlcache_get_hash_table(header
, *hash_table_off
);
2251 if(*hash_table_off
>= header
->size
) {
2252 *hash_table_off
= 0;
2256 hashtable_entry
= urlcache_get_hash_table(header
, *hash_table_off
);
2259 if(hashtable_entry
->header
.signature
!= HASH_SIGNATURE
) {
2260 *hash_table_off
= 0;
2265 if(*hash_table_entry
>= HASHTABLE_SIZE
) {
2266 *hash_table_off
= hashtable_entry
->next
;
2267 if(!*hash_table_off
) {
2268 *hash_table_off
= 0;
2272 hashtable_entry
= urlcache_get_hash_table(header
, *hash_table_off
);
2273 *hash_table_entry
= 0;
2276 if(hashtable_entry
->hash_table
[*hash_table_entry
].key
!= HASHTABLE_DEL
&&
2277 hashtable_entry
->hash_table
[*hash_table_entry
].key
!= HASHTABLE_FREE
) {
2278 *hash_entry
= &hashtable_entry
->hash_table
[*hash_table_entry
];
2279 *entry
= (entry_header
*)((LPBYTE
)header
+ hashtable_entry
->hash_table
[*hash_table_entry
].offset
);
2280 (*hash_table_entry
)++;
2284 (*hash_table_entry
)++;
2287 *hash_table_off
= 0;
2291 /* Rates an urlcache entry to determine if it can be deleted.
2293 * Score 0 means that entry can safely be removed, the bigger rating
2294 * the smaller chance of entry being removed.
2295 * DWORD_MAX means that entry can't be deleted at all.
2297 * Rating system is currently not fully compatible with native implementation.
2299 static DWORD
urlcache_rate_entry(entry_url
*url_entry
, FILETIME
*cur_time
)
2301 ULARGE_INTEGER time
, access_time
;
2304 access_time
.u
.LowPart
= url_entry
->access_time
.dwLowDateTime
;
2305 access_time
.u
.HighPart
= url_entry
->access_time
.dwHighDateTime
;
2307 time
.u
.LowPart
= cur_time
->dwLowDateTime
;
2308 time
.u
.HighPart
= cur_time
->dwHighDateTime
;
2310 /* Don't touch entries that were added less than 10 minutes ago */
2311 if(time
.QuadPart
< access_time
.QuadPart
+ (ULONGLONG
)10*60*FILETIME_SECOND
)
2314 if(url_entry
->cache_entry_type
& STICKY_CACHE_ENTRY
)
2315 if(time
.QuadPart
< access_time
.QuadPart
+ (ULONGLONG
)url_entry
->exempt_delta
*FILETIME_SECOND
)
2318 time
.QuadPart
= (time
.QuadPart
-access_time
.QuadPart
)/FILETIME_SECOND
;
2319 rating
= 400*60*60*24/(60*60*24+time
.QuadPart
);
2321 if(url_entry
->hit_rate
> 100)
2324 rating
+= url_entry
->hit_rate
;
2329 static int dword_cmp(const void *p1
, const void *p2
)
2331 return *(const DWORD
*)p1
- *(const DWORD
*)p2
;
2334 /***********************************************************************
2335 * FreeUrlCacheSpaceW (WININET.@)
2337 * Frees up some cache.
2340 * cache_path [I] Which volume to free up from, or NULL if you don't care.
2341 * size [I] Percentage of the cache that should be free.
2342 * filter [I] Which entries can't be deleted (CacheEntryType)
2345 * TRUE success. FALSE failure.
2348 * This implementation just retrieves the path of the cache directory, and
2349 * deletes its contents from the filesystem. The correct approach would
2350 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
2352 BOOL WINAPI
FreeUrlCacheSpaceW(LPCWSTR cache_path
, DWORD size
, DWORD filter
)
2354 cache_container
*container
;
2355 DWORD path_len
, err
;
2357 TRACE("(%s, %x, %x)\n", debugstr_w(cache_path
), size
, filter
);
2359 if(size
<1 || size
>100) {
2360 SetLastError(ERROR_INVALID_PARAMETER
);
2365 path_len
= strlenW(cache_path
);
2366 if(cache_path
[path_len
-1] == '\\')
2372 if(size
==100 && !filter
) {
2373 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
2375 /* When cache_path==NULL only clean Temporary Internet Files */
2376 if((!path_len
&& container
->cache_prefix
[0]==0) ||
2377 (path_len
&& !strncmpiW(container
->path
, cache_path
, path_len
) &&
2378 (container
->path
[path_len
]=='\0' || container
->path
[path_len
]=='\\')))
2382 WaitForSingleObject(container
->mutex
, INFINITE
);
2384 /* unlock, delete, recreate and lock cache */
2385 cache_container_close_index(container
);
2386 ret_del
= cache_container_delete_dir(container
->path
);
2387 err
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2389 ReleaseMutex(container
->mutex
);
2390 if(!ret_del
|| (err
!= ERROR_SUCCESS
))
2398 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
2400 urlcache_header
*header
;
2401 struct hash_entry
*hash_entry
;
2402 entry_header
*entry
;
2403 entry_url
*url_entry
;
2404 ULONGLONG desired_size
, cur_size
;
2405 DWORD delete_factor
, hash_table_off
, hash_table_entry
;
2406 DWORD rate
[100], rate_no
;
2409 if((path_len
|| container
->cache_prefix
[0]!=0) &&
2410 (!path_len
|| strncmpiW(container
->path
, cache_path
, path_len
) ||
2411 (container
->path
[path_len
]!='\0' && container
->path
[path_len
]!='\\')))
2414 err
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2415 if(err
!= ERROR_SUCCESS
)
2418 header
= cache_container_lock_index(container
);
2422 urlcache_clean_leaked_entries(container
, header
);
2424 desired_size
= header
->cache_limit
.QuadPart
*(100-size
)/100;
2425 cur_size
= header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
;
2426 if(cur_size
<= desired_size
)
2429 delete_factor
= (cur_size
-desired_size
)*100/cur_size
;
2431 if(!delete_factor
) {
2432 cache_container_unlock_index(container
, header
);
2437 hash_table_entry
= 0;
2439 GetSystemTimeAsFileTime(&cur_time
);
2440 while(rate_no
<sizeof(rate
)/sizeof(*rate
) &&
2441 urlcache_next_entry(header
, &hash_table_off
, &hash_table_entry
, &hash_entry
, &entry
)) {
2442 if(entry
->signature
!= URL_SIGNATURE
) {
2443 WARN("only url entries are currently supported\n");
2447 url_entry
= (entry_url
*)entry
;
2448 if(url_entry
->cache_entry_type
& filter
)
2451 rate
[rate_no
] = urlcache_rate_entry(url_entry
, &cur_time
);
2452 if(rate
[rate_no
] != -1)
2457 TRACE("nothing to delete\n");
2458 cache_container_unlock_index(container
, header
);
2462 qsort(rate
, rate_no
, sizeof(DWORD
), dword_cmp
);
2464 delete_factor
= delete_factor
*rate_no
/100;
2465 delete_factor
= rate
[delete_factor
];
2466 TRACE("deleting files with rating %d or less\n", delete_factor
);
2469 while(urlcache_next_entry(header
, &hash_table_off
, &hash_table_entry
, &hash_entry
, &entry
)) {
2470 if(entry
->signature
!= URL_SIGNATURE
)
2473 url_entry
= (entry_url
*)entry
;
2474 if(url_entry
->cache_entry_type
& filter
)
2477 if(urlcache_rate_entry(url_entry
, &cur_time
) <= delete_factor
) {
2478 TRACE("deleting file: %s\n", debugstr_a((char*)url_entry
+url_entry
->local_name_off
));
2479 urlcache_entry_delete(container
, header
, hash_entry
);
2481 if(header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
<= desired_size
)
2484 /* Allow other threads to use cache while cleaning */
2485 cache_container_unlock_index(container
, header
);
2486 if(WaitForSingleObject(dll_unload_event
, 0) == WAIT_OBJECT_0
) {
2487 TRACE("got dll_unload_event - finishing\n");
2491 header
= cache_container_lock_index(container
);
2495 TRACE("cache size after cleaning 0x%s/0x%s\n",
2496 wine_dbgstr_longlong(header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
),
2497 wine_dbgstr_longlong(header
->cache_limit
.QuadPart
));
2498 cache_container_unlock_index(container
, header
);
2504 /***********************************************************************
2505 * FreeUrlCacheSpaceA (WININET.@)
2507 * See FreeUrlCacheSpaceW.
2509 BOOL WINAPI
FreeUrlCacheSpaceA(LPCSTR lpszCachePath
, DWORD dwSize
, DWORD dwFilter
)
2512 LPWSTR path
= heap_strdupAtoW(lpszCachePath
);
2513 if (lpszCachePath
== NULL
|| path
!= NULL
)
2514 ret
= FreeUrlCacheSpaceW(path
, dwSize
, dwFilter
);
2519 /***********************************************************************
2520 * UnlockUrlCacheEntryFileA (WININET.@)
2523 BOOL WINAPI
UnlockUrlCacheEntryFileA(LPCSTR lpszUrlName
, DWORD dwReserved
)
2525 urlcache_header
*pHeader
;
2526 struct hash_entry
*pHashEntry
;
2527 entry_header
*pEntry
;
2528 entry_url
* pUrlEntry
;
2529 cache_container
*pContainer
;
2532 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName
), dwReserved
);
2536 ERR("dwReserved != 0\n");
2537 SetLastError(ERROR_INVALID_PARAMETER
);
2541 error
= cache_containers_find(lpszUrlName
, &pContainer
);
2542 if (error
!= ERROR_SUCCESS
)
2544 SetLastError(error
);
2548 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
2549 if (error
!= ERROR_SUCCESS
)
2551 SetLastError(error
);
2555 if (!(pHeader
= cache_container_lock_index(pContainer
)))
2558 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
2560 cache_container_unlock_index(pContainer
, pHeader
);
2561 TRACE("entry %s not found!\n", lpszUrlName
);
2562 SetLastError(ERROR_FILE_NOT_FOUND
);
2566 pEntry
= (entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2567 if (pEntry
->signature
!= URL_SIGNATURE
)
2569 cache_container_unlock_index(pContainer
, pHeader
);
2570 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR
)&pEntry
->signature
, sizeof(DWORD
)));
2571 SetLastError(ERROR_FILE_NOT_FOUND
);
2575 pUrlEntry
= (entry_url
*)pEntry
;
2577 if (pUrlEntry
->use_count
== 0)
2579 cache_container_unlock_index(pContainer
, pHeader
);
2582 pUrlEntry
->use_count
--;
2583 if (!pUrlEntry
->use_count
)
2585 urlcache_hash_entry_set_flags(pHashEntry
, HASHTABLE_URL
);
2586 if (pUrlEntry
->cache_entry_type
& PENDING_DELETE_CACHE_ENTRY
)
2587 urlcache_entry_delete(pContainer
, pHeader
, pHashEntry
);
2590 cache_container_unlock_index(pContainer
, pHeader
);
2595 /***********************************************************************
2596 * UnlockUrlCacheEntryFileW (WININET.@)
2599 BOOL WINAPI
UnlockUrlCacheEntryFileW(LPCWSTR lpszUrlName
, DWORD dwReserved
)
2604 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
2607 ret
= UnlockUrlCacheEntryFileA(url
, dwReserved
);
2612 static BOOL
urlcache_entry_create(const char *url
, const char *ext
, WCHAR
*full_path
)
2614 cache_container
*container
;
2615 urlcache_header
*header
;
2616 char file_name
[MAX_PATH
];
2617 WCHAR extW
[MAX_PATH
];
2619 LONG full_path_len
, ext_len
= 0;
2620 BOOL generate_name
= FALSE
;
2627 TRACE("(%s, %s, %p)\n", debugstr_a(url
), debugstr_a(ext
), full_path
);
2629 memset(&uc
, 0, sizeof(uc
));
2630 uc
.dwStructSize
= sizeof(uc
);
2631 uc
.dwUrlPathLength
= 1;
2632 uc
.dwExtraInfoLength
= 1;
2633 if(!InternetCrackUrlA(url
, 0, 0, &uc
))
2634 uc
.dwUrlPathLength
= 0;
2636 if(!uc
.dwUrlPathLength
) {
2641 p
= e
= uc
.lpszUrlPath
+uc
.dwUrlPathLength
;
2642 while(p
>uc
.lpszUrlPath
&& *(p
-1)!='/' && *(p
-1)!='\\' && *(p
-1)!='.')
2644 if(p
>uc
.lpszUrlPath
&& *(p
-1)=='.') {
2646 while(p
>uc
.lpszUrlPath
&& *(p
-1)!='/' && *(p
-1)!='\\')
2652 memcpy(file_name
, p
, e
-p
);
2655 for(p
=file_name
; *p
; p
++) {
2668 generate_name
= TRUE
;
2670 error
= cache_containers_find(url
, &container
);
2671 if(error
!= ERROR_SUCCESS
) {
2672 SetLastError(error
);
2676 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2677 if(error
!= ERROR_SUCCESS
) {
2678 SetLastError(error
);
2682 if(!(header
= cache_container_lock_index(container
)))
2686 cache_dir
= (BYTE
)(rand() % header
->dirs_no
);
2688 cache_dir
= CACHE_CONTAINER_NO_SUBDIR
;
2690 full_path_len
= MAX_PATH
* sizeof(WCHAR
);
2691 if(!urlcache_create_file_pathW(container
, header
, file_name
, cache_dir
, full_path
, &full_path_len
, TRUE
)) {
2692 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2693 debugstr_a(file_name
), full_path_len
);
2694 cache_container_unlock_index(container
, header
);
2697 full_path_len
= full_path_len
/sizeof(WCHAR
) - 1;
2699 cache_container_unlock_index(container
, header
);
2705 ext_len
= MultiByteToWideChar(CP_ACP
, 0, ext
, -1, extW
+1, MAX_PATH
-1);
2707 for(p
=extW
; *p
; p
++) {
2717 if(p
[-1]==' ' || p
[-1]=='.')
2723 if(!generate_name
&& full_path_len
+5+ext_len
>=MAX_PATH
) { /* strlen("[255]") = 5 */
2724 full_path_len
= MAX_PATH
-5-ext_len
-1;
2727 for(i
=0; i
<255 && !generate_name
; i
++) {
2728 static const WCHAR format
[] = {'[','%','u',']','%','s',0};
2730 wsprintfW(full_path
+full_path_len
, format
, i
, extW
);
2732 TRACE("Trying: %s\n", debugstr_w(full_path
));
2733 file
= CreateFileW(full_path
, GENERIC_READ
, 0, NULL
, CREATE_NEW
, 0, NULL
);
2734 if(file
!= INVALID_HANDLE_VALUE
) {
2740 if(full_path_len
+8+ext_len
>= MAX_PATH
)
2741 full_path_len
= MAX_PATH
-8-ext_len
-1;
2743 /* Try to generate random name */
2744 GetSystemTimeAsFileTime(&ft
);
2745 strcpyW(full_path
+full_path_len
+8, extW
);
2747 for(i
=0; i
<255; i
++) {
2749 ULONGLONG n
= ft
.dwHighDateTime
;
2751 n
+= ft
.dwLowDateTime
;
2752 n
^= (ULONGLONG
)i
<<48;
2754 for(j
=0; j
<8; j
++) {
2757 full_path
[full_path_len
+j
] = (r
< 10 ? '0' + r
: 'A' + r
- 10);
2760 TRACE("Trying: %s\n", debugstr_w(full_path
));
2761 file
= CreateFileW(full_path
, GENERIC_READ
, 0, NULL
, CREATE_NEW
, 0, NULL
);
2762 if(file
!= INVALID_HANDLE_VALUE
) {
2768 WARN("Could not find a unique filename\n");
2772 /***********************************************************************
2773 * CreateUrlCacheEntryA (WININET.@)
2776 BOOL WINAPI
CreateUrlCacheEntryA(LPCSTR lpszUrlName
, DWORD dwExpectedFileSize
,
2777 LPCSTR lpszFileExtension
, LPSTR lpszFileName
, DWORD dwReserved
)
2779 WCHAR file_name
[MAX_PATH
];
2782 FIXME("dwReserved 0x%08x\n", dwReserved
);
2784 if(!urlcache_entry_create(lpszUrlName
, lpszFileExtension
, file_name
))
2787 if(!WideCharToMultiByte(CP_ACP
, 0, file_name
, -1, lpszFileName
, MAX_PATH
, NULL
, NULL
))
2791 /***********************************************************************
2792 * CreateUrlCacheEntryW (WININET.@)
2795 BOOL WINAPI
CreateUrlCacheEntryW(LPCWSTR lpszUrlName
, DWORD dwExpectedFileSize
,
2796 LPCWSTR lpszFileExtension
, LPWSTR lpszFileName
, DWORD dwReserved
)
2798 char *url
, *ext
= NULL
;
2802 FIXME("dwReserved 0x%08x\n", dwReserved
);
2804 if(lpszFileExtension
) {
2805 ext
= heap_strdupWtoUTF8(lpszFileExtension
);
2810 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
)) {
2815 ret
= urlcache_entry_create(url
, ext
, lpszFileName
);
2821 static BOOL
urlcache_entry_commit(const char *url
, const WCHAR
*file_name
,
2822 FILETIME expire_time
, FILETIME modify_time
, DWORD entry_type
,
2823 BYTE
*header_info
, DWORD header_size
, const char *file_ext
,
2824 const char *original_url
)
2826 cache_container
*container
;
2827 urlcache_header
*header
;
2828 struct hash_entry
*hash_entry
;
2829 entry_header
*entry
;
2830 entry_url
*url_entry
;
2831 DWORD url_entry_offset
;
2832 DWORD size
= DWORD_ALIGN(sizeof(*url_entry
));
2833 DWORD file_name_off
= 0;
2834 DWORD header_info_off
= 0;
2835 DWORD file_ext_off
= 0;
2836 WIN32_FILE_ATTRIBUTE_DATA file_attr
;
2837 LARGE_INTEGER file_size
;
2839 char file_name_no_container
[MAX_PATH
];
2840 char *local_file_name
= 0;
2842 DWORD exempt_delta
= 0;
2845 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n", debugstr_a(url
), debugstr_w(file_name
),
2846 entry_type
, header_info
, header_size
, debugstr_a(file_ext
), debugstr_a(original_url
));
2848 if(entry_type
& STICKY_CACHE_ENTRY
&& !file_name
) {
2849 SetLastError(ERROR_INVALID_PARAMETER
);
2853 WARN(": original_url ignored\n");
2855 memset(&file_attr
, 0, sizeof(file_attr
));
2857 if(!GetFileAttributesExW(file_name
, GetFileExInfoStandard
, &file_attr
))
2860 file_size
.u
.LowPart
= file_attr
.nFileSizeLow
;
2861 file_size
.u
.HighPart
= file_attr
.nFileSizeHigh
;
2863 error
= cache_containers_find(url
, &container
);
2864 if(error
!= ERROR_SUCCESS
) {
2865 SetLastError(error
);
2869 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2870 if(error
!= ERROR_SUCCESS
) {
2871 SetLastError(error
);
2875 if(!(header
= cache_container_lock_index(container
)))
2878 if(urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
2879 entry_url
*url_entry
= (entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
2881 if(urlcache_hash_entry_is_locked(hash_entry
, url_entry
)) {
2882 TRACE("Trying to overwrite locked entry\n");
2883 cache_container_unlock_index(container
, header
);
2884 SetLastError(ERROR_SHARING_VIOLATION
);
2888 hit_rate
= url_entry
->hit_rate
;
2889 exempt_delta
= url_entry
->exempt_delta
;
2890 urlcache_entry_delete(container
, header
, hash_entry
);
2896 dir_id
= CACHE_CONTAINER_NO_SUBDIR
;
2899 BOOL bFound
= FALSE
;
2901 if(strncmpW(file_name
, container
->path
, lstrlenW(container
->path
))) {
2902 ERR("path %s must begin with cache content path %s\n", debugstr_w(file_name
), debugstr_w(container
->path
));
2903 cache_container_unlock_index(container
, header
);
2904 SetLastError(ERROR_INVALID_PARAMETER
);
2908 /* skip container path prefix */
2909 file_name
+= lstrlenW(container
->path
);
2911 WideCharToMultiByte(CP_ACP
, 0, file_name
, -1, file_name_no_container
, MAX_PATH
, NULL
, NULL
);
2912 local_file_name
= file_name_no_container
;
2914 if(header
->dirs_no
) {
2915 for(dir_id
= 0; dir_id
< header
->dirs_no
; dir_id
++) {
2916 if(!strncmp(header
->directory_data
[dir_id
].name
, local_file_name
, DIR_LENGTH
)) {
2923 ERR("cache directory not found in path %s\n", debugstr_w(file_name
));
2924 cache_container_unlock_index(container
, header
);
2925 SetLastError(ERROR_INVALID_PARAMETER
);
2929 file_name
+= DIR_LENGTH
+ 1;
2930 local_file_name
+= DIR_LENGTH
+ 1;
2934 size
= DWORD_ALIGN(size
+ strlen(url
) + 1);
2936 file_name_off
= size
;
2937 size
= DWORD_ALIGN(size
+ strlen(local_file_name
) + 1);
2939 if(header_info
&& header_size
) {
2940 header_info_off
= size
;
2941 size
= DWORD_ALIGN(size
+ header_size
);
2943 if(file_ext
&& (file_ext_off
= strlen(file_ext
))) {
2944 DWORD len
= file_ext_off
;
2946 file_ext_off
= size
;
2947 size
= DWORD_ALIGN(size
+ len
+ 1);
2950 /* round up to next block */
2951 if(size
% BLOCKSIZE
) {
2952 size
-= size
% BLOCKSIZE
;
2956 error
= urlcache_entry_alloc(header
, size
/ BLOCKSIZE
, &entry
);
2957 while(error
== ERROR_HANDLE_DISK_FULL
) {
2958 error
= cache_container_clean_index(container
, &header
);
2959 if(error
== ERROR_SUCCESS
)
2960 error
= urlcache_entry_alloc(header
, size
/ BLOCKSIZE
, &entry
);
2962 if(error
!= ERROR_SUCCESS
) {
2963 cache_container_unlock_index(container
, header
);
2964 SetLastError(error
);
2968 /* FindFirstFreeEntry fills in blocks used */
2969 url_entry
= (entry_url
*)entry
;
2970 url_entry_offset
= (LPBYTE
)url_entry
- (LPBYTE
)header
;
2971 url_entry
->header
.signature
= URL_SIGNATURE
;
2972 url_entry
->cache_dir
= dir_id
;
2973 url_entry
->cache_entry_type
= entry_type
| container
->default_entry_type
;
2974 url_entry
->header_info_size
= header_size
;
2975 if((entry_type
& STICKY_CACHE_ENTRY
) && !exempt_delta
) {
2976 /* Sticky entries have a default exempt time of one day */
2977 exempt_delta
= 86400;
2979 url_entry
->exempt_delta
= exempt_delta
;
2980 url_entry
->hit_rate
= hit_rate
+1;
2981 url_entry
->file_extension_off
= file_ext_off
;
2982 url_entry
->header_info_off
= header_info_off
;
2983 url_entry
->local_name_off
= file_name_off
;
2984 url_entry
->url_off
= DWORD_ALIGN(sizeof(*url_entry
));
2985 url_entry
->size
.QuadPart
= file_size
.QuadPart
;
2986 url_entry
->use_count
= 0;
2987 GetSystemTimeAsFileTime(&url_entry
->access_time
);
2988 url_entry
->modification_time
= modify_time
;
2989 file_time_to_dos_date_time(&url_entry
->access_time
, &url_entry
->sync_date
, &url_entry
->sync_time
);
2990 file_time_to_dos_date_time(&expire_time
, &url_entry
->expire_date
, &url_entry
->expire_time
);
2991 file_time_to_dos_date_time(&file_attr
.ftLastWriteTime
, &url_entry
->write_date
, &url_entry
->write_time
);
2994 url_entry
->unk1
= 0;
2995 url_entry
->unk2
= 0;
2996 url_entry
->unk3
= 0x60;
2997 url_entry
->unk4
= 0;
2998 url_entry
->unk5
= 0x1010;
2999 url_entry
->unk7
= 0;
3000 url_entry
->unk8
= 0;
3003 strcpy((LPSTR
)url_entry
+ url_entry
->url_off
, url
);
3005 strcpy((LPSTR
)((LPBYTE
)url_entry
+ file_name_off
), local_file_name
);
3007 memcpy((LPBYTE
)url_entry
+ header_info_off
, header_info
, header_size
);
3009 strcpy((LPSTR
)((LPBYTE
)url_entry
+ file_ext_off
), file_ext
);
3011 error
= urlcache_hash_entry_create(header
, url
, url_entry_offset
, HASHTABLE_URL
);
3012 while(error
== ERROR_HANDLE_DISK_FULL
) {
3013 error
= cache_container_clean_index(container
, &header
);
3014 if(error
== ERROR_SUCCESS
) {
3015 url_entry
= (entry_url
*)((LPBYTE
)header
+ url_entry_offset
);
3016 error
= urlcache_hash_entry_create(header
, url
,
3017 url_entry_offset
, HASHTABLE_URL
);
3020 if(error
!= ERROR_SUCCESS
) {
3021 urlcache_entry_free(header
, &url_entry
->header
);
3022 cache_container_unlock_index(container
, header
);
3023 SetLastError(error
);
3027 if(url_entry
->cache_dir
< header
->dirs_no
)
3028 header
->directory_data
[url_entry
->cache_dir
].files_no
++;
3029 if(entry_type
& STICKY_CACHE_ENTRY
)
3030 header
->exempt_usage
.QuadPart
+= file_size
.QuadPart
;
3032 header
->cache_usage
.QuadPart
+= file_size
.QuadPart
;
3033 if(header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
> header
->cache_limit
.QuadPart
)
3034 handle_full_cache();
3036 cache_container_unlock_index(container
, header
);
3040 /***********************************************************************
3041 * CommitUrlCacheEntryA (WININET.@)
3043 BOOL WINAPI
CommitUrlCacheEntryA(LPCSTR lpszUrlName
, LPCSTR lpszLocalFileName
,
3044 FILETIME ExpireTime
, FILETIME LastModifiedTime
, DWORD CacheEntryType
,
3045 LPBYTE lpHeaderInfo
, DWORD dwHeaderSize
, LPCSTR lpszFileExtension
, LPCSTR lpszOriginalUrl
)
3047 WCHAR
*file_name
= NULL
;
3050 if(lpszLocalFileName
) {
3051 file_name
= heap_strdupAtoW(lpszLocalFileName
);
3056 ret
= urlcache_entry_commit(lpszUrlName
, file_name
, ExpireTime
, LastModifiedTime
,
3057 CacheEntryType
, lpHeaderInfo
, dwHeaderSize
, lpszFileExtension
, lpszOriginalUrl
);
3058 heap_free(file_name
);
3062 /***********************************************************************
3063 * CommitUrlCacheEntryW (WININET.@)
3065 BOOL WINAPI
CommitUrlCacheEntryW(LPCWSTR lpszUrlName
, LPCWSTR lpszLocalFileName
,
3066 FILETIME ExpireTime
, FILETIME LastModifiedTime
, DWORD CacheEntryType
,
3067 LPWSTR lpHeaderInfo
, DWORD dwHeaderSize
, LPCWSTR lpszFileExtension
, LPCWSTR lpszOriginalUrl
)
3069 char *url
, *original_url
=NULL
, *file_ext
=NULL
, *header_info
=NULL
;
3072 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
3076 header_info
= heap_strdupWtoUTF8(lpHeaderInfo
);
3081 dwHeaderSize
= strlen(header_info
);
3084 if(lpszFileExtension
) {
3085 file_ext
= heap_strdupWtoA(lpszFileExtension
);
3088 heap_free(header_info
);
3093 if(lpszOriginalUrl
&& !urlcache_encode_url_alloc(lpszOriginalUrl
, &original_url
)) {
3095 heap_free(header_info
);
3096 heap_free(file_ext
);
3100 ret
= urlcache_entry_commit(url
, lpszLocalFileName
, ExpireTime
, LastModifiedTime
,
3101 CacheEntryType
, (BYTE
*)header_info
, dwHeaderSize
, file_ext
, original_url
);
3103 heap_free(header_info
);
3104 heap_free(file_ext
);
3105 heap_free(original_url
);
3109 /***********************************************************************
3110 * ReadUrlCacheEntryStream (WININET.@)
3113 BOOL WINAPI
ReadUrlCacheEntryStream(
3114 IN HANDLE hUrlCacheStream
,
3115 IN DWORD dwLocation
,
3116 IN OUT LPVOID lpBuffer
,
3117 IN OUT LPDWORD lpdwLen
,
3121 /* Get handle to file from 'stream' */
3122 stream_handle
*pStream
= (stream_handle
*)hUrlCacheStream
;
3124 if (dwReserved
!= 0)
3126 ERR("dwReserved != 0\n");
3127 SetLastError(ERROR_INVALID_PARAMETER
);
3131 if (IsBadReadPtr(pStream
, sizeof(*pStream
)) || IsBadStringPtrA(pStream
->url
, INTERNET_MAX_URL_LENGTH
))
3133 SetLastError(ERROR_INVALID_HANDLE
);
3137 if (SetFilePointer(pStream
->file
, dwLocation
, NULL
, FILE_CURRENT
) == INVALID_SET_FILE_POINTER
)
3139 return ReadFile(pStream
->file
, lpBuffer
, *lpdwLen
, lpdwLen
, NULL
);
3142 /***********************************************************************
3143 * RetrieveUrlCacheEntryStreamA (WININET.@)
3146 HANDLE WINAPI
RetrieveUrlCacheEntryStreamA(LPCSTR lpszUrlName
,
3147 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
3148 LPDWORD lpdwCacheEntryInfoBufferSize
, BOOL fRandomRead
, DWORD dwReserved
)
3150 /* NOTE: this is not the same as the way that the native
3151 * version allocates 'stream' handles. I did it this way
3152 * as it is much easier and no applications should depend
3153 * on this behaviour. (Native version appears to allocate
3154 * indices into a table)
3156 stream_handle
*stream
;
3159 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName
), lpCacheEntryInfo
,
3160 lpdwCacheEntryInfoBufferSize
, fRandomRead
, dwReserved
);
3162 if(!RetrieveUrlCacheEntryFileA(lpszUrlName
, lpCacheEntryInfo
,
3163 lpdwCacheEntryInfoBufferSize
, dwReserved
))
3166 file
= CreateFileA(lpCacheEntryInfo
->lpszLocalFileName
, GENERIC_READ
, FILE_SHARE_READ
,
3167 NULL
, OPEN_EXISTING
, fRandomRead
? FILE_FLAG_RANDOM_ACCESS
: 0, NULL
);
3168 if(file
== INVALID_HANDLE_VALUE
) {
3169 UnlockUrlCacheEntryFileA(lpszUrlName
, 0);
3173 /* allocate handle storage space */
3174 stream
= heap_alloc(sizeof(stream_handle
) + strlen(lpszUrlName
) * sizeof(CHAR
));
3177 UnlockUrlCacheEntryFileA(lpszUrlName
, 0);
3178 SetLastError(ERROR_OUTOFMEMORY
);
3182 stream
->file
= file
;
3183 strcpy(stream
->url
, lpszUrlName
);
3187 /***********************************************************************
3188 * RetrieveUrlCacheEntryStreamW (WININET.@)
3191 HANDLE WINAPI
RetrieveUrlCacheEntryStreamW(LPCWSTR lpszUrlName
,
3192 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
3193 LPDWORD lpdwCacheEntryInfoBufferSize
, BOOL fRandomRead
, DWORD dwReserved
)
3196 /* NOTE: this is not the same as the way that the native
3197 * version allocates 'stream' handles. I did it this way
3198 * as it is much easier and no applications should depend
3199 * on this behaviour. (Native version appears to allocate
3200 * indices into a table)
3202 stream_handle
*stream
;
3205 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName
), lpCacheEntryInfo
,
3206 lpdwCacheEntryInfoBufferSize
, fRandomRead
, dwReserved
);
3208 if(!(len
= urlcache_encode_url(lpszUrlName
, NULL
, 0)))
3211 if(!RetrieveUrlCacheEntryFileW(lpszUrlName
, lpCacheEntryInfo
,
3212 lpdwCacheEntryInfoBufferSize
, dwReserved
))
3215 file
= CreateFileW(lpCacheEntryInfo
->lpszLocalFileName
, GENERIC_READ
, FILE_SHARE_READ
,
3216 NULL
, OPEN_EXISTING
, fRandomRead
? FILE_FLAG_RANDOM_ACCESS
: 0, NULL
);
3217 if(file
== INVALID_HANDLE_VALUE
) {
3218 UnlockUrlCacheEntryFileW(lpszUrlName
, 0);
3222 /* allocate handle storage space */
3223 stream
= heap_alloc(sizeof(stream_handle
) + len
*sizeof(WCHAR
));
3226 UnlockUrlCacheEntryFileW(lpszUrlName
, 0);
3227 SetLastError(ERROR_OUTOFMEMORY
);
3231 stream
->file
= file
;
3232 if(!urlcache_encode_url(lpszUrlName
, stream
->url
, len
)) {
3234 UnlockUrlCacheEntryFileW(lpszUrlName
, 0);
3241 /***********************************************************************
3242 * UnlockUrlCacheEntryStream (WININET.@)
3245 BOOL WINAPI
UnlockUrlCacheEntryStream(
3246 IN HANDLE hUrlCacheStream
,
3250 stream_handle
*pStream
= (stream_handle
*)hUrlCacheStream
;
3252 if (dwReserved
!= 0)
3254 ERR("dwReserved != 0\n");
3255 SetLastError(ERROR_INVALID_PARAMETER
);
3259 if (IsBadReadPtr(pStream
, sizeof(*pStream
)) || IsBadStringPtrA(pStream
->url
, INTERNET_MAX_URL_LENGTH
))
3261 SetLastError(ERROR_INVALID_HANDLE
);
3265 if (!UnlockUrlCacheEntryFileA(pStream
->url
, 0))
3268 CloseHandle(pStream
->file
);
3274 /***********************************************************************
3275 * DeleteUrlCacheEntryA (WININET.@)
3278 BOOL WINAPI
DeleteUrlCacheEntryA(LPCSTR lpszUrlName
)
3280 cache_container
*pContainer
;
3281 urlcache_header
*pHeader
;
3282 struct hash_entry
*pHashEntry
;
3286 TRACE("(%s)\n", debugstr_a(lpszUrlName
));
3288 error
= cache_containers_find(lpszUrlName
, &pContainer
);
3289 if (error
!= ERROR_SUCCESS
)
3291 SetLastError(error
);
3295 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
3296 if (error
!= ERROR_SUCCESS
)
3298 SetLastError(error
);
3302 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3305 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
3307 cache_container_unlock_index(pContainer
, pHeader
);
3308 TRACE("entry %s not found!\n", lpszUrlName
);
3309 SetLastError(ERROR_FILE_NOT_FOUND
);
3313 ret
= urlcache_entry_delete(pContainer
, pHeader
, pHashEntry
);
3315 cache_container_unlock_index(pContainer
, pHeader
);
3320 /***********************************************************************
3321 * DeleteUrlCacheEntryW (WININET.@)
3324 BOOL WINAPI
DeleteUrlCacheEntryW(LPCWSTR lpszUrlName
)
3329 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
3332 ret
= DeleteUrlCacheEntryA(url
);
3337 BOOL WINAPI
DeleteUrlCacheContainerA(DWORD d1
, DWORD d2
)
3339 FIXME("(0x%08x, 0x%08x) stub\n", d1
, d2
);
3343 BOOL WINAPI
DeleteUrlCacheContainerW(DWORD d1
, DWORD d2
)
3345 FIXME("(0x%08x, 0x%08x) stub\n", d1
, d2
);
3349 /***********************************************************************
3350 * CreateCacheContainerA (WININET.@)
3352 BOOL WINAPI
CreateUrlCacheContainerA(DWORD d1
, DWORD d2
, DWORD d3
, DWORD d4
,
3353 DWORD d5
, DWORD d6
, DWORD d7
, DWORD d8
)
3355 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3356 d1
, d2
, d3
, d4
, d5
, d6
, d7
, d8
);
3360 /***********************************************************************
3361 * CreateCacheContainerW (WININET.@)
3363 BOOL WINAPI
CreateUrlCacheContainerW(DWORD d1
, DWORD d2
, DWORD d3
, DWORD d4
,
3364 DWORD d5
, DWORD d6
, DWORD d7
, DWORD d8
)
3366 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3367 d1
, d2
, d3
, d4
, d5
, d6
, d7
, d8
);
3371 /***********************************************************************
3372 * FindFirstUrlCacheContainerA (WININET.@)
3374 HANDLE WINAPI
FindFirstUrlCacheContainerA( LPVOID p1
, LPVOID p2
, LPVOID p3
, DWORD d1
)
3376 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1
, p2
, p3
, d1
);
3380 /***********************************************************************
3381 * FindFirstUrlCacheContainerW (WININET.@)
3383 HANDLE WINAPI
FindFirstUrlCacheContainerW( LPVOID p1
, LPVOID p2
, LPVOID p3
, DWORD d1
)
3385 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1
, p2
, p3
, d1
);
3389 /***********************************************************************
3390 * FindNextUrlCacheContainerA (WININET.@)
3392 BOOL WINAPI
FindNextUrlCacheContainerA( HANDLE handle
, LPVOID p1
, LPVOID p2
)
3394 FIXME("(%p, %p, %p) stub\n", handle
, p1
, p2
);
3398 /***********************************************************************
3399 * FindNextUrlCacheContainerW (WININET.@)
3401 BOOL WINAPI
FindNextUrlCacheContainerW( HANDLE handle
, LPVOID p1
, LPVOID p2
)
3403 FIXME("(%p, %p, %p) stub\n", handle
, p1
, p2
);
3407 HANDLE WINAPI
FindFirstUrlCacheEntryExA(
3408 LPCSTR lpszUrlSearchPattern
,
3412 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
,
3413 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3415 LPDWORD pcbReserved2
,
3419 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern
),
3420 dwFlags
, dwFilter
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
, lpFirstCacheEntryInfo
,
3421 lpdwFirstCacheEntryInfoBufferSize
, lpReserved
, pcbReserved2
,lpReserved3
);
3422 SetLastError(ERROR_FILE_NOT_FOUND
);
3426 HANDLE WINAPI
FindFirstUrlCacheEntryExW(
3427 LPCWSTR lpszUrlSearchPattern
,
3431 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
,
3432 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3434 LPDWORD pcbReserved2
,
3438 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern
),
3439 dwFlags
, dwFilter
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
, lpFirstCacheEntryInfo
,
3440 lpdwFirstCacheEntryInfoBufferSize
, lpReserved
, pcbReserved2
,lpReserved3
);
3441 SetLastError(ERROR_FILE_NOT_FOUND
);
3445 /***********************************************************************
3446 * FindFirstUrlCacheEntryA (WININET.@)
3449 INTERNETAPI HANDLE WINAPI
FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern
,
3450 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
, LPDWORD lpdwFirstCacheEntryInfoBufferSize
)
3452 find_handle
*pEntryHandle
;
3454 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern
), lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
);
3456 pEntryHandle
= heap_alloc(sizeof(*pEntryHandle
));
3460 pEntryHandle
->magic
= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
;
3461 if (lpszUrlSearchPattern
)
3463 pEntryHandle
->url_search_pattern
= heap_strdupA(lpszUrlSearchPattern
);
3464 if (!pEntryHandle
->url_search_pattern
)
3466 heap_free(pEntryHandle
);
3471 pEntryHandle
->url_search_pattern
= NULL
;
3472 pEntryHandle
->container_idx
= 0;
3473 pEntryHandle
->hash_table_idx
= 0;
3474 pEntryHandle
->hash_entry_idx
= 0;
3476 if (!FindNextUrlCacheEntryA(pEntryHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
))
3478 heap_free(pEntryHandle
);
3481 return pEntryHandle
;
3484 /***********************************************************************
3485 * FindFirstUrlCacheEntryW (WININET.@)
3488 INTERNETAPI HANDLE WINAPI
FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern
,
3489 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
, LPDWORD lpdwFirstCacheEntryInfoBufferSize
)
3491 find_handle
*pEntryHandle
;
3493 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern
), lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
);
3495 pEntryHandle
= heap_alloc(sizeof(*pEntryHandle
));
3499 pEntryHandle
->magic
= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
;
3500 if (lpszUrlSearchPattern
)
3502 pEntryHandle
->url_search_pattern
= heap_strdupWtoA(lpszUrlSearchPattern
);
3503 if (!pEntryHandle
->url_search_pattern
)
3505 heap_free(pEntryHandle
);
3510 pEntryHandle
->url_search_pattern
= NULL
;
3511 pEntryHandle
->container_idx
= 0;
3512 pEntryHandle
->hash_table_idx
= 0;
3513 pEntryHandle
->hash_entry_idx
= 0;
3515 if (!FindNextUrlCacheEntryW(pEntryHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
))
3517 heap_free(pEntryHandle
);
3520 return pEntryHandle
;
3523 static BOOL
urlcache_find_next_entry(
3525 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo
,
3526 LPDWORD lpdwNextCacheEntryInfoBufferSize
,
3529 find_handle
*pEntryHandle
= (find_handle
*)hEnumHandle
;
3530 cache_container
*pContainer
;
3532 if (pEntryHandle
->magic
!= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
)
3534 SetLastError(ERROR_INVALID_HANDLE
);
3538 for (; cache_containers_enum(pEntryHandle
->url_search_pattern
, pEntryHandle
->container_idx
, &pContainer
);
3539 pEntryHandle
->container_idx
++, pEntryHandle
->hash_table_idx
= 0)
3541 urlcache_header
*pHeader
;
3542 entry_hash_table
*pHashTableEntry
;
3545 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
3546 if (error
!= ERROR_SUCCESS
)
3548 SetLastError(error
);
3552 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3555 for (; urlcache_enum_hash_tables(pHeader
, &pEntryHandle
->hash_table_idx
, &pHashTableEntry
);
3556 pEntryHandle
->hash_table_idx
++, pEntryHandle
->hash_entry_idx
= 0)
3558 const struct hash_entry
*pHashEntry
= NULL
;
3559 for (; urlcache_enum_hash_table_entries(pHeader
, pHashTableEntry
, &pEntryHandle
->hash_entry_idx
, &pHashEntry
);
3560 pEntryHandle
->hash_entry_idx
++)
3562 const entry_url
*pUrlEntry
;
3563 const entry_header
*pEntry
= (const entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
3565 if (pEntry
->signature
!= URL_SIGNATURE
)
3568 pUrlEntry
= (const entry_url
*)pEntry
;
3569 TRACE("Found URL: %s\n",
3570 debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->url_off
));
3571 TRACE("Header info: %s\n",
3572 debugstr_an((LPCSTR
)pUrlEntry
+ pUrlEntry
->header_info_off
,
3573 pUrlEntry
->header_info_size
));
3575 error
= urlcache_copy_entry(
3578 lpNextCacheEntryInfo
,
3579 lpdwNextCacheEntryInfoBufferSize
,
3582 if (error
!= ERROR_SUCCESS
)
3584 cache_container_unlock_index(pContainer
, pHeader
);
3585 SetLastError(error
);
3588 if(pUrlEntry
->local_name_off
)
3589 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->local_name_off
));
3591 /* increment the current index so that next time the function
3592 * is called the next entry is returned */
3593 pEntryHandle
->hash_entry_idx
++;
3594 cache_container_unlock_index(pContainer
, pHeader
);
3599 cache_container_unlock_index(pContainer
, pHeader
);
3602 SetLastError(ERROR_NO_MORE_ITEMS
);
3606 /***********************************************************************
3607 * FindNextUrlCacheEntryA (WININET.@)
3609 BOOL WINAPI
FindNextUrlCacheEntryA(
3611 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo
,
3612 LPDWORD lpdwNextCacheEntryInfoBufferSize
)
3614 TRACE("(%p, %p, %p)\n", hEnumHandle
, lpNextCacheEntryInfo
, lpdwNextCacheEntryInfoBufferSize
);
3616 return urlcache_find_next_entry(hEnumHandle
, lpNextCacheEntryInfo
,
3617 lpdwNextCacheEntryInfoBufferSize
, FALSE
/* not UNICODE */);
3620 /***********************************************************************
3621 * FindNextUrlCacheEntryW (WININET.@)
3623 BOOL WINAPI
FindNextUrlCacheEntryW(
3625 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo
,
3626 LPDWORD lpdwNextCacheEntryInfoBufferSize
3629 TRACE("(%p, %p, %p)\n", hEnumHandle
, lpNextCacheEntryInfo
, lpdwNextCacheEntryInfoBufferSize
);
3631 return urlcache_find_next_entry(hEnumHandle
,
3632 (LPINTERNET_CACHE_ENTRY_INFOA
)lpNextCacheEntryInfo
,
3633 lpdwNextCacheEntryInfoBufferSize
, TRUE
/* UNICODE */);
3636 /***********************************************************************
3637 * FindCloseUrlCache (WININET.@)
3639 BOOL WINAPI
FindCloseUrlCache(HANDLE hEnumHandle
)
3641 find_handle
*pEntryHandle
= (find_handle
*)hEnumHandle
;
3643 TRACE("(%p)\n", hEnumHandle
);
3645 if (!pEntryHandle
|| pEntryHandle
->magic
!= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
)
3647 SetLastError(ERROR_INVALID_HANDLE
);
3651 pEntryHandle
->magic
= 0;
3652 heap_free(pEntryHandle
->url_search_pattern
);
3653 heap_free(pEntryHandle
);
3657 HANDLE WINAPI
FindFirstUrlCacheGroup( DWORD dwFlags
, DWORD dwFilter
, LPVOID lpSearchCondition
,
3658 DWORD dwSearchCondition
, GROUPID
* lpGroupId
, LPVOID lpReserved
)
3660 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags
, dwFilter
, lpSearchCondition
,
3661 dwSearchCondition
, lpGroupId
, lpReserved
);
3665 BOOL WINAPI
FindNextUrlCacheEntryExA(
3667 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
,
3668 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3670 LPDWORD pcbReserved2
,
3674 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
,
3675 lpReserved
, pcbReserved2
, lpReserved3
);
3679 BOOL WINAPI
FindNextUrlCacheEntryExW(
3681 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
,
3682 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3684 LPDWORD pcbReserved2
,
3688 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
,
3689 lpReserved
, pcbReserved2
, lpReserved3
);
3693 BOOL WINAPI
FindNextUrlCacheGroup( HANDLE hFind
, GROUPID
* lpGroupId
, LPVOID lpReserved
)
3695 FIXME("(%p, %p, %p) stub\n", hFind
, lpGroupId
, lpReserved
);
3699 /***********************************************************************
3700 * CreateUrlCacheGroup (WININET.@)
3703 INTERNETAPI GROUPID WINAPI
CreateUrlCacheGroup(DWORD dwFlags
, LPVOID lpReserved
)
3705 FIXME("(0x%08x, %p): stub\n", dwFlags
, lpReserved
);
3709 /***********************************************************************
3710 * DeleteUrlCacheGroup (WININET.@)
3713 BOOL WINAPI
DeleteUrlCacheGroup(GROUPID GroupId
, DWORD dwFlags
, LPVOID lpReserved
)
3715 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3716 (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
, dwFlags
, lpReserved
);
3720 /***********************************************************************
3721 * DeleteWpadCacheForNetworks (WININET.@)
3722 * Undocumented, added in IE8
3724 BOOL WINAPI
DeleteWpadCacheForNetworks(DWORD unk1
)
3726 FIXME("(%d) stub\n", unk1
);
3730 /***********************************************************************
3731 * SetUrlCacheEntryGroupA (WININET.@)
3734 BOOL WINAPI
SetUrlCacheEntryGroupA(LPCSTR lpszUrlName
, DWORD dwFlags
,
3735 GROUPID GroupId
, LPBYTE pbGroupAttributes
, DWORD cbGroupAttributes
,
3738 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3739 debugstr_a(lpszUrlName
), dwFlags
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
,
3740 pbGroupAttributes
, cbGroupAttributes
, lpReserved
);
3741 SetLastError(ERROR_FILE_NOT_FOUND
);
3745 /***********************************************************************
3746 * SetUrlCacheEntryGroupW (WININET.@)
3749 BOOL WINAPI
SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName
, DWORD dwFlags
,
3750 GROUPID GroupId
, LPBYTE pbGroupAttributes
, DWORD cbGroupAttributes
,
3753 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3754 debugstr_w(lpszUrlName
), dwFlags
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
,
3755 pbGroupAttributes
, cbGroupAttributes
, lpReserved
);
3756 SetLastError(ERROR_FILE_NOT_FOUND
);
3760 /***********************************************************************
3761 * GetUrlCacheConfigInfoW (WININET.@)
3763 BOOL WINAPI
GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo
, LPDWORD size
, DWORD bitmask
)
3765 FIXME("(%p, %p, %x)\n", CacheInfo
, size
, bitmask
);
3766 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
3770 /***********************************************************************
3771 * GetUrlCacheConfigInfoA (WININET.@)
3773 BOOL WINAPI
GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo
, LPDWORD size
, DWORD bitmask
)
3775 FIXME("(%p, %p, %x)\n", CacheInfo
, size
, bitmask
);
3776 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
3780 BOOL WINAPI
GetUrlCacheGroupAttributeA( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3781 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo
,
3782 LPDWORD lpdwGroupInfo
, LPVOID lpReserved
)
3784 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3785 (ULONG
)(gid
>> 32), (ULONG
)gid
, dwFlags
, dwAttributes
, lpGroupInfo
,
3786 lpdwGroupInfo
, lpReserved
);
3790 BOOL WINAPI
GetUrlCacheGroupAttributeW( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3791 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo
,
3792 LPDWORD lpdwGroupInfo
, LPVOID lpReserved
)
3794 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3795 (ULONG
)(gid
>> 32), (ULONG
)gid
, dwFlags
, dwAttributes
, lpGroupInfo
,
3796 lpdwGroupInfo
, lpReserved
);
3800 BOOL WINAPI
SetUrlCacheGroupAttributeA( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3801 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo
, LPVOID lpReserved
)
3803 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3804 (ULONG
)(gid
>> 32), (ULONG
)gid
, dwFlags
, dwAttributes
, lpGroupInfo
, lpReserved
);
3808 BOOL WINAPI
SetUrlCacheGroupAttributeW( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3809 LPINTERNET_CACHE_GROUP_INFOW 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
SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo
, DWORD dwFieldControl
)
3818 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo
, dwFieldControl
);
3822 BOOL WINAPI
SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo
, DWORD dwFieldControl
)
3824 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo
, dwFieldControl
);
3828 /***********************************************************************
3829 * DeleteIE3Cache (WININET.@)
3831 * Deletes the files used by the IE3 URL caching system.
3834 * hWnd [I] A dummy window.
3835 * hInst [I] Instance of process calling the function.
3836 * lpszCmdLine [I] Options used by function.
3837 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3839 DWORD WINAPI
DeleteIE3Cache(HWND hWnd
, HINSTANCE hInst
, LPSTR lpszCmdLine
, int nCmdShow
)
3841 FIXME("(%p, %p, %s, %d)\n", hWnd
, hInst
, debugstr_a(lpszCmdLine
), nCmdShow
);
3845 static BOOL
urlcache_entry_is_expired(const entry_url
*pUrlEntry
,
3846 FILETIME
*pftLastModified
)
3849 FILETIME now
, expired
;
3851 *pftLastModified
= pUrlEntry
->modification_time
;
3852 GetSystemTimeAsFileTime(&now
);
3853 dos_date_time_to_file_time(pUrlEntry
->expire_date
,
3854 pUrlEntry
->expire_time
, &expired
);
3855 /* If the expired time is 0, it's interpreted as not expired */
3856 if (!expired
.dwLowDateTime
&& !expired
.dwHighDateTime
)
3859 ret
= CompareFileTime(&expired
, &now
) < 0;
3863 /***********************************************************************
3864 * IsUrlCacheEntryExpiredA (WININET.@)
3868 * dwFlags [I] Unknown
3869 * pftLastModified [O] Last modified time
3871 BOOL WINAPI
IsUrlCacheEntryExpiredA(LPCSTR url
, DWORD dwFlags
, FILETIME
* pftLastModified
)
3873 urlcache_header
*pHeader
;
3874 struct hash_entry
*pHashEntry
;
3875 const entry_header
*pEntry
;
3876 const entry_url
* pUrlEntry
;
3877 cache_container
*pContainer
;
3880 TRACE("(%s, %08x, %p)\n", debugstr_a(url
), dwFlags
, pftLastModified
);
3882 if (!url
|| !pftLastModified
)
3885 FIXME("unknown flags 0x%08x\n", dwFlags
);
3887 /* Any error implies that the URL is expired, i.e. not in the cache */
3888 if (cache_containers_find(url
, &pContainer
))
3890 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3894 if (cache_container_open_index(pContainer
, MIN_BLOCK_NO
))
3896 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3900 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3902 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3906 if (!urlcache_find_hash_entry(pHeader
, url
, &pHashEntry
))
3908 cache_container_unlock_index(pContainer
, pHeader
);
3909 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3910 TRACE("entry %s not found!\n", url
);
3914 pEntry
= (const entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
3915 if (pEntry
->signature
!= URL_SIGNATURE
)
3917 cache_container_unlock_index(pContainer
, pHeader
);
3918 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3919 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR
)&pEntry
->signature
, sizeof(DWORD
)));
3923 pUrlEntry
= (const entry_url
*)pEntry
;
3924 expired
= urlcache_entry_is_expired(pUrlEntry
, pftLastModified
);
3926 cache_container_unlock_index(pContainer
, pHeader
);
3931 /***********************************************************************
3932 * IsUrlCacheEntryExpiredW (WININET.@)
3936 * dwFlags [I] Unknown
3937 * pftLastModified [O] Last modified time
3939 BOOL WINAPI
IsUrlCacheEntryExpiredW(LPCWSTR url
, DWORD dwFlags
, FILETIME
* pftLastModified
)
3944 if(!urlcache_encode_url_alloc(url
, &encoded_url
))
3947 ret
= IsUrlCacheEntryExpiredA(encoded_url
, dwFlags
, pftLastModified
);
3948 heap_free(encoded_url
);
3952 /***********************************************************************
3953 * GetDiskInfoA (WININET.@)
3955 BOOL WINAPI
GetDiskInfoA(PCSTR path
, PDWORD cluster_size
, PDWORDLONG free
, PDWORDLONG total
)
3958 ULARGE_INTEGER bytes_free
, bytes_total
;
3960 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path
), cluster_size
, free
, total
);
3964 SetLastError(ERROR_INVALID_PARAMETER
);
3968 if ((ret
= GetDiskFreeSpaceExA(path
, NULL
, &bytes_total
, &bytes_free
)))
3970 if (cluster_size
) *cluster_size
= 1;
3971 if (free
) *free
= bytes_free
.QuadPart
;
3972 if (total
) *total
= bytes_total
.QuadPart
;
3977 /***********************************************************************
3978 * RegisterUrlCacheNotification (WININET.@)
3980 DWORD WINAPI
RegisterUrlCacheNotification(LPVOID a
, DWORD b
, DWORD c
, DWORD d
, DWORD e
, DWORD f
)
3982 FIXME("(%p %x %x %x %x %x)\n", a
, b
, c
, d
, e
, f
);
3986 /***********************************************************************
3987 * IncrementUrlCacheHeaderData (WININET.@)
3989 BOOL WINAPI
IncrementUrlCacheHeaderData(DWORD index
, LPDWORD data
)
3991 FIXME("(%u, %p)\n", index
, data
);
3995 /***********************************************************************
3996 * RunOnceUrlCache (WININET.@)
3999 DWORD WINAPI
RunOnceUrlCache(HWND hwnd
, HINSTANCE hinst
, LPSTR cmd
, int cmdshow
)
4001 FIXME("(%p, %p, %s, %d): stub\n", hwnd
, hinst
, debugstr_a(cmd
), cmdshow
);
4005 BOOL
init_urlcache(void)
4007 dll_unload_event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
4008 if(!dll_unload_event
)
4011 free_cache_running
= CreateSemaphoreW(NULL
, 1, 1, NULL
);
4012 if(!free_cache_running
) {
4013 CloseHandle(dll_unload_event
);
4017 cache_containers_init();
4021 void free_urlcache(void)
4023 SetEvent(dll_unload_event
);
4024 WaitForSingleObject(free_cache_running
, INFINITE
);
4025 ReleaseSemaphore(free_cache_running
, 1, NULL
);
4026 CloseHandle(free_cache_running
);
4027 CloseHandle(dll_unload_event
);
4029 cache_containers_free();
4032 /***********************************************************************
4033 * LoadUrlCacheContent (WININET.@)
4035 BOOL WINAPI
LoadUrlCacheContent(void)