2 * Wininet - Url Cache functions
4 * Copyright 2001,2002 CodeWeavers
5 * Copyright 2003-2008 Robert Shearman
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #define NONAMELESSUNION
26 #define NONAMELESSSTRUCT
48 #include "wine/debug.h"
50 WINE_DEFAULT_DEBUG_CHANNEL(wininet
);
52 static const char urlcache_ver_prefix
[] = "WINE URLCache Ver ";
53 static const char urlcache_ver
[] = "0.2012001";
56 #define CHAR_BIT (8 * sizeof(CHAR))
59 #define ENTRY_START_OFFSET 0x4000
61 #define MAX_DIR_NO 0x20
63 #define HASHTABLE_SIZE 448
64 #define HASHTABLE_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
65 #define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
66 #define ALLOCATION_TABLE_OFFSET 0x250
67 #define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
68 #define MIN_BLOCK_NO 0x80
69 #define MAX_BLOCK_NO (ALLOCATION_TABLE_SIZE * CHAR_BIT)
70 #define FILE_SIZE(blocks) ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
72 #define HASHTABLE_URL 0
73 #define HASHTABLE_DEL 1
74 #define HASHTABLE_LOCK 2
75 #define HASHTABLE_FREE 3
76 #define HASHTABLE_REDR 5
77 #define HASHTABLE_FLAG_BITS 6
79 #define PENDING_DELETE_CACHE_ENTRY 0x00400000
80 #define INSTALLED_CACHE_ENTRY 0x10000000
81 #define GET_INSTALLED_ENTRY 0x200
82 #define CACHE_CONTAINER_NO_SUBDIR 0xFE
84 #define CACHE_HEADER_DATA_ROOT_LEAK_OFFSET 0x16
86 #define FILETIME_SECOND 10000000
88 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
89 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
90 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
91 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
92 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
94 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
96 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
101 DWORD blocks_used
; /* number of 128byte blocks used by this entry */
107 FILETIME modification_time
;
108 FILETIME access_time
;
109 WORD expire_date
; /* expire date in dos format */
110 WORD expire_time
; /* expire time in dos format */
111 DWORD unk1
; /* usually zero */
112 ULARGE_INTEGER size
; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
113 DWORD unk2
; /* usually zero */
114 DWORD exempt_delta
; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
115 DWORD unk3
; /* usually 0x60 */
116 DWORD url_off
; /* offset of start of url from start of entry */
117 BYTE cache_dir
; /* index of cache directory this url is stored in */
118 BYTE unk4
; /* usually zero */
119 WORD unk5
; /* usually 0x1010 */
120 DWORD local_name_off
; /* offset of start of local filename from start of entry */
121 DWORD cache_entry_type
; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
122 DWORD header_info_off
; /* offset of start of header info from start of entry */
123 DWORD header_info_size
;
124 DWORD file_extension_off
; /* offset of start of file extension from start of entry */
125 WORD sync_date
; /* last sync date in dos format */
126 WORD sync_time
; /* last sync time in dos format */
127 DWORD hit_rate
; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
128 DWORD use_count
; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
131 DWORD unk7
; /* usually zero */
132 DWORD unk8
; /* usually zero */
133 /* packing to dword align start of next field */
134 /* CHAR szSourceUrlName[]; (url) */
135 /* packing to dword align start of next field */
136 /* CHAR szLocalFileName[]; (local file name excluding path) */
137 /* packing to dword align start of next field */
138 /* CHAR szHeaderInfo[]; (header info) */
152 struct hash_entry hash_table
[HASHTABLE_SIZE
];
159 DWORD hash_table_off
;
160 DWORD capacity_in_blocks
;
163 ULARGE_INTEGER cache_limit
;
164 ULARGE_INTEGER cache_usage
;
165 ULARGE_INTEGER exempt_usage
;
167 struct _directory_data
170 char name
[DIR_LENGTH
];
171 } directory_data
[MAX_DIR_NO
];
173 BYTE allocation_table
[ALLOCATION_TABLE_SIZE
];
184 struct list entry
; /* part of a list */
185 char *cache_prefix
; /* string that has to be prefixed for this container to be used */
186 LPWSTR path
; /* path to url container directory */
187 HANDLE mapping
; /* handle of file mapping */
188 DWORD file_size
; /* size of file when mapping was opened */
189 HANDLE mutex
; /* handle of mutex */
190 DWORD default_entry_type
;
196 char *url_search_pattern
;
198 DWORD hash_table_idx
;
199 DWORD hash_entry_idx
;
202 /* List of all containers available */
203 static struct list UrlContainers
= LIST_INIT(UrlContainers
);
205 static inline char *heap_strdupWtoUTF8(LPCWSTR str
)
210 DWORD size
= WideCharToMultiByte(CP_UTF8
, 0, str
, -1, NULL
, 0, NULL
, NULL
);
211 ret
= heap_alloc(size
);
213 WideCharToMultiByte(CP_UTF8
, 0, str
, -1, ret
, size
, NULL
, NULL
);
219 /***********************************************************************
220 * urlcache_block_is_free (Internal)
222 * Is the specified block number free?
229 static inline BYTE
urlcache_block_is_free(BYTE
*allocation_table
, DWORD block_number
)
231 BYTE mask
= 1 << (block_number
%CHAR_BIT
);
232 return (allocation_table
[block_number
/CHAR_BIT
] & mask
) == 0;
235 /***********************************************************************
236 * urlcache_block_free (Internal)
238 * Marks the specified block as free
241 * this function is not updating used blocks count
247 static inline void urlcache_block_free(BYTE
*allocation_table
, DWORD block_number
)
249 BYTE mask
= ~(1 << (block_number
%CHAR_BIT
));
250 allocation_table
[block_number
/CHAR_BIT
] &= mask
;
253 /***********************************************************************
254 * urlcache_block_alloc (Internal)
256 * Marks the specified block as allocated
259 * this function is not updating used blocks count
265 static inline void urlcache_block_alloc(BYTE
*allocation_table
, DWORD block_number
)
267 BYTE mask
= 1 << (block_number
%CHAR_BIT
);
268 allocation_table
[block_number
/CHAR_BIT
] |= mask
;
271 /***********************************************************************
272 * urlcache_entry_alloc (Internal)
274 * Finds and allocates the first block of free space big enough and
275 * sets entry to point to it.
278 * ERROR_SUCCESS when free memory block was found
279 * Any other Win32 error code if the entry could not be added
282 static DWORD
urlcache_entry_alloc(urlcache_header
*header
, DWORD blocks_needed
, entry_header
**entry
)
284 DWORD block
, block_size
;
286 for(block
=0; block
<header
->capacity_in_blocks
; block
+=block_size
+1)
289 while(block_size
<blocks_needed
&& block_size
+block
<header
->capacity_in_blocks
290 && urlcache_block_is_free(header
->allocation_table
, block
+block_size
))
293 if(block_size
== blocks_needed
)
297 TRACE("Found free blocks starting at no. %d (0x%x)\n", block
, ENTRY_START_OFFSET
+block
*BLOCKSIZE
);
299 for(index
=0; index
<blocks_needed
; index
++)
300 urlcache_block_alloc(header
->allocation_table
, block
+index
);
302 *entry
= (entry_header
*)((BYTE
*)header
+ENTRY_START_OFFSET
+block
*BLOCKSIZE
);
303 for(index
=0; index
<blocks_needed
*BLOCKSIZE
/sizeof(DWORD
); index
++)
304 ((DWORD
*)*entry
)[index
] = 0xdeadbeef;
305 (*entry
)->blocks_used
= blocks_needed
;
307 header
->blocks_in_use
+= blocks_needed
;
308 return ERROR_SUCCESS
;
312 return ERROR_HANDLE_DISK_FULL
;
315 /***********************************************************************
316 * urlcache_entry_free (Internal)
318 * Deletes the specified entry and frees the space allocated to it
321 * TRUE if it succeeded
325 static BOOL
urlcache_entry_free(urlcache_header
*header
, entry_header
*entry
)
327 DWORD start_block
, block
;
329 /* update allocation table */
330 start_block
= ((DWORD
)((BYTE
*)entry
- (BYTE
*)header
) - ENTRY_START_OFFSET
) / BLOCKSIZE
;
331 for(block
= start_block
; block
< start_block
+entry
->blocks_used
; block
++)
332 urlcache_block_free(header
->allocation_table
, block
);
334 header
->blocks_in_use
-= entry
->blocks_used
;
338 /***********************************************************************
339 * urlcache_create_hash_table (Internal)
341 * Creates a new hash table in free space and adds it to the chain of existing
345 * ERROR_SUCCESS if the hash table was created
346 * ERROR_DISK_FULL if the hash table could not be created
349 static DWORD
urlcache_create_hash_table(urlcache_header
*header
, entry_hash_table
*hash_table_prev
, entry_hash_table
**hash_table
)
351 DWORD dwOffset
, error
;
354 if((error
= urlcache_entry_alloc(header
, 0x20, (entry_header
**)hash_table
)) != ERROR_SUCCESS
)
357 dwOffset
= (BYTE
*)*hash_table
-(BYTE
*)header
;
360 hash_table_prev
->next
= dwOffset
;
362 header
->hash_table_off
= dwOffset
;
364 (*hash_table
)->header
.signature
= HASH_SIGNATURE
;
365 (*hash_table
)->next
= 0;
366 (*hash_table
)->id
= hash_table_prev
? hash_table_prev
->id
+1 : 0;
367 for(i
= 0; i
< HASHTABLE_SIZE
; i
++) {
368 (*hash_table
)->hash_table
[i
].offset
= HASHTABLE_FREE
;
369 (*hash_table
)->hash_table
[i
].key
= HASHTABLE_FREE
;
371 return ERROR_SUCCESS
;
374 /***********************************************************************
375 * cache_container_create_object_name (Internal)
377 * Converts a path to a name suitable for use as a Win32 object name.
378 * Replaces '\\' characters in-place with the specified character
379 * (usually '_' or '!')
385 static void cache_container_create_object_name(LPWSTR lpszPath
, WCHAR replace
)
387 for (; *lpszPath
; lpszPath
++)
389 if (*lpszPath
== '\\')
394 /* Caller must hold container lock */
395 static HANDLE
cache_container_map_index(HANDLE file
, const WCHAR
*path
, DWORD size
, BOOL
*validate
)
397 WCHAR mapping_name
[MAX_PATH
];
400 wsprintfW(mapping_name
, L
"%sindex.dat_%lu", path
, size
);
401 cache_container_create_object_name(mapping_name
, '_');
403 mapping
= OpenFileMappingW(FILE_MAP_WRITE
, FALSE
, mapping_name
);
405 if(validate
) *validate
= FALSE
;
409 if(validate
) *validate
= TRUE
;
410 return CreateFileMappingW(file
, NULL
, PAGE_READWRITE
, 0, 0, mapping_name
);
413 /* Caller must hold container lock */
414 static DWORD
cache_container_set_size(cache_container
*container
, HANDLE file
, DWORD blocks_no
)
416 DWORD file_size
= FILE_SIZE(blocks_no
);
417 WCHAR dir_path
[MAX_PATH
], *dir_name
;
418 entry_hash_table
*hashtable_entry
;
419 urlcache_header
*header
;
425 if(SetFilePointer(file
, file_size
, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
426 return GetLastError();
428 if(!SetEndOfFile(file
))
429 return GetLastError();
431 mapping
= cache_container_map_index(file
, container
->path
, file_size
, NULL
);
433 return GetLastError();
435 header
= MapViewOfFile(mapping
, FILE_MAP_WRITE
, 0, 0, 0);
437 CloseHandle(mapping
);
438 return GetLastError();
441 if(blocks_no
!= MIN_BLOCK_NO
) {
442 if(file_size
> header
->size
)
443 memset((char*)header
+header
->size
, 0, file_size
-header
->size
);
444 header
->size
= file_size
;
445 header
->capacity_in_blocks
= blocks_no
;
447 UnmapViewOfFile(header
);
448 CloseHandle(container
->mapping
);
449 container
->mapping
= mapping
;
450 container
->file_size
= file_size
;
451 return ERROR_SUCCESS
;
454 memset(header
, 0, file_size
);
455 /* First set some constants and defaults in the header */
456 memcpy(header
->signature
, urlcache_ver_prefix
, sizeof(urlcache_ver_prefix
)-1);
457 memcpy(header
->signature
+sizeof(urlcache_ver_prefix
)-1, urlcache_ver
, sizeof(urlcache_ver
)-1);
458 header
->size
= file_size
;
459 header
->capacity_in_blocks
= blocks_no
;
460 /* 127MB - taken from default for Windows 2000 */
461 header
->cache_limit
.QuadPart
= 0x07ff5400;
462 /* Copied from a Windows 2000 cache index */
463 header
->dirs_no
= container
->default_entry_type
==NORMAL_CACHE_ENTRY
? 4 : 0;
465 /* If the registry has a cache size set, use the registry value */
466 if(RegOpenKeyW(HKEY_CURRENT_USER
,
467 L
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Cache\\Content", &key
) == ERROR_SUCCESS
) {
468 DWORD dw
, len
= sizeof(dw
), keytype
;
470 if(RegQueryValueExW(key
, L
"CacheLimit", NULL
, &keytype
, (BYTE
*)&dw
, &len
) == ERROR_SUCCESS
&&
471 keytype
== REG_DWORD
)
472 header
->cache_limit
.QuadPart
= (ULONGLONG
)dw
* 1024;
476 urlcache_create_hash_table(header
, NULL
, &hashtable_entry
);
478 /* Last step - create the directories */
479 lstrcpyW(dir_path
, container
->path
);
480 dir_name
= dir_path
+ lstrlenW(dir_path
);
483 GetSystemTimeAsFileTime(&ft
);
485 for(i
=0; i
<header
->dirs_no
; ++i
) {
486 header
->directory_data
[i
].files_no
= 0;
488 ULONGLONG n
= ft
.dwHighDateTime
;
491 /* Generate a file name to attempt to create.
492 * This algorithm will create what will appear
493 * to be random and unrelated directory names
494 * of up to 9 characters in length.
497 n
+= ft
.dwLowDateTime
;
498 n
^= ((ULONGLONG
) i
<< 56) | ((ULONGLONG
) j
<< 48);
500 for(k
= 0; k
< 8; ++k
) {
503 /* Dividing by a prime greater than 36 helps
504 * with the appearance of randomness
509 dir_name
[k
] = '0' + r
;
511 dir_name
[k
] = 'A' + (r
- 10);
514 if(CreateDirectoryW(dir_path
, 0)) {
515 /* The following is OK because we generated an
516 * 8 character directory name made from characters
517 * [A-Z0-9], which are equivalent for all code
518 * pages and for UTF-16
520 for (k
= 0; k
< 8; ++k
)
521 header
->directory_data
[i
].name
[k
] = dir_name
[k
];
524 /* Give up. The most likely cause of this
525 * is a full disk, but whatever the cause
526 * is, it should be more than apparent that
529 UnmapViewOfFile(header
);
530 CloseHandle(mapping
);
531 return GetLastError();
536 UnmapViewOfFile(header
);
537 CloseHandle(container
->mapping
);
538 container
->mapping
= mapping
;
539 container
->file_size
= file_size
;
540 return ERROR_SUCCESS
;
543 static BOOL
cache_container_is_valid(urlcache_header
*header
, DWORD file_size
)
545 DWORD allocation_size
, count_bits
, i
;
547 if(file_size
< FILE_SIZE(MIN_BLOCK_NO
))
550 if(file_size
!= header
->size
)
553 if (!memcmp(header
->signature
, urlcache_ver_prefix
, sizeof(urlcache_ver_prefix
)-1) &&
554 memcmp(header
->signature
+sizeof(urlcache_ver_prefix
)-1, urlcache_ver
, sizeof(urlcache_ver
)-1))
557 if(FILE_SIZE(header
->capacity_in_blocks
) != file_size
)
561 for(i
=0; i
<header
->capacity_in_blocks
/8; i
++) {
562 for(count_bits
= header
->allocation_table
[i
]; count_bits
!=0; count_bits
>>=1) {
567 if(allocation_size
!= header
->blocks_in_use
)
570 for(; i
<ALLOCATION_TABLE_SIZE
; i
++) {
571 if(header
->allocation_table
[i
])
578 /***********************************************************************
579 * cache_container_open_index (Internal)
581 * Opens the index file and saves mapping handle
584 * ERROR_SUCCESS if succeeded
585 * Any other Win32 error code if failed
588 static DWORD
cache_container_open_index(cache_container
*container
, DWORD blocks_no
)
591 WCHAR index_path
[MAX_PATH
];
595 WaitForSingleObject(container
->mutex
, INFINITE
);
597 if(container
->mapping
) {
598 ReleaseMutex(container
->mutex
);
599 return ERROR_SUCCESS
;
602 lstrcpyW(index_path
, container
->path
);
603 lstrcatW(index_path
, L
"index.dat");
605 file
= CreateFileW(index_path
, GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_ALWAYS
, 0, NULL
);
606 if(file
== INVALID_HANDLE_VALUE
) {
607 /* Maybe the directory wasn't there? Try to create it */
608 if(CreateDirectoryW(container
->path
, 0))
609 file
= CreateFileW(index_path
, GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_ALWAYS
, 0, NULL
);
611 if(file
== INVALID_HANDLE_VALUE
) {
612 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(index_path
));
613 ReleaseMutex(container
->mutex
);
614 return GetLastError();
617 file_size
= GetFileSize(file
, NULL
);
618 if(file_size
== INVALID_FILE_SIZE
) {
620 ReleaseMutex(container
->mutex
);
621 return GetLastError();
624 if(blocks_no
< MIN_BLOCK_NO
)
625 blocks_no
= MIN_BLOCK_NO
;
626 else if(blocks_no
> MAX_BLOCK_NO
)
627 blocks_no
= MAX_BLOCK_NO
;
629 if(file_size
< FILE_SIZE(blocks_no
)) {
630 DWORD ret
= cache_container_set_size(container
, file
, blocks_no
);
632 ReleaseMutex(container
->mutex
);
636 container
->file_size
= file_size
;
637 container
->mapping
= cache_container_map_index(file
, container
->path
, file_size
, &validate
);
639 if(container
->mapping
&& validate
) {
640 urlcache_header
*header
= MapViewOfFile(container
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
642 if(header
&& !cache_container_is_valid(header
, file_size
)) {
643 WARN("detected old or broken index.dat file\n");
644 UnmapViewOfFile(header
);
645 FreeUrlCacheSpaceW(container
->path
, 100, 0);
647 UnmapViewOfFile(header
);
649 CloseHandle(container
->mapping
);
650 container
->mapping
= NULL
;
654 if(!container
->mapping
)
656 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
657 ReleaseMutex(container
->mutex
);
658 return GetLastError();
661 ReleaseMutex(container
->mutex
);
662 return ERROR_SUCCESS
;
665 /***********************************************************************
666 * cache_container_close_index (Internal)
674 static void cache_container_close_index(cache_container
*pContainer
)
676 CloseHandle(pContainer
->mapping
);
677 pContainer
->mapping
= NULL
;
680 static BOOL
cache_containers_add(const char *cache_prefix
, LPCWSTR path
,
681 DWORD default_entry_type
, LPWSTR mutex_name
)
683 cache_container
*pContainer
= heap_alloc(sizeof(cache_container
));
684 int cache_prefix_len
= strlen(cache_prefix
);
691 pContainer
->mapping
= NULL
;
692 pContainer
->file_size
= 0;
693 pContainer
->default_entry_type
= default_entry_type
;
695 pContainer
->path
= heap_strdupW(path
);
696 if (!pContainer
->path
)
698 heap_free(pContainer
);
702 pContainer
->cache_prefix
= heap_alloc(cache_prefix_len
+1);
703 if (!pContainer
->cache_prefix
)
705 heap_free(pContainer
->path
);
706 heap_free(pContainer
);
710 memcpy(pContainer
->cache_prefix
, cache_prefix
, cache_prefix_len
+1);
712 CharLowerW(mutex_name
);
713 cache_container_create_object_name(mutex_name
, '!');
715 if ((pContainer
->mutex
= CreateMutexW(NULL
, FALSE
, mutex_name
)) == NULL
)
717 ERR("couldn't create mutex (error is %d)\n", GetLastError());
718 heap_free(pContainer
->path
);
719 heap_free(pContainer
);
723 list_add_head(&UrlContainers
, &pContainer
->entry
);
728 static void cache_container_delete_container(cache_container
*pContainer
)
730 list_remove(&pContainer
->entry
);
732 cache_container_close_index(pContainer
);
733 CloseHandle(pContainer
->mutex
);
734 heap_free(pContainer
->path
);
735 heap_free(pContainer
->cache_prefix
);
736 heap_free(pContainer
);
739 static void cache_containers_init(void)
743 int nFolder
; /* CSIDL_* constant */
744 const WCHAR
*shpath_suffix
; /* suffix on path returned by SHGetSpecialFolderPath */
745 const char *cache_prefix
; /* prefix used to reference the container */
746 DWORD default_entry_type
;
747 } DefaultContainerData
[] =
749 { CSIDL_INTERNET_CACHE
, L
"Content.IE5", "", NORMAL_CACHE_ENTRY
},
750 { CSIDL_HISTORY
, L
"History.IE5", "Visited:", URLHISTORY_CACHE_ENTRY
},
751 { CSIDL_COOKIES
, L
"", "Cookie:", COOKIE_CACHE_ENTRY
},
755 for (i
= 0; i
< ARRAY_SIZE(DefaultContainerData
); i
++)
757 WCHAR wszCachePath
[MAX_PATH
];
758 WCHAR wszMutexName
[MAX_PATH
];
759 int path_len
, suffix_len
;
762 if (!SHGetSpecialFolderPathW(NULL
, wszCachePath
, DefaultContainerData
[i
].nFolder
, TRUE
))
764 ERR("Couldn't get path for default container %u\n", i
);
767 path_len
= lstrlenW(wszCachePath
);
768 suffix_len
= lstrlenW(DefaultContainerData
[i
].shpath_suffix
);
770 if (path_len
+ suffix_len
+ 2 > MAX_PATH
)
772 ERR("Path too long\n");
776 wszCachePath
[path_len
] = '\\';
777 wszCachePath
[path_len
+1] = 0;
779 lstrcpyW(wszMutexName
, wszCachePath
);
783 memcpy(wszCachePath
+ path_len
+ 1, DefaultContainerData
[i
].shpath_suffix
, (suffix_len
+ 1) * sizeof(WCHAR
));
784 wszCachePath
[path_len
+ suffix_len
+ 1] = '\\';
785 wszCachePath
[path_len
+ suffix_len
+ 2] = '\0';
788 if (!WideCharToMultiByte(CP_ACP
, WC_NO_BEST_FIT_CHARS
, wszCachePath
, path_len
,
789 NULL
, 0, NULL
, &def_char
) || def_char
)
793 /* cannot convert path to ANSI code page */
794 if (!(path_len
= GetShortPathNameW(wszCachePath
, tmp
, MAX_PATH
)) ||
795 !WideCharToMultiByte(CP_ACP
, WC_NO_BEST_FIT_CHARS
, tmp
, path_len
,
796 NULL
, 0, NULL
, &def_char
) || def_char
)
797 ERR("Can't create container path accessible by ANSI functions\n");
799 memcpy(wszCachePath
, tmp
, (path_len
+1)*sizeof(WCHAR
));
802 cache_containers_add(DefaultContainerData
[i
].cache_prefix
, wszCachePath
,
803 DefaultContainerData
[i
].default_entry_type
, wszMutexName
);
807 static void cache_containers_free(void)
809 while(!list_empty(&UrlContainers
))
810 cache_container_delete_container(
811 LIST_ENTRY(list_head(&UrlContainers
), cache_container
, entry
)
815 static DWORD
cache_containers_find(const char *url
, cache_container
**ret
)
817 cache_container
*container
;
819 TRACE("searching for prefix for URL: %s\n", debugstr_a(url
));
822 return ERROR_INVALID_PARAMETER
;
824 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
826 int prefix_len
= strlen(container
->cache_prefix
);
828 if(!strncmp(container
->cache_prefix
, url
, prefix_len
)) {
829 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
831 return ERROR_SUCCESS
;
835 ERR("no container found\n");
836 return ERROR_FILE_NOT_FOUND
;
839 static BOOL
cache_containers_enum(char *search_pattern
, DWORD index
, cache_container
**ret
)
842 cache_container
*container
;
844 TRACE("searching for prefix: %s\n", debugstr_a(search_pattern
));
846 /* non-NULL search pattern only returns one container ever */
847 if (search_pattern
&& index
> 0)
850 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
854 if (!strcmp(container
->cache_prefix
, search_pattern
))
856 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
865 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
875 /***********************************************************************
876 * cache_container_lock_index (Internal)
878 * Locks the index for system-wide exclusive access.
881 * Cache file header if successful
882 * NULL if failed and calls SetLastError.
884 static urlcache_header
* cache_container_lock_index(cache_container
*pContainer
)
888 urlcache_header
* pHeader
;
892 WaitForSingleObject(pContainer
->mutex
, INFINITE
);
894 pIndexData
= MapViewOfFile(pContainer
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
898 ReleaseMutex(pContainer
->mutex
);
899 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
902 pHeader
= (urlcache_header
*)pIndexData
;
904 /* file has grown - we need to remap to prevent us getting
905 * access violations when we try and access beyond the end
906 * of the memory mapped file */
907 if (pHeader
->size
!= pContainer
->file_size
)
909 UnmapViewOfFile( pHeader
);
910 cache_container_close_index(pContainer
);
911 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
912 if (error
!= ERROR_SUCCESS
)
914 ReleaseMutex(pContainer
->mutex
);
918 pIndexData
= MapViewOfFile(pContainer
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
922 ReleaseMutex(pContainer
->mutex
);
923 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
926 pHeader
= (urlcache_header
*)pIndexData
;
929 TRACE("Signature: %s, file size: %d bytes\n", pHeader
->signature
, pHeader
->size
);
931 for (index
= 0; index
< pHeader
->dirs_no
; index
++)
933 TRACE("Directory[%d] = \"%.8s\"\n", index
, pHeader
->directory_data
[index
].name
);
939 /***********************************************************************
940 * cache_container_unlock_index (Internal)
943 static BOOL
cache_container_unlock_index(cache_container
*pContainer
, urlcache_header
*pHeader
)
946 ReleaseMutex(pContainer
->mutex
);
947 return UnmapViewOfFile(pHeader
);
950 /***********************************************************************
951 * urlcache_create_file_pathW (Internal)
953 * Copies the full path to the specified buffer given the local file
954 * name and the index of the directory it is in. Always sets value in
955 * lpBufferSize to the required buffer size (in bytes).
958 * TRUE if the buffer was big enough
959 * FALSE if the buffer was too small
962 static BOOL
urlcache_create_file_pathW(
963 const cache_container
*pContainer
,
964 const urlcache_header
*pHeader
,
965 LPCSTR szLocalFileName
,
972 int path_len
= lstrlenW(pContainer
->path
);
973 int file_name_len
= MultiByteToWideChar(CP_ACP
, 0, szLocalFileName
, -1, NULL
, 0);
974 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
&& Directory
>=pHeader
->dirs_no
)
980 nRequired
= (path_len
+ file_name_len
) * sizeof(WCHAR
);
981 if(Directory
!= CACHE_CONTAINER_NO_SUBDIR
)
982 nRequired
+= (DIR_LENGTH
+ 1) * sizeof(WCHAR
);
983 if (trunc_name
&& nRequired
>= *lpBufferSize
)
984 nRequired
= *lpBufferSize
;
985 if (nRequired
<= *lpBufferSize
)
989 memcpy(wszPath
, pContainer
->path
, path_len
* sizeof(WCHAR
));
990 if (Directory
!= CACHE_CONTAINER_NO_SUBDIR
)
992 dir_len
= MultiByteToWideChar(CP_ACP
, 0, pHeader
->directory_data
[Directory
].name
, DIR_LENGTH
, wszPath
+ path_len
, DIR_LENGTH
);
993 wszPath
[dir_len
+ path_len
] = '\\';
1000 MultiByteToWideChar(CP_ACP
, 0, szLocalFileName
, -1, wszPath
+ dir_len
+ path_len
,
1001 *lpBufferSize
/sizeof(WCHAR
)-dir_len
-path_len
);
1002 wszPath
[*lpBufferSize
/sizeof(WCHAR
)-1] = 0;
1003 *lpBufferSize
= nRequired
;
1006 *lpBufferSize
= nRequired
;
1010 /***********************************************************************
1011 * urlcache_create_file_pathA (Internal)
1013 * Copies the full path to the specified buffer given the local file
1014 * name and the index of the directory it is in. Always sets value in
1015 * lpBufferSize to the required buffer size.
1018 * TRUE if the buffer was big enough
1019 * FALSE if the buffer was too small
1022 static BOOL
urlcache_create_file_pathA(
1023 const cache_container
*pContainer
,
1024 const urlcache_header
*pHeader
,
1025 LPCSTR szLocalFileName
,
1028 LPLONG lpBufferSize
)
1031 int path_len
, file_name_len
, dir_len
;
1033 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
&& Directory
>=pHeader
->dirs_no
)
1039 path_len
= WideCharToMultiByte(CP_ACP
, 0, pContainer
->path
, -1, NULL
, 0, NULL
, NULL
) - 1;
1040 file_name_len
= strlen(szLocalFileName
) + 1 /* for nul-terminator */;
1041 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
)
1042 dir_len
= DIR_LENGTH
+1;
1046 nRequired
= (path_len
+ dir_len
+ file_name_len
) * sizeof(char);
1047 if (nRequired
<= *lpBufferSize
)
1049 WideCharToMultiByte(CP_ACP
, 0, pContainer
->path
, -1, szPath
, path_len
, NULL
, NULL
);
1051 memcpy(szPath
+path_len
, pHeader
->directory_data
[Directory
].name
, dir_len
-1);
1052 szPath
[path_len
+ dir_len
-1] = '\\';
1054 memcpy(szPath
+ path_len
+ dir_len
, szLocalFileName
, file_name_len
);
1055 *lpBufferSize
= nRequired
;
1058 *lpBufferSize
= nRequired
;
1062 /* Just like FileTimeToDosDateTime, except that it also maps the special
1063 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1065 static void file_time_to_dos_date_time(const FILETIME
*ft
, WORD
*fatdate
,
1068 if (!ft
->dwLowDateTime
&& !ft
->dwHighDateTime
)
1069 *fatdate
= *fattime
= 0;
1071 FileTimeToDosDateTime(ft
, fatdate
, fattime
);
1074 /***********************************************************************
1075 * urlcache_delete_file (Internal)
1077 static DWORD
urlcache_delete_file(const cache_container
*container
,
1078 urlcache_header
*header
, entry_url
*url_entry
)
1080 WIN32_FILE_ATTRIBUTE_DATA attr
;
1081 WCHAR path
[MAX_PATH
];
1082 LONG path_size
= sizeof(path
);
1086 if(!url_entry
->local_name_off
)
1089 if(!urlcache_create_file_pathW(container
, header
,
1090 (LPCSTR
)url_entry
+url_entry
->local_name_off
,
1091 url_entry
->cache_dir
, path
, &path_size
, FALSE
))
1094 if(!GetFileAttributesExW(path
, GetFileExInfoStandard
, &attr
))
1096 file_time_to_dos_date_time(&attr
.ftLastWriteTime
, &date
, &time
);
1097 if(date
!= url_entry
->write_date
|| time
!= url_entry
->write_time
)
1100 err
= (DeleteFileW(path
) ? ERROR_SUCCESS
: GetLastError());
1101 if(err
== ERROR_ACCESS_DENIED
|| err
== ERROR_SHARING_VIOLATION
)
1105 if (url_entry
->cache_dir
< header
->dirs_no
)
1107 if (header
->directory_data
[url_entry
->cache_dir
].files_no
)
1108 header
->directory_data
[url_entry
->cache_dir
].files_no
--;
1110 if (url_entry
->cache_entry_type
& STICKY_CACHE_ENTRY
)
1112 if (url_entry
->size
.QuadPart
< header
->exempt_usage
.QuadPart
)
1113 header
->exempt_usage
.QuadPart
-= url_entry
->size
.QuadPart
;
1115 header
->exempt_usage
.QuadPart
= 0;
1119 if (url_entry
->size
.QuadPart
< header
->cache_usage
.QuadPart
)
1120 header
->cache_usage
.QuadPart
-= url_entry
->size
.QuadPart
;
1122 header
->cache_usage
.QuadPart
= 0;
1125 return ERROR_SUCCESS
;
1128 static BOOL
urlcache_clean_leaked_entries(cache_container
*container
, urlcache_header
*header
)
1133 leak_off
= &header
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
];
1135 entry_url
*url_entry
= (entry_url
*)((LPBYTE
)header
+ *leak_off
);
1137 if(SUCCEEDED(urlcache_delete_file(container
, header
, url_entry
))) {
1138 *leak_off
= url_entry
->exempt_delta
;
1139 urlcache_entry_free(header
, &url_entry
->header
);
1142 leak_off
= &url_entry
->exempt_delta
;
1149 /***********************************************************************
1150 * cache_container_clean_index (Internal)
1152 * This function is meant to make place in index file by removing leaked
1153 * files entries and resizing the file.
1155 * CAUTION: file view may get mapped to new memory
1158 * ERROR_SUCCESS when new memory is available
1159 * error code otherwise
1161 static DWORD
cache_container_clean_index(cache_container
*container
, urlcache_header
**file_view
)
1163 urlcache_header
*header
= *file_view
;
1166 TRACE("(%s %s)\n", debugstr_a(container
->cache_prefix
), debugstr_w(container
->path
));
1168 if(urlcache_clean_leaked_entries(container
, header
))
1169 return ERROR_SUCCESS
;
1171 if(header
->size
>= ALLOCATION_TABLE_SIZE
*8*BLOCKSIZE
+ ENTRY_START_OFFSET
) {
1172 WARN("index file has maximal size\n");
1173 return ERROR_NOT_ENOUGH_MEMORY
;
1176 cache_container_close_index(container
);
1177 ret
= cache_container_open_index(container
, header
->capacity_in_blocks
*2);
1178 if(ret
!= ERROR_SUCCESS
)
1180 header
= MapViewOfFile(container
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
1182 return GetLastError();
1184 UnmapViewOfFile(*file_view
);
1185 *file_view
= header
;
1186 return ERROR_SUCCESS
;
1189 /* Just like DosDateTimeToFileTime, except that it also maps the special
1190 * case of a DOS date/time of (0,0) to a filetime of (0,0).
1192 static void dos_date_time_to_file_time(WORD fatdate
, WORD fattime
,
1195 if (!fatdate
&& !fattime
)
1196 ft
->dwLowDateTime
= ft
->dwHighDateTime
= 0;
1198 DosDateTimeToFileTime(fatdate
, fattime
, ft
);
1201 static int urlcache_decode_url(const char *url
, WCHAR
*decoded_url
, int decoded_len
)
1204 DWORD len
, part_len
;
1207 memset(&uc
, 0, sizeof(uc
));
1208 uc
.dwStructSize
= sizeof(uc
);
1209 uc
.dwHostNameLength
= 1;
1210 if(!InternetCrackUrlA(url
, 0, 0, &uc
))
1211 uc
.nScheme
= INTERNET_SCHEME_UNKNOWN
;
1213 if(uc
.nScheme
!=INTERNET_SCHEME_HTTP
&& uc
.nScheme
!=INTERNET_SCHEME_HTTPS
)
1214 return MultiByteToWideChar(CP_UTF8
, 0, url
, -1, decoded_url
, decoded_len
);
1219 len
= MultiByteToWideChar(CP_UTF8
, 0, url
, uc
.lpszHostName
-url
, decoded_url
, decoded_len
);
1225 host_name
= heap_alloc(uc
.dwHostNameLength
*sizeof(WCHAR
));
1228 if(!MultiByteToWideChar(CP_UTF8
, 0, uc
.lpszHostName
, uc
.dwHostNameLength
,
1229 host_name
, uc
.dwHostNameLength
)) {
1230 heap_free(host_name
);
1233 part_len
= IdnToUnicode(0, host_name
, uc
.dwHostNameLength
,
1234 decoded_url
? decoded_url
+len
: NULL
, decoded_len
);
1235 heap_free(host_name
);
1237 SetLastError(ERROR_INTERNET_INVALID_URL
);
1242 decoded_len
-= part_len
;
1244 part_len
= MultiByteToWideChar(CP_UTF8
, 0,
1245 uc
.lpszHostName
+uc
.dwHostNameLength
,
1246 -1, decoded_url
? decoded_url
+len
: NULL
, decoded_len
);
1254 /***********************************************************************
1255 * urlcache_copy_entry (Internal)
1257 * Copies an entry from the cache index file to the Win32 structure
1260 * ERROR_SUCCESS if the buffer was big enough
1261 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1264 static DWORD
urlcache_copy_entry(cache_container
*container
, const urlcache_header
*header
,
1265 INTERNET_CACHE_ENTRY_INFOA
*entry_info
, DWORD
*info_size
, const entry_url
*url_entry
, BOOL unicode
)
1268 DWORD size
= sizeof(*entry_info
);
1270 if(*info_size
>= size
) {
1271 entry_info
->lpHeaderInfo
= NULL
;
1272 entry_info
->lpszFileExtension
= NULL
;
1273 entry_info
->lpszLocalFileName
= NULL
;
1274 entry_info
->lpszSourceUrlName
= NULL
;
1275 entry_info
->CacheEntryType
= url_entry
->cache_entry_type
;
1276 entry_info
->u
.dwExemptDelta
= url_entry
->exempt_delta
;
1277 entry_info
->dwHeaderInfoSize
= url_entry
->header_info_size
;
1278 entry_info
->dwHitRate
= url_entry
->hit_rate
;
1279 entry_info
->dwSizeHigh
= url_entry
->size
.u
.HighPart
;
1280 entry_info
->dwSizeLow
= url_entry
->size
.u
.LowPart
;
1281 entry_info
->dwStructSize
= sizeof(*entry_info
);
1282 entry_info
->dwUseCount
= url_entry
->use_count
;
1283 dos_date_time_to_file_time(url_entry
->expire_date
, url_entry
->expire_time
, &entry_info
->ExpireTime
);
1284 entry_info
->LastAccessTime
= url_entry
->access_time
;
1285 entry_info
->LastModifiedTime
= url_entry
->modification_time
;
1286 dos_date_time_to_file_time(url_entry
->sync_date
, url_entry
->sync_time
, &entry_info
->LastSyncTime
);
1290 url_len
= urlcache_decode_url((const char*)url_entry
+url_entry
->url_off
, NULL
, 0);
1292 url_len
= strlen((LPCSTR
)url_entry
+url_entry
->url_off
) + 1;
1293 size
+= url_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1295 if(*info_size
>= size
) {
1296 DWORD url_size
= url_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1298 entry_info
->lpszSourceUrlName
= (LPSTR
)entry_info
+size
-url_size
;
1300 urlcache_decode_url((const char*)url_entry
+url_entry
->url_off
, (WCHAR
*)entry_info
->lpszSourceUrlName
, url_len
);
1302 memcpy(entry_info
->lpszSourceUrlName
, (LPCSTR
)url_entry
+url_entry
->url_off
, url_size
);
1305 if(size
%4 && size
<*info_size
)
1306 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1307 size
= DWORD_ALIGN(size
);
1309 if(url_entry
->local_name_off
) {
1310 LONG file_name_size
;
1312 file_name
= (LPSTR
)entry_info
+size
;
1313 file_name_size
= *info_size
-size
;
1314 if((unicode
&& urlcache_create_file_pathW(container
, header
, (LPCSTR
)url_entry
+url_entry
->local_name_off
,
1315 url_entry
->cache_dir
, (LPWSTR
)file_name
, &file_name_size
, FALSE
)) ||
1316 (!unicode
&& urlcache_create_file_pathA(container
, header
, (LPCSTR
)url_entry
+url_entry
->local_name_off
,
1317 url_entry
->cache_dir
, file_name
, &file_name_size
))) {
1318 entry_info
->lpszLocalFileName
= file_name
;
1320 size
+= file_name_size
;
1322 if(size
%4 && size
<*info_size
)
1323 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1324 size
= DWORD_ALIGN(size
);
1327 if(url_entry
->header_info_off
) {
1331 header_len
= MultiByteToWideChar(CP_UTF8
, 0, (const char*)url_entry
+url_entry
->header_info_off
,
1332 url_entry
->header_info_size
, NULL
, 0);
1334 header_len
= url_entry
->header_info_size
;
1335 size
+= header_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1337 if(*info_size
>= size
) {
1338 DWORD header_size
= header_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1339 entry_info
->lpHeaderInfo
= (LPBYTE
)entry_info
+size
-header_size
;
1341 MultiByteToWideChar(CP_UTF8
, 0, (const char*)url_entry
+url_entry
->header_info_off
,
1342 url_entry
->header_info_size
, (LPWSTR
)entry_info
->lpHeaderInfo
, header_len
);
1344 memcpy(entry_info
->lpHeaderInfo
, (LPCSTR
)url_entry
+url_entry
->header_info_off
, header_len
);
1346 if(size
%4 && size
<*info_size
)
1347 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1348 size
= DWORD_ALIGN(size
);
1351 if(url_entry
->file_extension_off
) {
1355 ext_len
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)url_entry
+url_entry
->file_extension_off
, -1, NULL
, 0);
1357 ext_len
= strlen((LPCSTR
)url_entry
+url_entry
->file_extension_off
) + 1;
1358 size
+= ext_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1360 if(*info_size
>= size
) {
1361 DWORD ext_size
= ext_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1362 entry_info
->lpszFileExtension
= (LPSTR
)entry_info
+size
-ext_size
;
1364 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)url_entry
+url_entry
->file_extension_off
, -1, (LPWSTR
)entry_info
->lpszFileExtension
, ext_len
);
1366 memcpy(entry_info
->lpszFileExtension
, (LPCSTR
)url_entry
+url_entry
->file_extension_off
, ext_len
*sizeof(CHAR
));
1369 if(size
%4 && size
<*info_size
)
1370 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1371 size
= DWORD_ALIGN(size
);
1374 if(size
> *info_size
) {
1376 return ERROR_INSUFFICIENT_BUFFER
;
1379 return ERROR_SUCCESS
;
1382 /***********************************************************************
1383 * urlcache_set_entry_info (Internal)
1385 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1386 * according to the flags set by field_control.
1389 * ERROR_SUCCESS if the buffer was big enough
1390 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1393 static DWORD
urlcache_set_entry_info(entry_url
*url_entry
, const INTERNET_CACHE_ENTRY_INFOA
*entry_info
, DWORD field_control
)
1395 if (field_control
& CACHE_ENTRY_ACCTIME_FC
)
1396 url_entry
->access_time
= entry_info
->LastAccessTime
;
1397 if (field_control
& CACHE_ENTRY_ATTRIBUTE_FC
)
1398 url_entry
->cache_entry_type
= entry_info
->CacheEntryType
;
1399 if (field_control
& CACHE_ENTRY_EXEMPT_DELTA_FC
)
1400 url_entry
->exempt_delta
= entry_info
->u
.dwExemptDelta
;
1401 if (field_control
& CACHE_ENTRY_EXPTIME_FC
)
1402 file_time_to_dos_date_time(&entry_info
->ExpireTime
, &url_entry
->expire_date
, &url_entry
->expire_time
);
1403 if (field_control
& CACHE_ENTRY_HEADERINFO_FC
)
1404 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1405 if (field_control
& CACHE_ENTRY_HITRATE_FC
)
1406 url_entry
->hit_rate
= entry_info
->dwHitRate
;
1407 if (field_control
& CACHE_ENTRY_MODTIME_FC
)
1408 url_entry
->modification_time
= entry_info
->LastModifiedTime
;
1409 if (field_control
& CACHE_ENTRY_SYNCTIME_FC
)
1410 file_time_to_dos_date_time(&entry_info
->LastAccessTime
, &url_entry
->sync_date
, &url_entry
->sync_time
);
1412 return ERROR_SUCCESS
;
1415 /***********************************************************************
1416 * urlcache_hash_key (Internal)
1418 * Returns the hash key for a given string
1421 * hash key for the string
1424 static DWORD
urlcache_hash_key(LPCSTR lpszKey
)
1426 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1427 * but the algorithm and result are not the same!
1429 static const unsigned char lookupTable
[256] =
1431 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1432 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1433 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1434 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1435 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1436 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1437 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1438 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1439 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1440 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1441 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1442 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1443 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1444 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1445 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1446 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1447 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1448 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1449 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1450 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1451 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1452 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1453 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1454 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1455 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1456 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1457 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1458 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1459 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1460 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1461 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1462 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1467 for (i
= 0; i
< ARRAY_SIZE(key
); i
++)
1468 key
[i
] = lookupTable
[(*lpszKey
+ i
) & 0xFF];
1470 for (lpszKey
++; *lpszKey
; lpszKey
++)
1472 for (i
= 0; i
< ARRAY_SIZE(key
); i
++)
1473 key
[i
] = lookupTable
[*lpszKey
^ key
[i
]];
1476 return *(DWORD
*)key
;
1479 static inline entry_hash_table
* urlcache_get_hash_table(const urlcache_header
*pHeader
, DWORD dwOffset
)
1483 return (entry_hash_table
*)((LPBYTE
)pHeader
+ dwOffset
);
1486 static BOOL
urlcache_find_hash_entry(const urlcache_header
*pHeader
, LPCSTR lpszUrl
, struct hash_entry
**ppHashEntry
)
1488 /* structure of hash table:
1489 * 448 entries divided into 64 blocks
1490 * each block therefore contains a chain of 7 key/offset pairs
1491 * how position in table is calculated:
1492 * 1. the url is hashed in helper function
1493 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1494 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1497 * there can be multiple hash tables in the file and the offset to
1498 * the next one is stored in the header of the hash table
1500 DWORD key
= urlcache_hash_key(lpszUrl
);
1501 DWORD offset
= (key
& (HASHTABLE_NUM_ENTRIES
-1)) * HASHTABLE_BLOCKSIZE
;
1502 entry_hash_table
* pHashEntry
;
1505 key
>>= HASHTABLE_FLAG_BITS
;
1507 for (pHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1508 pHashEntry
; pHashEntry
= urlcache_get_hash_table(pHeader
, pHashEntry
->next
))
1511 if (pHashEntry
->id
!= id
++)
1513 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry
->id
, id
);
1516 /* make sure that it is in fact a hash entry */
1517 if (pHashEntry
->header
.signature
!= HASH_SIGNATURE
)
1519 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&pHashEntry
->header
.signature
);
1523 for (i
= 0; i
< HASHTABLE_BLOCKSIZE
; i
++)
1525 struct hash_entry
*pHashElement
= &pHashEntry
->hash_table
[offset
+ i
];
1526 if (key
== pHashElement
->key
>>HASHTABLE_FLAG_BITS
)
1528 /* FIXME: we should make sure that this is the right element
1529 * before returning and claiming that it is. We can do this
1530 * by doing a simple compare between the URL we were given
1531 * and the URL stored in the entry. However, this assumes
1532 * we know the format of all the entries stored in the
1534 *ppHashEntry
= pHashElement
;
1542 /***********************************************************************
1543 * urlcache_hash_entry_set_flags (Internal)
1545 * Sets special bits in hash key
1551 static void urlcache_hash_entry_set_flags(struct hash_entry
*pHashEntry
, DWORD dwFlag
)
1553 pHashEntry
->key
= (pHashEntry
->key
>> HASHTABLE_FLAG_BITS
<< HASHTABLE_FLAG_BITS
) | dwFlag
;
1556 /***********************************************************************
1557 * urlcache_hash_entry_delete (Internal)
1559 * Searches all the hash tables in the index for the given URL and
1560 * then if found deletes the entry.
1563 * TRUE if the entry was found
1564 * FALSE if the entry could not be found
1567 static BOOL
urlcache_hash_entry_delete(struct hash_entry
*pHashEntry
)
1569 pHashEntry
->key
= HASHTABLE_DEL
;
1573 /***********************************************************************
1574 * urlcache_hash_entry_create (Internal)
1576 * Searches all the hash tables for a free slot based on the offset
1577 * generated from the hash key. If a free slot is found, the offset and
1578 * key are entered into the hash table.
1581 * ERROR_SUCCESS if the entry was added
1582 * Any other Win32 error code if the entry could not be added
1585 static DWORD
urlcache_hash_entry_create(urlcache_header
*pHeader
, LPCSTR lpszUrl
, DWORD dwOffsetEntry
, DWORD dwFieldType
)
1587 /* see urlcache_find_hash_entry for structure of hash tables */
1589 DWORD key
= urlcache_hash_key(lpszUrl
);
1590 DWORD offset
= (key
& (HASHTABLE_NUM_ENTRIES
-1)) * HASHTABLE_BLOCKSIZE
;
1591 entry_hash_table
* pHashEntry
, *pHashPrev
= NULL
;
1595 key
= ((key
>> HASHTABLE_FLAG_BITS
) << HASHTABLE_FLAG_BITS
) + dwFieldType
;
1597 for (pHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1598 pHashEntry
; pHashEntry
= urlcache_get_hash_table(pHeader
, pHashEntry
->next
))
1601 pHashPrev
= pHashEntry
;
1603 if (pHashEntry
->id
!= id
++)
1605 ERR("not right hash table number (%d) expected %d\n", pHashEntry
->id
, id
);
1608 /* make sure that it is in fact a hash entry */
1609 if (pHashEntry
->header
.signature
!= HASH_SIGNATURE
)
1611 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&pHashEntry
->header
.signature
);
1615 for (i
= 0; i
< HASHTABLE_BLOCKSIZE
; i
++)
1617 struct hash_entry
*pHashElement
= &pHashEntry
->hash_table
[offset
+ i
];
1618 if (pHashElement
->key
==HASHTABLE_FREE
|| pHashElement
->key
==HASHTABLE_DEL
) /* if the slot is free */
1620 pHashElement
->key
= key
;
1621 pHashElement
->offset
= dwOffsetEntry
;
1622 return ERROR_SUCCESS
;
1626 error
= urlcache_create_hash_table(pHeader
, pHashPrev
, &pHashEntry
);
1627 if (error
!= ERROR_SUCCESS
)
1630 pHashEntry
->hash_table
[offset
].key
= key
;
1631 pHashEntry
->hash_table
[offset
].offset
= dwOffsetEntry
;
1632 return ERROR_SUCCESS
;
1635 /***********************************************************************
1636 * urlcache_enum_hash_tables (Internal)
1638 * Enumerates the hash tables in a container.
1641 * TRUE if an entry was found
1642 * FALSE if there are no more tables to enumerate.
1645 static BOOL
urlcache_enum_hash_tables(const urlcache_header
*pHeader
, DWORD
*id
, entry_hash_table
**ppHashEntry
)
1647 for (*ppHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1648 *ppHashEntry
; *ppHashEntry
= urlcache_get_hash_table(pHeader
, (*ppHashEntry
)->next
))
1650 TRACE("looking at hash table number %d\n", (*ppHashEntry
)->id
);
1651 if ((*ppHashEntry
)->id
!= *id
)
1653 /* make sure that it is in fact a hash entry */
1654 if ((*ppHashEntry
)->header
.signature
!= HASH_SIGNATURE
)
1656 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&(*ppHashEntry
)->header
.signature
);
1661 TRACE("hash table number %d found\n", *id
);
1667 /***********************************************************************
1668 * urlcache_enum_hash_table_entries (Internal)
1670 * Enumerates entries in a hash table and returns the next non-free entry.
1673 * TRUE if an entry was found
1674 * FALSE if the hash table is empty or there are no more entries to
1678 static BOOL
urlcache_enum_hash_table_entries(const urlcache_header
*pHeader
, const entry_hash_table
*pHashEntry
,
1679 DWORD
* index
, const struct hash_entry
**ppHashEntry
)
1681 for (; *index
< HASHTABLE_SIZE
; (*index
)++)
1683 if (pHashEntry
->hash_table
[*index
].key
==HASHTABLE_FREE
|| pHashEntry
->hash_table
[*index
].key
==HASHTABLE_DEL
)
1686 *ppHashEntry
= &pHashEntry
->hash_table
[*index
];
1687 TRACE("entry found %d\n", *index
);
1690 TRACE("no more entries (%d)\n", *index
);
1694 /***********************************************************************
1695 * cache_container_delete_dir (Internal)
1697 * Erase a directory containing an URL cache.
1700 * TRUE success, FALSE failure/aborted.
1703 static BOOL
cache_container_delete_dir(LPCWSTR lpszPath
)
1706 WCHAR path
[MAX_PATH
+ 1];
1707 SHFILEOPSTRUCTW shfos
;
1710 path_len
= lstrlenW(lpszPath
);
1711 if (path_len
>= MAX_PATH
)
1713 lstrcpyW(path
, lpszPath
);
1714 path
[path_len
+ 1] = 0; /* double-NUL-terminate path */
1717 shfos
.wFunc
= FO_DELETE
;
1720 shfos
.fFlags
= FOF_NOCONFIRMATION
;
1721 shfos
.fAnyOperationsAborted
= FALSE
;
1722 ret
= SHFileOperationW(&shfos
);
1724 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path
), ret
);
1725 return !(ret
|| shfos
.fAnyOperationsAborted
);
1728 /***********************************************************************
1729 * urlcache_hash_entry_is_locked (Internal)
1731 * Checks if entry is locked. Unlocks it if possible.
1733 static BOOL
urlcache_hash_entry_is_locked(struct hash_entry
*hash_entry
, entry_url
*url_entry
)
1736 ULARGE_INTEGER acc_time
, time
;
1738 if ((hash_entry
->key
& ((1<<HASHTABLE_FLAG_BITS
)-1)) != HASHTABLE_LOCK
)
1741 GetSystemTimeAsFileTime(&cur_time
);
1742 time
.u
.LowPart
= cur_time
.dwLowDateTime
;
1743 time
.u
.HighPart
= cur_time
.dwHighDateTime
;
1745 acc_time
.u
.LowPart
= url_entry
->access_time
.dwLowDateTime
;
1746 acc_time
.u
.HighPart
= url_entry
->access_time
.dwHighDateTime
;
1748 time
.QuadPart
-= acc_time
.QuadPart
;
1750 /* check if entry was locked for at least a day */
1751 if(time
.QuadPart
> (ULONGLONG
)24*60*60*FILETIME_SECOND
) {
1752 urlcache_hash_entry_set_flags(hash_entry
, HASHTABLE_URL
);
1753 url_entry
->use_count
= 0;
1760 static BOOL
urlcache_get_entry_info(const char *url
, void *entry_info
,
1761 DWORD
*size
, DWORD flags
, BOOL unicode
)
1763 urlcache_header
*header
;
1764 struct hash_entry
*hash_entry
;
1765 const entry_url
*url_entry
;
1766 cache_container
*container
;
1769 TRACE("(%s, %p, %p, %x, %x)\n", debugstr_a(url
), entry_info
, size
, flags
, unicode
);
1771 if(flags
& ~GET_INSTALLED_ENTRY
)
1772 FIXME("ignoring unsupported flags: %x\n", flags
);
1774 error
= cache_containers_find(url
, &container
);
1775 if(error
!= ERROR_SUCCESS
) {
1776 SetLastError(error
);
1780 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
1781 if(error
!= ERROR_SUCCESS
) {
1782 SetLastError(error
);
1786 if(!(header
= cache_container_lock_index(container
)))
1789 if(!urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
1790 cache_container_unlock_index(container
, header
);
1791 WARN("entry %s not found!\n", debugstr_a(url
));
1792 SetLastError(ERROR_FILE_NOT_FOUND
);
1796 url_entry
= (const entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
1797 if(url_entry
->header
.signature
!= URL_SIGNATURE
) {
1798 FIXME("Trying to retrieve entry of unknown format %s\n",
1799 debugstr_an((LPCSTR
)&url_entry
->header
.signature
, sizeof(DWORD
)));
1800 cache_container_unlock_index(container
, header
);
1801 SetLastError(ERROR_FILE_NOT_FOUND
);
1805 TRACE("Found URL: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->url_off
));
1806 TRACE("Header info: %s\n", debugstr_an((LPCSTR
)url_entry
+
1807 url_entry
->header_info_off
, url_entry
->header_info_size
));
1809 if((flags
& GET_INSTALLED_ENTRY
) && !(url_entry
->cache_entry_type
& INSTALLED_CACHE_ENTRY
)) {
1810 cache_container_unlock_index(container
, header
);
1811 SetLastError(ERROR_FILE_NOT_FOUND
);
1819 error
= urlcache_copy_entry(container
, header
, entry_info
, size
, url_entry
, unicode
);
1820 if(error
!= ERROR_SUCCESS
) {
1821 cache_container_unlock_index(container
, header
);
1822 SetLastError(error
);
1825 if(url_entry
->local_name_off
)
1826 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->local_name_off
));
1829 cache_container_unlock_index(container
, header
);
1833 /***********************************************************************
1834 * GetUrlCacheEntryInfoExA (WININET.@)
1837 BOOL WINAPI
GetUrlCacheEntryInfoExA(LPCSTR lpszUrl
,
1838 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1839 LPDWORD lpdwCacheEntryInfoBufSize
, LPSTR lpszReserved
,
1840 LPDWORD lpdwReserved
, LPVOID lpReserved
, DWORD dwFlags
)
1842 if(lpszReserved
!=NULL
|| lpdwReserved
!=NULL
|| lpReserved
!=NULL
) {
1843 ERR("Reserved value was not 0\n");
1844 SetLastError(ERROR_INVALID_PARAMETER
);
1848 return urlcache_get_entry_info(lpszUrl
, lpCacheEntryInfo
,
1849 lpdwCacheEntryInfoBufSize
, dwFlags
, FALSE
);
1852 /***********************************************************************
1853 * GetUrlCacheEntryInfoA (WININET.@)
1856 BOOL WINAPI
GetUrlCacheEntryInfoA(LPCSTR lpszUrlName
,
1857 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1858 LPDWORD lpdwCacheEntryInfoBufferSize
)
1860 return GetUrlCacheEntryInfoExA(lpszUrlName
, lpCacheEntryInfo
,
1861 lpdwCacheEntryInfoBufferSize
, NULL
, NULL
, NULL
, 0);
1864 static int urlcache_encode_url(const WCHAR
*url
, char *encoded_url
, int encoded_len
)
1867 DWORD len
, part_len
;
1870 TRACE("%s\n", debugstr_w(url
));
1872 memset(&uc
, 0, sizeof(uc
));
1873 uc
.dwStructSize
= sizeof(uc
);
1874 uc
.dwHostNameLength
= 1;
1875 if(!InternetCrackUrlW(url
, 0, 0, &uc
))
1876 uc
.nScheme
= INTERNET_SCHEME_UNKNOWN
;
1878 if(uc
.nScheme
!=INTERNET_SCHEME_HTTP
&& uc
.nScheme
!=INTERNET_SCHEME_HTTPS
)
1879 return WideCharToMultiByte(CP_UTF8
, 0, url
, -1, encoded_url
, encoded_len
, NULL
, NULL
);
1881 len
= WideCharToMultiByte(CP_UTF8
, 0, url
, uc
.lpszHostName
-url
,
1882 encoded_url
, encoded_len
, NULL
, NULL
);
1888 part_len
= IdnToAscii(0, uc
.lpszHostName
, uc
.dwHostNameLength
, NULL
, 0);
1890 SetLastError(ERROR_INTERNET_INVALID_URL
);
1894 punycode
= heap_alloc(part_len
*sizeof(WCHAR
));
1898 part_len
= IdnToAscii(0, uc
.lpszHostName
, uc
.dwHostNameLength
, punycode
, part_len
);
1900 heap_free(punycode
);
1904 part_len
= WideCharToMultiByte(CP_UTF8
, 0, punycode
, part_len
,
1905 encoded_url
? encoded_url
+len
: NULL
, encoded_len
, NULL
, NULL
);
1906 heap_free(punycode
);
1910 encoded_len
-= part_len
;
1913 part_len
= WideCharToMultiByte(CP_UTF8
, 0, uc
.lpszHostName
+uc
.dwHostNameLength
,
1914 -1, encoded_url
? encoded_url
+len
: NULL
, encoded_len
, NULL
, NULL
);
1919 TRACE("got (%d)%s\n", len
, debugstr_a(encoded_url
));
1923 static BOOL
urlcache_encode_url_alloc(const WCHAR
*url
, char **encoded_url
)
1928 encoded_len
= urlcache_encode_url(url
, NULL
, 0);
1932 ret
= heap_alloc(encoded_len
*sizeof(WCHAR
));
1936 encoded_len
= urlcache_encode_url(url
, ret
, encoded_len
);
1946 /***********************************************************************
1947 * GetUrlCacheEntryInfoExW (WININET.@)
1950 BOOL WINAPI
GetUrlCacheEntryInfoExW(LPCWSTR lpszUrl
,
1951 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
1952 LPDWORD lpdwCacheEntryInfoBufSize
, LPWSTR lpszReserved
,
1953 LPDWORD lpdwReserved
, LPVOID lpReserved
, DWORD dwFlags
)
1958 if(lpszReserved
!=NULL
|| lpdwReserved
!=NULL
|| lpReserved
!=NULL
) {
1959 ERR("Reserved value was not 0\n");
1960 SetLastError(ERROR_INVALID_PARAMETER
);
1964 /* Ignore GET_INSTALLED_ENTRY flag in unicode version of function */
1965 dwFlags
&= ~GET_INSTALLED_ENTRY
;
1967 if(!urlcache_encode_url_alloc(lpszUrl
, &url
))
1970 ret
= urlcache_get_entry_info(url
, lpCacheEntryInfo
,
1971 lpdwCacheEntryInfoBufSize
, dwFlags
, TRUE
);
1976 /***********************************************************************
1977 * GetUrlCacheEntryInfoW (WININET.@)
1980 BOOL WINAPI
GetUrlCacheEntryInfoW(LPCWSTR lpszUrl
,
1981 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
1982 LPDWORD lpdwCacheEntryInfoBufferSize
)
1984 return GetUrlCacheEntryInfoExW(lpszUrl
, lpCacheEntryInfo
,
1985 lpdwCacheEntryInfoBufferSize
, NULL
, NULL
, NULL
, 0);
1988 /***********************************************************************
1989 * SetUrlCacheEntryInfoA (WININET.@)
1991 BOOL WINAPI
SetUrlCacheEntryInfoA(LPCSTR lpszUrlName
,
1992 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1993 DWORD dwFieldControl
)
1995 urlcache_header
*pHeader
;
1996 struct hash_entry
*pHashEntry
;
1997 entry_header
*pEntry
;
1998 cache_container
*pContainer
;
2001 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName
), lpCacheEntryInfo
, dwFieldControl
);
2003 error
= cache_containers_find(lpszUrlName
, &pContainer
);
2004 if (error
!= ERROR_SUCCESS
)
2006 SetLastError(error
);
2010 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
2011 if (error
!= ERROR_SUCCESS
)
2013 SetLastError(error
);
2017 if (!(pHeader
= cache_container_lock_index(pContainer
)))
2020 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
2022 cache_container_unlock_index(pContainer
, pHeader
);
2023 WARN("entry %s not found!\n", debugstr_a(lpszUrlName
));
2024 SetLastError(ERROR_FILE_NOT_FOUND
);
2028 pEntry
= (entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2029 if (pEntry
->signature
!= URL_SIGNATURE
)
2031 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR
)&pEntry
->signature
, sizeof(DWORD
)));
2032 cache_container_unlock_index(pContainer
, pHeader
);
2033 SetLastError(ERROR_FILE_NOT_FOUND
);
2037 urlcache_set_entry_info((entry_url
*)pEntry
, lpCacheEntryInfo
, dwFieldControl
);
2039 cache_container_unlock_index(pContainer
, pHeader
);
2044 /***********************************************************************
2045 * SetUrlCacheEntryInfoW (WININET.@)
2047 BOOL WINAPI
SetUrlCacheEntryInfoW(LPCWSTR lpszUrl
,
2048 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
2049 DWORD dwFieldControl
)
2054 if(!urlcache_encode_url_alloc(lpszUrl
, &url
))
2057 ret
= SetUrlCacheEntryInfoA(url
, (INTERNET_CACHE_ENTRY_INFOA
*)lpCacheEntryInfo
, dwFieldControl
);
2062 static BOOL
urlcache_entry_get_file(const char *url
, void *entry_info
, DWORD
*size
, BOOL unicode
)
2064 urlcache_header
*header
;
2065 struct hash_entry
*hash_entry
;
2066 entry_url
*url_entry
;
2067 cache_container
*container
;
2070 TRACE("(%s, %p, %p, %x)\n", debugstr_a(url
), entry_info
, size
, unicode
);
2072 if(!url
|| !size
|| (!entry_info
&& *size
)) {
2073 SetLastError(ERROR_INVALID_PARAMETER
);
2077 error
= cache_containers_find(url
, &container
);
2078 if(error
!= ERROR_SUCCESS
) {
2079 SetLastError(error
);
2083 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2084 if (error
!= ERROR_SUCCESS
) {
2085 SetLastError(error
);
2089 if (!(header
= cache_container_lock_index(container
)))
2092 if (!urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
2093 cache_container_unlock_index(container
, header
);
2094 TRACE("entry %s not found!\n", debugstr_a(url
));
2095 SetLastError(ERROR_FILE_NOT_FOUND
);
2099 url_entry
= (entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
2100 if(url_entry
->header
.signature
!= URL_SIGNATURE
) {
2101 FIXME("Trying to retrieve entry of unknown format %s\n",
2102 debugstr_an((LPSTR
)&url_entry
->header
.signature
, sizeof(DWORD
)));
2103 cache_container_unlock_index(container
, header
);
2104 SetLastError(ERROR_FILE_NOT_FOUND
);
2108 if(!url_entry
->local_name_off
) {
2109 cache_container_unlock_index(container
, header
);
2110 SetLastError(ERROR_INVALID_DATA
);
2114 TRACE("Found URL: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->url_off
));
2115 TRACE("Header info: %s\n", debugstr_an((LPCSTR
)url_entry
+ url_entry
->header_info_off
,
2116 url_entry
->header_info_size
));
2118 error
= urlcache_copy_entry(container
, header
, entry_info
,
2119 size
, url_entry
, unicode
);
2120 if(error
!= ERROR_SUCCESS
) {
2121 cache_container_unlock_index(container
, header
);
2122 SetLastError(error
);
2125 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->local_name_off
));
2127 url_entry
->hit_rate
++;
2128 url_entry
->use_count
++;
2129 urlcache_hash_entry_set_flags(hash_entry
, HASHTABLE_LOCK
);
2130 GetSystemTimeAsFileTime(&url_entry
->access_time
);
2132 cache_container_unlock_index(container
, header
);
2137 /***********************************************************************
2138 * RetrieveUrlCacheEntryFileA (WININET.@)
2141 BOOL WINAPI
RetrieveUrlCacheEntryFileA(LPCSTR lpszUrlName
,
2142 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
2143 LPDWORD lpdwCacheEntryInfoBufferSize
, DWORD dwReserved
)
2145 return urlcache_entry_get_file(lpszUrlName
, lpCacheEntryInfo
,
2146 lpdwCacheEntryInfoBufferSize
, FALSE
);
2149 /***********************************************************************
2150 * RetrieveUrlCacheEntryFileW (WININET.@)
2153 BOOL WINAPI
RetrieveUrlCacheEntryFileW(LPCWSTR lpszUrlName
,
2154 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
2155 LPDWORD lpdwCacheEntryInfoBufferSize
, DWORD dwReserved
)
2160 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
2163 ret
= urlcache_entry_get_file(url
, lpCacheEntryInfo
,
2164 lpdwCacheEntryInfoBufferSize
, TRUE
);
2169 static BOOL
urlcache_entry_delete(const cache_container
*pContainer
,
2170 urlcache_header
*pHeader
, struct hash_entry
*pHashEntry
)
2172 entry_header
*pEntry
;
2173 entry_url
* pUrlEntry
;
2175 pEntry
= (entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2176 if (pEntry
->signature
!= URL_SIGNATURE
)
2178 FIXME("Trying to delete entry of unknown format %s\n",
2179 debugstr_an((LPCSTR
)&pEntry
->signature
, sizeof(DWORD
)));
2180 SetLastError(ERROR_FILE_NOT_FOUND
);
2184 pUrlEntry
= (entry_url
*)pEntry
;
2185 if(urlcache_hash_entry_is_locked(pHashEntry
, pUrlEntry
))
2187 TRACE("Trying to delete locked entry\n");
2188 pUrlEntry
->cache_entry_type
|= PENDING_DELETE_CACHE_ENTRY
;
2189 SetLastError(ERROR_SHARING_VIOLATION
);
2193 if(!urlcache_delete_file(pContainer
, pHeader
, pUrlEntry
))
2195 urlcache_entry_free(pHeader
, pEntry
);
2199 /* Add entry to leaked files list */
2200 pUrlEntry
->header
.signature
= LEAK_SIGNATURE
;
2201 pUrlEntry
->exempt_delta
= pHeader
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
];
2202 pHeader
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
] = pHashEntry
->offset
;
2205 urlcache_hash_entry_delete(pHashEntry
);
2209 static HANDLE free_cache_running
;
2210 static HANDLE dll_unload_event
;
2211 static DWORD WINAPI
handle_full_cache_worker(void *param
)
2213 FreeUrlCacheSpaceW(NULL
, 20, 0);
2214 ReleaseSemaphore(free_cache_running
, 1, NULL
);
2218 static void handle_full_cache(void)
2220 if(WaitForSingleObject(free_cache_running
, 0) == WAIT_OBJECT_0
) {
2221 if(!QueueUserWorkItem(handle_full_cache_worker
, NULL
, 0))
2222 ReleaseSemaphore(free_cache_running
, 1, NULL
);
2226 /* Enumerates entries in cache, allows cache unlocking between calls. */
2227 static BOOL
urlcache_next_entry(urlcache_header
*header
, DWORD
*hash_table_off
, DWORD
*hash_table_entry
,
2228 struct hash_entry
**hash_entry
, entry_header
**entry
)
2230 entry_hash_table
*hashtable_entry
;
2235 if(!*hash_table_off
) {
2236 *hash_table_off
= header
->hash_table_off
;
2237 *hash_table_entry
= 0;
2239 hashtable_entry
= urlcache_get_hash_table(header
, *hash_table_off
);
2241 if(*hash_table_off
>= header
->size
) {
2242 *hash_table_off
= 0;
2246 hashtable_entry
= urlcache_get_hash_table(header
, *hash_table_off
);
2249 if(hashtable_entry
->header
.signature
!= HASH_SIGNATURE
) {
2250 *hash_table_off
= 0;
2255 if(*hash_table_entry
>= HASHTABLE_SIZE
) {
2256 *hash_table_off
= hashtable_entry
->next
;
2257 if(!*hash_table_off
) {
2258 *hash_table_off
= 0;
2262 hashtable_entry
= urlcache_get_hash_table(header
, *hash_table_off
);
2263 *hash_table_entry
= 0;
2266 if(hashtable_entry
->hash_table
[*hash_table_entry
].key
!= HASHTABLE_DEL
&&
2267 hashtable_entry
->hash_table
[*hash_table_entry
].key
!= HASHTABLE_FREE
) {
2268 *hash_entry
= &hashtable_entry
->hash_table
[*hash_table_entry
];
2269 *entry
= (entry_header
*)((LPBYTE
)header
+ hashtable_entry
->hash_table
[*hash_table_entry
].offset
);
2270 (*hash_table_entry
)++;
2274 (*hash_table_entry
)++;
2277 *hash_table_off
= 0;
2281 /* Rates an urlcache entry to determine if it can be deleted.
2283 * Score 0 means that entry can safely be removed, the bigger rating
2284 * the smaller chance of entry being removed.
2285 * DWORD_MAX means that entry can't be deleted at all.
2287 * Rating system is currently not fully compatible with native implementation.
2289 static DWORD
urlcache_rate_entry(entry_url
*url_entry
, FILETIME
*cur_time
)
2291 ULARGE_INTEGER time
, access_time
;
2294 access_time
.u
.LowPart
= url_entry
->access_time
.dwLowDateTime
;
2295 access_time
.u
.HighPart
= url_entry
->access_time
.dwHighDateTime
;
2297 time
.u
.LowPart
= cur_time
->dwLowDateTime
;
2298 time
.u
.HighPart
= cur_time
->dwHighDateTime
;
2300 /* Don't touch entries that were added less than 10 minutes ago */
2301 if(time
.QuadPart
< access_time
.QuadPart
+ (ULONGLONG
)10*60*FILETIME_SECOND
)
2304 if(url_entry
->cache_entry_type
& STICKY_CACHE_ENTRY
)
2305 if(time
.QuadPart
< access_time
.QuadPart
+ (ULONGLONG
)url_entry
->exempt_delta
*FILETIME_SECOND
)
2308 time
.QuadPart
= (time
.QuadPart
-access_time
.QuadPart
)/FILETIME_SECOND
;
2309 rating
= 400*60*60*24/(60*60*24+time
.QuadPart
);
2311 if(url_entry
->hit_rate
> 100)
2314 rating
+= url_entry
->hit_rate
;
2319 static int __cdecl
dword_cmp(const void *p1
, const void *p2
)
2321 return *(const DWORD
*)p1
- *(const DWORD
*)p2
;
2324 /***********************************************************************
2325 * FreeUrlCacheSpaceW (WININET.@)
2327 * Frees up some cache.
2330 * cache_path [I] Which volume to free up from, or NULL if you don't care.
2331 * size [I] Percentage of the cache that should be free.
2332 * filter [I] Which entries can't be deleted (CacheEntryType)
2335 * TRUE success. FALSE failure.
2338 * This implementation just retrieves the path of the cache directory, and
2339 * deletes its contents from the filesystem. The correct approach would
2340 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
2342 BOOL WINAPI
FreeUrlCacheSpaceW(LPCWSTR cache_path
, DWORD size
, DWORD filter
)
2344 cache_container
*container
;
2345 DWORD path_len
, err
;
2347 TRACE("(%s, %x, %x)\n", debugstr_w(cache_path
), size
, filter
);
2349 if(size
<1 || size
>100) {
2350 SetLastError(ERROR_INVALID_PARAMETER
);
2355 path_len
= lstrlenW(cache_path
);
2356 if(cache_path
[path_len
-1] == '\\')
2362 if(size
==100 && !filter
) {
2363 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
2365 /* When cache_path==NULL only clean Temporary Internet Files */
2366 if((!path_len
&& container
->cache_prefix
[0]==0) ||
2367 (path_len
&& !wcsnicmp(container
->path
, cache_path
, path_len
) &&
2368 (container
->path
[path_len
]=='\0' || container
->path
[path_len
]=='\\')))
2372 WaitForSingleObject(container
->mutex
, INFINITE
);
2374 /* unlock, delete, recreate and lock cache */
2375 cache_container_close_index(container
);
2376 ret_del
= cache_container_delete_dir(container
->path
);
2377 err
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2379 ReleaseMutex(container
->mutex
);
2380 if(!ret_del
|| (err
!= ERROR_SUCCESS
))
2388 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
2390 urlcache_header
*header
;
2391 struct hash_entry
*hash_entry
;
2392 entry_header
*entry
;
2393 entry_url
*url_entry
;
2394 ULONGLONG desired_size
, cur_size
;
2395 DWORD delete_factor
, hash_table_off
, hash_table_entry
;
2396 DWORD rate
[100], rate_no
;
2399 if((path_len
|| container
->cache_prefix
[0]!=0) &&
2400 (!path_len
|| wcsnicmp(container
->path
, cache_path
, path_len
) ||
2401 (container
->path
[path_len
]!='\0' && container
->path
[path_len
]!='\\')))
2404 err
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2405 if(err
!= ERROR_SUCCESS
)
2408 header
= cache_container_lock_index(container
);
2412 urlcache_clean_leaked_entries(container
, header
);
2414 desired_size
= header
->cache_limit
.QuadPart
*(100-size
)/100;
2415 cur_size
= header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
;
2416 if(cur_size
<= desired_size
)
2419 delete_factor
= (cur_size
-desired_size
)*100/cur_size
;
2421 if(!delete_factor
) {
2422 cache_container_unlock_index(container
, header
);
2427 hash_table_entry
= 0;
2429 GetSystemTimeAsFileTime(&cur_time
);
2430 while(rate_no
< ARRAY_SIZE(rate
) &&
2431 urlcache_next_entry(header
, &hash_table_off
, &hash_table_entry
, &hash_entry
, &entry
)) {
2432 if(entry
->signature
!= URL_SIGNATURE
) {
2433 WARN("only url entries are currently supported\n");
2437 url_entry
= (entry_url
*)entry
;
2438 if(url_entry
->cache_entry_type
& filter
)
2441 rate
[rate_no
] = urlcache_rate_entry(url_entry
, &cur_time
);
2442 if(rate
[rate_no
] != -1)
2447 TRACE("nothing to delete\n");
2448 cache_container_unlock_index(container
, header
);
2452 qsort(rate
, rate_no
, sizeof(DWORD
), dword_cmp
);
2454 delete_factor
= delete_factor
*rate_no
/100;
2455 delete_factor
= rate
[delete_factor
];
2456 TRACE("deleting files with rating %d or less\n", delete_factor
);
2459 while(urlcache_next_entry(header
, &hash_table_off
, &hash_table_entry
, &hash_entry
, &entry
)) {
2460 if(entry
->signature
!= URL_SIGNATURE
)
2463 url_entry
= (entry_url
*)entry
;
2464 if(url_entry
->cache_entry_type
& filter
)
2467 if(urlcache_rate_entry(url_entry
, &cur_time
) <= delete_factor
) {
2468 TRACE("deleting file: %s\n", debugstr_a((char*)url_entry
+url_entry
->local_name_off
));
2469 urlcache_entry_delete(container
, header
, hash_entry
);
2471 if(header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
<= desired_size
)
2474 /* Allow other threads to use cache while cleaning */
2475 cache_container_unlock_index(container
, header
);
2476 if(WaitForSingleObject(dll_unload_event
, 0) == WAIT_OBJECT_0
) {
2477 TRACE("got dll_unload_event - finishing\n");
2481 header
= cache_container_lock_index(container
);
2485 TRACE("cache size after cleaning 0x%s/0x%s\n",
2486 wine_dbgstr_longlong(header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
),
2487 wine_dbgstr_longlong(header
->cache_limit
.QuadPart
));
2488 cache_container_unlock_index(container
, header
);
2494 /***********************************************************************
2495 * FreeUrlCacheSpaceA (WININET.@)
2497 * See FreeUrlCacheSpaceW.
2499 BOOL WINAPI
FreeUrlCacheSpaceA(LPCSTR lpszCachePath
, DWORD dwSize
, DWORD dwFilter
)
2502 LPWSTR path
= heap_strdupAtoW(lpszCachePath
);
2503 if (lpszCachePath
== NULL
|| path
!= NULL
)
2504 ret
= FreeUrlCacheSpaceW(path
, dwSize
, dwFilter
);
2509 /***********************************************************************
2510 * UnlockUrlCacheEntryFileA (WININET.@)
2513 BOOL WINAPI
UnlockUrlCacheEntryFileA(LPCSTR lpszUrlName
, DWORD dwReserved
)
2515 urlcache_header
*pHeader
;
2516 struct hash_entry
*pHashEntry
;
2517 entry_header
*pEntry
;
2518 entry_url
* pUrlEntry
;
2519 cache_container
*pContainer
;
2522 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName
), dwReserved
);
2526 ERR("dwReserved != 0\n");
2527 SetLastError(ERROR_INVALID_PARAMETER
);
2531 error
= cache_containers_find(lpszUrlName
, &pContainer
);
2532 if (error
!= ERROR_SUCCESS
)
2534 SetLastError(error
);
2538 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
2539 if (error
!= ERROR_SUCCESS
)
2541 SetLastError(error
);
2545 if (!(pHeader
= cache_container_lock_index(pContainer
)))
2548 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
2550 cache_container_unlock_index(pContainer
, pHeader
);
2551 TRACE("entry %s not found!\n", debugstr_a(lpszUrlName
));
2552 SetLastError(ERROR_FILE_NOT_FOUND
);
2556 pEntry
= (entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2557 if (pEntry
->signature
!= URL_SIGNATURE
)
2559 cache_container_unlock_index(pContainer
, pHeader
);
2560 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR
)&pEntry
->signature
, sizeof(DWORD
)));
2561 SetLastError(ERROR_FILE_NOT_FOUND
);
2565 pUrlEntry
= (entry_url
*)pEntry
;
2567 if (pUrlEntry
->use_count
== 0)
2569 cache_container_unlock_index(pContainer
, pHeader
);
2572 pUrlEntry
->use_count
--;
2573 if (!pUrlEntry
->use_count
)
2575 urlcache_hash_entry_set_flags(pHashEntry
, HASHTABLE_URL
);
2576 if (pUrlEntry
->cache_entry_type
& PENDING_DELETE_CACHE_ENTRY
)
2577 urlcache_entry_delete(pContainer
, pHeader
, pHashEntry
);
2580 cache_container_unlock_index(pContainer
, pHeader
);
2585 /***********************************************************************
2586 * UnlockUrlCacheEntryFileW (WININET.@)
2589 BOOL WINAPI
UnlockUrlCacheEntryFileW(LPCWSTR lpszUrlName
, DWORD dwReserved
)
2594 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
2597 ret
= UnlockUrlCacheEntryFileA(url
, dwReserved
);
2602 static BOOL
urlcache_entry_create(const char *url
, const char *ext
, WCHAR
*full_path
)
2604 cache_container
*container
;
2605 urlcache_header
*header
;
2606 char file_name
[MAX_PATH
];
2607 WCHAR extW
[MAX_PATH
];
2609 LONG full_path_len
, ext_len
= 0;
2610 BOOL generate_name
= FALSE
;
2617 TRACE("(%s, %s, %p)\n", debugstr_a(url
), debugstr_a(ext
), full_path
);
2619 memset(&uc
, 0, sizeof(uc
));
2620 uc
.dwStructSize
= sizeof(uc
);
2621 uc
.dwUrlPathLength
= 1;
2622 uc
.dwExtraInfoLength
= 1;
2623 if(!InternetCrackUrlA(url
, 0, 0, &uc
))
2624 uc
.dwUrlPathLength
= 0;
2626 if(!uc
.dwUrlPathLength
) {
2631 p
= e
= uc
.lpszUrlPath
+uc
.dwUrlPathLength
;
2632 while(p
>uc
.lpszUrlPath
&& *(p
-1)!='/' && *(p
-1)!='\\' && *(p
-1)!='.')
2634 if(p
>uc
.lpszUrlPath
&& *(p
-1)=='.') {
2636 while(p
>uc
.lpszUrlPath
&& *(p
-1)!='/' && *(p
-1)!='\\')
2642 memcpy(file_name
, p
, e
-p
);
2645 for(p
=file_name
; *p
; p
++) {
2658 generate_name
= TRUE
;
2660 error
= cache_containers_find(url
, &container
);
2661 if(error
!= ERROR_SUCCESS
) {
2662 SetLastError(error
);
2666 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2667 if(error
!= ERROR_SUCCESS
) {
2668 SetLastError(error
);
2672 if(!(header
= cache_container_lock_index(container
)))
2676 cache_dir
= (BYTE
)(rand() % header
->dirs_no
);
2678 cache_dir
= CACHE_CONTAINER_NO_SUBDIR
;
2680 full_path_len
= MAX_PATH
* sizeof(WCHAR
);
2681 if(!urlcache_create_file_pathW(container
, header
, file_name
, cache_dir
, full_path
, &full_path_len
, TRUE
)) {
2682 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2683 debugstr_a(file_name
), full_path_len
);
2684 cache_container_unlock_index(container
, header
);
2687 full_path_len
= full_path_len
/sizeof(WCHAR
) - 1;
2689 cache_container_unlock_index(container
, header
);
2695 ext_len
= MultiByteToWideChar(CP_ACP
, 0, ext
, -1, extW
+1, MAX_PATH
-1);
2697 for(p
=extW
; *p
; p
++) {
2707 if(p
[-1]==' ' || p
[-1]=='.')
2713 if(!generate_name
&& full_path_len
+5+ext_len
>=MAX_PATH
) { /* strlen("[255]") = 5 */
2714 full_path_len
= MAX_PATH
-5-ext_len
-1;
2717 for(i
=0; i
<255 && !generate_name
; i
++) {
2718 wsprintfW(full_path
+full_path_len
, L
"[%u]%s", i
, extW
);
2720 TRACE("Trying: %s\n", debugstr_w(full_path
));
2721 file
= CreateFileW(full_path
, GENERIC_READ
, 0, NULL
, CREATE_NEW
, 0, NULL
);
2722 if(file
!= INVALID_HANDLE_VALUE
) {
2728 if(full_path_len
+8+ext_len
>= MAX_PATH
)
2729 full_path_len
= MAX_PATH
-8-ext_len
-1;
2731 /* Try to generate random name */
2732 GetSystemTimeAsFileTime(&ft
);
2733 lstrcpyW(full_path
+full_path_len
+8, extW
);
2735 for(i
=0; i
<255; i
++) {
2737 ULONGLONG n
= ft
.dwHighDateTime
;
2739 n
+= ft
.dwLowDateTime
;
2740 n
^= (ULONGLONG
)i
<<48;
2742 for(j
=0; j
<8; j
++) {
2745 full_path
[full_path_len
+j
] = (r
< 10 ? '0' + r
: 'A' + r
- 10);
2748 TRACE("Trying: %s\n", debugstr_w(full_path
));
2749 file
= CreateFileW(full_path
, GENERIC_READ
, 0, NULL
, CREATE_NEW
, 0, NULL
);
2750 if(file
!= INVALID_HANDLE_VALUE
) {
2756 WARN("Could not find a unique filename\n");
2760 /***********************************************************************
2761 * CreateUrlCacheEntryA (WININET.@)
2764 BOOL WINAPI
CreateUrlCacheEntryA(LPCSTR lpszUrlName
, DWORD dwExpectedFileSize
,
2765 LPCSTR lpszFileExtension
, LPSTR lpszFileName
, DWORD dwReserved
)
2767 WCHAR file_name
[MAX_PATH
];
2770 FIXME("dwReserved 0x%08x\n", dwReserved
);
2772 if(!urlcache_entry_create(lpszUrlName
, lpszFileExtension
, file_name
))
2775 if(!WideCharToMultiByte(CP_ACP
, 0, file_name
, -1, lpszFileName
, MAX_PATH
, NULL
, NULL
))
2779 /***********************************************************************
2780 * CreateUrlCacheEntryW (WININET.@)
2783 BOOL WINAPI
CreateUrlCacheEntryW(LPCWSTR lpszUrlName
, DWORD dwExpectedFileSize
,
2784 LPCWSTR lpszFileExtension
, LPWSTR lpszFileName
, DWORD dwReserved
)
2786 char *url
, *ext
= NULL
;
2790 FIXME("dwReserved 0x%08x\n", dwReserved
);
2792 if(lpszFileExtension
) {
2793 ext
= heap_strdupWtoUTF8(lpszFileExtension
);
2798 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
)) {
2803 ret
= urlcache_entry_create(url
, ext
, lpszFileName
);
2809 static BOOL
urlcache_entry_commit(const char *url
, const WCHAR
*file_name
,
2810 FILETIME expire_time
, FILETIME modify_time
, DWORD entry_type
,
2811 BYTE
*header_info
, DWORD header_size
, const char *file_ext
,
2812 const char *original_url
)
2814 cache_container
*container
;
2815 urlcache_header
*header
;
2816 struct hash_entry
*hash_entry
;
2817 entry_header
*entry
;
2818 entry_url
*url_entry
;
2819 DWORD url_entry_offset
;
2820 DWORD size
= DWORD_ALIGN(sizeof(*url_entry
));
2821 DWORD file_name_off
= 0;
2822 DWORD header_info_off
= 0;
2823 DWORD file_ext_off
= 0;
2824 WIN32_FILE_ATTRIBUTE_DATA file_attr
;
2825 LARGE_INTEGER file_size
;
2827 char file_name_no_container
[MAX_PATH
];
2828 char *local_file_name
= 0;
2830 DWORD exempt_delta
= 0;
2833 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n", debugstr_a(url
), debugstr_w(file_name
),
2834 entry_type
, header_info
, header_size
, debugstr_a(file_ext
), debugstr_a(original_url
));
2836 if(entry_type
& STICKY_CACHE_ENTRY
&& !file_name
) {
2837 SetLastError(ERROR_INVALID_PARAMETER
);
2841 WARN(": original_url ignored\n");
2843 memset(&file_attr
, 0, sizeof(file_attr
));
2845 if(!GetFileAttributesExW(file_name
, GetFileExInfoStandard
, &file_attr
))
2848 file_size
.u
.LowPart
= file_attr
.nFileSizeLow
;
2849 file_size
.u
.HighPart
= file_attr
.nFileSizeHigh
;
2851 error
= cache_containers_find(url
, &container
);
2852 if(error
!= ERROR_SUCCESS
) {
2853 SetLastError(error
);
2857 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2858 if(error
!= ERROR_SUCCESS
) {
2859 SetLastError(error
);
2863 if(!(header
= cache_container_lock_index(container
)))
2866 if(urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
2867 entry_url
*url_entry
= (entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
2869 if(urlcache_hash_entry_is_locked(hash_entry
, url_entry
)) {
2870 TRACE("Trying to overwrite locked entry\n");
2871 cache_container_unlock_index(container
, header
);
2872 SetLastError(ERROR_SHARING_VIOLATION
);
2876 hit_rate
= url_entry
->hit_rate
;
2877 exempt_delta
= url_entry
->exempt_delta
;
2878 urlcache_entry_delete(container
, header
, hash_entry
);
2884 dir_id
= CACHE_CONTAINER_NO_SUBDIR
;
2887 BOOL bFound
= FALSE
;
2889 if(wcsncmp(file_name
, container
->path
, lstrlenW(container
->path
))) {
2890 ERR("path %s must begin with cache content path %s\n", debugstr_w(file_name
), debugstr_w(container
->path
));
2891 cache_container_unlock_index(container
, header
);
2892 SetLastError(ERROR_INVALID_PARAMETER
);
2896 /* skip container path prefix */
2897 file_name
+= lstrlenW(container
->path
);
2899 WideCharToMultiByte(CP_ACP
, 0, file_name
, -1, file_name_no_container
, MAX_PATH
, NULL
, NULL
);
2900 local_file_name
= file_name_no_container
;
2902 if(header
->dirs_no
) {
2903 for(dir_id
= 0; dir_id
< header
->dirs_no
; dir_id
++) {
2904 if(!strncmp(header
->directory_data
[dir_id
].name
, local_file_name
, DIR_LENGTH
)) {
2911 ERR("cache directory not found in path %s\n", debugstr_w(file_name
));
2912 cache_container_unlock_index(container
, header
);
2913 SetLastError(ERROR_INVALID_PARAMETER
);
2917 file_name
+= DIR_LENGTH
+ 1;
2918 local_file_name
+= DIR_LENGTH
+ 1;
2922 size
= DWORD_ALIGN(size
+ strlen(url
) + 1);
2924 file_name_off
= size
;
2925 size
= DWORD_ALIGN(size
+ strlen(local_file_name
) + 1);
2927 if(header_info
&& header_size
) {
2928 header_info_off
= size
;
2929 size
= DWORD_ALIGN(size
+ header_size
);
2931 if(file_ext
&& (file_ext_off
= strlen(file_ext
))) {
2932 DWORD len
= file_ext_off
;
2934 file_ext_off
= size
;
2935 size
= DWORD_ALIGN(size
+ len
+ 1);
2938 /* round up to next block */
2939 if(size
% BLOCKSIZE
) {
2940 size
-= size
% BLOCKSIZE
;
2944 error
= urlcache_entry_alloc(header
, size
/ BLOCKSIZE
, &entry
);
2945 while(error
== ERROR_HANDLE_DISK_FULL
) {
2946 error
= cache_container_clean_index(container
, &header
);
2947 if(error
== ERROR_SUCCESS
)
2948 error
= urlcache_entry_alloc(header
, size
/ BLOCKSIZE
, &entry
);
2950 if(error
!= ERROR_SUCCESS
) {
2951 cache_container_unlock_index(container
, header
);
2952 SetLastError(error
);
2956 /* FindFirstFreeEntry fills in blocks used */
2957 url_entry
= (entry_url
*)entry
;
2958 url_entry_offset
= (LPBYTE
)url_entry
- (LPBYTE
)header
;
2959 url_entry
->header
.signature
= URL_SIGNATURE
;
2960 url_entry
->cache_dir
= dir_id
;
2961 url_entry
->cache_entry_type
= entry_type
| container
->default_entry_type
;
2962 url_entry
->header_info_size
= header_size
;
2963 if((entry_type
& STICKY_CACHE_ENTRY
) && !exempt_delta
) {
2964 /* Sticky entries have a default exempt time of one day */
2965 exempt_delta
= 86400;
2967 url_entry
->exempt_delta
= exempt_delta
;
2968 url_entry
->hit_rate
= hit_rate
+1;
2969 url_entry
->file_extension_off
= file_ext_off
;
2970 url_entry
->header_info_off
= header_info_off
;
2971 url_entry
->local_name_off
= file_name_off
;
2972 url_entry
->url_off
= DWORD_ALIGN(sizeof(*url_entry
));
2973 url_entry
->size
.QuadPart
= file_size
.QuadPart
;
2974 url_entry
->use_count
= 0;
2975 GetSystemTimeAsFileTime(&url_entry
->access_time
);
2976 url_entry
->modification_time
= modify_time
;
2977 file_time_to_dos_date_time(&url_entry
->access_time
, &url_entry
->sync_date
, &url_entry
->sync_time
);
2978 file_time_to_dos_date_time(&expire_time
, &url_entry
->expire_date
, &url_entry
->expire_time
);
2979 file_time_to_dos_date_time(&file_attr
.ftLastWriteTime
, &url_entry
->write_date
, &url_entry
->write_time
);
2982 url_entry
->unk1
= 0;
2983 url_entry
->unk2
= 0;
2984 url_entry
->unk3
= 0x60;
2985 url_entry
->unk4
= 0;
2986 url_entry
->unk5
= 0x1010;
2987 url_entry
->unk7
= 0;
2988 url_entry
->unk8
= 0;
2991 strcpy((LPSTR
)url_entry
+ url_entry
->url_off
, url
);
2993 strcpy((LPSTR
)((LPBYTE
)url_entry
+ file_name_off
), local_file_name
);
2995 memcpy((LPBYTE
)url_entry
+ header_info_off
, header_info
, header_size
);
2997 strcpy((LPSTR
)((LPBYTE
)url_entry
+ file_ext_off
), file_ext
);
2999 error
= urlcache_hash_entry_create(header
, url
, url_entry_offset
, HASHTABLE_URL
);
3000 while(error
== ERROR_HANDLE_DISK_FULL
) {
3001 error
= cache_container_clean_index(container
, &header
);
3002 if(error
== ERROR_SUCCESS
) {
3003 url_entry
= (entry_url
*)((LPBYTE
)header
+ url_entry_offset
);
3004 error
= urlcache_hash_entry_create(header
, url
,
3005 url_entry_offset
, HASHTABLE_URL
);
3008 if(error
!= ERROR_SUCCESS
) {
3009 urlcache_entry_free(header
, &url_entry
->header
);
3010 cache_container_unlock_index(container
, header
);
3011 SetLastError(error
);
3015 if(url_entry
->cache_dir
< header
->dirs_no
)
3016 header
->directory_data
[url_entry
->cache_dir
].files_no
++;
3017 if(entry_type
& STICKY_CACHE_ENTRY
)
3018 header
->exempt_usage
.QuadPart
+= file_size
.QuadPart
;
3020 header
->cache_usage
.QuadPart
+= file_size
.QuadPart
;
3021 if(header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
> header
->cache_limit
.QuadPart
)
3022 handle_full_cache();
3024 cache_container_unlock_index(container
, header
);
3028 /***********************************************************************
3029 * CommitUrlCacheEntryA (WININET.@)
3031 BOOL WINAPI
CommitUrlCacheEntryA(LPCSTR lpszUrlName
, LPCSTR lpszLocalFileName
,
3032 FILETIME ExpireTime
, FILETIME LastModifiedTime
, DWORD CacheEntryType
,
3033 LPBYTE lpHeaderInfo
, DWORD dwHeaderSize
, LPCSTR lpszFileExtension
, LPCSTR lpszOriginalUrl
)
3035 WCHAR
*file_name
= NULL
;
3038 if(lpszLocalFileName
) {
3039 file_name
= heap_strdupAtoW(lpszLocalFileName
);
3044 ret
= urlcache_entry_commit(lpszUrlName
, file_name
, ExpireTime
, LastModifiedTime
,
3045 CacheEntryType
, lpHeaderInfo
, dwHeaderSize
, lpszFileExtension
, lpszOriginalUrl
);
3046 heap_free(file_name
);
3050 /***********************************************************************
3051 * CommitUrlCacheEntryW (WININET.@)
3053 BOOL WINAPI
CommitUrlCacheEntryW(LPCWSTR lpszUrlName
, LPCWSTR lpszLocalFileName
,
3054 FILETIME ExpireTime
, FILETIME LastModifiedTime
, DWORD CacheEntryType
,
3055 LPWSTR lpHeaderInfo
, DWORD dwHeaderSize
, LPCWSTR lpszFileExtension
, LPCWSTR lpszOriginalUrl
)
3057 char *url
, *original_url
=NULL
, *file_ext
=NULL
, *header_info
=NULL
;
3060 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
3064 header_info
= heap_strdupWtoUTF8(lpHeaderInfo
);
3069 dwHeaderSize
= strlen(header_info
);
3072 if(lpszFileExtension
) {
3073 file_ext
= heap_strdupWtoA(lpszFileExtension
);
3076 heap_free(header_info
);
3081 if(lpszOriginalUrl
&& !urlcache_encode_url_alloc(lpszOriginalUrl
, &original_url
)) {
3083 heap_free(header_info
);
3084 heap_free(file_ext
);
3088 ret
= urlcache_entry_commit(url
, lpszLocalFileName
, ExpireTime
, LastModifiedTime
,
3089 CacheEntryType
, (BYTE
*)header_info
, dwHeaderSize
, file_ext
, original_url
);
3091 heap_free(header_info
);
3092 heap_free(file_ext
);
3093 heap_free(original_url
);
3097 /***********************************************************************
3098 * ReadUrlCacheEntryStream (WININET.@)
3101 BOOL WINAPI
ReadUrlCacheEntryStream(
3102 IN HANDLE hUrlCacheStream
,
3103 IN DWORD dwLocation
,
3104 IN OUT LPVOID lpBuffer
,
3105 IN OUT LPDWORD lpdwLen
,
3109 /* Get handle to file from 'stream' */
3110 stream_handle
*pStream
= (stream_handle
*)hUrlCacheStream
;
3112 if (dwReserved
!= 0)
3114 ERR("dwReserved != 0\n");
3115 SetLastError(ERROR_INVALID_PARAMETER
);
3119 if (IsBadReadPtr(pStream
, sizeof(*pStream
)) || IsBadStringPtrA(pStream
->url
, INTERNET_MAX_URL_LENGTH
))
3121 SetLastError(ERROR_INVALID_HANDLE
);
3125 if (SetFilePointer(pStream
->file
, dwLocation
, NULL
, FILE_CURRENT
) == INVALID_SET_FILE_POINTER
)
3127 return ReadFile(pStream
->file
, lpBuffer
, *lpdwLen
, lpdwLen
, NULL
);
3130 /***********************************************************************
3131 * RetrieveUrlCacheEntryStreamA (WININET.@)
3134 HANDLE WINAPI
RetrieveUrlCacheEntryStreamA(LPCSTR lpszUrlName
,
3135 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
3136 LPDWORD lpdwCacheEntryInfoBufferSize
, BOOL fRandomRead
, DWORD dwReserved
)
3138 /* NOTE: this is not the same as the way that the native
3139 * version allocates 'stream' handles. I did it this way
3140 * as it is much easier and no applications should depend
3141 * on this behaviour. (Native version appears to allocate
3142 * indices into a table)
3144 stream_handle
*stream
;
3147 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName
), lpCacheEntryInfo
,
3148 lpdwCacheEntryInfoBufferSize
, fRandomRead
, dwReserved
);
3150 if(!RetrieveUrlCacheEntryFileA(lpszUrlName
, lpCacheEntryInfo
,
3151 lpdwCacheEntryInfoBufferSize
, dwReserved
))
3154 file
= CreateFileA(lpCacheEntryInfo
->lpszLocalFileName
, GENERIC_READ
, FILE_SHARE_READ
,
3155 NULL
, OPEN_EXISTING
, fRandomRead
? FILE_FLAG_RANDOM_ACCESS
: 0, NULL
);
3156 if(file
== INVALID_HANDLE_VALUE
) {
3157 UnlockUrlCacheEntryFileA(lpszUrlName
, 0);
3161 /* allocate handle storage space */
3162 stream
= heap_alloc(sizeof(stream_handle
) + strlen(lpszUrlName
) * sizeof(CHAR
));
3165 UnlockUrlCacheEntryFileA(lpszUrlName
, 0);
3166 SetLastError(ERROR_OUTOFMEMORY
);
3170 stream
->file
= file
;
3171 strcpy(stream
->url
, lpszUrlName
);
3175 /***********************************************************************
3176 * RetrieveUrlCacheEntryStreamW (WININET.@)
3179 HANDLE WINAPI
RetrieveUrlCacheEntryStreamW(LPCWSTR lpszUrlName
,
3180 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
3181 LPDWORD lpdwCacheEntryInfoBufferSize
, BOOL fRandomRead
, DWORD dwReserved
)
3184 /* NOTE: this is not the same as the way that the native
3185 * version allocates 'stream' handles. I did it this way
3186 * as it is much easier and no applications should depend
3187 * on this behaviour. (Native version appears to allocate
3188 * indices into a table)
3190 stream_handle
*stream
;
3193 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName
), lpCacheEntryInfo
,
3194 lpdwCacheEntryInfoBufferSize
, fRandomRead
, dwReserved
);
3196 if(!(len
= urlcache_encode_url(lpszUrlName
, NULL
, 0)))
3199 if(!RetrieveUrlCacheEntryFileW(lpszUrlName
, lpCacheEntryInfo
,
3200 lpdwCacheEntryInfoBufferSize
, dwReserved
))
3203 file
= CreateFileW(lpCacheEntryInfo
->lpszLocalFileName
, GENERIC_READ
, FILE_SHARE_READ
,
3204 NULL
, OPEN_EXISTING
, fRandomRead
? FILE_FLAG_RANDOM_ACCESS
: 0, NULL
);
3205 if(file
== INVALID_HANDLE_VALUE
) {
3206 UnlockUrlCacheEntryFileW(lpszUrlName
, 0);
3210 /* allocate handle storage space */
3211 stream
= heap_alloc(sizeof(stream_handle
) + len
*sizeof(WCHAR
));
3214 UnlockUrlCacheEntryFileW(lpszUrlName
, 0);
3215 SetLastError(ERROR_OUTOFMEMORY
);
3219 stream
->file
= file
;
3220 if(!urlcache_encode_url(lpszUrlName
, stream
->url
, len
)) {
3222 UnlockUrlCacheEntryFileW(lpszUrlName
, 0);
3229 /***********************************************************************
3230 * UnlockUrlCacheEntryStream (WININET.@)
3233 BOOL WINAPI
UnlockUrlCacheEntryStream(
3234 IN HANDLE hUrlCacheStream
,
3238 stream_handle
*pStream
= (stream_handle
*)hUrlCacheStream
;
3240 if (dwReserved
!= 0)
3242 ERR("dwReserved != 0\n");
3243 SetLastError(ERROR_INVALID_PARAMETER
);
3247 if (IsBadReadPtr(pStream
, sizeof(*pStream
)) || IsBadStringPtrA(pStream
->url
, INTERNET_MAX_URL_LENGTH
))
3249 SetLastError(ERROR_INVALID_HANDLE
);
3253 if (!UnlockUrlCacheEntryFileA(pStream
->url
, 0))
3256 CloseHandle(pStream
->file
);
3262 /***********************************************************************
3263 * DeleteUrlCacheEntryA (WININET.@)
3266 BOOL WINAPI
DeleteUrlCacheEntryA(LPCSTR lpszUrlName
)
3268 cache_container
*pContainer
;
3269 urlcache_header
*pHeader
;
3270 struct hash_entry
*pHashEntry
;
3274 TRACE("(%s)\n", debugstr_a(lpszUrlName
));
3276 error
= cache_containers_find(lpszUrlName
, &pContainer
);
3277 if (error
!= ERROR_SUCCESS
)
3279 SetLastError(error
);
3283 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
3284 if (error
!= ERROR_SUCCESS
)
3286 SetLastError(error
);
3290 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3293 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
3295 cache_container_unlock_index(pContainer
, pHeader
);
3296 TRACE("entry %s not found!\n", debugstr_a(lpszUrlName
));
3297 SetLastError(ERROR_FILE_NOT_FOUND
);
3301 ret
= urlcache_entry_delete(pContainer
, pHeader
, pHashEntry
);
3303 cache_container_unlock_index(pContainer
, pHeader
);
3308 /***********************************************************************
3309 * DeleteUrlCacheEntryW (WININET.@)
3312 BOOL WINAPI
DeleteUrlCacheEntryW(LPCWSTR lpszUrlName
)
3317 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
3320 ret
= DeleteUrlCacheEntryA(url
);
3325 BOOL WINAPI
DeleteUrlCacheContainerA(DWORD d1
, DWORD d2
)
3327 FIXME("(0x%08x, 0x%08x) stub\n", d1
, d2
);
3331 BOOL WINAPI
DeleteUrlCacheContainerW(DWORD d1
, DWORD d2
)
3333 FIXME("(0x%08x, 0x%08x) stub\n", d1
, d2
);
3337 /***********************************************************************
3338 * CreateCacheContainerA (WININET.@)
3340 BOOL WINAPI
CreateUrlCacheContainerA(DWORD d1
, DWORD d2
, DWORD d3
, DWORD d4
,
3341 DWORD d5
, DWORD d6
, DWORD d7
, DWORD d8
)
3343 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3344 d1
, d2
, d3
, d4
, d5
, d6
, d7
, d8
);
3348 /***********************************************************************
3349 * CreateCacheContainerW (WININET.@)
3351 BOOL WINAPI
CreateUrlCacheContainerW(DWORD d1
, DWORD d2
, DWORD d3
, DWORD d4
,
3352 DWORD d5
, DWORD d6
, DWORD d7
, DWORD d8
)
3354 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3355 d1
, d2
, d3
, d4
, d5
, d6
, d7
, d8
);
3359 /***********************************************************************
3360 * FindFirstUrlCacheContainerA (WININET.@)
3362 HANDLE WINAPI
FindFirstUrlCacheContainerA( LPVOID p1
, LPVOID p2
, LPVOID p3
, DWORD d1
)
3364 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1
, p2
, p3
, d1
);
3368 /***********************************************************************
3369 * FindFirstUrlCacheContainerW (WININET.@)
3371 HANDLE WINAPI
FindFirstUrlCacheContainerW( LPVOID p1
, LPVOID p2
, LPVOID p3
, DWORD d1
)
3373 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1
, p2
, p3
, d1
);
3377 /***********************************************************************
3378 * FindNextUrlCacheContainerA (WININET.@)
3380 BOOL WINAPI
FindNextUrlCacheContainerA( HANDLE handle
, LPVOID p1
, LPVOID p2
)
3382 FIXME("(%p, %p, %p) stub\n", handle
, p1
, p2
);
3386 /***********************************************************************
3387 * FindNextUrlCacheContainerW (WININET.@)
3389 BOOL WINAPI
FindNextUrlCacheContainerW( HANDLE handle
, LPVOID p1
, LPVOID p2
)
3391 FIXME("(%p, %p, %p) stub\n", handle
, p1
, p2
);
3395 HANDLE WINAPI
FindFirstUrlCacheEntryExA(
3396 LPCSTR lpszUrlSearchPattern
,
3400 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
,
3401 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3403 LPDWORD pcbReserved2
,
3407 FIXME("(%s, 0x%08x, 0x%08x, 0x%s, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern
),
3408 dwFlags
, dwFilter
, wine_dbgstr_longlong(GroupId
), lpFirstCacheEntryInfo
,
3409 lpdwFirstCacheEntryInfoBufferSize
, lpReserved
, pcbReserved2
,lpReserved3
);
3410 SetLastError(ERROR_FILE_NOT_FOUND
);
3414 HANDLE WINAPI
FindFirstUrlCacheEntryExW(
3415 LPCWSTR lpszUrlSearchPattern
,
3419 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
,
3420 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3422 LPDWORD pcbReserved2
,
3426 FIXME("(%s, 0x%08x, 0x%08x, 0x%s, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern
),
3427 dwFlags
, dwFilter
, wine_dbgstr_longlong(GroupId
), lpFirstCacheEntryInfo
,
3428 lpdwFirstCacheEntryInfoBufferSize
, lpReserved
, pcbReserved2
,lpReserved3
);
3429 SetLastError(ERROR_FILE_NOT_FOUND
);
3433 /***********************************************************************
3434 * FindFirstUrlCacheEntryA (WININET.@)
3437 INTERNETAPI HANDLE WINAPI
FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern
,
3438 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
, LPDWORD lpdwFirstCacheEntryInfoBufferSize
)
3440 find_handle
*pEntryHandle
;
3442 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern
), lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
);
3444 pEntryHandle
= heap_alloc(sizeof(*pEntryHandle
));
3448 pEntryHandle
->magic
= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
;
3449 if (lpszUrlSearchPattern
)
3451 pEntryHandle
->url_search_pattern
= heap_strdupA(lpszUrlSearchPattern
);
3452 if (!pEntryHandle
->url_search_pattern
)
3454 heap_free(pEntryHandle
);
3459 pEntryHandle
->url_search_pattern
= NULL
;
3460 pEntryHandle
->container_idx
= 0;
3461 pEntryHandle
->hash_table_idx
= 0;
3462 pEntryHandle
->hash_entry_idx
= 0;
3464 if (!FindNextUrlCacheEntryA(pEntryHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
))
3466 heap_free(pEntryHandle
);
3469 return pEntryHandle
;
3472 /***********************************************************************
3473 * FindFirstUrlCacheEntryW (WININET.@)
3476 INTERNETAPI HANDLE WINAPI
FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern
,
3477 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
, LPDWORD lpdwFirstCacheEntryInfoBufferSize
)
3479 find_handle
*pEntryHandle
;
3481 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern
), lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
);
3483 pEntryHandle
= heap_alloc(sizeof(*pEntryHandle
));
3487 pEntryHandle
->magic
= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
;
3488 if (lpszUrlSearchPattern
)
3490 pEntryHandle
->url_search_pattern
= heap_strdupWtoA(lpszUrlSearchPattern
);
3491 if (!pEntryHandle
->url_search_pattern
)
3493 heap_free(pEntryHandle
);
3498 pEntryHandle
->url_search_pattern
= NULL
;
3499 pEntryHandle
->container_idx
= 0;
3500 pEntryHandle
->hash_table_idx
= 0;
3501 pEntryHandle
->hash_entry_idx
= 0;
3503 if (!FindNextUrlCacheEntryW(pEntryHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
))
3505 heap_free(pEntryHandle
);
3508 return pEntryHandle
;
3511 static BOOL
urlcache_find_next_entry(
3513 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo
,
3514 LPDWORD lpdwNextCacheEntryInfoBufferSize
,
3517 find_handle
*pEntryHandle
= (find_handle
*)hEnumHandle
;
3518 cache_container
*pContainer
;
3520 if (pEntryHandle
->magic
!= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
)
3522 SetLastError(ERROR_INVALID_HANDLE
);
3526 for (; cache_containers_enum(pEntryHandle
->url_search_pattern
, pEntryHandle
->container_idx
, &pContainer
);
3527 pEntryHandle
->container_idx
++, pEntryHandle
->hash_table_idx
= 0)
3529 urlcache_header
*pHeader
;
3530 entry_hash_table
*pHashTableEntry
;
3533 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
3534 if (error
!= ERROR_SUCCESS
)
3536 SetLastError(error
);
3540 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3543 for (; urlcache_enum_hash_tables(pHeader
, &pEntryHandle
->hash_table_idx
, &pHashTableEntry
);
3544 pEntryHandle
->hash_table_idx
++, pEntryHandle
->hash_entry_idx
= 0)
3546 const struct hash_entry
*pHashEntry
= NULL
;
3547 for (; urlcache_enum_hash_table_entries(pHeader
, pHashTableEntry
, &pEntryHandle
->hash_entry_idx
, &pHashEntry
);
3548 pEntryHandle
->hash_entry_idx
++)
3550 const entry_url
*pUrlEntry
;
3551 const entry_header
*pEntry
= (const entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
3553 if (pEntry
->signature
!= URL_SIGNATURE
)
3556 pUrlEntry
= (const entry_url
*)pEntry
;
3557 TRACE("Found URL: %s\n",
3558 debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->url_off
));
3559 TRACE("Header info: %s\n",
3560 debugstr_an((LPCSTR
)pUrlEntry
+ pUrlEntry
->header_info_off
,
3561 pUrlEntry
->header_info_size
));
3563 error
= urlcache_copy_entry(
3566 lpNextCacheEntryInfo
,
3567 lpdwNextCacheEntryInfoBufferSize
,
3570 if (error
!= ERROR_SUCCESS
)
3572 cache_container_unlock_index(pContainer
, pHeader
);
3573 SetLastError(error
);
3576 if(pUrlEntry
->local_name_off
)
3577 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->local_name_off
));
3579 /* increment the current index so that next time the function
3580 * is called the next entry is returned */
3581 pEntryHandle
->hash_entry_idx
++;
3582 cache_container_unlock_index(pContainer
, pHeader
);
3587 cache_container_unlock_index(pContainer
, pHeader
);
3590 SetLastError(ERROR_NO_MORE_ITEMS
);
3594 /***********************************************************************
3595 * FindNextUrlCacheEntryA (WININET.@)
3597 BOOL WINAPI
FindNextUrlCacheEntryA(
3599 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo
,
3600 LPDWORD lpdwNextCacheEntryInfoBufferSize
)
3602 TRACE("(%p, %p, %p)\n", hEnumHandle
, lpNextCacheEntryInfo
, lpdwNextCacheEntryInfoBufferSize
);
3604 return urlcache_find_next_entry(hEnumHandle
, lpNextCacheEntryInfo
,
3605 lpdwNextCacheEntryInfoBufferSize
, FALSE
/* not UNICODE */);
3608 /***********************************************************************
3609 * FindNextUrlCacheEntryW (WININET.@)
3611 BOOL WINAPI
FindNextUrlCacheEntryW(
3613 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo
,
3614 LPDWORD lpdwNextCacheEntryInfoBufferSize
3617 TRACE("(%p, %p, %p)\n", hEnumHandle
, lpNextCacheEntryInfo
, lpdwNextCacheEntryInfoBufferSize
);
3619 return urlcache_find_next_entry(hEnumHandle
,
3620 (LPINTERNET_CACHE_ENTRY_INFOA
)lpNextCacheEntryInfo
,
3621 lpdwNextCacheEntryInfoBufferSize
, TRUE
/* UNICODE */);
3624 /***********************************************************************
3625 * FindCloseUrlCache (WININET.@)
3627 BOOL WINAPI
FindCloseUrlCache(HANDLE hEnumHandle
)
3629 find_handle
*pEntryHandle
= (find_handle
*)hEnumHandle
;
3631 TRACE("(%p)\n", hEnumHandle
);
3633 if (!pEntryHandle
|| pEntryHandle
->magic
!= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
)
3635 SetLastError(ERROR_INVALID_HANDLE
);
3639 pEntryHandle
->magic
= 0;
3640 heap_free(pEntryHandle
->url_search_pattern
);
3641 heap_free(pEntryHandle
);
3645 HANDLE WINAPI
FindFirstUrlCacheGroup( DWORD dwFlags
, DWORD dwFilter
, LPVOID lpSearchCondition
,
3646 DWORD dwSearchCondition
, GROUPID
* lpGroupId
, LPVOID lpReserved
)
3648 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags
, dwFilter
, lpSearchCondition
,
3649 dwSearchCondition
, lpGroupId
, lpReserved
);
3653 BOOL WINAPI
FindNextUrlCacheEntryExA(
3655 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
,
3656 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3658 LPDWORD pcbReserved2
,
3662 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
,
3663 lpReserved
, pcbReserved2
, lpReserved3
);
3667 BOOL WINAPI
FindNextUrlCacheEntryExW(
3669 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
,
3670 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3672 LPDWORD pcbReserved2
,
3676 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
,
3677 lpReserved
, pcbReserved2
, lpReserved3
);
3681 BOOL WINAPI
FindNextUrlCacheGroup( HANDLE hFind
, GROUPID
* lpGroupId
, LPVOID lpReserved
)
3683 FIXME("(%p, %p, %p) stub\n", hFind
, lpGroupId
, lpReserved
);
3687 /***********************************************************************
3688 * CreateUrlCacheGroup (WININET.@)
3691 INTERNETAPI GROUPID WINAPI
CreateUrlCacheGroup(DWORD dwFlags
, LPVOID lpReserved
)
3693 FIXME("(0x%08x, %p): stub\n", dwFlags
, lpReserved
);
3697 /***********************************************************************
3698 * DeleteUrlCacheGroup (WININET.@)
3701 BOOL WINAPI
DeleteUrlCacheGroup(GROUPID GroupId
, DWORD dwFlags
, LPVOID lpReserved
)
3703 FIXME("(0x%s, 0x%08x, %p) stub\n",
3704 wine_dbgstr_longlong(GroupId
), dwFlags
, lpReserved
);
3708 /***********************************************************************
3709 * DeleteWpadCacheForNetworks (WININET.@)
3710 * Undocumented, added in IE8
3712 BOOL WINAPI
DeleteWpadCacheForNetworks(DWORD unk1
)
3714 FIXME("(%d) stub\n", unk1
);
3718 /***********************************************************************
3719 * SetUrlCacheEntryGroupA (WININET.@)
3722 BOOL WINAPI
SetUrlCacheEntryGroupA(LPCSTR lpszUrlName
, DWORD dwFlags
,
3723 GROUPID GroupId
, LPBYTE pbGroupAttributes
, DWORD cbGroupAttributes
,
3726 FIXME("(%s, 0x%08x, 0x%s, %p, 0x%08x, %p) stub\n",
3727 debugstr_a(lpszUrlName
), dwFlags
, wine_dbgstr_longlong(GroupId
),
3728 pbGroupAttributes
, cbGroupAttributes
, lpReserved
);
3729 SetLastError(ERROR_FILE_NOT_FOUND
);
3733 /***********************************************************************
3734 * SetUrlCacheEntryGroupW (WININET.@)
3737 BOOL WINAPI
SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName
, DWORD dwFlags
,
3738 GROUPID GroupId
, LPBYTE pbGroupAttributes
, DWORD cbGroupAttributes
,
3741 FIXME("(%s, 0x%08x, 0x%s, %p, 0x%08x, %p) stub\n",
3742 debugstr_w(lpszUrlName
), dwFlags
, wine_dbgstr_longlong(GroupId
),
3743 pbGroupAttributes
, cbGroupAttributes
, lpReserved
);
3744 SetLastError(ERROR_FILE_NOT_FOUND
);
3748 static cache_container
*find_container(DWORD flags
)
3750 cache_container
*container
;
3752 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
3754 switch (flags
& (CACHE_CONFIG_CONTENT_PATHS_FC
| CACHE_CONFIG_COOKIES_PATHS_FC
| CACHE_CONFIG_HISTORY_PATHS_FC
))
3757 case CACHE_CONFIG_CONTENT_PATHS_FC
:
3758 if (container
->default_entry_type
== NORMAL_CACHE_ENTRY
)
3762 case CACHE_CONFIG_COOKIES_PATHS_FC
:
3763 if (container
->default_entry_type
== COOKIE_CACHE_ENTRY
)
3767 case CACHE_CONFIG_HISTORY_PATHS_FC
:
3768 if (container
->default_entry_type
== URLHISTORY_CACHE_ENTRY
)
3773 FIXME("flags %08x not handled\n", flags
);
3781 /***********************************************************************
3782 * GetUrlCacheConfigInfoW (WININET.@)
3784 BOOL WINAPI
GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW info
, LPDWORD size
, DWORD flags
)
3786 cache_container
*container
;
3789 FIXME("(%p, %p, %x): semi-stub\n", info
, size
, flags
);
3791 if (!info
|| !(container
= find_container(flags
)))
3793 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
3797 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
3798 if (error
!= ERROR_SUCCESS
)
3800 INTERNET_SetLastError(error
);
3804 info
->dwContainer
= 0;
3805 info
->dwQuota
= FILE_SIZE(MAX_BLOCK_NO
) / 1024;
3806 info
->dwReserved4
= 0;
3807 info
->fPerUser
= TRUE
;
3808 info
->dwSyncMode
= 0;
3809 info
->dwNumCachePaths
= 1;
3810 info
->dwNormalUsage
= 0;
3811 info
->dwExemptUsage
= 0;
3812 info
->u
.s
.dwCacheSize
= container
->file_size
/ 1024;
3813 lstrcpynW(info
->u
.s
.CachePath
, container
->path
, MAX_PATH
);
3815 cache_container_close_index(container
);
3817 TRACE("CachePath %s\n", debugstr_w(info
->u
.s
.CachePath
));
3822 /***********************************************************************
3823 * GetUrlCacheConfigInfoA (WININET.@)
3825 BOOL WINAPI
GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA info
, LPDWORD size
, DWORD flags
)
3827 INTERNET_CACHE_CONFIG_INFOW infoW
;
3829 TRACE("(%p, %p, %x)\n", info
, size
, flags
);
3833 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
3837 infoW
.dwStructSize
= sizeof(infoW
);
3838 if (!GetUrlCacheConfigInfoW(&infoW
, size
, flags
))
3841 info
->dwContainer
= infoW
.dwContainer
;
3842 info
->dwQuota
= infoW
.dwQuota
;
3843 info
->dwReserved4
= infoW
.dwReserved4
;
3844 info
->fPerUser
= infoW
.fPerUser
;
3845 info
->dwSyncMode
= infoW
.dwSyncMode
;
3846 info
->dwNumCachePaths
= infoW
.dwNumCachePaths
;
3847 info
->dwNormalUsage
= infoW
.dwNormalUsage
;
3848 info
->dwExemptUsage
= infoW
.dwExemptUsage
;
3849 info
->u
.s
.dwCacheSize
= infoW
.u
.s
.dwCacheSize
;
3850 WideCharToMultiByte(CP_ACP
, 0, infoW
.u
.s
.CachePath
, -1, info
->u
.s
.CachePath
, MAX_PATH
, NULL
, NULL
);
3855 BOOL WINAPI
GetUrlCacheGroupAttributeA( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3856 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo
,
3857 LPDWORD lpdwGroupInfo
, LPVOID lpReserved
)
3859 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3860 wine_dbgstr_longlong(gid
), dwFlags
, dwAttributes
, lpGroupInfo
,
3861 lpdwGroupInfo
, lpReserved
);
3865 BOOL WINAPI
GetUrlCacheGroupAttributeW( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3866 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo
,
3867 LPDWORD lpdwGroupInfo
, LPVOID lpReserved
)
3869 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3870 wine_dbgstr_longlong(gid
), dwFlags
, dwAttributes
, lpGroupInfo
,
3871 lpdwGroupInfo
, lpReserved
);
3875 BOOL WINAPI
SetUrlCacheGroupAttributeA( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3876 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo
, LPVOID lpReserved
)
3878 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p) stub\n",
3879 wine_dbgstr_longlong(gid
), dwFlags
, dwAttributes
, lpGroupInfo
, lpReserved
);
3883 BOOL WINAPI
SetUrlCacheGroupAttributeW( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3884 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo
, LPVOID lpReserved
)
3886 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p) stub\n",
3887 wine_dbgstr_longlong(gid
), dwFlags
, dwAttributes
, lpGroupInfo
, lpReserved
);
3891 BOOL WINAPI
SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo
, DWORD dwFieldControl
)
3893 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo
, dwFieldControl
);
3897 BOOL WINAPI
SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo
, DWORD dwFieldControl
)
3899 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo
, dwFieldControl
);
3903 /***********************************************************************
3904 * DeleteIE3Cache (WININET.@)
3906 * Deletes the files used by the IE3 URL caching system.
3909 * hWnd [I] A dummy window.
3910 * hInst [I] Instance of process calling the function.
3911 * lpszCmdLine [I] Options used by function.
3912 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3914 DWORD WINAPI
DeleteIE3Cache(HWND hWnd
, HINSTANCE hInst
, LPSTR lpszCmdLine
, int nCmdShow
)
3916 FIXME("(%p, %p, %s, %d)\n", hWnd
, hInst
, debugstr_a(lpszCmdLine
), nCmdShow
);
3920 static BOOL
urlcache_entry_is_expired(const entry_url
*pUrlEntry
,
3921 FILETIME
*pftLastModified
)
3924 FILETIME now
, expired
;
3926 *pftLastModified
= pUrlEntry
->modification_time
;
3927 GetSystemTimeAsFileTime(&now
);
3928 dos_date_time_to_file_time(pUrlEntry
->expire_date
,
3929 pUrlEntry
->expire_time
, &expired
);
3930 /* If the expired time is 0, it's interpreted as not expired */
3931 if (!expired
.dwLowDateTime
&& !expired
.dwHighDateTime
)
3934 ret
= CompareFileTime(&expired
, &now
) < 0;
3938 /***********************************************************************
3939 * IsUrlCacheEntryExpiredA (WININET.@)
3943 * dwFlags [I] Unknown
3944 * pftLastModified [O] Last modified time
3946 BOOL WINAPI
IsUrlCacheEntryExpiredA(LPCSTR url
, DWORD dwFlags
, FILETIME
* pftLastModified
)
3948 urlcache_header
*pHeader
;
3949 struct hash_entry
*pHashEntry
;
3950 const entry_header
*pEntry
;
3951 const entry_url
* pUrlEntry
;
3952 cache_container
*pContainer
;
3955 TRACE("(%s, %08x, %p)\n", debugstr_a(url
), dwFlags
, pftLastModified
);
3957 if (!url
|| !pftLastModified
)
3960 FIXME("unknown flags 0x%08x\n", dwFlags
);
3962 /* Any error implies that the URL is expired, i.e. not in the cache */
3963 if (cache_containers_find(url
, &pContainer
))
3965 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3969 if (cache_container_open_index(pContainer
, MIN_BLOCK_NO
))
3971 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3975 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3977 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3981 if (!urlcache_find_hash_entry(pHeader
, url
, &pHashEntry
))
3983 cache_container_unlock_index(pContainer
, pHeader
);
3984 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3985 TRACE("entry %s not found!\n", url
);
3989 pEntry
= (const entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
3990 if (pEntry
->signature
!= URL_SIGNATURE
)
3992 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR
)&pEntry
->signature
, sizeof(DWORD
)));
3993 cache_container_unlock_index(pContainer
, pHeader
);
3994 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3998 pUrlEntry
= (const entry_url
*)pEntry
;
3999 expired
= urlcache_entry_is_expired(pUrlEntry
, pftLastModified
);
4001 cache_container_unlock_index(pContainer
, pHeader
);
4006 /***********************************************************************
4007 * IsUrlCacheEntryExpiredW (WININET.@)
4011 * dwFlags [I] Unknown
4012 * pftLastModified [O] Last modified time
4014 BOOL WINAPI
IsUrlCacheEntryExpiredW(LPCWSTR url
, DWORD dwFlags
, FILETIME
* pftLastModified
)
4019 if(!urlcache_encode_url_alloc(url
, &encoded_url
))
4022 ret
= IsUrlCacheEntryExpiredA(encoded_url
, dwFlags
, pftLastModified
);
4023 heap_free(encoded_url
);
4027 /***********************************************************************
4028 * GetDiskInfoA (WININET.@)
4030 BOOL WINAPI
GetDiskInfoA(PCSTR path
, PDWORD cluster_size
, PDWORDLONG free
, PDWORDLONG total
)
4033 ULARGE_INTEGER bytes_free
, bytes_total
;
4035 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path
), cluster_size
, free
, total
);
4039 SetLastError(ERROR_INVALID_PARAMETER
);
4043 if ((ret
= GetDiskFreeSpaceExA(path
, NULL
, &bytes_total
, &bytes_free
)))
4045 if (cluster_size
) *cluster_size
= 1;
4046 if (free
) *free
= bytes_free
.QuadPart
;
4047 if (total
) *total
= bytes_total
.QuadPart
;
4052 /***********************************************************************
4053 * RegisterUrlCacheNotification (WININET.@)
4055 DWORD WINAPI
RegisterUrlCacheNotification(LPVOID a
, DWORD b
, DWORD c
, DWORD d
, DWORD e
, DWORD f
)
4057 FIXME("(%p %x %x %x %x %x)\n", a
, b
, c
, d
, e
, f
);
4061 /***********************************************************************
4062 * IncrementUrlCacheHeaderData (WININET.@)
4064 BOOL WINAPI
IncrementUrlCacheHeaderData(DWORD index
, LPDWORD data
)
4066 FIXME("(%u, %p)\n", index
, data
);
4070 /***********************************************************************
4071 * RunOnceUrlCache (WININET.@)
4074 DWORD WINAPI
RunOnceUrlCache(HWND hwnd
, HINSTANCE hinst
, LPSTR cmd
, int cmdshow
)
4076 FIXME("(%p, %p, %s, %d): stub\n", hwnd
, hinst
, debugstr_a(cmd
), cmdshow
);
4080 BOOL
init_urlcache(void)
4082 dll_unload_event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
4083 if(!dll_unload_event
)
4086 free_cache_running
= CreateSemaphoreW(NULL
, 1, 1, NULL
);
4087 if(!free_cache_running
) {
4088 CloseHandle(dll_unload_event
);
4092 cache_containers_init();
4096 void free_urlcache(void)
4098 SetEvent(dll_unload_event
);
4099 WaitForSingleObject(free_cache_running
, INFINITE
);
4100 ReleaseSemaphore(free_cache_running
, 1, NULL
);
4101 CloseHandle(free_cache_running
);
4102 CloseHandle(dll_unload_event
);
4104 cache_containers_free();
4107 /***********************************************************************
4108 * LoadUrlCacheContent (WININET.@)
4110 BOOL WINAPI
LoadUrlCacheContent(void)