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
46 #include "wine/debug.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(wininet
);
50 static const char urlcache_ver_prefix
[] = "WINE URLCache Ver ";
51 static const char urlcache_ver
[] = "0.2012001";
53 #define ENTRY_START_OFFSET 0x4000
55 #define MAX_DIR_NO 0x20
57 #define HASHTABLE_SIZE 448
58 #define HASHTABLE_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
59 #define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
60 #define ALLOCATION_TABLE_OFFSET 0x250
61 #define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
62 #define MIN_BLOCK_NO 0x80
63 #define MAX_BLOCK_NO (ALLOCATION_TABLE_SIZE * CHAR_BIT)
64 #define FILE_SIZE(blocks) ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
66 #define HASHTABLE_URL 0
67 #define HASHTABLE_DEL 1
68 #define HASHTABLE_LOCK 2
69 #define HASHTABLE_FREE 3
70 #define HASHTABLE_REDR 5
71 #define HASHTABLE_FLAG_BITS 6
73 #define PENDING_DELETE_CACHE_ENTRY 0x00400000
74 #define INSTALLED_CACHE_ENTRY 0x10000000
75 #define GET_INSTALLED_ENTRY 0x200
76 #define CACHE_CONTAINER_NO_SUBDIR 0xFE
78 #define CACHE_HEADER_DATA_ROOT_LEAK_OFFSET 0x16
80 #define FILETIME_SECOND 10000000
82 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
83 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
84 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
85 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
86 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
88 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
90 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
95 DWORD blocks_used
; /* number of 128byte blocks used by this entry */
101 FILETIME modification_time
;
102 FILETIME access_time
;
103 WORD expire_date
; /* expire date in dos format */
104 WORD expire_time
; /* expire time in dos format */
105 DWORD unk1
; /* usually zero */
106 ULARGE_INTEGER size
; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
107 DWORD unk2
; /* usually zero */
108 DWORD exempt_delta
; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
109 DWORD unk3
; /* usually 0x60 */
110 DWORD url_off
; /* offset of start of url from start of entry */
111 BYTE cache_dir
; /* index of cache directory this url is stored in */
112 BYTE unk4
; /* usually zero */
113 WORD unk5
; /* usually 0x1010 */
114 DWORD local_name_off
; /* offset of start of local filename from start of entry */
115 DWORD cache_entry_type
; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
116 DWORD header_info_off
; /* offset of start of header info from start of entry */
117 DWORD header_info_size
;
118 DWORD file_extension_off
; /* offset of start of file extension from start of entry */
119 WORD sync_date
; /* last sync date in dos format */
120 WORD sync_time
; /* last sync time in dos format */
121 DWORD hit_rate
; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
122 DWORD use_count
; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
125 DWORD unk7
; /* usually zero */
126 DWORD unk8
; /* usually zero */
127 /* packing to dword align start of next field */
128 /* CHAR szSourceUrlName[]; (url) */
129 /* packing to dword align start of next field */
130 /* CHAR szLocalFileName[]; (local file name excluding path) */
131 /* packing to dword align start of next field */
132 /* CHAR szHeaderInfo[]; (header info) */
146 struct hash_entry hash_table
[HASHTABLE_SIZE
];
153 DWORD hash_table_off
;
154 DWORD capacity_in_blocks
;
157 ULARGE_INTEGER cache_limit
;
158 ULARGE_INTEGER cache_usage
;
159 ULARGE_INTEGER exempt_usage
;
161 struct _directory_data
164 char name
[DIR_LENGTH
];
165 } directory_data
[MAX_DIR_NO
];
167 BYTE allocation_table
[ALLOCATION_TABLE_SIZE
];
178 struct list entry
; /* part of a list */
179 char *cache_prefix
; /* string that has to be prefixed for this container to be used */
180 LPWSTR path
; /* path to url container directory */
181 HANDLE mapping
; /* handle of file mapping */
182 DWORD file_size
; /* size of file when mapping was opened */
183 HANDLE mutex
; /* handle of mutex */
184 DWORD default_entry_type
;
190 char *url_search_pattern
;
192 DWORD hash_table_idx
;
193 DWORD hash_entry_idx
;
196 /* List of all containers available */
197 static struct list UrlContainers
= LIST_INIT(UrlContainers
);
199 static inline char *strdupWtoUTF8(const WCHAR
*str
)
204 DWORD size
= WideCharToMultiByte(CP_UTF8
, 0, str
, -1, NULL
, 0, NULL
, NULL
);
207 WideCharToMultiByte(CP_UTF8
, 0, str
, -1, ret
, size
, NULL
, NULL
);
213 /***********************************************************************
214 * urlcache_block_is_free (Internal)
216 * Is the specified block number free?
223 static inline BYTE
urlcache_block_is_free(BYTE
*allocation_table
, DWORD block_number
)
225 BYTE mask
= 1 << (block_number
%CHAR_BIT
);
226 return (allocation_table
[block_number
/CHAR_BIT
] & mask
) == 0;
229 /***********************************************************************
230 * urlcache_block_free (Internal)
232 * Marks the specified block as free
235 * this function is not updating used blocks count
241 static inline void urlcache_block_free(BYTE
*allocation_table
, DWORD block_number
)
243 BYTE mask
= ~(1 << (block_number
%CHAR_BIT
));
244 allocation_table
[block_number
/CHAR_BIT
] &= mask
;
247 /***********************************************************************
248 * urlcache_block_alloc (Internal)
250 * Marks the specified block as allocated
253 * this function is not updating used blocks count
259 static inline void urlcache_block_alloc(BYTE
*allocation_table
, DWORD block_number
)
261 BYTE mask
= 1 << (block_number
%CHAR_BIT
);
262 allocation_table
[block_number
/CHAR_BIT
] |= mask
;
265 /***********************************************************************
266 * urlcache_entry_alloc (Internal)
268 * Finds and allocates the first block of free space big enough and
269 * sets entry to point to it.
272 * ERROR_SUCCESS when free memory block was found
273 * Any other Win32 error code if the entry could not be added
276 static DWORD
urlcache_entry_alloc(urlcache_header
*header
, DWORD blocks_needed
, entry_header
**entry
)
278 DWORD block
, block_size
;
280 for(block
=0; block
<header
->capacity_in_blocks
; block
+=block_size
+1)
283 while(block_size
<blocks_needed
&& block_size
+block
<header
->capacity_in_blocks
284 && urlcache_block_is_free(header
->allocation_table
, block
+block_size
))
287 if(block_size
== blocks_needed
)
291 TRACE("Found free blocks starting at no. %ld (0x%lx)\n", block
, ENTRY_START_OFFSET
+block
*BLOCKSIZE
);
293 for(index
=0; index
<blocks_needed
; index
++)
294 urlcache_block_alloc(header
->allocation_table
, block
+index
);
296 *entry
= (entry_header
*)((BYTE
*)header
+ENTRY_START_OFFSET
+block
*BLOCKSIZE
);
297 for(index
=0; index
<blocks_needed
*BLOCKSIZE
/sizeof(DWORD
); index
++)
298 ((DWORD
*)*entry
)[index
] = 0xdeadbeef;
299 (*entry
)->blocks_used
= blocks_needed
;
301 header
->blocks_in_use
+= blocks_needed
;
302 return ERROR_SUCCESS
;
306 return ERROR_HANDLE_DISK_FULL
;
309 /***********************************************************************
310 * urlcache_entry_free (Internal)
312 * Deletes the specified entry and frees the space allocated to it
315 * TRUE if it succeeded
319 static BOOL
urlcache_entry_free(urlcache_header
*header
, entry_header
*entry
)
321 DWORD start_block
, block
;
323 /* update allocation table */
324 start_block
= ((DWORD
)((BYTE
*)entry
- (BYTE
*)header
) - ENTRY_START_OFFSET
) / BLOCKSIZE
;
325 for(block
= start_block
; block
< start_block
+entry
->blocks_used
; block
++)
326 urlcache_block_free(header
->allocation_table
, block
);
328 header
->blocks_in_use
-= entry
->blocks_used
;
332 /***********************************************************************
333 * urlcache_create_hash_table (Internal)
335 * Creates a new hash table in free space and adds it to the chain of existing
339 * ERROR_SUCCESS if the hash table was created
340 * ERROR_DISK_FULL if the hash table could not be created
343 static DWORD
urlcache_create_hash_table(urlcache_header
*header
, entry_hash_table
*hash_table_prev
, entry_hash_table
**hash_table
)
345 DWORD dwOffset
, error
;
348 if((error
= urlcache_entry_alloc(header
, 0x20, (entry_header
**)hash_table
)) != ERROR_SUCCESS
)
351 dwOffset
= (BYTE
*)*hash_table
-(BYTE
*)header
;
354 hash_table_prev
->next
= dwOffset
;
356 header
->hash_table_off
= dwOffset
;
358 (*hash_table
)->header
.signature
= HASH_SIGNATURE
;
359 (*hash_table
)->next
= 0;
360 (*hash_table
)->id
= hash_table_prev
? hash_table_prev
->id
+1 : 0;
361 for(i
= 0; i
< HASHTABLE_SIZE
; i
++) {
362 (*hash_table
)->hash_table
[i
].offset
= HASHTABLE_FREE
;
363 (*hash_table
)->hash_table
[i
].key
= HASHTABLE_FREE
;
365 return ERROR_SUCCESS
;
368 /***********************************************************************
369 * cache_container_create_object_name (Internal)
371 * Converts a path to a name suitable for use as a Win32 object name.
372 * Replaces '\\' characters in-place with the specified character
373 * (usually '_' or '!')
379 static void cache_container_create_object_name(LPWSTR lpszPath
, WCHAR replace
)
381 for (; *lpszPath
; lpszPath
++)
383 if (*lpszPath
== '\\')
388 /* Caller must hold container lock */
389 static HANDLE
cache_container_map_index(HANDLE file
, const WCHAR
*path
, DWORD size
, BOOL
*validate
)
391 WCHAR mapping_name
[MAX_PATH
];
394 wsprintfW(mapping_name
, L
"%sindex.dat_%lu", path
, size
);
395 cache_container_create_object_name(mapping_name
, '_');
397 mapping
= OpenFileMappingW(FILE_MAP_WRITE
, FALSE
, mapping_name
);
399 if(validate
) *validate
= FALSE
;
403 if(validate
) *validate
= TRUE
;
404 return CreateFileMappingW(file
, NULL
, PAGE_READWRITE
, 0, 0, mapping_name
);
407 /* Caller must hold container lock */
408 static DWORD
cache_container_set_size(cache_container
*container
, HANDLE file
, DWORD blocks_no
)
410 DWORD file_size
= FILE_SIZE(blocks_no
);
411 WCHAR dir_path
[MAX_PATH
], *dir_name
;
412 entry_hash_table
*hashtable_entry
;
413 urlcache_header
*header
;
419 if(SetFilePointer(file
, file_size
, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
420 return GetLastError();
422 if(!SetEndOfFile(file
))
423 return GetLastError();
425 mapping
= cache_container_map_index(file
, container
->path
, file_size
, NULL
);
427 return GetLastError();
429 header
= MapViewOfFile(mapping
, FILE_MAP_WRITE
, 0, 0, 0);
431 CloseHandle(mapping
);
432 return GetLastError();
435 if(blocks_no
!= MIN_BLOCK_NO
) {
436 if(file_size
> header
->size
)
437 memset((char*)header
+header
->size
, 0, file_size
-header
->size
);
438 header
->size
= file_size
;
439 header
->capacity_in_blocks
= blocks_no
;
441 UnmapViewOfFile(header
);
442 CloseHandle(container
->mapping
);
443 container
->mapping
= mapping
;
444 container
->file_size
= file_size
;
445 return ERROR_SUCCESS
;
448 memset(header
, 0, file_size
);
449 /* First set some constants and defaults in the header */
450 memcpy(header
->signature
, urlcache_ver_prefix
, sizeof(urlcache_ver_prefix
)-1);
451 memcpy(header
->signature
+sizeof(urlcache_ver_prefix
)-1, urlcache_ver
, sizeof(urlcache_ver
)-1);
452 header
->size
= file_size
;
453 header
->capacity_in_blocks
= blocks_no
;
454 /* 127MB - taken from default for Windows 2000 */
455 header
->cache_limit
.QuadPart
= 0x07ff5400;
456 /* Copied from a Windows 2000 cache index */
457 header
->dirs_no
= container
->default_entry_type
==NORMAL_CACHE_ENTRY
? 4 : 0;
459 /* If the registry has a cache size set, use the registry value */
460 if(RegOpenKeyW(HKEY_CURRENT_USER
,
461 L
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Cache\\Content", &key
) == ERROR_SUCCESS
) {
462 DWORD dw
, len
= sizeof(dw
), keytype
;
464 if(RegQueryValueExW(key
, L
"CacheLimit", NULL
, &keytype
, (BYTE
*)&dw
, &len
) == ERROR_SUCCESS
&&
465 keytype
== REG_DWORD
)
466 header
->cache_limit
.QuadPart
= (ULONGLONG
)dw
* 1024;
470 urlcache_create_hash_table(header
, NULL
, &hashtable_entry
);
472 /* Last step - create the directories */
473 lstrcpyW(dir_path
, container
->path
);
474 dir_name
= dir_path
+ lstrlenW(dir_path
);
477 GetSystemTimeAsFileTime(&ft
);
479 for(i
=0; i
<header
->dirs_no
; ++i
) {
480 header
->directory_data
[i
].files_no
= 0;
482 ULONGLONG n
= ft
.dwHighDateTime
;
485 /* Generate a file name to attempt to create.
486 * This algorithm will create what will appear
487 * to be random and unrelated directory names
488 * of up to 9 characters in length.
491 n
+= ft
.dwLowDateTime
;
492 n
^= ((ULONGLONG
) i
<< 56) | ((ULONGLONG
) j
<< 48);
494 for(k
= 0; k
< 8; ++k
) {
497 /* Dividing by a prime greater than 36 helps
498 * with the appearance of randomness
503 dir_name
[k
] = '0' + r
;
505 dir_name
[k
] = 'A' + (r
- 10);
508 if(CreateDirectoryW(dir_path
, 0)) {
509 /* The following is OK because we generated an
510 * 8 character directory name made from characters
511 * [A-Z0-9], which are equivalent for all code
512 * pages and for UTF-16
514 for (k
= 0; k
< 8; ++k
)
515 header
->directory_data
[i
].name
[k
] = dir_name
[k
];
518 /* Give up. The most likely cause of this
519 * is a full disk, but whatever the cause
520 * is, it should be more than apparent that
523 UnmapViewOfFile(header
);
524 CloseHandle(mapping
);
525 return GetLastError();
530 UnmapViewOfFile(header
);
531 CloseHandle(container
->mapping
);
532 container
->mapping
= mapping
;
533 container
->file_size
= file_size
;
534 return ERROR_SUCCESS
;
537 static BOOL
cache_container_is_valid(urlcache_header
*header
, DWORD file_size
)
539 DWORD allocation_size
, count_bits
, i
;
541 if(file_size
< FILE_SIZE(MIN_BLOCK_NO
))
544 if(file_size
!= header
->size
)
547 if (!memcmp(header
->signature
, urlcache_ver_prefix
, sizeof(urlcache_ver_prefix
)-1) &&
548 memcmp(header
->signature
+sizeof(urlcache_ver_prefix
)-1, urlcache_ver
, sizeof(urlcache_ver
)-1))
551 if(FILE_SIZE(header
->capacity_in_blocks
) != file_size
)
555 for(i
=0; i
<header
->capacity_in_blocks
/8; i
++) {
556 for(count_bits
= header
->allocation_table
[i
]; count_bits
!=0; count_bits
>>=1) {
561 if(allocation_size
!= header
->blocks_in_use
)
564 for(; i
<ALLOCATION_TABLE_SIZE
; i
++) {
565 if(header
->allocation_table
[i
])
572 /***********************************************************************
573 * cache_container_open_index (Internal)
575 * Opens the index file and saves mapping handle
578 * ERROR_SUCCESS if succeeded
579 * Any other Win32 error code if failed
582 static DWORD
cache_container_open_index(cache_container
*container
, DWORD blocks_no
)
585 WCHAR index_path
[MAX_PATH
];
589 WaitForSingleObject(container
->mutex
, INFINITE
);
591 if(container
->mapping
) {
592 ReleaseMutex(container
->mutex
);
593 return ERROR_SUCCESS
;
596 lstrcpyW(index_path
, container
->path
);
597 lstrcatW(index_path
, L
"index.dat");
599 file
= CreateFileW(index_path
, GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_ALWAYS
, 0, NULL
);
600 if(file
== INVALID_HANDLE_VALUE
) {
601 /* Maybe the directory wasn't there? Try to create it */
602 if(CreateDirectoryW(container
->path
, 0))
603 file
= CreateFileW(index_path
, GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_ALWAYS
, 0, NULL
);
605 if(file
== INVALID_HANDLE_VALUE
) {
606 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(index_path
));
607 ReleaseMutex(container
->mutex
);
608 return GetLastError();
611 file_size
= GetFileSize(file
, NULL
);
612 if(file_size
== INVALID_FILE_SIZE
) {
614 ReleaseMutex(container
->mutex
);
615 return GetLastError();
618 if(blocks_no
< MIN_BLOCK_NO
)
619 blocks_no
= MIN_BLOCK_NO
;
620 else if(blocks_no
> MAX_BLOCK_NO
)
621 blocks_no
= MAX_BLOCK_NO
;
623 if(file_size
< FILE_SIZE(blocks_no
)) {
624 DWORD ret
= cache_container_set_size(container
, file
, blocks_no
);
626 ReleaseMutex(container
->mutex
);
630 container
->file_size
= file_size
;
631 container
->mapping
= cache_container_map_index(file
, container
->path
, file_size
, &validate
);
633 if(container
->mapping
&& validate
) {
634 urlcache_header
*header
= MapViewOfFile(container
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
636 if(header
&& !cache_container_is_valid(header
, file_size
)) {
637 WARN("detected old or broken index.dat file\n");
638 UnmapViewOfFile(header
);
639 FreeUrlCacheSpaceW(container
->path
, 100, 0);
641 UnmapViewOfFile(header
);
643 CloseHandle(container
->mapping
);
644 container
->mapping
= NULL
;
648 if(!container
->mapping
)
650 ERR("Couldn't create file mapping (error is %ld)\n", GetLastError());
651 ReleaseMutex(container
->mutex
);
652 return GetLastError();
655 ReleaseMutex(container
->mutex
);
656 return ERROR_SUCCESS
;
659 /***********************************************************************
660 * cache_container_close_index (Internal)
668 static void cache_container_close_index(cache_container
*pContainer
)
670 CloseHandle(pContainer
->mapping
);
671 pContainer
->mapping
= NULL
;
674 static BOOL
cache_containers_add(const char *cache_prefix
, LPCWSTR path
,
675 DWORD default_entry_type
, LPWSTR mutex_name
)
677 cache_container
*pContainer
= malloc(sizeof(cache_container
));
678 int cache_prefix_len
= strlen(cache_prefix
);
685 pContainer
->mapping
= NULL
;
686 pContainer
->file_size
= 0;
687 pContainer
->default_entry_type
= default_entry_type
;
689 pContainer
->path
= wcsdup(path
);
690 if (!pContainer
->path
)
696 pContainer
->cache_prefix
= malloc(cache_prefix_len
+1);
697 if (!pContainer
->cache_prefix
)
699 free(pContainer
->path
);
704 memcpy(pContainer
->cache_prefix
, cache_prefix
, cache_prefix_len
+1);
706 CharLowerW(mutex_name
);
707 cache_container_create_object_name(mutex_name
, '!');
709 if ((pContainer
->mutex
= CreateMutexW(NULL
, FALSE
, mutex_name
)) == NULL
)
711 ERR("couldn't create mutex (error is %ld)\n", GetLastError());
712 free(pContainer
->path
);
717 list_add_head(&UrlContainers
, &pContainer
->entry
);
722 static void cache_container_delete_container(cache_container
*pContainer
)
724 list_remove(&pContainer
->entry
);
726 cache_container_close_index(pContainer
);
727 CloseHandle(pContainer
->mutex
);
728 free(pContainer
->path
);
729 free(pContainer
->cache_prefix
);
733 static void cache_containers_init(void)
737 int nFolder
; /* CSIDL_* constant */
738 const WCHAR
*shpath_suffix
; /* suffix on path returned by SHGetSpecialFolderPath */
739 const char *cache_prefix
; /* prefix used to reference the container */
740 DWORD default_entry_type
;
741 } DefaultContainerData
[] =
743 { CSIDL_INTERNET_CACHE
, L
"Content.IE5", "", NORMAL_CACHE_ENTRY
},
744 { CSIDL_HISTORY
, L
"History.IE5", "Visited:", URLHISTORY_CACHE_ENTRY
},
745 { CSIDL_COOKIES
, L
"", "Cookie:", COOKIE_CACHE_ENTRY
},
749 for (i
= 0; i
< ARRAY_SIZE(DefaultContainerData
); i
++)
751 WCHAR wszCachePath
[MAX_PATH
];
752 WCHAR wszMutexName
[MAX_PATH
];
753 int path_len
, suffix_len
;
756 if (!SHGetSpecialFolderPathW(NULL
, wszCachePath
, DefaultContainerData
[i
].nFolder
, TRUE
))
758 ERR("Couldn't get path for default container %lu\n", i
);
761 path_len
= lstrlenW(wszCachePath
);
762 suffix_len
= lstrlenW(DefaultContainerData
[i
].shpath_suffix
);
764 if (path_len
+ suffix_len
+ 2 > MAX_PATH
)
766 ERR("Path too long\n");
770 wszCachePath
[path_len
] = '\\';
771 wszCachePath
[path_len
+1] = 0;
773 lstrcpyW(wszMutexName
, wszCachePath
);
777 memcpy(wszCachePath
+ path_len
+ 1, DefaultContainerData
[i
].shpath_suffix
, (suffix_len
+ 1) * sizeof(WCHAR
));
778 wszCachePath
[path_len
+ suffix_len
+ 1] = '\\';
779 wszCachePath
[path_len
+ suffix_len
+ 2] = '\0';
782 if (!WideCharToMultiByte(CP_ACP
, WC_NO_BEST_FIT_CHARS
, wszCachePath
, path_len
,
783 NULL
, 0, NULL
, &def_char
) || def_char
)
787 /* cannot convert path to ANSI code page */
788 if (!(path_len
= GetShortPathNameW(wszCachePath
, tmp
, MAX_PATH
)) ||
789 !WideCharToMultiByte(CP_ACP
, WC_NO_BEST_FIT_CHARS
, tmp
, path_len
,
790 NULL
, 0, NULL
, &def_char
) || def_char
)
791 ERR("Can't create container path accessible by ANSI functions\n");
793 memcpy(wszCachePath
, tmp
, (path_len
+1)*sizeof(WCHAR
));
796 cache_containers_add(DefaultContainerData
[i
].cache_prefix
, wszCachePath
,
797 DefaultContainerData
[i
].default_entry_type
, wszMutexName
);
801 static void cache_containers_free(void)
803 while(!list_empty(&UrlContainers
))
804 cache_container_delete_container(
805 LIST_ENTRY(list_head(&UrlContainers
), cache_container
, entry
)
809 static DWORD
cache_containers_find(const char *url
, cache_container
**ret
)
811 cache_container
*container
;
813 TRACE("searching for prefix for URL: %s\n", debugstr_a(url
));
816 return ERROR_INVALID_PARAMETER
;
818 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
820 int prefix_len
= strlen(container
->cache_prefix
);
822 if(!strncmp(container
->cache_prefix
, url
, prefix_len
)) {
823 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
825 return ERROR_SUCCESS
;
829 ERR("no container found\n");
830 return ERROR_FILE_NOT_FOUND
;
833 static BOOL
cache_containers_enum(char *search_pattern
, DWORD index
, cache_container
**ret
)
836 cache_container
*container
;
838 TRACE("searching for prefix: %s\n", debugstr_a(search_pattern
));
840 /* non-NULL search pattern only returns one container ever */
841 if (search_pattern
&& index
> 0)
844 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
848 if (!strcmp(container
->cache_prefix
, search_pattern
))
850 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
859 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
869 /***********************************************************************
870 * cache_container_lock_index (Internal)
872 * Locks the index for system-wide exclusive access.
875 * Cache file header if successful
876 * NULL if failed and calls SetLastError.
878 static urlcache_header
* cache_container_lock_index(cache_container
*pContainer
)
882 urlcache_header
* pHeader
;
886 WaitForSingleObject(pContainer
->mutex
, INFINITE
);
888 pIndexData
= MapViewOfFile(pContainer
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
892 ReleaseMutex(pContainer
->mutex
);
893 ERR("Couldn't MapViewOfFile. Error: %ld\n", GetLastError());
896 pHeader
= (urlcache_header
*)pIndexData
;
898 /* file has grown - we need to remap to prevent us getting
899 * access violations when we try and access beyond the end
900 * of the memory mapped file */
901 if (pHeader
->size
!= pContainer
->file_size
)
903 UnmapViewOfFile( pHeader
);
904 cache_container_close_index(pContainer
);
905 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
906 if (error
!= ERROR_SUCCESS
)
908 ReleaseMutex(pContainer
->mutex
);
912 pIndexData
= MapViewOfFile(pContainer
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
916 ReleaseMutex(pContainer
->mutex
);
917 ERR("Couldn't MapViewOfFile. Error: %ld\n", GetLastError());
920 pHeader
= (urlcache_header
*)pIndexData
;
923 TRACE("Signature: %s, file size: %ld bytes\n", pHeader
->signature
, pHeader
->size
);
925 for (index
= 0; index
< pHeader
->dirs_no
; index
++)
927 TRACE("Directory[%d] = \"%.8s\"\n", index
, pHeader
->directory_data
[index
].name
);
933 /***********************************************************************
934 * cache_container_unlock_index (Internal)
937 static BOOL
cache_container_unlock_index(cache_container
*pContainer
, urlcache_header
*pHeader
)
940 ReleaseMutex(pContainer
->mutex
);
941 return UnmapViewOfFile(pHeader
);
944 /***********************************************************************
945 * urlcache_create_file_pathW (Internal)
947 * Copies the full path to the specified buffer given the local file
948 * name and the index of the directory it is in. Always sets value in
949 * lpBufferSize to the required buffer size (in bytes).
952 * TRUE if the buffer was big enough
953 * FALSE if the buffer was too small
956 static BOOL
urlcache_create_file_pathW(
957 const cache_container
*pContainer
,
958 const urlcache_header
*pHeader
,
959 LPCSTR szLocalFileName
,
966 int path_len
= lstrlenW(pContainer
->path
);
967 int file_name_len
= MultiByteToWideChar(CP_ACP
, 0, szLocalFileName
, -1, NULL
, 0);
968 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
&& Directory
>=pHeader
->dirs_no
)
974 nRequired
= (path_len
+ file_name_len
) * sizeof(WCHAR
);
975 if(Directory
!= CACHE_CONTAINER_NO_SUBDIR
)
976 nRequired
+= (DIR_LENGTH
+ 1) * sizeof(WCHAR
);
977 if (trunc_name
&& nRequired
>= *lpBufferSize
)
978 nRequired
= *lpBufferSize
;
979 if (nRequired
<= *lpBufferSize
)
983 memcpy(wszPath
, pContainer
->path
, path_len
* sizeof(WCHAR
));
984 if (Directory
!= CACHE_CONTAINER_NO_SUBDIR
)
986 dir_len
= MultiByteToWideChar(CP_ACP
, 0, pHeader
->directory_data
[Directory
].name
, DIR_LENGTH
, wszPath
+ path_len
, DIR_LENGTH
);
987 wszPath
[dir_len
+ path_len
] = '\\';
994 MultiByteToWideChar(CP_ACP
, 0, szLocalFileName
, -1, wszPath
+ dir_len
+ path_len
,
995 *lpBufferSize
/sizeof(WCHAR
)-dir_len
-path_len
);
996 wszPath
[*lpBufferSize
/sizeof(WCHAR
)-1] = 0;
997 *lpBufferSize
= nRequired
;
1000 *lpBufferSize
= nRequired
;
1004 /***********************************************************************
1005 * urlcache_create_file_pathA (Internal)
1007 * Copies the full path to the specified buffer given the local file
1008 * name and the index of the directory it is in. Always sets value in
1009 * lpBufferSize to the required buffer size.
1012 * TRUE if the buffer was big enough
1013 * FALSE if the buffer was too small
1016 static BOOL
urlcache_create_file_pathA(
1017 const cache_container
*pContainer
,
1018 const urlcache_header
*pHeader
,
1019 LPCSTR szLocalFileName
,
1022 LPLONG lpBufferSize
)
1025 int path_len
, file_name_len
, dir_len
;
1027 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
&& Directory
>=pHeader
->dirs_no
)
1033 path_len
= WideCharToMultiByte(CP_ACP
, 0, pContainer
->path
, -1, NULL
, 0, NULL
, NULL
) - 1;
1034 file_name_len
= strlen(szLocalFileName
) + 1 /* for nul-terminator */;
1035 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
)
1036 dir_len
= DIR_LENGTH
+1;
1040 nRequired
= (path_len
+ dir_len
+ file_name_len
) * sizeof(char);
1041 if (nRequired
<= *lpBufferSize
)
1043 WideCharToMultiByte(CP_ACP
, 0, pContainer
->path
, -1, szPath
, path_len
, NULL
, NULL
);
1045 memcpy(szPath
+path_len
, pHeader
->directory_data
[Directory
].name
, dir_len
-1);
1046 szPath
[path_len
+ dir_len
-1] = '\\';
1048 memcpy(szPath
+ path_len
+ dir_len
, szLocalFileName
, file_name_len
);
1049 *lpBufferSize
= nRequired
;
1052 *lpBufferSize
= nRequired
;
1056 /* Just like FileTimeToDosDateTime, except that it also maps the special
1057 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1059 static void file_time_to_dos_date_time(const FILETIME
*ft
, WORD
*fatdate
,
1062 if (!ft
->dwLowDateTime
&& !ft
->dwHighDateTime
)
1063 *fatdate
= *fattime
= 0;
1065 FileTimeToDosDateTime(ft
, fatdate
, fattime
);
1068 /***********************************************************************
1069 * urlcache_delete_file (Internal)
1071 static DWORD
urlcache_delete_file(const cache_container
*container
,
1072 urlcache_header
*header
, entry_url
*url_entry
)
1074 WIN32_FILE_ATTRIBUTE_DATA attr
;
1075 WCHAR path
[MAX_PATH
];
1076 LONG path_size
= sizeof(path
);
1080 if(!url_entry
->local_name_off
)
1083 if(!urlcache_create_file_pathW(container
, header
,
1084 (LPCSTR
)url_entry
+url_entry
->local_name_off
,
1085 url_entry
->cache_dir
, path
, &path_size
, FALSE
))
1088 if(!GetFileAttributesExW(path
, GetFileExInfoStandard
, &attr
))
1090 file_time_to_dos_date_time(&attr
.ftLastWriteTime
, &date
, &time
);
1091 if(date
!= url_entry
->write_date
|| time
!= url_entry
->write_time
)
1094 err
= (DeleteFileW(path
) ? ERROR_SUCCESS
: GetLastError());
1095 if(err
== ERROR_ACCESS_DENIED
|| err
== ERROR_SHARING_VIOLATION
)
1099 if (url_entry
->cache_dir
< header
->dirs_no
)
1101 if (header
->directory_data
[url_entry
->cache_dir
].files_no
)
1102 header
->directory_data
[url_entry
->cache_dir
].files_no
--;
1104 if (url_entry
->cache_entry_type
& STICKY_CACHE_ENTRY
)
1106 if (url_entry
->size
.QuadPart
< header
->exempt_usage
.QuadPart
)
1107 header
->exempt_usage
.QuadPart
-= url_entry
->size
.QuadPart
;
1109 header
->exempt_usage
.QuadPart
= 0;
1113 if (url_entry
->size
.QuadPart
< header
->cache_usage
.QuadPart
)
1114 header
->cache_usage
.QuadPart
-= url_entry
->size
.QuadPart
;
1116 header
->cache_usage
.QuadPart
= 0;
1119 return ERROR_SUCCESS
;
1122 static BOOL
urlcache_clean_leaked_entries(cache_container
*container
, urlcache_header
*header
)
1127 leak_off
= &header
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
];
1129 entry_url
*url_entry
= (entry_url
*)((LPBYTE
)header
+ *leak_off
);
1131 if(SUCCEEDED(urlcache_delete_file(container
, header
, url_entry
))) {
1132 *leak_off
= url_entry
->exempt_delta
;
1133 urlcache_entry_free(header
, &url_entry
->header
);
1136 leak_off
= &url_entry
->exempt_delta
;
1143 /***********************************************************************
1144 * cache_container_clean_index (Internal)
1146 * This function is meant to make place in index file by removing leaked
1147 * files entries and resizing the file.
1149 * CAUTION: file view may get mapped to new memory
1152 * ERROR_SUCCESS when new memory is available
1153 * error code otherwise
1155 static DWORD
cache_container_clean_index(cache_container
*container
, urlcache_header
**file_view
)
1157 urlcache_header
*header
= *file_view
;
1160 TRACE("(%s %s)\n", debugstr_a(container
->cache_prefix
), debugstr_w(container
->path
));
1162 if(urlcache_clean_leaked_entries(container
, header
))
1163 return ERROR_SUCCESS
;
1165 if(header
->size
>= ALLOCATION_TABLE_SIZE
*8*BLOCKSIZE
+ ENTRY_START_OFFSET
) {
1166 WARN("index file has maximal size\n");
1167 return ERROR_NOT_ENOUGH_MEMORY
;
1170 cache_container_close_index(container
);
1171 ret
= cache_container_open_index(container
, header
->capacity_in_blocks
*2);
1172 if(ret
!= ERROR_SUCCESS
)
1174 header
= MapViewOfFile(container
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
1176 return GetLastError();
1178 UnmapViewOfFile(*file_view
);
1179 *file_view
= header
;
1180 return ERROR_SUCCESS
;
1183 /* Just like DosDateTimeToFileTime, except that it also maps the special
1184 * case of a DOS date/time of (0,0) to a filetime of (0,0).
1186 static void dos_date_time_to_file_time(WORD fatdate
, WORD fattime
,
1189 if (!fatdate
&& !fattime
)
1190 ft
->dwLowDateTime
= ft
->dwHighDateTime
= 0;
1192 DosDateTimeToFileTime(fatdate
, fattime
, ft
);
1195 static int urlcache_decode_url(const char *url
, WCHAR
*decoded_url
, int decoded_len
)
1198 DWORD len
, part_len
;
1201 memset(&uc
, 0, sizeof(uc
));
1202 uc
.dwStructSize
= sizeof(uc
);
1203 uc
.dwHostNameLength
= 1;
1204 if(!InternetCrackUrlA(url
, 0, 0, &uc
))
1205 uc
.nScheme
= INTERNET_SCHEME_UNKNOWN
;
1207 if(uc
.nScheme
!=INTERNET_SCHEME_HTTP
&& uc
.nScheme
!=INTERNET_SCHEME_HTTPS
)
1208 return MultiByteToWideChar(CP_UTF8
, 0, url
, -1, decoded_url
, decoded_len
);
1213 len
= MultiByteToWideChar(CP_UTF8
, 0, url
, uc
.lpszHostName
-url
, decoded_url
, decoded_len
);
1219 host_name
= malloc(uc
.dwHostNameLength
* sizeof(WCHAR
));
1222 if(!MultiByteToWideChar(CP_UTF8
, 0, uc
.lpszHostName
, uc
.dwHostNameLength
,
1223 host_name
, uc
.dwHostNameLength
)) {
1227 part_len
= IdnToUnicode(0, host_name
, uc
.dwHostNameLength
,
1228 decoded_url
? decoded_url
+len
: NULL
, decoded_len
);
1231 SetLastError(ERROR_INTERNET_INVALID_URL
);
1236 decoded_len
-= part_len
;
1238 part_len
= MultiByteToWideChar(CP_UTF8
, 0,
1239 uc
.lpszHostName
+uc
.dwHostNameLength
,
1240 -1, decoded_url
? decoded_url
+len
: NULL
, decoded_len
);
1248 /***********************************************************************
1249 * urlcache_copy_entry (Internal)
1251 * Copies an entry from the cache index file to the Win32 structure
1254 * ERROR_SUCCESS if the buffer was big enough
1255 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1258 static DWORD
urlcache_copy_entry(cache_container
*container
, const urlcache_header
*header
,
1259 INTERNET_CACHE_ENTRY_INFOA
*entry_info
, DWORD
*info_size
, const entry_url
*url_entry
, BOOL unicode
)
1262 DWORD size
= sizeof(*entry_info
);
1264 if(*info_size
>= size
) {
1265 entry_info
->lpHeaderInfo
= NULL
;
1266 entry_info
->lpszFileExtension
= NULL
;
1267 entry_info
->lpszLocalFileName
= NULL
;
1268 entry_info
->lpszSourceUrlName
= NULL
;
1269 entry_info
->CacheEntryType
= url_entry
->cache_entry_type
;
1270 entry_info
->dwExemptDelta
= url_entry
->exempt_delta
;
1271 entry_info
->dwHeaderInfoSize
= url_entry
->header_info_size
;
1272 entry_info
->dwHitRate
= url_entry
->hit_rate
;
1273 entry_info
->dwSizeHigh
= url_entry
->size
.u
.HighPart
;
1274 entry_info
->dwSizeLow
= url_entry
->size
.u
.LowPart
;
1275 entry_info
->dwStructSize
= sizeof(*entry_info
);
1276 entry_info
->dwUseCount
= url_entry
->use_count
;
1277 dos_date_time_to_file_time(url_entry
->expire_date
, url_entry
->expire_time
, &entry_info
->ExpireTime
);
1278 entry_info
->LastAccessTime
= url_entry
->access_time
;
1279 entry_info
->LastModifiedTime
= url_entry
->modification_time
;
1280 dos_date_time_to_file_time(url_entry
->sync_date
, url_entry
->sync_time
, &entry_info
->LastSyncTime
);
1284 url_len
= urlcache_decode_url((const char*)url_entry
+url_entry
->url_off
, NULL
, 0);
1286 url_len
= strlen((LPCSTR
)url_entry
+url_entry
->url_off
) + 1;
1287 size
+= url_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1289 if(*info_size
>= size
) {
1290 DWORD url_size
= url_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1292 entry_info
->lpszSourceUrlName
= (LPSTR
)entry_info
+size
-url_size
;
1294 urlcache_decode_url((const char*)url_entry
+url_entry
->url_off
, (WCHAR
*)entry_info
->lpszSourceUrlName
, url_len
);
1296 memcpy(entry_info
->lpszSourceUrlName
, (LPCSTR
)url_entry
+url_entry
->url_off
, url_size
);
1299 if(size
%4 && size
<*info_size
)
1300 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1301 size
= DWORD_ALIGN(size
);
1303 if(url_entry
->local_name_off
) {
1304 LONG file_name_size
;
1306 file_name
= (LPSTR
)entry_info
+size
;
1307 file_name_size
= *info_size
-size
;
1308 if((unicode
&& urlcache_create_file_pathW(container
, header
, (LPCSTR
)url_entry
+url_entry
->local_name_off
,
1309 url_entry
->cache_dir
, (LPWSTR
)file_name
, &file_name_size
, FALSE
)) ||
1310 (!unicode
&& urlcache_create_file_pathA(container
, header
, (LPCSTR
)url_entry
+url_entry
->local_name_off
,
1311 url_entry
->cache_dir
, file_name
, &file_name_size
))) {
1312 entry_info
->lpszLocalFileName
= file_name
;
1314 size
+= file_name_size
;
1316 if(size
%4 && size
<*info_size
)
1317 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1318 size
= DWORD_ALIGN(size
);
1321 if(url_entry
->header_info_off
) {
1325 header_len
= MultiByteToWideChar(CP_UTF8
, 0, (const char*)url_entry
+url_entry
->header_info_off
,
1326 url_entry
->header_info_size
, NULL
, 0);
1328 header_len
= url_entry
->header_info_size
;
1329 size
+= header_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1331 if(*info_size
>= size
) {
1332 DWORD header_size
= header_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1333 entry_info
->lpHeaderInfo
= (LPBYTE
)entry_info
+size
-header_size
;
1335 MultiByteToWideChar(CP_UTF8
, 0, (const char*)url_entry
+url_entry
->header_info_off
,
1336 url_entry
->header_info_size
, (LPWSTR
)entry_info
->lpHeaderInfo
, header_len
);
1338 memcpy(entry_info
->lpHeaderInfo
, (LPCSTR
)url_entry
+url_entry
->header_info_off
, header_len
);
1340 if(size
%4 && size
<*info_size
)
1341 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1342 size
= DWORD_ALIGN(size
);
1345 if(url_entry
->file_extension_off
) {
1349 ext_len
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)url_entry
+url_entry
->file_extension_off
, -1, NULL
, 0);
1351 ext_len
= strlen((LPCSTR
)url_entry
+url_entry
->file_extension_off
) + 1;
1352 size
+= ext_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1354 if(*info_size
>= size
) {
1355 DWORD ext_size
= ext_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1356 entry_info
->lpszFileExtension
= (LPSTR
)entry_info
+size
-ext_size
;
1358 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)url_entry
+url_entry
->file_extension_off
, -1, (LPWSTR
)entry_info
->lpszFileExtension
, ext_len
);
1360 memcpy(entry_info
->lpszFileExtension
, (LPCSTR
)url_entry
+url_entry
->file_extension_off
, ext_len
*sizeof(CHAR
));
1363 if(size
%4 && size
<*info_size
)
1364 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1365 size
= DWORD_ALIGN(size
);
1368 if(size
> *info_size
) {
1370 return ERROR_INSUFFICIENT_BUFFER
;
1373 return ERROR_SUCCESS
;
1376 /***********************************************************************
1377 * urlcache_set_entry_info (Internal)
1379 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1380 * according to the flags set by field_control.
1383 * ERROR_SUCCESS if the buffer was big enough
1384 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1387 static DWORD
urlcache_set_entry_info(entry_url
*url_entry
, const INTERNET_CACHE_ENTRY_INFOA
*entry_info
, DWORD field_control
)
1389 if (field_control
& CACHE_ENTRY_ACCTIME_FC
)
1390 url_entry
->access_time
= entry_info
->LastAccessTime
;
1391 if (field_control
& CACHE_ENTRY_ATTRIBUTE_FC
)
1392 url_entry
->cache_entry_type
= entry_info
->CacheEntryType
;
1393 if (field_control
& CACHE_ENTRY_EXEMPT_DELTA_FC
)
1394 url_entry
->exempt_delta
= entry_info
->dwExemptDelta
;
1395 if (field_control
& CACHE_ENTRY_EXPTIME_FC
)
1396 file_time_to_dos_date_time(&entry_info
->ExpireTime
, &url_entry
->expire_date
, &url_entry
->expire_time
);
1397 if (field_control
& CACHE_ENTRY_HEADERINFO_FC
)
1398 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1399 if (field_control
& CACHE_ENTRY_HITRATE_FC
)
1400 url_entry
->hit_rate
= entry_info
->dwHitRate
;
1401 if (field_control
& CACHE_ENTRY_MODTIME_FC
)
1402 url_entry
->modification_time
= entry_info
->LastModifiedTime
;
1403 if (field_control
& CACHE_ENTRY_SYNCTIME_FC
)
1404 file_time_to_dos_date_time(&entry_info
->LastAccessTime
, &url_entry
->sync_date
, &url_entry
->sync_time
);
1406 return ERROR_SUCCESS
;
1409 /***********************************************************************
1410 * urlcache_hash_key (Internal)
1412 * Returns the hash key for a given string
1415 * hash key for the string
1418 static DWORD
urlcache_hash_key(LPCSTR lpszKey
)
1420 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1421 * but the algorithm and result are not the same!
1423 static const unsigned char lookupTable
[256] =
1425 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1426 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1427 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1428 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1429 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1430 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1431 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1432 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1433 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1434 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1435 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1436 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1437 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1438 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1439 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1440 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1441 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1442 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1443 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1444 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1445 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1446 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1447 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1448 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1449 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1450 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1451 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1452 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1453 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1454 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1455 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1456 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1461 for (i
= 0; i
< ARRAY_SIZE(key
); i
++)
1462 key
[i
] = lookupTable
[(*lpszKey
+ i
) & 0xFF];
1464 for (lpszKey
++; *lpszKey
; lpszKey
++)
1466 for (i
= 0; i
< ARRAY_SIZE(key
); i
++)
1467 key
[i
] = lookupTable
[*lpszKey
^ key
[i
]];
1470 return *(DWORD
*)key
;
1473 static inline entry_hash_table
* urlcache_get_hash_table(const urlcache_header
*pHeader
, DWORD dwOffset
)
1477 return (entry_hash_table
*)((LPBYTE
)pHeader
+ dwOffset
);
1480 static BOOL
urlcache_find_hash_entry(const urlcache_header
*pHeader
, LPCSTR lpszUrl
, struct hash_entry
**ppHashEntry
)
1482 /* structure of hash table:
1483 * 448 entries divided into 64 blocks
1484 * each block therefore contains a chain of 7 key/offset pairs
1485 * how position in table is calculated:
1486 * 1. the url is hashed in helper function
1487 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1488 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1491 * there can be multiple hash tables in the file and the offset to
1492 * the next one is stored in the header of the hash table
1494 DWORD key
= urlcache_hash_key(lpszUrl
);
1495 DWORD offset
= (key
& (HASHTABLE_NUM_ENTRIES
-1)) * HASHTABLE_BLOCKSIZE
;
1496 entry_hash_table
* pHashEntry
;
1499 key
>>= HASHTABLE_FLAG_BITS
;
1501 for (pHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1502 pHashEntry
; pHashEntry
= urlcache_get_hash_table(pHeader
, pHashEntry
->next
))
1505 if (pHashEntry
->id
!= id
++)
1507 ERR("Error: not right hash table number (%ld) expected %ld\n", pHashEntry
->id
, id
);
1510 /* make sure that it is in fact a hash entry */
1511 if (pHashEntry
->header
.signature
!= HASH_SIGNATURE
)
1513 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&pHashEntry
->header
.signature
);
1517 for (i
= 0; i
< HASHTABLE_BLOCKSIZE
; i
++)
1519 struct hash_entry
*pHashElement
= &pHashEntry
->hash_table
[offset
+ i
];
1520 if (key
== pHashElement
->key
>>HASHTABLE_FLAG_BITS
)
1522 /* FIXME: we should make sure that this is the right element
1523 * before returning and claiming that it is. We can do this
1524 * by doing a simple compare between the URL we were given
1525 * and the URL stored in the entry. However, this assumes
1526 * we know the format of all the entries stored in the
1528 *ppHashEntry
= pHashElement
;
1536 /***********************************************************************
1537 * urlcache_hash_entry_set_flags (Internal)
1539 * Sets special bits in hash key
1545 static void urlcache_hash_entry_set_flags(struct hash_entry
*pHashEntry
, DWORD dwFlag
)
1547 pHashEntry
->key
= (pHashEntry
->key
>> HASHTABLE_FLAG_BITS
<< HASHTABLE_FLAG_BITS
) | dwFlag
;
1550 /***********************************************************************
1551 * urlcache_hash_entry_delete (Internal)
1553 * Searches all the hash tables in the index for the given URL and
1554 * then if found deletes the entry.
1557 * TRUE if the entry was found
1558 * FALSE if the entry could not be found
1561 static BOOL
urlcache_hash_entry_delete(struct hash_entry
*pHashEntry
)
1563 pHashEntry
->key
= HASHTABLE_DEL
;
1567 /***********************************************************************
1568 * urlcache_hash_entry_create (Internal)
1570 * Searches all the hash tables for a free slot based on the offset
1571 * generated from the hash key. If a free slot is found, the offset and
1572 * key are entered into the hash table.
1575 * ERROR_SUCCESS if the entry was added
1576 * Any other Win32 error code if the entry could not be added
1579 static DWORD
urlcache_hash_entry_create(urlcache_header
*pHeader
, LPCSTR lpszUrl
, DWORD dwOffsetEntry
, DWORD dwFieldType
)
1581 /* see urlcache_find_hash_entry for structure of hash tables */
1583 DWORD key
= urlcache_hash_key(lpszUrl
);
1584 DWORD offset
= (key
& (HASHTABLE_NUM_ENTRIES
-1)) * HASHTABLE_BLOCKSIZE
;
1585 entry_hash_table
* pHashEntry
, *pHashPrev
= NULL
;
1589 key
= ((key
>> HASHTABLE_FLAG_BITS
) << HASHTABLE_FLAG_BITS
) + dwFieldType
;
1591 for (pHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1592 pHashEntry
; pHashEntry
= urlcache_get_hash_table(pHeader
, pHashEntry
->next
))
1595 pHashPrev
= pHashEntry
;
1597 if (pHashEntry
->id
!= id
++)
1599 ERR("not right hash table number (%ld) expected %ld\n", pHashEntry
->id
, id
);
1602 /* make sure that it is in fact a hash entry */
1603 if (pHashEntry
->header
.signature
!= HASH_SIGNATURE
)
1605 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&pHashEntry
->header
.signature
);
1609 for (i
= 0; i
< HASHTABLE_BLOCKSIZE
; i
++)
1611 struct hash_entry
*pHashElement
= &pHashEntry
->hash_table
[offset
+ i
];
1612 if (pHashElement
->key
==HASHTABLE_FREE
|| pHashElement
->key
==HASHTABLE_DEL
) /* if the slot is free */
1614 pHashElement
->key
= key
;
1615 pHashElement
->offset
= dwOffsetEntry
;
1616 return ERROR_SUCCESS
;
1620 error
= urlcache_create_hash_table(pHeader
, pHashPrev
, &pHashEntry
);
1621 if (error
!= ERROR_SUCCESS
)
1624 pHashEntry
->hash_table
[offset
].key
= key
;
1625 pHashEntry
->hash_table
[offset
].offset
= dwOffsetEntry
;
1626 return ERROR_SUCCESS
;
1629 /***********************************************************************
1630 * urlcache_enum_hash_tables (Internal)
1632 * Enumerates the hash tables in a container.
1635 * TRUE if an entry was found
1636 * FALSE if there are no more tables to enumerate.
1639 static BOOL
urlcache_enum_hash_tables(const urlcache_header
*pHeader
, DWORD
*id
, entry_hash_table
**ppHashEntry
)
1641 for (*ppHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1642 *ppHashEntry
; *ppHashEntry
= urlcache_get_hash_table(pHeader
, (*ppHashEntry
)->next
))
1644 TRACE("looking at hash table number %ld\n", (*ppHashEntry
)->id
);
1645 if ((*ppHashEntry
)->id
!= *id
)
1647 /* make sure that it is in fact a hash entry */
1648 if ((*ppHashEntry
)->header
.signature
!= HASH_SIGNATURE
)
1650 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&(*ppHashEntry
)->header
.signature
);
1655 TRACE("hash table number %ld found\n", *id
);
1661 /***********************************************************************
1662 * urlcache_enum_hash_table_entries (Internal)
1664 * Enumerates entries in a hash table and returns the next non-free entry.
1667 * TRUE if an entry was found
1668 * FALSE if the hash table is empty or there are no more entries to
1672 static BOOL
urlcache_enum_hash_table_entries(const urlcache_header
*pHeader
, const entry_hash_table
*pHashEntry
,
1673 DWORD
* index
, const struct hash_entry
**ppHashEntry
)
1675 for (; *index
< HASHTABLE_SIZE
; (*index
)++)
1677 if (pHashEntry
->hash_table
[*index
].key
==HASHTABLE_FREE
|| pHashEntry
->hash_table
[*index
].key
==HASHTABLE_DEL
)
1680 *ppHashEntry
= &pHashEntry
->hash_table
[*index
];
1681 TRACE("entry found %ld\n", *index
);
1684 TRACE("no more entries (%ld)\n", *index
);
1688 /***********************************************************************
1689 * cache_container_delete_dir (Internal)
1691 * Erase a directory containing an URL cache.
1694 * TRUE success, FALSE failure/aborted.
1697 static BOOL
cache_container_delete_dir(LPCWSTR lpszPath
)
1700 WCHAR path
[MAX_PATH
+ 1];
1701 SHFILEOPSTRUCTW shfos
;
1704 path_len
= lstrlenW(lpszPath
);
1705 if (path_len
>= MAX_PATH
)
1707 lstrcpyW(path
, lpszPath
);
1708 path
[path_len
+ 1] = 0; /* double-NUL-terminate path */
1711 shfos
.wFunc
= FO_DELETE
;
1714 shfos
.fFlags
= FOF_NOCONFIRMATION
;
1715 shfos
.fAnyOperationsAborted
= FALSE
;
1716 ret
= SHFileOperationW(&shfos
);
1718 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path
), ret
);
1719 return !(ret
|| shfos
.fAnyOperationsAborted
);
1722 /***********************************************************************
1723 * urlcache_hash_entry_is_locked (Internal)
1725 * Checks if entry is locked. Unlocks it if possible.
1727 static BOOL
urlcache_hash_entry_is_locked(struct hash_entry
*hash_entry
, entry_url
*url_entry
)
1730 ULARGE_INTEGER acc_time
, time
;
1732 if ((hash_entry
->key
& ((1<<HASHTABLE_FLAG_BITS
)-1)) != HASHTABLE_LOCK
)
1735 GetSystemTimeAsFileTime(&cur_time
);
1736 time
.u
.LowPart
= cur_time
.dwLowDateTime
;
1737 time
.u
.HighPart
= cur_time
.dwHighDateTime
;
1739 acc_time
.u
.LowPart
= url_entry
->access_time
.dwLowDateTime
;
1740 acc_time
.u
.HighPart
= url_entry
->access_time
.dwHighDateTime
;
1742 time
.QuadPart
-= acc_time
.QuadPart
;
1744 /* check if entry was locked for at least a day */
1745 if(time
.QuadPart
> (ULONGLONG
)24*60*60*FILETIME_SECOND
) {
1746 urlcache_hash_entry_set_flags(hash_entry
, HASHTABLE_URL
);
1747 url_entry
->use_count
= 0;
1754 static BOOL
urlcache_get_entry_info(const char *url
, void *entry_info
,
1755 DWORD
*size
, DWORD flags
, BOOL unicode
)
1757 urlcache_header
*header
;
1758 struct hash_entry
*hash_entry
;
1759 const entry_url
*url_entry
;
1760 cache_container
*container
;
1763 TRACE("(%s, %p, %p, %lx, %x)\n", debugstr_a(url
), entry_info
, size
, flags
, unicode
);
1765 if(flags
& ~GET_INSTALLED_ENTRY
)
1766 FIXME("ignoring unsupported flags: %lx\n", flags
);
1768 error
= cache_containers_find(url
, &container
);
1769 if(error
!= ERROR_SUCCESS
) {
1770 SetLastError(error
);
1774 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
1775 if(error
!= ERROR_SUCCESS
) {
1776 SetLastError(error
);
1780 if(!(header
= cache_container_lock_index(container
)))
1783 if(!urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
1784 cache_container_unlock_index(container
, header
);
1785 WARN("entry %s not found!\n", debugstr_a(url
));
1786 SetLastError(ERROR_FILE_NOT_FOUND
);
1790 url_entry
= (const entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
1791 if(url_entry
->header
.signature
!= URL_SIGNATURE
) {
1792 FIXME("Trying to retrieve entry of unknown format %s\n",
1793 debugstr_an((LPCSTR
)&url_entry
->header
.signature
, sizeof(DWORD
)));
1794 cache_container_unlock_index(container
, header
);
1795 SetLastError(ERROR_FILE_NOT_FOUND
);
1799 TRACE("Found URL: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->url_off
));
1800 TRACE("Header info: %s\n", debugstr_an((LPCSTR
)url_entry
+
1801 url_entry
->header_info_off
, url_entry
->header_info_size
));
1803 if((flags
& GET_INSTALLED_ENTRY
) && !(url_entry
->cache_entry_type
& INSTALLED_CACHE_ENTRY
)) {
1804 cache_container_unlock_index(container
, header
);
1805 SetLastError(ERROR_FILE_NOT_FOUND
);
1813 error
= urlcache_copy_entry(container
, header
, entry_info
, size
, url_entry
, unicode
);
1814 if(error
!= ERROR_SUCCESS
) {
1815 cache_container_unlock_index(container
, header
);
1816 SetLastError(error
);
1819 if(url_entry
->local_name_off
)
1820 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->local_name_off
));
1823 cache_container_unlock_index(container
, header
);
1827 /***********************************************************************
1828 * GetUrlCacheEntryInfoExA (WININET.@)
1831 BOOL WINAPI
GetUrlCacheEntryInfoExA(LPCSTR lpszUrl
,
1832 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1833 LPDWORD lpdwCacheEntryInfoBufSize
, LPSTR lpszReserved
,
1834 LPDWORD lpdwReserved
, LPVOID lpReserved
, DWORD dwFlags
)
1836 if(lpszReserved
!=NULL
|| lpdwReserved
!=NULL
|| lpReserved
!=NULL
) {
1837 ERR("Reserved value was not 0\n");
1838 SetLastError(ERROR_INVALID_PARAMETER
);
1842 return urlcache_get_entry_info(lpszUrl
, lpCacheEntryInfo
,
1843 lpdwCacheEntryInfoBufSize
, dwFlags
, FALSE
);
1846 /***********************************************************************
1847 * GetUrlCacheEntryInfoA (WININET.@)
1850 BOOL WINAPI
GetUrlCacheEntryInfoA(LPCSTR lpszUrlName
,
1851 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1852 LPDWORD lpdwCacheEntryInfoBufferSize
)
1854 return GetUrlCacheEntryInfoExA(lpszUrlName
, lpCacheEntryInfo
,
1855 lpdwCacheEntryInfoBufferSize
, NULL
, NULL
, NULL
, 0);
1858 static int urlcache_encode_url(const WCHAR
*url
, char *encoded_url
, int encoded_len
)
1861 DWORD len
, part_len
;
1864 TRACE("%s\n", debugstr_w(url
));
1866 memset(&uc
, 0, sizeof(uc
));
1867 uc
.dwStructSize
= sizeof(uc
);
1868 uc
.dwHostNameLength
= 1;
1869 if(!InternetCrackUrlW(url
, 0, 0, &uc
))
1870 uc
.nScheme
= INTERNET_SCHEME_UNKNOWN
;
1872 if(uc
.nScheme
!=INTERNET_SCHEME_HTTP
&& uc
.nScheme
!=INTERNET_SCHEME_HTTPS
)
1873 return WideCharToMultiByte(CP_UTF8
, 0, url
, -1, encoded_url
, encoded_len
, NULL
, NULL
);
1875 len
= WideCharToMultiByte(CP_UTF8
, 0, url
, uc
.lpszHostName
-url
,
1876 encoded_url
, encoded_len
, NULL
, NULL
);
1882 part_len
= IdnToAscii(0, uc
.lpszHostName
, uc
.dwHostNameLength
, NULL
, 0);
1884 SetLastError(ERROR_INTERNET_INVALID_URL
);
1888 punycode
= malloc(part_len
* sizeof(WCHAR
));
1892 part_len
= IdnToAscii(0, uc
.lpszHostName
, uc
.dwHostNameLength
, punycode
, part_len
);
1898 part_len
= WideCharToMultiByte(CP_UTF8
, 0, punycode
, part_len
,
1899 encoded_url
? encoded_url
+len
: NULL
, encoded_len
, NULL
, NULL
);
1904 encoded_len
-= part_len
;
1907 part_len
= WideCharToMultiByte(CP_UTF8
, 0, uc
.lpszHostName
+uc
.dwHostNameLength
,
1908 -1, encoded_url
? encoded_url
+len
: NULL
, encoded_len
, NULL
, NULL
);
1913 TRACE("got (%ld)%s\n", len
, debugstr_a(encoded_url
));
1917 static BOOL
urlcache_encode_url_alloc(const WCHAR
*url
, char **encoded_url
)
1922 encoded_len
= urlcache_encode_url(url
, NULL
, 0);
1926 ret
= malloc(encoded_len
);
1930 encoded_len
= urlcache_encode_url(url
, ret
, encoded_len
);
1940 /***********************************************************************
1941 * GetUrlCacheEntryInfoExW (WININET.@)
1944 BOOL WINAPI
GetUrlCacheEntryInfoExW(LPCWSTR lpszUrl
,
1945 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
1946 LPDWORD lpdwCacheEntryInfoBufSize
, LPWSTR lpszReserved
,
1947 LPDWORD lpdwReserved
, LPVOID lpReserved
, DWORD dwFlags
)
1952 if(lpszReserved
!=NULL
|| lpdwReserved
!=NULL
|| lpReserved
!=NULL
) {
1953 ERR("Reserved value was not 0\n");
1954 SetLastError(ERROR_INVALID_PARAMETER
);
1958 /* Ignore GET_INSTALLED_ENTRY flag in unicode version of function */
1959 dwFlags
&= ~GET_INSTALLED_ENTRY
;
1961 if(!urlcache_encode_url_alloc(lpszUrl
, &url
))
1964 ret
= urlcache_get_entry_info(url
, lpCacheEntryInfo
,
1965 lpdwCacheEntryInfoBufSize
, dwFlags
, TRUE
);
1970 /***********************************************************************
1971 * GetUrlCacheEntryInfoW (WININET.@)
1974 BOOL WINAPI
GetUrlCacheEntryInfoW(LPCWSTR lpszUrl
,
1975 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
1976 LPDWORD lpdwCacheEntryInfoBufferSize
)
1978 return GetUrlCacheEntryInfoExW(lpszUrl
, lpCacheEntryInfo
,
1979 lpdwCacheEntryInfoBufferSize
, NULL
, NULL
, NULL
, 0);
1982 /***********************************************************************
1983 * SetUrlCacheEntryInfoA (WININET.@)
1985 BOOL WINAPI
SetUrlCacheEntryInfoA(LPCSTR lpszUrlName
,
1986 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1987 DWORD dwFieldControl
)
1989 urlcache_header
*pHeader
;
1990 struct hash_entry
*pHashEntry
;
1991 entry_header
*pEntry
;
1992 cache_container
*pContainer
;
1995 TRACE("(%s, %p, 0x%08lx)\n", debugstr_a(lpszUrlName
), lpCacheEntryInfo
, dwFieldControl
);
1997 error
= cache_containers_find(lpszUrlName
, &pContainer
);
1998 if (error
!= ERROR_SUCCESS
)
2000 SetLastError(error
);
2004 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
2005 if (error
!= ERROR_SUCCESS
)
2007 SetLastError(error
);
2011 if (!(pHeader
= cache_container_lock_index(pContainer
)))
2014 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
2016 cache_container_unlock_index(pContainer
, pHeader
);
2017 WARN("entry %s not found!\n", debugstr_a(lpszUrlName
));
2018 SetLastError(ERROR_FILE_NOT_FOUND
);
2022 pEntry
= (entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2023 if (pEntry
->signature
!= URL_SIGNATURE
)
2025 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR
)&pEntry
->signature
, sizeof(DWORD
)));
2026 cache_container_unlock_index(pContainer
, pHeader
);
2027 SetLastError(ERROR_FILE_NOT_FOUND
);
2031 urlcache_set_entry_info((entry_url
*)pEntry
, lpCacheEntryInfo
, dwFieldControl
);
2033 cache_container_unlock_index(pContainer
, pHeader
);
2038 /***********************************************************************
2039 * SetUrlCacheEntryInfoW (WININET.@)
2041 BOOL WINAPI
SetUrlCacheEntryInfoW(LPCWSTR lpszUrl
,
2042 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
2043 DWORD dwFieldControl
)
2048 if(!urlcache_encode_url_alloc(lpszUrl
, &url
))
2051 ret
= SetUrlCacheEntryInfoA(url
, (INTERNET_CACHE_ENTRY_INFOA
*)lpCacheEntryInfo
, dwFieldControl
);
2056 static BOOL
urlcache_entry_get_file(const char *url
, void *entry_info
, DWORD
*size
, BOOL unicode
)
2058 urlcache_header
*header
;
2059 struct hash_entry
*hash_entry
;
2060 entry_url
*url_entry
;
2061 cache_container
*container
;
2064 TRACE("(%s, %p, %p, %x)\n", debugstr_a(url
), entry_info
, size
, unicode
);
2066 if(!url
|| !size
|| (!entry_info
&& *size
)) {
2067 SetLastError(ERROR_INVALID_PARAMETER
);
2071 error
= cache_containers_find(url
, &container
);
2072 if(error
!= ERROR_SUCCESS
) {
2073 SetLastError(error
);
2077 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2078 if (error
!= ERROR_SUCCESS
) {
2079 SetLastError(error
);
2083 if (!(header
= cache_container_lock_index(container
)))
2086 if (!urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
2087 cache_container_unlock_index(container
, header
);
2088 TRACE("entry %s not found!\n", debugstr_a(url
));
2089 SetLastError(ERROR_FILE_NOT_FOUND
);
2093 url_entry
= (entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
2094 if(url_entry
->header
.signature
!= URL_SIGNATURE
) {
2095 FIXME("Trying to retrieve entry of unknown format %s\n",
2096 debugstr_an((LPSTR
)&url_entry
->header
.signature
, sizeof(DWORD
)));
2097 cache_container_unlock_index(container
, header
);
2098 SetLastError(ERROR_FILE_NOT_FOUND
);
2102 if(!url_entry
->local_name_off
) {
2103 cache_container_unlock_index(container
, header
);
2104 SetLastError(ERROR_INVALID_DATA
);
2108 TRACE("Found URL: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->url_off
));
2109 TRACE("Header info: %s\n", debugstr_an((LPCSTR
)url_entry
+ url_entry
->header_info_off
,
2110 url_entry
->header_info_size
));
2112 error
= urlcache_copy_entry(container
, header
, entry_info
,
2113 size
, url_entry
, unicode
);
2114 if(error
!= ERROR_SUCCESS
) {
2115 cache_container_unlock_index(container
, header
);
2116 SetLastError(error
);
2119 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->local_name_off
));
2121 url_entry
->hit_rate
++;
2122 url_entry
->use_count
++;
2123 urlcache_hash_entry_set_flags(hash_entry
, HASHTABLE_LOCK
);
2124 GetSystemTimeAsFileTime(&url_entry
->access_time
);
2126 cache_container_unlock_index(container
, header
);
2131 /***********************************************************************
2132 * RetrieveUrlCacheEntryFileA (WININET.@)
2135 BOOL WINAPI
RetrieveUrlCacheEntryFileA(LPCSTR lpszUrlName
,
2136 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
2137 LPDWORD lpdwCacheEntryInfoBufferSize
, DWORD dwReserved
)
2139 return urlcache_entry_get_file(lpszUrlName
, lpCacheEntryInfo
,
2140 lpdwCacheEntryInfoBufferSize
, FALSE
);
2143 /***********************************************************************
2144 * RetrieveUrlCacheEntryFileW (WININET.@)
2147 BOOL WINAPI
RetrieveUrlCacheEntryFileW(LPCWSTR lpszUrlName
,
2148 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
2149 LPDWORD lpdwCacheEntryInfoBufferSize
, DWORD dwReserved
)
2154 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
2157 ret
= urlcache_entry_get_file(url
, lpCacheEntryInfo
,
2158 lpdwCacheEntryInfoBufferSize
, TRUE
);
2163 static BOOL
urlcache_entry_delete(const cache_container
*pContainer
,
2164 urlcache_header
*pHeader
, struct hash_entry
*pHashEntry
)
2166 entry_header
*pEntry
;
2167 entry_url
* pUrlEntry
;
2169 pEntry
= (entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2170 if (pEntry
->signature
!= URL_SIGNATURE
)
2172 FIXME("Trying to delete entry of unknown format %s\n",
2173 debugstr_an((LPCSTR
)&pEntry
->signature
, sizeof(DWORD
)));
2174 SetLastError(ERROR_FILE_NOT_FOUND
);
2178 pUrlEntry
= (entry_url
*)pEntry
;
2179 if(urlcache_hash_entry_is_locked(pHashEntry
, pUrlEntry
))
2181 TRACE("Trying to delete locked entry\n");
2182 pUrlEntry
->cache_entry_type
|= PENDING_DELETE_CACHE_ENTRY
;
2183 SetLastError(ERROR_SHARING_VIOLATION
);
2187 if(!urlcache_delete_file(pContainer
, pHeader
, pUrlEntry
))
2189 urlcache_entry_free(pHeader
, pEntry
);
2193 /* Add entry to leaked files list */
2194 pUrlEntry
->header
.signature
= LEAK_SIGNATURE
;
2195 pUrlEntry
->exempt_delta
= pHeader
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
];
2196 pHeader
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
] = pHashEntry
->offset
;
2199 urlcache_hash_entry_delete(pHashEntry
);
2203 static HANDLE free_cache_running
;
2204 static HANDLE dll_unload_event
;
2205 static DWORD WINAPI
handle_full_cache_worker(void *param
)
2207 FreeUrlCacheSpaceW(NULL
, 20, 0);
2208 ReleaseSemaphore(free_cache_running
, 1, NULL
);
2212 static void handle_full_cache(void)
2214 if(WaitForSingleObject(free_cache_running
, 0) == WAIT_OBJECT_0
) {
2215 if(!QueueUserWorkItem(handle_full_cache_worker
, NULL
, 0))
2216 ReleaseSemaphore(free_cache_running
, 1, NULL
);
2220 /* Enumerates entries in cache, allows cache unlocking between calls. */
2221 static BOOL
urlcache_next_entry(urlcache_header
*header
, DWORD
*hash_table_off
, DWORD
*hash_table_entry
,
2222 struct hash_entry
**hash_entry
, entry_header
**entry
)
2224 entry_hash_table
*hashtable_entry
;
2229 if(!*hash_table_off
) {
2230 *hash_table_off
= header
->hash_table_off
;
2231 *hash_table_entry
= 0;
2233 hashtable_entry
= urlcache_get_hash_table(header
, *hash_table_off
);
2235 if(*hash_table_off
>= header
->size
) {
2236 *hash_table_off
= 0;
2240 hashtable_entry
= urlcache_get_hash_table(header
, *hash_table_off
);
2243 if(hashtable_entry
->header
.signature
!= HASH_SIGNATURE
) {
2244 *hash_table_off
= 0;
2249 if(*hash_table_entry
>= HASHTABLE_SIZE
) {
2250 *hash_table_off
= hashtable_entry
->next
;
2251 if(!*hash_table_off
) {
2252 *hash_table_off
= 0;
2256 hashtable_entry
= urlcache_get_hash_table(header
, *hash_table_off
);
2257 *hash_table_entry
= 0;
2260 if(hashtable_entry
->hash_table
[*hash_table_entry
].key
!= HASHTABLE_DEL
&&
2261 hashtable_entry
->hash_table
[*hash_table_entry
].key
!= HASHTABLE_FREE
) {
2262 *hash_entry
= &hashtable_entry
->hash_table
[*hash_table_entry
];
2263 *entry
= (entry_header
*)((LPBYTE
)header
+ hashtable_entry
->hash_table
[*hash_table_entry
].offset
);
2264 (*hash_table_entry
)++;
2268 (*hash_table_entry
)++;
2271 *hash_table_off
= 0;
2275 /* Rates an urlcache entry to determine if it can be deleted.
2277 * Score 0 means that entry can safely be removed, the bigger rating
2278 * the smaller chance of entry being removed.
2279 * DWORD_MAX means that entry can't be deleted at all.
2281 * Rating system is currently not fully compatible with native implementation.
2283 static DWORD
urlcache_rate_entry(entry_url
*url_entry
, FILETIME
*cur_time
)
2285 ULARGE_INTEGER time
, access_time
;
2288 access_time
.u
.LowPart
= url_entry
->access_time
.dwLowDateTime
;
2289 access_time
.u
.HighPart
= url_entry
->access_time
.dwHighDateTime
;
2291 time
.u
.LowPart
= cur_time
->dwLowDateTime
;
2292 time
.u
.HighPart
= cur_time
->dwHighDateTime
;
2294 /* Don't touch entries that were added less than 10 minutes ago */
2295 if(time
.QuadPart
< access_time
.QuadPart
+ (ULONGLONG
)10*60*FILETIME_SECOND
)
2298 if(url_entry
->cache_entry_type
& STICKY_CACHE_ENTRY
)
2299 if(time
.QuadPart
< access_time
.QuadPart
+ (ULONGLONG
)url_entry
->exempt_delta
*FILETIME_SECOND
)
2302 time
.QuadPart
= (time
.QuadPart
-access_time
.QuadPart
)/FILETIME_SECOND
;
2303 rating
= 400*60*60*24/(60*60*24+time
.QuadPart
);
2305 if(url_entry
->hit_rate
> 100)
2308 rating
+= url_entry
->hit_rate
;
2313 static int __cdecl
dword_cmp(const void *p1
, const void *p2
)
2315 return *(const DWORD
*)p1
- *(const DWORD
*)p2
;
2318 /***********************************************************************
2319 * FreeUrlCacheSpaceW (WININET.@)
2321 * Frees up some cache.
2324 * cache_path [I] Which volume to free up from, or NULL if you don't care.
2325 * size [I] Percentage of the cache that should be free.
2326 * filter [I] Which entries can't be deleted (CacheEntryType)
2329 * TRUE success. FALSE failure.
2332 * This implementation just retrieves the path of the cache directory, and
2333 * deletes its contents from the filesystem. The correct approach would
2334 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
2336 BOOL WINAPI
FreeUrlCacheSpaceW(LPCWSTR cache_path
, DWORD size
, DWORD filter
)
2338 cache_container
*container
;
2339 DWORD path_len
, err
;
2341 TRACE("(%s, %lx, %lx)\n", debugstr_w(cache_path
), size
, filter
);
2343 if(size
<1 || size
>100) {
2344 SetLastError(ERROR_INVALID_PARAMETER
);
2349 path_len
= lstrlenW(cache_path
);
2350 if(cache_path
[path_len
-1] == '\\')
2356 if(size
==100 && !filter
) {
2357 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
2359 /* When cache_path==NULL only clean Temporary Internet Files */
2360 if((!path_len
&& container
->cache_prefix
[0]==0) ||
2361 (path_len
&& !wcsnicmp(container
->path
, cache_path
, path_len
) &&
2362 (container
->path
[path_len
]=='\0' || container
->path
[path_len
]=='\\')))
2366 WaitForSingleObject(container
->mutex
, INFINITE
);
2368 /* unlock, delete, recreate and lock cache */
2369 cache_container_close_index(container
);
2370 ret_del
= cache_container_delete_dir(container
->path
);
2371 err
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2373 ReleaseMutex(container
->mutex
);
2374 if(!ret_del
|| (err
!= ERROR_SUCCESS
))
2382 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
2384 urlcache_header
*header
;
2385 struct hash_entry
*hash_entry
;
2386 entry_header
*entry
;
2387 entry_url
*url_entry
;
2388 ULONGLONG desired_size
, cur_size
;
2389 DWORD delete_factor
, hash_table_off
, hash_table_entry
;
2390 DWORD rate
[100], rate_no
;
2393 if((path_len
|| container
->cache_prefix
[0]!=0) &&
2394 (!path_len
|| wcsnicmp(container
->path
, cache_path
, path_len
) ||
2395 (container
->path
[path_len
]!='\0' && container
->path
[path_len
]!='\\')))
2398 err
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2399 if(err
!= ERROR_SUCCESS
)
2402 header
= cache_container_lock_index(container
);
2406 urlcache_clean_leaked_entries(container
, header
);
2408 desired_size
= header
->cache_limit
.QuadPart
*(100-size
)/100;
2409 cur_size
= header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
;
2410 if(cur_size
<= desired_size
)
2413 delete_factor
= (cur_size
-desired_size
)*100/cur_size
;
2415 if(!delete_factor
) {
2416 cache_container_unlock_index(container
, header
);
2421 hash_table_entry
= 0;
2423 GetSystemTimeAsFileTime(&cur_time
);
2424 while(rate_no
< ARRAY_SIZE(rate
) &&
2425 urlcache_next_entry(header
, &hash_table_off
, &hash_table_entry
, &hash_entry
, &entry
)) {
2426 if(entry
->signature
!= URL_SIGNATURE
) {
2427 WARN("only url entries are currently supported\n");
2431 url_entry
= (entry_url
*)entry
;
2432 if(url_entry
->cache_entry_type
& filter
)
2435 rate
[rate_no
] = urlcache_rate_entry(url_entry
, &cur_time
);
2436 if(rate
[rate_no
] != -1)
2441 TRACE("nothing to delete\n");
2442 cache_container_unlock_index(container
, header
);
2446 qsort(rate
, rate_no
, sizeof(DWORD
), dword_cmp
);
2448 delete_factor
= delete_factor
*rate_no
/100;
2449 delete_factor
= rate
[delete_factor
];
2450 TRACE("deleting files with rating %ld or less\n", delete_factor
);
2453 while(urlcache_next_entry(header
, &hash_table_off
, &hash_table_entry
, &hash_entry
, &entry
)) {
2454 if(entry
->signature
!= URL_SIGNATURE
)
2457 url_entry
= (entry_url
*)entry
;
2458 if(url_entry
->cache_entry_type
& filter
)
2461 if(urlcache_rate_entry(url_entry
, &cur_time
) <= delete_factor
) {
2462 TRACE("deleting file: %s\n", debugstr_a((char*)url_entry
+url_entry
->local_name_off
));
2463 urlcache_entry_delete(container
, header
, hash_entry
);
2465 if(header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
<= desired_size
)
2468 /* Allow other threads to use cache while cleaning */
2469 cache_container_unlock_index(container
, header
);
2470 if(WaitForSingleObject(dll_unload_event
, 0) == WAIT_OBJECT_0
) {
2471 TRACE("got dll_unload_event - finishing\n");
2475 header
= cache_container_lock_index(container
);
2479 TRACE("cache size after cleaning 0x%s/0x%s\n",
2480 wine_dbgstr_longlong(header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
),
2481 wine_dbgstr_longlong(header
->cache_limit
.QuadPart
));
2482 cache_container_unlock_index(container
, header
);
2488 /***********************************************************************
2489 * FreeUrlCacheSpaceA (WININET.@)
2491 * See FreeUrlCacheSpaceW.
2493 BOOL WINAPI
FreeUrlCacheSpaceA(LPCSTR lpszCachePath
, DWORD dwSize
, DWORD dwFilter
)
2496 WCHAR
*path
= strdupAtoW(lpszCachePath
);
2497 if (lpszCachePath
== NULL
|| path
!= NULL
)
2498 ret
= FreeUrlCacheSpaceW(path
, dwSize
, dwFilter
);
2503 /***********************************************************************
2504 * UnlockUrlCacheEntryFileA (WININET.@)
2507 BOOL WINAPI
UnlockUrlCacheEntryFileA(LPCSTR lpszUrlName
, DWORD dwReserved
)
2509 urlcache_header
*pHeader
;
2510 struct hash_entry
*pHashEntry
;
2511 entry_header
*pEntry
;
2512 entry_url
* pUrlEntry
;
2513 cache_container
*pContainer
;
2516 TRACE("(%s, 0x%08lx)\n", debugstr_a(lpszUrlName
), dwReserved
);
2520 ERR("dwReserved != 0\n");
2521 SetLastError(ERROR_INVALID_PARAMETER
);
2525 error
= cache_containers_find(lpszUrlName
, &pContainer
);
2526 if (error
!= ERROR_SUCCESS
)
2528 SetLastError(error
);
2532 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
2533 if (error
!= ERROR_SUCCESS
)
2535 SetLastError(error
);
2539 if (!(pHeader
= cache_container_lock_index(pContainer
)))
2542 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
2544 cache_container_unlock_index(pContainer
, pHeader
);
2545 TRACE("entry %s not found!\n", debugstr_a(lpszUrlName
));
2546 SetLastError(ERROR_FILE_NOT_FOUND
);
2550 pEntry
= (entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2551 if (pEntry
->signature
!= URL_SIGNATURE
)
2553 cache_container_unlock_index(pContainer
, pHeader
);
2554 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR
)&pEntry
->signature
, sizeof(DWORD
)));
2555 SetLastError(ERROR_FILE_NOT_FOUND
);
2559 pUrlEntry
= (entry_url
*)pEntry
;
2561 if (pUrlEntry
->use_count
== 0)
2563 cache_container_unlock_index(pContainer
, pHeader
);
2566 pUrlEntry
->use_count
--;
2567 if (!pUrlEntry
->use_count
)
2569 urlcache_hash_entry_set_flags(pHashEntry
, HASHTABLE_URL
);
2570 if (pUrlEntry
->cache_entry_type
& PENDING_DELETE_CACHE_ENTRY
)
2571 urlcache_entry_delete(pContainer
, pHeader
, pHashEntry
);
2574 cache_container_unlock_index(pContainer
, pHeader
);
2579 /***********************************************************************
2580 * UnlockUrlCacheEntryFileW (WININET.@)
2583 BOOL WINAPI
UnlockUrlCacheEntryFileW(LPCWSTR lpszUrlName
, DWORD dwReserved
)
2588 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
2591 ret
= UnlockUrlCacheEntryFileA(url
, dwReserved
);
2596 static BOOL
urlcache_entry_create(const char *url
, const char *ext
, WCHAR
*full_path
)
2598 cache_container
*container
;
2599 urlcache_header
*header
;
2600 char file_name
[MAX_PATH
];
2601 WCHAR extW
[MAX_PATH
];
2603 LONG full_path_len
, ext_len
= 0;
2604 BOOL generate_name
= FALSE
;
2611 TRACE("(%s, %s, %p)\n", debugstr_a(url
), debugstr_a(ext
), full_path
);
2613 memset(&uc
, 0, sizeof(uc
));
2614 uc
.dwStructSize
= sizeof(uc
);
2615 uc
.dwUrlPathLength
= 1;
2616 uc
.dwExtraInfoLength
= 1;
2617 if(!InternetCrackUrlA(url
, 0, 0, &uc
))
2618 uc
.dwUrlPathLength
= 0;
2620 if(!uc
.dwUrlPathLength
) {
2625 p
= e
= uc
.lpszUrlPath
+uc
.dwUrlPathLength
;
2626 while(p
>uc
.lpszUrlPath
&& *(p
-1)!='/' && *(p
-1)!='\\' && *(p
-1)!='.')
2628 if(p
>uc
.lpszUrlPath
&& *(p
-1)=='.') {
2630 while(p
>uc
.lpszUrlPath
&& *(p
-1)!='/' && *(p
-1)!='\\')
2636 memcpy(file_name
, p
, e
-p
);
2639 for(p
=file_name
; *p
; p
++) {
2652 generate_name
= TRUE
;
2654 error
= cache_containers_find(url
, &container
);
2655 if(error
!= ERROR_SUCCESS
) {
2656 SetLastError(error
);
2660 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2661 if(error
!= ERROR_SUCCESS
) {
2662 SetLastError(error
);
2666 if(!(header
= cache_container_lock_index(container
)))
2670 cache_dir
= (BYTE
)(rand() % header
->dirs_no
);
2672 cache_dir
= CACHE_CONTAINER_NO_SUBDIR
;
2674 full_path_len
= MAX_PATH
* sizeof(WCHAR
);
2675 if(!urlcache_create_file_pathW(container
, header
, file_name
, cache_dir
, full_path
, &full_path_len
, TRUE
)) {
2676 WARN("Failed to get full path for filename %s, needed %lu bytes.\n",
2677 debugstr_a(file_name
), full_path_len
);
2678 cache_container_unlock_index(container
, header
);
2681 full_path_len
= full_path_len
/sizeof(WCHAR
) - 1;
2683 cache_container_unlock_index(container
, header
);
2689 ext_len
= MultiByteToWideChar(CP_ACP
, 0, ext
, -1, extW
+1, MAX_PATH
-1);
2691 for(p
=extW
; *p
; p
++) {
2701 if(p
[-1]==' ' || p
[-1]=='.')
2707 if(!generate_name
&& full_path_len
+5+ext_len
>=MAX_PATH
) { /* strlen("[255]") = 5 */
2708 full_path_len
= MAX_PATH
-5-ext_len
-1;
2711 for(i
=0; i
<255 && !generate_name
; i
++) {
2712 wsprintfW(full_path
+full_path_len
, L
"[%u]%s", i
, extW
);
2714 TRACE("Trying: %s\n", debugstr_w(full_path
));
2715 file
= CreateFileW(full_path
, GENERIC_READ
, 0, NULL
, CREATE_NEW
, 0, NULL
);
2716 if(file
!= INVALID_HANDLE_VALUE
) {
2722 if(full_path_len
+8+ext_len
>= MAX_PATH
)
2723 full_path_len
= MAX_PATH
-8-ext_len
-1;
2725 /* Try to generate random name */
2726 GetSystemTimeAsFileTime(&ft
);
2727 lstrcpyW(full_path
+full_path_len
+8, extW
);
2729 for(i
=0; i
<255; i
++) {
2731 ULONGLONG n
= ft
.dwHighDateTime
;
2733 n
+= ft
.dwLowDateTime
;
2734 n
^= (ULONGLONG
)i
<<48;
2736 for(j
=0; j
<8; j
++) {
2739 full_path
[full_path_len
+j
] = (r
< 10 ? '0' + r
: 'A' + r
- 10);
2742 TRACE("Trying: %s\n", debugstr_w(full_path
));
2743 file
= CreateFileW(full_path
, GENERIC_READ
, 0, NULL
, CREATE_NEW
, 0, NULL
);
2744 if(file
!= INVALID_HANDLE_VALUE
) {
2750 WARN("Could not find a unique filename\n");
2754 /***********************************************************************
2755 * CreateUrlCacheEntryA (WININET.@)
2758 BOOL WINAPI
CreateUrlCacheEntryA(LPCSTR lpszUrlName
, DWORD dwExpectedFileSize
,
2759 LPCSTR lpszFileExtension
, LPSTR lpszFileName
, DWORD dwReserved
)
2761 WCHAR file_name
[MAX_PATH
];
2764 FIXME("dwReserved 0x%08lx\n", dwReserved
);
2766 if(!urlcache_entry_create(lpszUrlName
, lpszFileExtension
, file_name
))
2769 if(!WideCharToMultiByte(CP_ACP
, 0, file_name
, -1, lpszFileName
, MAX_PATH
, NULL
, NULL
))
2773 /***********************************************************************
2774 * CreateUrlCacheEntryW (WININET.@)
2777 BOOL WINAPI
CreateUrlCacheEntryW(LPCWSTR lpszUrlName
, DWORD dwExpectedFileSize
,
2778 LPCWSTR lpszFileExtension
, LPWSTR lpszFileName
, DWORD dwReserved
)
2780 char *url
, *ext
= NULL
;
2784 FIXME("dwReserved 0x%08lx\n", dwReserved
);
2786 if(lpszFileExtension
) {
2787 ext
= strdupWtoUTF8(lpszFileExtension
);
2792 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
)) {
2797 ret
= urlcache_entry_create(url
, ext
, lpszFileName
);
2803 static BOOL
urlcache_entry_commit(const char *url
, const WCHAR
*file_name
,
2804 FILETIME expire_time
, FILETIME modify_time
, DWORD entry_type
,
2805 BYTE
*header_info
, DWORD header_size
, const char *file_ext
,
2806 const char *original_url
)
2808 cache_container
*container
;
2809 urlcache_header
*header
;
2810 struct hash_entry
*hash_entry
;
2811 entry_header
*entry
;
2812 entry_url
*url_entry
;
2813 DWORD url_entry_offset
;
2814 DWORD size
= DWORD_ALIGN(sizeof(*url_entry
));
2815 DWORD file_name_off
= 0;
2816 DWORD header_info_off
= 0;
2817 DWORD file_ext_off
= 0;
2818 WIN32_FILE_ATTRIBUTE_DATA file_attr
;
2819 LARGE_INTEGER file_size
;
2821 char file_name_no_container
[MAX_PATH
];
2822 char *local_file_name
= 0;
2824 DWORD exempt_delta
= 0;
2827 TRACE("(%s, %s, ..., ..., %lx, %p, %ld, %s, %s)\n", debugstr_a(url
), debugstr_w(file_name
),
2828 entry_type
, header_info
, header_size
, debugstr_a(file_ext
), debugstr_a(original_url
));
2830 if(entry_type
& STICKY_CACHE_ENTRY
&& !file_name
) {
2831 SetLastError(ERROR_INVALID_PARAMETER
);
2835 WARN(": original_url ignored\n");
2837 memset(&file_attr
, 0, sizeof(file_attr
));
2839 if(!GetFileAttributesExW(file_name
, GetFileExInfoStandard
, &file_attr
))
2842 file_size
.u
.LowPart
= file_attr
.nFileSizeLow
;
2843 file_size
.u
.HighPart
= file_attr
.nFileSizeHigh
;
2845 error
= cache_containers_find(url
, &container
);
2846 if(error
!= ERROR_SUCCESS
) {
2847 SetLastError(error
);
2851 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2852 if(error
!= ERROR_SUCCESS
) {
2853 SetLastError(error
);
2857 if(!(header
= cache_container_lock_index(container
)))
2860 if(urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
2861 entry_url
*url_entry
= (entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
2863 if(urlcache_hash_entry_is_locked(hash_entry
, url_entry
)) {
2864 TRACE("Trying to overwrite locked entry\n");
2865 cache_container_unlock_index(container
, header
);
2866 SetLastError(ERROR_SHARING_VIOLATION
);
2870 hit_rate
= url_entry
->hit_rate
;
2871 exempt_delta
= url_entry
->exempt_delta
;
2872 urlcache_entry_delete(container
, header
, hash_entry
);
2878 dir_id
= CACHE_CONTAINER_NO_SUBDIR
;
2881 BOOL bFound
= FALSE
;
2883 if(wcsncmp(file_name
, container
->path
, lstrlenW(container
->path
))) {
2884 ERR("path %s must begin with cache content path %s\n", debugstr_w(file_name
), debugstr_w(container
->path
));
2885 cache_container_unlock_index(container
, header
);
2886 SetLastError(ERROR_INVALID_PARAMETER
);
2890 /* skip container path prefix */
2891 file_name
+= lstrlenW(container
->path
);
2893 WideCharToMultiByte(CP_ACP
, 0, file_name
, -1, file_name_no_container
, MAX_PATH
, NULL
, NULL
);
2894 local_file_name
= file_name_no_container
;
2896 if(header
->dirs_no
) {
2897 for(dir_id
= 0; dir_id
< header
->dirs_no
; dir_id
++) {
2898 if(!strncmp(header
->directory_data
[dir_id
].name
, local_file_name
, DIR_LENGTH
)) {
2905 ERR("cache directory not found in path %s\n", debugstr_w(file_name
));
2906 cache_container_unlock_index(container
, header
);
2907 SetLastError(ERROR_INVALID_PARAMETER
);
2911 file_name
+= DIR_LENGTH
+ 1;
2912 local_file_name
+= DIR_LENGTH
+ 1;
2916 size
= DWORD_ALIGN(size
+ strlen(url
) + 1);
2918 file_name_off
= size
;
2919 size
= DWORD_ALIGN(size
+ strlen(local_file_name
) + 1);
2921 if(header_info
&& header_size
) {
2922 header_info_off
= size
;
2923 size
= DWORD_ALIGN(size
+ header_size
);
2925 if(file_ext
&& (file_ext_off
= strlen(file_ext
))) {
2926 DWORD len
= file_ext_off
;
2928 file_ext_off
= size
;
2929 size
= DWORD_ALIGN(size
+ len
+ 1);
2932 /* round up to next block */
2933 if(size
% BLOCKSIZE
) {
2934 size
-= size
% BLOCKSIZE
;
2938 error
= urlcache_entry_alloc(header
, size
/ BLOCKSIZE
, &entry
);
2939 while(error
== ERROR_HANDLE_DISK_FULL
) {
2940 error
= cache_container_clean_index(container
, &header
);
2941 if(error
== ERROR_SUCCESS
)
2942 error
= urlcache_entry_alloc(header
, size
/ BLOCKSIZE
, &entry
);
2944 if(error
!= ERROR_SUCCESS
) {
2945 cache_container_unlock_index(container
, header
);
2946 SetLastError(error
);
2950 /* FindFirstFreeEntry fills in blocks used */
2951 url_entry
= (entry_url
*)entry
;
2952 url_entry_offset
= (LPBYTE
)url_entry
- (LPBYTE
)header
;
2953 url_entry
->header
.signature
= URL_SIGNATURE
;
2954 url_entry
->cache_dir
= dir_id
;
2955 url_entry
->cache_entry_type
= entry_type
| container
->default_entry_type
;
2956 url_entry
->header_info_size
= header_size
;
2957 if((entry_type
& STICKY_CACHE_ENTRY
) && !exempt_delta
) {
2958 /* Sticky entries have a default exempt time of one day */
2959 exempt_delta
= 86400;
2961 url_entry
->exempt_delta
= exempt_delta
;
2962 url_entry
->hit_rate
= hit_rate
+1;
2963 url_entry
->file_extension_off
= file_ext_off
;
2964 url_entry
->header_info_off
= header_info_off
;
2965 url_entry
->local_name_off
= file_name_off
;
2966 url_entry
->url_off
= DWORD_ALIGN(sizeof(*url_entry
));
2967 url_entry
->size
.QuadPart
= file_size
.QuadPart
;
2968 url_entry
->use_count
= 0;
2969 GetSystemTimeAsFileTime(&url_entry
->access_time
);
2970 url_entry
->modification_time
= modify_time
;
2971 file_time_to_dos_date_time(&url_entry
->access_time
, &url_entry
->sync_date
, &url_entry
->sync_time
);
2972 file_time_to_dos_date_time(&expire_time
, &url_entry
->expire_date
, &url_entry
->expire_time
);
2973 file_time_to_dos_date_time(&file_attr
.ftLastWriteTime
, &url_entry
->write_date
, &url_entry
->write_time
);
2976 url_entry
->unk1
= 0;
2977 url_entry
->unk2
= 0;
2978 url_entry
->unk3
= 0x60;
2979 url_entry
->unk4
= 0;
2980 url_entry
->unk5
= 0x1010;
2981 url_entry
->unk7
= 0;
2982 url_entry
->unk8
= 0;
2985 strcpy((LPSTR
)url_entry
+ url_entry
->url_off
, url
);
2987 strcpy((LPSTR
)((LPBYTE
)url_entry
+ file_name_off
), local_file_name
);
2989 memcpy((LPBYTE
)url_entry
+ header_info_off
, header_info
, header_size
);
2991 strcpy((LPSTR
)((LPBYTE
)url_entry
+ file_ext_off
), file_ext
);
2993 error
= urlcache_hash_entry_create(header
, url
, url_entry_offset
, HASHTABLE_URL
);
2994 while(error
== ERROR_HANDLE_DISK_FULL
) {
2995 error
= cache_container_clean_index(container
, &header
);
2996 if(error
== ERROR_SUCCESS
) {
2997 url_entry
= (entry_url
*)((LPBYTE
)header
+ url_entry_offset
);
2998 error
= urlcache_hash_entry_create(header
, url
,
2999 url_entry_offset
, HASHTABLE_URL
);
3002 if(error
!= ERROR_SUCCESS
) {
3003 urlcache_entry_free(header
, &url_entry
->header
);
3004 cache_container_unlock_index(container
, header
);
3005 SetLastError(error
);
3009 if(url_entry
->cache_dir
< header
->dirs_no
)
3010 header
->directory_data
[url_entry
->cache_dir
].files_no
++;
3011 if(entry_type
& STICKY_CACHE_ENTRY
)
3012 header
->exempt_usage
.QuadPart
+= file_size
.QuadPart
;
3014 header
->cache_usage
.QuadPart
+= file_size
.QuadPart
;
3015 if(header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
> header
->cache_limit
.QuadPart
)
3016 handle_full_cache();
3018 cache_container_unlock_index(container
, header
);
3022 /***********************************************************************
3023 * CommitUrlCacheEntryA (WININET.@)
3025 BOOL WINAPI
CommitUrlCacheEntryA(LPCSTR lpszUrlName
, LPCSTR lpszLocalFileName
,
3026 FILETIME ExpireTime
, FILETIME LastModifiedTime
, DWORD CacheEntryType
,
3027 LPBYTE lpHeaderInfo
, DWORD dwHeaderSize
, LPCSTR lpszFileExtension
, LPCSTR lpszOriginalUrl
)
3029 WCHAR
*file_name
= NULL
;
3032 if(lpszLocalFileName
) {
3033 file_name
= strdupAtoW(lpszLocalFileName
);
3038 ret
= urlcache_entry_commit(lpszUrlName
, file_name
, ExpireTime
, LastModifiedTime
,
3039 CacheEntryType
, lpHeaderInfo
, dwHeaderSize
, lpszFileExtension
, lpszOriginalUrl
);
3044 /***********************************************************************
3045 * CommitUrlCacheEntryW (WININET.@)
3047 BOOL WINAPI
CommitUrlCacheEntryW(LPCWSTR lpszUrlName
, LPCWSTR lpszLocalFileName
,
3048 FILETIME ExpireTime
, FILETIME LastModifiedTime
, DWORD CacheEntryType
,
3049 LPWSTR lpHeaderInfo
, DWORD dwHeaderSize
, LPCWSTR lpszFileExtension
, LPCWSTR lpszOriginalUrl
)
3051 char *url
, *original_url
=NULL
, *file_ext
=NULL
, *header_info
=NULL
;
3054 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
3058 header_info
= strdupWtoUTF8(lpHeaderInfo
);
3063 dwHeaderSize
= strlen(header_info
);
3066 if(lpszFileExtension
) {
3067 file_ext
= strdupWtoA(lpszFileExtension
);
3075 if(lpszOriginalUrl
&& !urlcache_encode_url_alloc(lpszOriginalUrl
, &original_url
)) {
3082 ret
= urlcache_entry_commit(url
, lpszLocalFileName
, ExpireTime
, LastModifiedTime
,
3083 CacheEntryType
, (BYTE
*)header_info
, dwHeaderSize
, file_ext
, original_url
);
3091 /***********************************************************************
3092 * ReadUrlCacheEntryStream (WININET.@)
3095 BOOL WINAPI
ReadUrlCacheEntryStream(
3096 IN HANDLE hUrlCacheStream
,
3097 IN DWORD dwLocation
,
3098 IN OUT LPVOID lpBuffer
,
3099 IN OUT LPDWORD lpdwLen
,
3103 /* Get handle to file from 'stream' */
3104 stream_handle
*pStream
= (stream_handle
*)hUrlCacheStream
;
3106 if (dwReserved
!= 0)
3108 ERR("dwReserved != 0\n");
3109 SetLastError(ERROR_INVALID_PARAMETER
);
3113 if (IsBadReadPtr(pStream
, sizeof(*pStream
)) || IsBadStringPtrA(pStream
->url
, INTERNET_MAX_URL_LENGTH
))
3115 SetLastError(ERROR_INVALID_HANDLE
);
3119 if (SetFilePointer(pStream
->file
, dwLocation
, NULL
, FILE_CURRENT
) == INVALID_SET_FILE_POINTER
)
3121 return ReadFile(pStream
->file
, lpBuffer
, *lpdwLen
, lpdwLen
, NULL
);
3124 /***********************************************************************
3125 * RetrieveUrlCacheEntryStreamA (WININET.@)
3128 HANDLE WINAPI
RetrieveUrlCacheEntryStreamA(LPCSTR lpszUrlName
,
3129 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
3130 LPDWORD lpdwCacheEntryInfoBufferSize
, BOOL fRandomRead
, DWORD dwReserved
)
3132 /* NOTE: this is not the same as the way that the native
3133 * version allocates 'stream' handles. I did it this way
3134 * as it is much easier and no applications should depend
3135 * on this behaviour. (Native version appears to allocate
3136 * indices into a table)
3138 stream_handle
*stream
;
3141 TRACE("(%s, %p, %p, %x, 0x%08lx)\n", debugstr_a(lpszUrlName
), lpCacheEntryInfo
,
3142 lpdwCacheEntryInfoBufferSize
, fRandomRead
, dwReserved
);
3144 if(!RetrieveUrlCacheEntryFileA(lpszUrlName
, lpCacheEntryInfo
,
3145 lpdwCacheEntryInfoBufferSize
, dwReserved
))
3148 file
= CreateFileA(lpCacheEntryInfo
->lpszLocalFileName
, GENERIC_READ
, FILE_SHARE_READ
,
3149 NULL
, OPEN_EXISTING
, fRandomRead
? FILE_FLAG_RANDOM_ACCESS
: 0, NULL
);
3150 if(file
== INVALID_HANDLE_VALUE
) {
3151 UnlockUrlCacheEntryFileA(lpszUrlName
, 0);
3155 /* allocate handle storage space */
3156 stream
= malloc(sizeof(stream_handle
) + strlen(lpszUrlName
) * sizeof(CHAR
));
3159 UnlockUrlCacheEntryFileA(lpszUrlName
, 0);
3160 SetLastError(ERROR_OUTOFMEMORY
);
3164 stream
->file
= file
;
3165 strcpy(stream
->url
, lpszUrlName
);
3169 /***********************************************************************
3170 * RetrieveUrlCacheEntryStreamW (WININET.@)
3173 HANDLE WINAPI
RetrieveUrlCacheEntryStreamW(LPCWSTR lpszUrlName
,
3174 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
3175 LPDWORD lpdwCacheEntryInfoBufferSize
, BOOL fRandomRead
, DWORD dwReserved
)
3178 /* NOTE: this is not the same as the way that the native
3179 * version allocates 'stream' handles. I did it this way
3180 * as it is much easier and no applications should depend
3181 * on this behaviour. (Native version appears to allocate
3182 * indices into a table)
3184 stream_handle
*stream
;
3187 TRACE("(%s, %p, %p, %x, 0x%08lx)\n", debugstr_w(lpszUrlName
), lpCacheEntryInfo
,
3188 lpdwCacheEntryInfoBufferSize
, fRandomRead
, dwReserved
);
3190 if(!(len
= urlcache_encode_url(lpszUrlName
, NULL
, 0)))
3193 if(!RetrieveUrlCacheEntryFileW(lpszUrlName
, lpCacheEntryInfo
,
3194 lpdwCacheEntryInfoBufferSize
, dwReserved
))
3197 file
= CreateFileW(lpCacheEntryInfo
->lpszLocalFileName
, GENERIC_READ
, FILE_SHARE_READ
,
3198 NULL
, OPEN_EXISTING
, fRandomRead
? FILE_FLAG_RANDOM_ACCESS
: 0, NULL
);
3199 if(file
== INVALID_HANDLE_VALUE
) {
3200 UnlockUrlCacheEntryFileW(lpszUrlName
, 0);
3204 /* allocate handle storage space */
3205 stream
= malloc(sizeof(stream_handle
) + len
* sizeof(WCHAR
));
3208 UnlockUrlCacheEntryFileW(lpszUrlName
, 0);
3209 SetLastError(ERROR_OUTOFMEMORY
);
3213 stream
->file
= file
;
3214 if(!urlcache_encode_url(lpszUrlName
, stream
->url
, len
)) {
3216 UnlockUrlCacheEntryFileW(lpszUrlName
, 0);
3223 /***********************************************************************
3224 * UnlockUrlCacheEntryStream (WININET.@)
3227 BOOL WINAPI
UnlockUrlCacheEntryStream(
3228 IN HANDLE hUrlCacheStream
,
3232 stream_handle
*pStream
= (stream_handle
*)hUrlCacheStream
;
3234 if (dwReserved
!= 0)
3236 ERR("dwReserved != 0\n");
3237 SetLastError(ERROR_INVALID_PARAMETER
);
3241 if (IsBadReadPtr(pStream
, sizeof(*pStream
)) || IsBadStringPtrA(pStream
->url
, INTERNET_MAX_URL_LENGTH
))
3243 SetLastError(ERROR_INVALID_HANDLE
);
3247 if (!UnlockUrlCacheEntryFileA(pStream
->url
, 0))
3250 CloseHandle(pStream
->file
);
3256 /***********************************************************************
3257 * DeleteUrlCacheEntryA (WININET.@)
3260 BOOL WINAPI
DeleteUrlCacheEntryA(LPCSTR lpszUrlName
)
3262 cache_container
*pContainer
;
3263 urlcache_header
*pHeader
;
3264 struct hash_entry
*pHashEntry
;
3268 TRACE("(%s)\n", debugstr_a(lpszUrlName
));
3270 error
= cache_containers_find(lpszUrlName
, &pContainer
);
3271 if (error
!= ERROR_SUCCESS
)
3273 SetLastError(error
);
3277 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
3278 if (error
!= ERROR_SUCCESS
)
3280 SetLastError(error
);
3284 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3287 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
3289 cache_container_unlock_index(pContainer
, pHeader
);
3290 TRACE("entry %s not found!\n", debugstr_a(lpszUrlName
));
3291 SetLastError(ERROR_FILE_NOT_FOUND
);
3295 ret
= urlcache_entry_delete(pContainer
, pHeader
, pHashEntry
);
3297 cache_container_unlock_index(pContainer
, pHeader
);
3302 /***********************************************************************
3303 * DeleteUrlCacheEntryW (WININET.@)
3306 BOOL WINAPI
DeleteUrlCacheEntryW(LPCWSTR lpszUrlName
)
3311 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
3314 ret
= DeleteUrlCacheEntryA(url
);
3319 BOOL WINAPI
DeleteUrlCacheContainerA(DWORD d1
, DWORD d2
)
3321 FIXME("(0x%08lx, 0x%08lx) stub\n", d1
, d2
);
3325 BOOL WINAPI
DeleteUrlCacheContainerW(DWORD d1
, DWORD d2
)
3327 FIXME("(0x%08lx, 0x%08lx) stub\n", d1
, d2
);
3331 /***********************************************************************
3332 * CreateCacheContainerA (WININET.@)
3334 BOOL WINAPI
CreateUrlCacheContainerA(DWORD d1
, DWORD d2
, DWORD d3
, DWORD d4
,
3335 DWORD d5
, DWORD d6
, DWORD d7
, DWORD d8
)
3337 FIXME("(0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx) stub\n",
3338 d1
, d2
, d3
, d4
, d5
, d6
, d7
, d8
);
3342 /***********************************************************************
3343 * CreateCacheContainerW (WININET.@)
3345 BOOL WINAPI
CreateUrlCacheContainerW(DWORD d1
, DWORD d2
, DWORD d3
, DWORD d4
,
3346 DWORD d5
, DWORD d6
, DWORD d7
, DWORD d8
)
3348 FIXME("(0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx) stub\n",
3349 d1
, d2
, d3
, d4
, d5
, d6
, d7
, d8
);
3353 /***********************************************************************
3354 * FindFirstUrlCacheContainerA (WININET.@)
3356 HANDLE WINAPI
FindFirstUrlCacheContainerA( LPVOID p1
, LPVOID p2
, LPVOID p3
, DWORD d1
)
3358 FIXME("(%p, %p, %p, 0x%08lx) stub\n", p1
, p2
, p3
, d1
);
3362 /***********************************************************************
3363 * FindFirstUrlCacheContainerW (WININET.@)
3365 HANDLE WINAPI
FindFirstUrlCacheContainerW( LPVOID p1
, LPVOID p2
, LPVOID p3
, DWORD d1
)
3367 FIXME("(%p, %p, %p, 0x%08lx) stub\n", p1
, p2
, p3
, d1
);
3371 /***********************************************************************
3372 * FindNextUrlCacheContainerA (WININET.@)
3374 BOOL WINAPI
FindNextUrlCacheContainerA( HANDLE handle
, LPVOID p1
, LPVOID p2
)
3376 FIXME("(%p, %p, %p) stub\n", handle
, p1
, p2
);
3380 /***********************************************************************
3381 * FindNextUrlCacheContainerW (WININET.@)
3383 BOOL WINAPI
FindNextUrlCacheContainerW( HANDLE handle
, LPVOID p1
, LPVOID p2
)
3385 FIXME("(%p, %p, %p) stub\n", handle
, p1
, p2
);
3389 HANDLE WINAPI
FindFirstUrlCacheEntryExA(
3390 LPCSTR lpszUrlSearchPattern
,
3394 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
,
3395 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3397 LPDWORD pcbReserved2
,
3401 FIXME("(%s, 0x%08lx, 0x%08lx, 0x%s, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern
),
3402 dwFlags
, dwFilter
, wine_dbgstr_longlong(GroupId
), lpFirstCacheEntryInfo
,
3403 lpdwFirstCacheEntryInfoBufferSize
, lpReserved
, pcbReserved2
,lpReserved3
);
3404 SetLastError(ERROR_FILE_NOT_FOUND
);
3408 HANDLE WINAPI
FindFirstUrlCacheEntryExW(
3409 LPCWSTR lpszUrlSearchPattern
,
3413 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
,
3414 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3416 LPDWORD pcbReserved2
,
3420 FIXME("(%s, 0x%08lx, 0x%08lx, 0x%s, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern
),
3421 dwFlags
, dwFilter
, wine_dbgstr_longlong(GroupId
), lpFirstCacheEntryInfo
,
3422 lpdwFirstCacheEntryInfoBufferSize
, lpReserved
, pcbReserved2
,lpReserved3
);
3423 SetLastError(ERROR_FILE_NOT_FOUND
);
3427 /***********************************************************************
3428 * FindFirstUrlCacheEntryA (WININET.@)
3431 INTERNETAPI HANDLE WINAPI
FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern
,
3432 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
, LPDWORD lpdwFirstCacheEntryInfoBufferSize
)
3434 find_handle
*pEntryHandle
;
3436 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern
), lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
);
3438 pEntryHandle
= malloc(sizeof(*pEntryHandle
));
3442 pEntryHandle
->magic
= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
;
3443 if (lpszUrlSearchPattern
)
3445 pEntryHandle
->url_search_pattern
= strdup(lpszUrlSearchPattern
);
3446 if (!pEntryHandle
->url_search_pattern
)
3453 pEntryHandle
->url_search_pattern
= NULL
;
3454 pEntryHandle
->container_idx
= 0;
3455 pEntryHandle
->hash_table_idx
= 0;
3456 pEntryHandle
->hash_entry_idx
= 0;
3458 if (!FindNextUrlCacheEntryA(pEntryHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
))
3463 return pEntryHandle
;
3466 /***********************************************************************
3467 * FindFirstUrlCacheEntryW (WININET.@)
3470 INTERNETAPI HANDLE WINAPI
FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern
,
3471 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
, LPDWORD lpdwFirstCacheEntryInfoBufferSize
)
3473 find_handle
*pEntryHandle
;
3475 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern
), lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
);
3477 pEntryHandle
= malloc(sizeof(*pEntryHandle
));
3481 pEntryHandle
->magic
= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
;
3482 if (lpszUrlSearchPattern
)
3484 pEntryHandle
->url_search_pattern
= strdupWtoA(lpszUrlSearchPattern
);
3485 if (!pEntryHandle
->url_search_pattern
)
3492 pEntryHandle
->url_search_pattern
= NULL
;
3493 pEntryHandle
->container_idx
= 0;
3494 pEntryHandle
->hash_table_idx
= 0;
3495 pEntryHandle
->hash_entry_idx
= 0;
3497 if (!FindNextUrlCacheEntryW(pEntryHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
))
3502 return pEntryHandle
;
3505 static BOOL
urlcache_find_next_entry(
3507 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo
,
3508 LPDWORD lpdwNextCacheEntryInfoBufferSize
,
3511 find_handle
*pEntryHandle
= (find_handle
*)hEnumHandle
;
3512 cache_container
*pContainer
;
3514 if (pEntryHandle
->magic
!= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
)
3516 SetLastError(ERROR_INVALID_HANDLE
);
3520 for (; cache_containers_enum(pEntryHandle
->url_search_pattern
, pEntryHandle
->container_idx
, &pContainer
);
3521 pEntryHandle
->container_idx
++, pEntryHandle
->hash_table_idx
= 0)
3523 urlcache_header
*pHeader
;
3524 entry_hash_table
*pHashTableEntry
;
3527 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
3528 if (error
!= ERROR_SUCCESS
)
3530 SetLastError(error
);
3534 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3537 for (; urlcache_enum_hash_tables(pHeader
, &pEntryHandle
->hash_table_idx
, &pHashTableEntry
);
3538 pEntryHandle
->hash_table_idx
++, pEntryHandle
->hash_entry_idx
= 0)
3540 const struct hash_entry
*pHashEntry
= NULL
;
3541 for (; urlcache_enum_hash_table_entries(pHeader
, pHashTableEntry
, &pEntryHandle
->hash_entry_idx
, &pHashEntry
);
3542 pEntryHandle
->hash_entry_idx
++)
3544 const entry_url
*pUrlEntry
;
3545 const entry_header
*pEntry
= (const entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
3547 if (pEntry
->signature
!= URL_SIGNATURE
)
3550 pUrlEntry
= (const entry_url
*)pEntry
;
3551 TRACE("Found URL: %s\n",
3552 debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->url_off
));
3553 TRACE("Header info: %s\n",
3554 debugstr_an((LPCSTR
)pUrlEntry
+ pUrlEntry
->header_info_off
,
3555 pUrlEntry
->header_info_size
));
3557 error
= urlcache_copy_entry(
3560 lpNextCacheEntryInfo
,
3561 lpdwNextCacheEntryInfoBufferSize
,
3564 if (error
!= ERROR_SUCCESS
)
3566 cache_container_unlock_index(pContainer
, pHeader
);
3567 SetLastError(error
);
3570 if(pUrlEntry
->local_name_off
)
3571 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->local_name_off
));
3573 /* increment the current index so that next time the function
3574 * is called the next entry is returned */
3575 pEntryHandle
->hash_entry_idx
++;
3576 cache_container_unlock_index(pContainer
, pHeader
);
3581 cache_container_unlock_index(pContainer
, pHeader
);
3584 SetLastError(ERROR_NO_MORE_ITEMS
);
3588 /***********************************************************************
3589 * FindNextUrlCacheEntryA (WININET.@)
3591 BOOL WINAPI
FindNextUrlCacheEntryA(
3593 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo
,
3594 LPDWORD lpdwNextCacheEntryInfoBufferSize
)
3596 TRACE("(%p, %p, %p)\n", hEnumHandle
, lpNextCacheEntryInfo
, lpdwNextCacheEntryInfoBufferSize
);
3598 return urlcache_find_next_entry(hEnumHandle
, lpNextCacheEntryInfo
,
3599 lpdwNextCacheEntryInfoBufferSize
, FALSE
/* not UNICODE */);
3602 /***********************************************************************
3603 * FindNextUrlCacheEntryW (WININET.@)
3605 BOOL WINAPI
FindNextUrlCacheEntryW(
3607 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo
,
3608 LPDWORD lpdwNextCacheEntryInfoBufferSize
3611 TRACE("(%p, %p, %p)\n", hEnumHandle
, lpNextCacheEntryInfo
, lpdwNextCacheEntryInfoBufferSize
);
3613 return urlcache_find_next_entry(hEnumHandle
,
3614 (LPINTERNET_CACHE_ENTRY_INFOA
)lpNextCacheEntryInfo
,
3615 lpdwNextCacheEntryInfoBufferSize
, TRUE
/* UNICODE */);
3618 /***********************************************************************
3619 * FindCloseUrlCache (WININET.@)
3621 BOOL WINAPI
FindCloseUrlCache(HANDLE hEnumHandle
)
3623 find_handle
*pEntryHandle
= (find_handle
*)hEnumHandle
;
3625 TRACE("(%p)\n", hEnumHandle
);
3627 if (!pEntryHandle
|| pEntryHandle
->magic
!= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
)
3629 SetLastError(ERROR_INVALID_HANDLE
);
3633 /* Ensure compiler doesn't optimize out the assignment with 0. */
3634 SecureZeroMemory(&pEntryHandle
->magic
, sizeof(pEntryHandle
->magic
));
3635 free(pEntryHandle
->url_search_pattern
);
3640 HANDLE WINAPI
FindFirstUrlCacheGroup( DWORD dwFlags
, DWORD dwFilter
, LPVOID lpSearchCondition
,
3641 DWORD dwSearchCondition
, GROUPID
* lpGroupId
, LPVOID lpReserved
)
3643 FIXME("(0x%08lx, 0x%08lx, %p, 0x%08lx, %p, %p) stub\n", dwFlags
, dwFilter
, lpSearchCondition
,
3644 dwSearchCondition
, lpGroupId
, lpReserved
);
3648 BOOL WINAPI
FindNextUrlCacheEntryExA(
3650 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
,
3651 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3653 LPDWORD pcbReserved2
,
3657 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
,
3658 lpReserved
, pcbReserved2
, lpReserved3
);
3662 BOOL WINAPI
FindNextUrlCacheEntryExW(
3664 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
,
3665 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3667 LPDWORD pcbReserved2
,
3671 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
,
3672 lpReserved
, pcbReserved2
, lpReserved3
);
3676 BOOL WINAPI
FindNextUrlCacheGroup( HANDLE hFind
, GROUPID
* lpGroupId
, LPVOID lpReserved
)
3678 FIXME("(%p, %p, %p) stub\n", hFind
, lpGroupId
, lpReserved
);
3682 /***********************************************************************
3683 * CreateUrlCacheGroup (WININET.@)
3686 INTERNETAPI GROUPID WINAPI
CreateUrlCacheGroup(DWORD dwFlags
, LPVOID lpReserved
)
3688 FIXME("(0x%08lx, %p): stub\n", dwFlags
, lpReserved
);
3692 /***********************************************************************
3693 * DeleteUrlCacheGroup (WININET.@)
3696 BOOL WINAPI
DeleteUrlCacheGroup(GROUPID GroupId
, DWORD dwFlags
, LPVOID lpReserved
)
3698 FIXME("(0x%s, 0x%08lx, %p) stub\n",
3699 wine_dbgstr_longlong(GroupId
), dwFlags
, lpReserved
);
3703 /***********************************************************************
3704 * DeleteWpadCacheForNetworks (WININET.@)
3705 * Undocumented, added in IE8
3707 BOOL WINAPI
DeleteWpadCacheForNetworks(DWORD unk1
)
3709 FIXME("(%ld) stub\n", unk1
);
3713 /***********************************************************************
3714 * SetUrlCacheEntryGroupA (WININET.@)
3717 BOOL WINAPI
SetUrlCacheEntryGroupA(LPCSTR lpszUrlName
, DWORD dwFlags
,
3718 GROUPID GroupId
, LPBYTE pbGroupAttributes
, DWORD cbGroupAttributes
,
3721 FIXME("(%s, 0x%08lx, 0x%s, %p, 0x%08lx, %p) stub\n",
3722 debugstr_a(lpszUrlName
), dwFlags
, wine_dbgstr_longlong(GroupId
),
3723 pbGroupAttributes
, cbGroupAttributes
, lpReserved
);
3724 SetLastError(ERROR_FILE_NOT_FOUND
);
3728 /***********************************************************************
3729 * SetUrlCacheEntryGroupW (WININET.@)
3732 BOOL WINAPI
SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName
, DWORD dwFlags
,
3733 GROUPID GroupId
, LPBYTE pbGroupAttributes
, DWORD cbGroupAttributes
,
3736 FIXME("(%s, 0x%08lx, 0x%s, %p, 0x%08lx, %p) stub\n",
3737 debugstr_w(lpszUrlName
), dwFlags
, wine_dbgstr_longlong(GroupId
),
3738 pbGroupAttributes
, cbGroupAttributes
, lpReserved
);
3739 SetLastError(ERROR_FILE_NOT_FOUND
);
3743 static cache_container
*find_container(DWORD flags
)
3745 cache_container
*container
;
3747 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
3749 switch (flags
& (CACHE_CONFIG_CONTENT_PATHS_FC
| CACHE_CONFIG_COOKIES_PATHS_FC
| CACHE_CONFIG_HISTORY_PATHS_FC
))
3752 case CACHE_CONFIG_CONTENT_PATHS_FC
:
3753 if (container
->default_entry_type
== NORMAL_CACHE_ENTRY
)
3757 case CACHE_CONFIG_COOKIES_PATHS_FC
:
3758 if (container
->default_entry_type
== COOKIE_CACHE_ENTRY
)
3762 case CACHE_CONFIG_HISTORY_PATHS_FC
:
3763 if (container
->default_entry_type
== URLHISTORY_CACHE_ENTRY
)
3768 FIXME("flags %08lx not handled\n", flags
);
3776 /***********************************************************************
3777 * GetUrlCacheConfigInfoW (WININET.@)
3779 BOOL WINAPI
GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW info
, LPDWORD size
, DWORD flags
)
3781 cache_container
*container
;
3784 FIXME("(%p, %p, %lx): semi-stub\n", info
, size
, flags
);
3786 if (!info
|| !(container
= find_container(flags
)))
3788 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
3792 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
3793 if (error
!= ERROR_SUCCESS
)
3795 INTERNET_SetLastError(error
);
3799 info
->dwContainer
= 0;
3800 info
->dwQuota
= FILE_SIZE(MAX_BLOCK_NO
) / 1024;
3801 info
->dwReserved4
= 0;
3802 info
->fPerUser
= TRUE
;
3803 info
->dwSyncMode
= 0;
3804 info
->dwNumCachePaths
= 1;
3805 info
->dwNormalUsage
= 0;
3806 info
->dwExemptUsage
= 0;
3807 info
->dwCacheSize
= container
->file_size
/ 1024;
3808 lstrcpynW(info
->CachePath
, container
->path
, MAX_PATH
);
3810 cache_container_close_index(container
);
3812 TRACE("CachePath %s\n", debugstr_w(info
->CachePath
));
3817 /***********************************************************************
3818 * GetUrlCacheConfigInfoA (WININET.@)
3820 BOOL WINAPI
GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA info
, LPDWORD size
, DWORD flags
)
3822 INTERNET_CACHE_CONFIG_INFOW infoW
;
3824 TRACE("(%p, %p, %lx)\n", info
, size
, flags
);
3828 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
3832 infoW
.dwStructSize
= sizeof(infoW
);
3833 if (!GetUrlCacheConfigInfoW(&infoW
, size
, flags
))
3836 info
->dwContainer
= infoW
.dwContainer
;
3837 info
->dwQuota
= infoW
.dwQuota
;
3838 info
->dwReserved4
= infoW
.dwReserved4
;
3839 info
->fPerUser
= infoW
.fPerUser
;
3840 info
->dwSyncMode
= infoW
.dwSyncMode
;
3841 info
->dwNumCachePaths
= infoW
.dwNumCachePaths
;
3842 info
->dwNormalUsage
= infoW
.dwNormalUsage
;
3843 info
->dwExemptUsage
= infoW
.dwExemptUsage
;
3844 info
->dwCacheSize
= infoW
.dwCacheSize
;
3845 WideCharToMultiByte(CP_ACP
, 0, infoW
.CachePath
, -1, info
->CachePath
, MAX_PATH
, NULL
, NULL
);
3850 BOOL WINAPI
GetUrlCacheGroupAttributeA( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3851 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo
,
3852 LPDWORD lpdwGroupInfo
, LPVOID lpReserved
)
3854 FIXME("(0x%s, 0x%08lx, 0x%08lx, %p, %p, %p) stub\n",
3855 wine_dbgstr_longlong(gid
), dwFlags
, dwAttributes
, lpGroupInfo
,
3856 lpdwGroupInfo
, lpReserved
);
3860 BOOL WINAPI
GetUrlCacheGroupAttributeW( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3861 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo
,
3862 LPDWORD lpdwGroupInfo
, LPVOID lpReserved
)
3864 FIXME("(0x%s, 0x%08lx, 0x%08lx, %p, %p, %p) stub\n",
3865 wine_dbgstr_longlong(gid
), dwFlags
, dwAttributes
, lpGroupInfo
,
3866 lpdwGroupInfo
, lpReserved
);
3870 BOOL WINAPI
SetUrlCacheGroupAttributeA( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3871 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo
, LPVOID lpReserved
)
3873 FIXME("(0x%s, 0x%08lx, 0x%08lx, %p, %p) stub\n",
3874 wine_dbgstr_longlong(gid
), dwFlags
, dwAttributes
, lpGroupInfo
, lpReserved
);
3878 BOOL WINAPI
SetUrlCacheGroupAttributeW( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3879 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo
, LPVOID lpReserved
)
3881 FIXME("(0x%s, 0x%08lx, 0x%08lx, %p, %p) stub\n",
3882 wine_dbgstr_longlong(gid
), dwFlags
, dwAttributes
, lpGroupInfo
, lpReserved
);
3886 BOOL WINAPI
SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo
, DWORD dwFieldControl
)
3888 FIXME("(%p, 0x%08lx) stub\n", lpCacheConfigInfo
, dwFieldControl
);
3892 BOOL WINAPI
SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo
, DWORD dwFieldControl
)
3894 FIXME("(%p, 0x%08lx) stub\n", lpCacheConfigInfo
, dwFieldControl
);
3898 /***********************************************************************
3899 * DeleteIE3Cache (WININET.@)
3901 * Deletes the files used by the IE3 URL caching system.
3904 * hWnd [I] A dummy window.
3905 * hInst [I] Instance of process calling the function.
3906 * lpszCmdLine [I] Options used by function.
3907 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3909 DWORD WINAPI
DeleteIE3Cache(HWND hWnd
, HINSTANCE hInst
, LPSTR lpszCmdLine
, int nCmdShow
)
3911 FIXME("(%p, %p, %s, %d)\n", hWnd
, hInst
, debugstr_a(lpszCmdLine
), nCmdShow
);
3915 static BOOL
urlcache_entry_is_expired(const entry_url
*pUrlEntry
,
3916 FILETIME
*pftLastModified
)
3919 FILETIME now
, expired
;
3921 *pftLastModified
= pUrlEntry
->modification_time
;
3922 GetSystemTimeAsFileTime(&now
);
3923 dos_date_time_to_file_time(pUrlEntry
->expire_date
,
3924 pUrlEntry
->expire_time
, &expired
);
3925 /* If the expired time is 0, it's interpreted as not expired */
3926 if (!expired
.dwLowDateTime
&& !expired
.dwHighDateTime
)
3929 ret
= CompareFileTime(&expired
, &now
) < 0;
3933 /***********************************************************************
3934 * IsUrlCacheEntryExpiredA (WININET.@)
3938 * dwFlags [I] Unknown
3939 * pftLastModified [O] Last modified time
3941 BOOL WINAPI
IsUrlCacheEntryExpiredA(LPCSTR url
, DWORD dwFlags
, FILETIME
* pftLastModified
)
3943 urlcache_header
*pHeader
;
3944 struct hash_entry
*pHashEntry
;
3945 const entry_header
*pEntry
;
3946 const entry_url
* pUrlEntry
;
3947 cache_container
*pContainer
;
3950 TRACE("(%s, %08lx, %p)\n", debugstr_a(url
), dwFlags
, pftLastModified
);
3952 if (!url
|| !pftLastModified
)
3955 FIXME("unknown flags 0x%08lx\n", dwFlags
);
3957 /* Any error implies that the URL is expired, i.e. not in the cache */
3958 if (cache_containers_find(url
, &pContainer
))
3960 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3964 if (cache_container_open_index(pContainer
, MIN_BLOCK_NO
))
3966 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3970 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3972 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3976 if (!urlcache_find_hash_entry(pHeader
, url
, &pHashEntry
))
3978 cache_container_unlock_index(pContainer
, pHeader
);
3979 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3980 TRACE("entry %s not found!\n", url
);
3984 pEntry
= (const entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
3985 if (pEntry
->signature
!= URL_SIGNATURE
)
3987 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR
)&pEntry
->signature
, sizeof(DWORD
)));
3988 cache_container_unlock_index(pContainer
, pHeader
);
3989 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3993 pUrlEntry
= (const entry_url
*)pEntry
;
3994 expired
= urlcache_entry_is_expired(pUrlEntry
, pftLastModified
);
3996 cache_container_unlock_index(pContainer
, pHeader
);
4001 /***********************************************************************
4002 * IsUrlCacheEntryExpiredW (WININET.@)
4006 * dwFlags [I] Unknown
4007 * pftLastModified [O] Last modified time
4009 BOOL WINAPI
IsUrlCacheEntryExpiredW(LPCWSTR url
, DWORD dwFlags
, FILETIME
* pftLastModified
)
4014 if(!urlcache_encode_url_alloc(url
, &encoded_url
))
4017 ret
= IsUrlCacheEntryExpiredA(encoded_url
, dwFlags
, pftLastModified
);
4022 /***********************************************************************
4023 * GetDiskInfoA (WININET.@)
4025 BOOL WINAPI
GetDiskInfoA(PCSTR path
, PDWORD cluster_size
, PDWORDLONG free
, PDWORDLONG total
)
4028 ULARGE_INTEGER bytes_free
, bytes_total
;
4030 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path
), cluster_size
, free
, total
);
4034 SetLastError(ERROR_INVALID_PARAMETER
);
4038 if ((ret
= GetDiskFreeSpaceExA(path
, NULL
, &bytes_total
, &bytes_free
)))
4040 if (cluster_size
) *cluster_size
= 1;
4041 if (free
) *free
= bytes_free
.QuadPart
;
4042 if (total
) *total
= bytes_total
.QuadPart
;
4047 /***********************************************************************
4048 * RegisterUrlCacheNotification (WININET.@)
4050 DWORD WINAPI
RegisterUrlCacheNotification(LPVOID a
, DWORD b
, DWORD c
, DWORD d
, DWORD e
, DWORD f
)
4052 FIXME("(%p %lx %lx %lx %lx %lx)\n", a
, b
, c
, d
, e
, f
);
4056 /***********************************************************************
4057 * IncrementUrlCacheHeaderData (WININET.@)
4059 BOOL WINAPI
IncrementUrlCacheHeaderData(DWORD index
, LPDWORD data
)
4061 FIXME("(%lu, %p)\n", index
, data
);
4065 /***********************************************************************
4066 * RunOnceUrlCache (WININET.@)
4069 DWORD WINAPI
RunOnceUrlCache(HWND hwnd
, HINSTANCE hinst
, LPSTR cmd
, int cmdshow
)
4071 FIXME("(%p, %p, %s, %d): stub\n", hwnd
, hinst
, debugstr_a(cmd
), cmdshow
);
4075 BOOL
init_urlcache(void)
4077 dll_unload_event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
4078 if(!dll_unload_event
)
4081 free_cache_running
= CreateSemaphoreW(NULL
, 1, 1, NULL
);
4082 if(!free_cache_running
) {
4083 CloseHandle(dll_unload_event
);
4087 cache_containers_init();
4091 void free_urlcache(void)
4093 SetEvent(dll_unload_event
);
4094 WaitForSingleObject(free_cache_running
, INFINITE
);
4095 ReleaseSemaphore(free_cache_running
, 1, NULL
);
4096 CloseHandle(free_cache_running
);
4097 CloseHandle(dll_unload_event
);
4099 cache_containers_free();
4102 /***********************************************************************
4103 * LoadUrlCacheContent (WININET.@)
4105 BOOL WINAPI
LoadUrlCacheContent(void)