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
26 #include "wine/port.h"
28 #define NONAMELESSUNION
29 #define NONAMELESSSTRUCT
31 #if defined(__MINGW32__) || defined (_MSC_VER)
39 #include <sys/types.h>
40 #ifdef HAVE_SYS_SOCKET_H
41 # include <sys/socket.h>
58 #include "wine/unicode.h"
59 #include "wine/debug.h"
61 WINE_DEFAULT_DEBUG_CHANNEL(wininet
);
63 static const char urlcache_ver_prefix
[] = "WINE URLCache Ver ";
64 static const char urlcache_ver
[] = "0.2012001";
67 #define CHAR_BIT (8 * sizeof(CHAR))
70 #define ENTRY_START_OFFSET 0x4000
72 #define MAX_DIR_NO 0x20
74 #define HASHTABLE_SIZE 448
75 #define HASHTABLE_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
76 #define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
77 #define ALLOCATION_TABLE_OFFSET 0x250
78 #define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
79 #define MIN_BLOCK_NO 0x80
80 #define MAX_BLOCK_NO (ALLOCATION_TABLE_SIZE * CHAR_BIT)
81 #define FILE_SIZE(blocks) ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
83 #define HASHTABLE_URL 0
84 #define HASHTABLE_DEL 1
85 #define HASHTABLE_LOCK 2
86 #define HASHTABLE_FREE 3
87 #define HASHTABLE_REDR 5
88 #define HASHTABLE_FLAG_BITS 6
90 #define PENDING_DELETE_CACHE_ENTRY 0x00400000
91 #define INSTALLED_CACHE_ENTRY 0x10000000
92 #define GET_INSTALLED_ENTRY 0x200
93 #define CACHE_CONTAINER_NO_SUBDIR 0xFE
95 #define CACHE_HEADER_DATA_ROOT_LEAK_OFFSET 0x16
97 #define FILETIME_SECOND 10000000
99 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
100 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
101 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
102 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
103 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
105 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
107 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
112 DWORD blocks_used
; /* number of 128byte blocks used by this entry */
118 FILETIME modification_time
;
119 FILETIME access_time
;
120 WORD expire_date
; /* expire date in dos format */
121 WORD expire_time
; /* expire time in dos format */
122 DWORD unk1
; /* usually zero */
123 ULARGE_INTEGER size
; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
124 DWORD unk2
; /* usually zero */
125 DWORD exempt_delta
; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
126 DWORD unk3
; /* usually 0x60 */
127 DWORD url_off
; /* offset of start of url from start of entry */
128 BYTE cache_dir
; /* index of cache directory this url is stored in */
129 BYTE unk4
; /* usually zero */
130 WORD unk5
; /* usually 0x1010 */
131 DWORD local_name_off
; /* offset of start of local filename from start of entry */
132 DWORD cache_entry_type
; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
133 DWORD header_info_off
; /* offset of start of header info from start of entry */
134 DWORD header_info_size
;
135 DWORD file_extension_off
; /* offset of start of file extension from start of entry */
136 WORD sync_date
; /* last sync date in dos format */
137 WORD sync_time
; /* last sync time in dos format */
138 DWORD hit_rate
; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
139 DWORD use_count
; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
142 DWORD unk7
; /* usually zero */
143 DWORD unk8
; /* usually zero */
144 /* packing to dword align start of next field */
145 /* CHAR szSourceUrlName[]; (url) */
146 /* packing to dword align start of next field */
147 /* CHAR szLocalFileName[]; (local file name excluding path) */
148 /* packing to dword align start of next field */
149 /* CHAR szHeaderInfo[]; (header info) */
163 struct hash_entry hash_table
[HASHTABLE_SIZE
];
170 DWORD hash_table_off
;
171 DWORD capacity_in_blocks
;
174 ULARGE_INTEGER cache_limit
;
175 ULARGE_INTEGER cache_usage
;
176 ULARGE_INTEGER exempt_usage
;
178 struct _directory_data
181 char name
[DIR_LENGTH
];
182 } directory_data
[MAX_DIR_NO
];
184 BYTE allocation_table
[ALLOCATION_TABLE_SIZE
];
195 struct list entry
; /* part of a list */
196 char *cache_prefix
; /* string that has to be prefixed for this container to be used */
197 LPWSTR path
; /* path to url container directory */
198 HANDLE mapping
; /* handle of file mapping */
199 DWORD file_size
; /* size of file when mapping was opened */
200 HANDLE mutex
; /* handle of mutex */
201 DWORD default_entry_type
;
207 char *url_search_pattern
;
209 DWORD hash_table_idx
;
210 DWORD hash_entry_idx
;
213 /* List of all containers available */
214 static struct list UrlContainers
= LIST_INIT(UrlContainers
);
216 /***********************************************************************
217 * urlcache_block_is_free (Internal)
219 * Is the specified block number free?
226 static inline BYTE
urlcache_block_is_free(BYTE
*allocation_table
, DWORD block_number
)
228 BYTE mask
= 1 << (block_number
%CHAR_BIT
);
229 return (allocation_table
[block_number
/CHAR_BIT
] & mask
) == 0;
232 /***********************************************************************
233 * urlcache_block_free (Internal)
235 * Marks the specified block as free
238 * this function is not updating used blocks count
244 static inline void urlcache_block_free(BYTE
*allocation_table
, DWORD block_number
)
246 BYTE mask
= ~(1 << (block_number
%CHAR_BIT
));
247 allocation_table
[block_number
/CHAR_BIT
] &= mask
;
250 /***********************************************************************
251 * urlcache_block_alloc (Internal)
253 * Marks the specified block as allocated
256 * this function is not updating used blocks count
262 static inline void urlcache_block_alloc(BYTE
*allocation_table
, DWORD block_number
)
264 BYTE mask
= 1 << (block_number
%CHAR_BIT
);
265 allocation_table
[block_number
/CHAR_BIT
] |= mask
;
268 /***********************************************************************
269 * urlcache_entry_alloc (Internal)
271 * Finds and allocates the first block of free space big enough and
272 * sets entry to point to it.
275 * ERROR_SUCCESS when free memory block was found
276 * Any other Win32 error code if the entry could not be added
279 static DWORD
urlcache_entry_alloc(urlcache_header
*header
, DWORD blocks_needed
, entry_header
**entry
)
281 DWORD block
, block_size
;
283 for(block
=0; block
<header
->capacity_in_blocks
; block
+=block_size
+1)
286 while(block_size
<blocks_needed
&& block_size
+block
<header
->capacity_in_blocks
287 && urlcache_block_is_free(header
->allocation_table
, block
+block_size
))
290 if(block_size
== blocks_needed
)
294 TRACE("Found free blocks starting at no. %d (0x%x)\n", block
, ENTRY_START_OFFSET
+block
*BLOCKSIZE
);
296 for(index
=0; index
<blocks_needed
; index
++)
297 urlcache_block_alloc(header
->allocation_table
, block
+index
);
299 *entry
= (entry_header
*)((BYTE
*)header
+ENTRY_START_OFFSET
+block
*BLOCKSIZE
);
300 for(index
=0; index
<blocks_needed
*BLOCKSIZE
/sizeof(DWORD
); index
++)
301 ((DWORD
*)*entry
)[index
] = 0xdeadbeef;
302 (*entry
)->blocks_used
= blocks_needed
;
304 header
->blocks_in_use
+= blocks_needed
;
305 return ERROR_SUCCESS
;
309 return ERROR_HANDLE_DISK_FULL
;
312 /***********************************************************************
313 * urlcache_entry_free (Internal)
315 * Deletes the specified entry and frees the space allocated to it
318 * TRUE if it succeeded
322 static BOOL
urlcache_entry_free(urlcache_header
*header
, entry_header
*entry
)
324 DWORD start_block
, block
;
326 /* update allocation table */
327 start_block
= ((DWORD
)((BYTE
*)entry
- (BYTE
*)header
) - ENTRY_START_OFFSET
) / BLOCKSIZE
;
328 for(block
= start_block
; block
< start_block
+entry
->blocks_used
; block
++)
329 urlcache_block_free(header
->allocation_table
, block
);
331 header
->blocks_in_use
-= entry
->blocks_used
;
335 /***********************************************************************
336 * urlcache_create_hash_table (Internal)
338 * Creates a new hash table in free space and adds it to the chain of existing
342 * ERROR_SUCCESS if the hash table was created
343 * ERROR_DISK_FULL if the hash table could not be created
346 static DWORD
urlcache_create_hash_table(urlcache_header
*header
, entry_hash_table
*hash_table_prev
, entry_hash_table
**hash_table
)
348 DWORD dwOffset
, error
;
351 if((error
= urlcache_entry_alloc(header
, 0x20, (entry_header
**)hash_table
)) != ERROR_SUCCESS
)
354 dwOffset
= (BYTE
*)*hash_table
-(BYTE
*)header
;
357 hash_table_prev
->next
= dwOffset
;
359 header
->hash_table_off
= dwOffset
;
361 (*hash_table
)->header
.signature
= HASH_SIGNATURE
;
362 (*hash_table
)->next
= 0;
363 (*hash_table
)->id
= hash_table_prev
? hash_table_prev
->id
+1 : 0;
364 for(i
= 0; i
< HASHTABLE_SIZE
; i
++) {
365 (*hash_table
)->hash_table
[i
].offset
= HASHTABLE_FREE
;
366 (*hash_table
)->hash_table
[i
].key
= HASHTABLE_FREE
;
368 return ERROR_SUCCESS
;
371 /***********************************************************************
372 * cache_container_create_object_name (Internal)
374 * Converts a path to a name suitable for use as a Win32 object name.
375 * Replaces '\\' characters in-place with the specified character
376 * (usually '_' or '!')
382 static void cache_container_create_object_name(LPWSTR lpszPath
, WCHAR replace
)
384 for (; *lpszPath
; lpszPath
++)
386 if (*lpszPath
== '\\')
391 /* Caller must hold container lock */
392 static HANDLE
cache_container_map_index(HANDLE file
, const WCHAR
*path
, DWORD size
, BOOL
*validate
)
394 static const WCHAR mapping_name_format
[]
395 = {'%','s','i','n','d','e','x','.','d','a','t','_','%','l','u',0};
396 WCHAR mapping_name
[MAX_PATH
];
399 wsprintfW(mapping_name
, mapping_name_format
, path
, size
);
400 cache_container_create_object_name(mapping_name
, '_');
402 mapping
= OpenFileMappingW(FILE_MAP_WRITE
, FALSE
, mapping_name
);
404 if(validate
) *validate
= FALSE
;
408 if(validate
) *validate
= TRUE
;
409 return CreateFileMappingW(file
, NULL
, PAGE_READWRITE
, 0, 0, mapping_name
);
412 /* Caller must hold container lock */
413 static DWORD
cache_container_set_size(cache_container
*container
, HANDLE file
, DWORD blocks_no
)
415 static const WCHAR cache_content_key
[] = {'S','o','f','t','w','a','r','e','\\',
416 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
417 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
418 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s','\\',
419 'C','a','c','h','e','\\','C','o','n','t','e','n','t',0};
420 static const WCHAR cache_limit
[] = {'C','a','c','h','e','L','i','m','i','t',0};
422 DWORD file_size
= FILE_SIZE(blocks_no
);
423 WCHAR dir_path
[MAX_PATH
], *dir_name
;
424 entry_hash_table
*hashtable_entry
;
425 urlcache_header
*header
;
431 if(SetFilePointer(file
, file_size
, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
432 return GetLastError();
434 if(!SetEndOfFile(file
))
435 return GetLastError();
437 mapping
= cache_container_map_index(file
, container
->path
, file_size
, NULL
);
439 return GetLastError();
441 header
= MapViewOfFile(mapping
, FILE_MAP_WRITE
, 0, 0, 0);
443 CloseHandle(mapping
);
444 return GetLastError();
447 if(blocks_no
!= MIN_BLOCK_NO
) {
448 if(file_size
> header
->size
)
449 memset((char*)header
+header
->size
, 0, file_size
-header
->size
);
450 header
->size
= file_size
;
451 header
->capacity_in_blocks
= blocks_no
;
453 UnmapViewOfFile(header
);
454 CloseHandle(container
->mapping
);
455 container
->mapping
= mapping
;
456 container
->file_size
= file_size
;
457 return ERROR_SUCCESS
;
460 memset(header
, 0, file_size
);
461 /* First set some constants and defaults in the header */
462 memcpy(header
->signature
, urlcache_ver_prefix
, sizeof(urlcache_ver_prefix
)-1);
463 memcpy(header
->signature
+sizeof(urlcache_ver_prefix
)-1, urlcache_ver
, sizeof(urlcache_ver
)-1);
464 header
->size
= file_size
;
465 header
->capacity_in_blocks
= blocks_no
;
466 /* 127MB - taken from default for Windows 2000 */
467 header
->cache_limit
.QuadPart
= 0x07ff5400;
468 /* Copied from a Windows 2000 cache index */
469 header
->dirs_no
= container
->default_entry_type
==NORMAL_CACHE_ENTRY
? 4 : 0;
471 /* If the registry has a cache size set, use the registry value */
472 if(RegOpenKeyW(HKEY_CURRENT_USER
, cache_content_key
, &key
) == ERROR_SUCCESS
) {
473 DWORD dw
, len
= sizeof(dw
), keytype
;
475 if(RegQueryValueExW(key
, cache_limit
, NULL
, &keytype
, (BYTE
*)&dw
, &len
) == ERROR_SUCCESS
&&
476 keytype
== REG_DWORD
)
477 header
->cache_limit
.QuadPart
= (ULONGLONG
)dw
* 1024;
481 urlcache_create_hash_table(header
, NULL
, &hashtable_entry
);
483 /* Last step - create the directories */
484 strcpyW(dir_path
, container
->path
);
485 dir_name
= dir_path
+ strlenW(dir_path
);
488 GetSystemTimeAsFileTime(&ft
);
490 for(i
=0; i
<header
->dirs_no
; ++i
) {
491 header
->directory_data
[i
].files_no
= 0;
493 ULONGLONG n
= ft
.dwHighDateTime
;
496 /* Generate a file name to attempt to create.
497 * This algorithm will create what will appear
498 * to be random and unrelated directory names
499 * of up to 9 characters in length.
502 n
+= ft
.dwLowDateTime
;
503 n
^= ((ULONGLONG
) i
<< 56) | ((ULONGLONG
) j
<< 48);
505 for(k
= 0; k
< 8; ++k
) {
508 /* Dividing by a prime greater than 36 helps
509 * with the appearance of randomness
514 dir_name
[k
] = '0' + r
;
516 dir_name
[k
] = 'A' + (r
- 10);
519 if(CreateDirectoryW(dir_path
, 0)) {
520 /* The following is OK because we generated an
521 * 8 character directory name made from characters
522 * [A-Z0-9], which are equivalent for all code
523 * pages and for UTF-16
525 for (k
= 0; k
< 8; ++k
)
526 header
->directory_data
[i
].name
[k
] = dir_name
[k
];
529 /* Give up. The most likely cause of this
530 * is a full disk, but whatever the cause
531 * is, it should be more than apparent that
534 UnmapViewOfFile(header
);
535 CloseHandle(mapping
);
536 return GetLastError();
541 UnmapViewOfFile(header
);
542 CloseHandle(container
->mapping
);
543 container
->mapping
= mapping
;
544 container
->file_size
= file_size
;
545 return ERROR_SUCCESS
;
548 static BOOL
cache_container_is_valid(urlcache_header
*header
, DWORD file_size
)
550 DWORD allocation_size
, count_bits
, i
;
552 if(file_size
< FILE_SIZE(MIN_BLOCK_NO
))
555 if(file_size
!= header
->size
)
558 if (!memcmp(header
->signature
, urlcache_ver_prefix
, sizeof(urlcache_ver_prefix
)-1) &&
559 memcmp(header
->signature
+sizeof(urlcache_ver_prefix
)-1, urlcache_ver
, sizeof(urlcache_ver
)-1))
562 if(FILE_SIZE(header
->capacity_in_blocks
) != file_size
)
566 for(i
=0; i
<header
->capacity_in_blocks
/8; i
++) {
567 for(count_bits
= header
->allocation_table
[i
]; count_bits
!=0; count_bits
>>=1) {
572 if(allocation_size
!= header
->blocks_in_use
)
575 for(; i
<ALLOCATION_TABLE_SIZE
; i
++) {
576 if(header
->allocation_table
[i
])
583 /***********************************************************************
584 * cache_container_open_index (Internal)
586 * Opens the index file and saves mapping handle
589 * ERROR_SUCCESS if succeeded
590 * Any other Win32 error code if failed
593 static DWORD
cache_container_open_index(cache_container
*container
, DWORD blocks_no
)
595 static const WCHAR index_dat
[] = {'i','n','d','e','x','.','d','a','t',0};
598 WCHAR index_path
[MAX_PATH
];
602 WaitForSingleObject(container
->mutex
, INFINITE
);
604 if(container
->mapping
) {
605 ReleaseMutex(container
->mutex
);
606 return ERROR_SUCCESS
;
609 strcpyW(index_path
, container
->path
);
610 strcatW(index_path
, index_dat
);
612 file
= CreateFileW(index_path
, GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_ALWAYS
, 0, NULL
);
613 if(file
== INVALID_HANDLE_VALUE
) {
614 /* Maybe the directory wasn't there? Try to create it */
615 if(CreateDirectoryW(container
->path
, 0))
616 file
= CreateFileW(index_path
, GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_ALWAYS
, 0, NULL
);
618 if(file
== INVALID_HANDLE_VALUE
) {
619 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(index_path
));
620 ReleaseMutex(container
->mutex
);
621 return GetLastError();
624 file_size
= GetFileSize(file
, NULL
);
625 if(file_size
== INVALID_FILE_SIZE
) {
627 ReleaseMutex(container
->mutex
);
628 return GetLastError();
631 if(blocks_no
< MIN_BLOCK_NO
)
632 blocks_no
= MIN_BLOCK_NO
;
633 else if(blocks_no
> MAX_BLOCK_NO
)
634 blocks_no
= MAX_BLOCK_NO
;
636 if(file_size
< FILE_SIZE(blocks_no
)) {
637 DWORD ret
= cache_container_set_size(container
, file
, blocks_no
);
639 ReleaseMutex(container
->mutex
);
643 container
->file_size
= file_size
;
644 container
->mapping
= cache_container_map_index(file
, container
->path
, file_size
, &validate
);
646 if(container
->mapping
&& validate
) {
647 urlcache_header
*header
= MapViewOfFile(container
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
649 if(header
&& !cache_container_is_valid(header
, file_size
)) {
650 WARN("detected old or broken index.dat file\n");
651 UnmapViewOfFile(header
);
652 FreeUrlCacheSpaceW(container
->path
, 100, 0);
654 UnmapViewOfFile(header
);
656 CloseHandle(container
->mapping
);
657 container
->mapping
= NULL
;
661 if(!container
->mapping
)
663 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
664 ReleaseMutex(container
->mutex
);
665 return GetLastError();
668 ReleaseMutex(container
->mutex
);
669 return ERROR_SUCCESS
;
672 /***********************************************************************
673 * cache_container_close_index (Internal)
681 static void cache_container_close_index(cache_container
*pContainer
)
683 CloseHandle(pContainer
->mapping
);
684 pContainer
->mapping
= NULL
;
687 static BOOL
cache_containers_add(LPCWSTR cache_prefix
,
688 LPCWSTR path
, DWORD default_entry_type
, LPWSTR mutex_name
)
690 cache_container
*pContainer
= heap_alloc(sizeof(cache_container
));
691 int cache_prefix_len
= strlenW(cache_prefix
);
698 pContainer
->mapping
= NULL
;
699 pContainer
->file_size
= 0;
700 pContainer
->default_entry_type
= default_entry_type
;
702 pContainer
->path
= heap_strdupW(path
);
703 if (!pContainer
->path
)
705 heap_free(pContainer
);
709 pContainer
->cache_prefix
= heap_alloc((cache_prefix_len
+ 1) * sizeof(WCHAR
));
710 if (!pContainer
->cache_prefix
)
712 heap_free(pContainer
->path
);
713 heap_free(pContainer
);
717 memcpy(pContainer
->cache_prefix
, cache_prefix
, (cache_prefix_len
+ 1) * sizeof(WCHAR
));
719 CharLowerW(mutex_name
);
720 cache_container_create_object_name(mutex_name
, '!');
722 if ((pContainer
->mutex
= CreateMutexW(NULL
, FALSE
, mutex_name
)) == NULL
)
724 ERR("couldn't create mutex (error is %d)\n", GetLastError());
725 heap_free(pContainer
->path
);
726 heap_free(pContainer
);
730 list_add_head(&UrlContainers
, &pContainer
->entry
);
735 static void cache_container_delete_container(cache_container
*pContainer
)
737 list_remove(&pContainer
->entry
);
739 cache_container_close_index(pContainer
);
740 CloseHandle(pContainer
->mutex
);
741 heap_free(pContainer
->path
);
742 heap_free(pContainer
->cache_prefix
);
743 heap_free(pContainer
);
746 static void cache_containers_init(void)
748 static const WCHAR UrlSuffix
[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
749 static const WCHAR UrlPrefix
[] = {0};
750 static const WCHAR HistorySuffix
[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
751 static const WCHAR HistoryPrefix
[] = {'V','i','s','i','t','e','d',':',0};
752 static const WCHAR CookieSuffix
[] = {0};
753 static const WCHAR CookiePrefix
[] = {'C','o','o','k','i','e',':',0};
756 int nFolder
; /* CSIDL_* constant */
757 const WCHAR
* shpath_suffix
; /* suffix on path returned by SHGetSpecialFolderPath */
758 const WCHAR
* cache_prefix
; /* prefix used to reference the container */
759 DWORD default_entry_type
;
760 } DefaultContainerData
[] =
762 { CSIDL_INTERNET_CACHE
, UrlSuffix
, UrlPrefix
, NORMAL_CACHE_ENTRY
},
763 { CSIDL_HISTORY
, HistorySuffix
, HistoryPrefix
, URLHISTORY_CACHE_ENTRY
},
764 { CSIDL_COOKIES
, CookieSuffix
, CookiePrefix
, COOKIE_CACHE_ENTRY
},
768 for (i
= 0; i
< sizeof(DefaultContainerData
) / sizeof(DefaultContainerData
[0]); i
++)
770 WCHAR wszCachePath
[MAX_PATH
];
771 WCHAR wszMutexName
[MAX_PATH
];
772 int path_len
, suffix_len
;
775 if (!SHGetSpecialFolderPathW(NULL
, wszCachePath
, DefaultContainerData
[i
].nFolder
, TRUE
))
777 ERR("Couldn't get path for default container %u\n", i
);
780 path_len
= strlenW(wszCachePath
);
781 suffix_len
= strlenW(DefaultContainerData
[i
].shpath_suffix
);
783 if (path_len
+ suffix_len
+ 2 > MAX_PATH
)
785 ERR("Path too long\n");
789 wszCachePath
[path_len
] = '\\';
790 wszCachePath
[path_len
+1] = 0;
792 strcpyW(wszMutexName
, wszCachePath
);
796 memcpy(wszCachePath
+ path_len
+ 1, DefaultContainerData
[i
].shpath_suffix
, (suffix_len
+ 1) * sizeof(WCHAR
));
797 wszCachePath
[path_len
+ suffix_len
+ 1] = '\\';
798 wszCachePath
[path_len
+ suffix_len
+ 2] = '\0';
801 if (!WideCharToMultiByte(CP_ACP
, WC_NO_BEST_FIT_CHARS
, wszCachePath
, path_len
,
802 NULL
, 0, NULL
, &def_char
) || def_char
)
806 /* cannot convert path to ANSI code page */
807 if (!(path_len
= GetShortPathNameW(wszCachePath
, tmp
, MAX_PATH
)) ||
808 !WideCharToMultiByte(CP_ACP
, WC_NO_BEST_FIT_CHARS
, tmp
, path_len
,
809 NULL
, 0, NULL
, &def_char
) || def_char
)
810 ERR("Can't create container path accessible by ANSI functions\n");
812 memcpy(wszCachePath
, tmp
, (path_len
+1)*sizeof(WCHAR
));
815 cache_containers_add(DefaultContainerData
[i
].cache_prefix
, wszCachePath
,
816 DefaultContainerData
[i
].default_entry_type
, wszMutexName
);
820 static void cache_containers_free(void)
822 while(!list_empty(&UrlContainers
))
823 cache_container_delete_container(
824 LIST_ENTRY(list_head(&UrlContainers
), cache_container
, entry
)
828 static DWORD
cache_containers_find(const char *url
, cache_container
**ret
)
830 cache_container
*container
;
832 TRACE("searching for prefix for URL: %s\n", debugstr_a(url
));
835 return ERROR_INVALID_PARAMETER
;
837 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
839 int prefix_len
= strlen(container
->cache_prefix
);
841 if(!strncmp(container
->cache_prefix
, url
, prefix_len
)) {
842 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
844 return ERROR_SUCCESS
;
848 ERR("no container found\n");
849 return ERROR_FILE_NOT_FOUND
;
852 static DWORD
cache_containers_findW(LPCWSTR url
, cache_container
**container
)
854 char *encoded_url
= NULL
;
857 if (url
&& !(encoded_url
= heap_strdupWtoA(url
)))
858 return ERROR_OUTOFMEMORY
;
860 ret
= cache_containers_find(encoded_url
, container
);
861 heap_free(encoded_url
);
865 static BOOL
cache_containers_enum(char *search_pattern
, DWORD index
, cache_container
**ret
)
868 cache_container
*container
;
870 TRACE("searching for prefix: %s\n", debugstr_a(search_pattern
));
872 /* non-NULL search pattern only returns one container ever */
873 if (search_pattern
&& index
> 0)
876 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
880 if (!strcmp(container
->cache_prefix
, search_pattern
))
882 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
891 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
901 /***********************************************************************
902 * cache_container_lock_index (Internal)
904 * Locks the index for system-wide exclusive access.
907 * Cache file header if successful
908 * NULL if failed and calls SetLastError.
910 static urlcache_header
* cache_container_lock_index(cache_container
*pContainer
)
914 urlcache_header
* pHeader
;
918 WaitForSingleObject(pContainer
->mutex
, INFINITE
);
920 pIndexData
= MapViewOfFile(pContainer
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
924 ReleaseMutex(pContainer
->mutex
);
925 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
928 pHeader
= (urlcache_header
*)pIndexData
;
930 /* file has grown - we need to remap to prevent us getting
931 * access violations when we try and access beyond the end
932 * of the memory mapped file */
933 if (pHeader
->size
!= pContainer
->file_size
)
935 UnmapViewOfFile( pHeader
);
936 cache_container_close_index(pContainer
);
937 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
938 if (error
!= ERROR_SUCCESS
)
940 ReleaseMutex(pContainer
->mutex
);
944 pIndexData
= MapViewOfFile(pContainer
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
948 ReleaseMutex(pContainer
->mutex
);
949 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
952 pHeader
= (urlcache_header
*)pIndexData
;
955 TRACE("Signature: %s, file size: %d bytes\n", pHeader
->signature
, pHeader
->size
);
957 for (index
= 0; index
< pHeader
->dirs_no
; index
++)
959 TRACE("Directory[%d] = \"%.8s\"\n", index
, pHeader
->directory_data
[index
].name
);
965 /***********************************************************************
966 * cache_container_unlock_index (Internal)
969 static BOOL
cache_container_unlock_index(cache_container
*pContainer
, urlcache_header
*pHeader
)
972 ReleaseMutex(pContainer
->mutex
);
973 return UnmapViewOfFile(pHeader
);
976 /***********************************************************************
977 * urlcache_create_file_pathW (Internal)
979 * Copies the full path to the specified buffer given the local file
980 * name and the index of the directory it is in. Always sets value in
981 * lpBufferSize to the required buffer size (in bytes).
984 * TRUE if the buffer was big enough
985 * FALSE if the buffer was too small
988 static BOOL
urlcache_create_file_pathW(
989 const cache_container
*pContainer
,
990 const urlcache_header
*pHeader
,
991 LPCSTR szLocalFileName
,
997 int path_len
= strlenW(pContainer
->path
);
998 int file_name_len
= MultiByteToWideChar(CP_ACP
, 0, szLocalFileName
, -1, NULL
, 0);
999 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
&& Directory
>=pHeader
->dirs_no
)
1005 nRequired
= (path_len
+ file_name_len
) * sizeof(WCHAR
);
1006 if(Directory
!= CACHE_CONTAINER_NO_SUBDIR
)
1007 nRequired
+= (DIR_LENGTH
+ 1) * sizeof(WCHAR
);
1008 if (nRequired
<= *lpBufferSize
)
1012 memcpy(wszPath
, pContainer
->path
, path_len
* sizeof(WCHAR
));
1013 if (Directory
!= CACHE_CONTAINER_NO_SUBDIR
)
1015 dir_len
= MultiByteToWideChar(CP_ACP
, 0, pHeader
->directory_data
[Directory
].name
, DIR_LENGTH
, wszPath
+ path_len
, DIR_LENGTH
);
1016 wszPath
[dir_len
+ path_len
] = '\\';
1023 MultiByteToWideChar(CP_ACP
, 0, szLocalFileName
, -1, wszPath
+ dir_len
+ path_len
, file_name_len
);
1024 *lpBufferSize
= nRequired
;
1027 *lpBufferSize
= nRequired
;
1031 /***********************************************************************
1032 * urlcache_create_file_pathA (Internal)
1034 * Copies the full path to the specified buffer given the local file
1035 * name and the index of the directory it is in. Always sets value in
1036 * lpBufferSize to the required buffer size.
1039 * TRUE if the buffer was big enough
1040 * FALSE if the buffer was too small
1043 static BOOL
urlcache_create_file_pathA(
1044 const cache_container
*pContainer
,
1045 const urlcache_header
*pHeader
,
1046 LPCSTR szLocalFileName
,
1049 LPLONG lpBufferSize
)
1052 int path_len
, file_name_len
, dir_len
;
1054 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
&& Directory
>=pHeader
->dirs_no
)
1060 path_len
= WideCharToMultiByte(CP_ACP
, 0, pContainer
->path
, -1, NULL
, 0, NULL
, NULL
) - 1;
1061 file_name_len
= strlen(szLocalFileName
) + 1 /* for nul-terminator */;
1062 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
)
1063 dir_len
= DIR_LENGTH
+1;
1067 nRequired
= (path_len
+ dir_len
+ file_name_len
) * sizeof(char);
1068 if (nRequired
<= *lpBufferSize
)
1070 WideCharToMultiByte(CP_ACP
, 0, pContainer
->path
, -1, szPath
, path_len
, NULL
, NULL
);
1072 memcpy(szPath
+path_len
, pHeader
->directory_data
[Directory
].name
, dir_len
-1);
1073 szPath
[path_len
+ dir_len
-1] = '\\';
1075 memcpy(szPath
+ path_len
+ dir_len
, szLocalFileName
, file_name_len
);
1076 *lpBufferSize
= nRequired
;
1079 *lpBufferSize
= nRequired
;
1083 /* Just like FileTimeToDosDateTime, except that it also maps the special
1084 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1086 static void file_time_to_dos_date_time(const FILETIME
*ft
, WORD
*fatdate
,
1089 if (!ft
->dwLowDateTime
&& !ft
->dwHighDateTime
)
1090 *fatdate
= *fattime
= 0;
1092 FileTimeToDosDateTime(ft
, fatdate
, fattime
);
1095 /***********************************************************************
1096 * urlcache_delete_file (Internal)
1098 static DWORD
urlcache_delete_file(const cache_container
*container
,
1099 urlcache_header
*header
, entry_url
*url_entry
)
1101 WIN32_FILE_ATTRIBUTE_DATA attr
;
1102 WCHAR path
[MAX_PATH
];
1103 LONG path_size
= sizeof(path
);
1107 if(!url_entry
->local_name_off
)
1110 if(!urlcache_create_file_pathW(container
, header
,
1111 (LPCSTR
)url_entry
+url_entry
->local_name_off
,
1112 url_entry
->cache_dir
, path
, &path_size
))
1115 if(!GetFileAttributesExW(path
, GetFileExInfoStandard
, &attr
))
1117 file_time_to_dos_date_time(&attr
.ftLastWriteTime
, &date
, &time
);
1118 if(date
!= url_entry
->write_date
|| time
!= url_entry
->write_time
)
1121 err
= (DeleteFileW(path
) ? ERROR_SUCCESS
: GetLastError());
1122 if(err
== ERROR_ACCESS_DENIED
|| err
== ERROR_SHARING_VIOLATION
)
1126 if (url_entry
->cache_dir
< header
->dirs_no
)
1128 if (header
->directory_data
[url_entry
->cache_dir
].files_no
)
1129 header
->directory_data
[url_entry
->cache_dir
].files_no
--;
1131 if (url_entry
->cache_entry_type
& STICKY_CACHE_ENTRY
)
1133 if (url_entry
->size
.QuadPart
< header
->exempt_usage
.QuadPart
)
1134 header
->exempt_usage
.QuadPart
-= url_entry
->size
.QuadPart
;
1136 header
->exempt_usage
.QuadPart
= 0;
1140 if (url_entry
->size
.QuadPart
< header
->cache_usage
.QuadPart
)
1141 header
->cache_usage
.QuadPart
-= url_entry
->size
.QuadPart
;
1143 header
->cache_usage
.QuadPart
= 0;
1146 return ERROR_SUCCESS
;
1149 static BOOL
urlcache_clean_leaked_entries(cache_container
*container
, urlcache_header
*header
)
1154 leak_off
= &header
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
];
1156 entry_url
*url_entry
= (entry_url
*)((LPBYTE
)header
+ *leak_off
);
1158 if(SUCCEEDED(urlcache_delete_file(container
, header
, url_entry
))) {
1159 *leak_off
= url_entry
->exempt_delta
;
1160 urlcache_entry_free(header
, &url_entry
->header
);
1163 leak_off
= &url_entry
->exempt_delta
;
1170 /***********************************************************************
1171 * cache_container_clean_index (Internal)
1173 * This function is meant to make place in index file by removing leaked
1174 * files entries and resizing the file.
1176 * CAUTION: file view may get mapped to new memory
1179 * ERROR_SUCCESS when new memory is available
1180 * error code otherwise
1182 static DWORD
cache_container_clean_index(cache_container
*container
, urlcache_header
**file_view
)
1184 urlcache_header
*header
= *file_view
;
1187 TRACE("(%s %s)\n", debugstr_a(container
->cache_prefix
), debugstr_w(container
->path
));
1189 if(urlcache_clean_leaked_entries(container
, header
))
1190 return ERROR_SUCCESS
;
1192 if(header
->size
>= ALLOCATION_TABLE_SIZE
*8*BLOCKSIZE
+ ENTRY_START_OFFSET
) {
1193 WARN("index file has maximal size\n");
1194 return ERROR_NOT_ENOUGH_MEMORY
;
1197 cache_container_close_index(container
);
1198 ret
= cache_container_open_index(container
, header
->capacity_in_blocks
*2);
1199 if(ret
!= ERROR_SUCCESS
)
1201 header
= MapViewOfFile(container
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
1203 return GetLastError();
1205 UnmapViewOfFile(*file_view
);
1206 *file_view
= header
;
1207 return ERROR_SUCCESS
;
1210 /* Just like DosDateTimeToFileTime, except that it also maps the special
1211 * case of a DOS date/time of (0,0) to a filetime of (0,0).
1213 static void dos_date_time_to_file_time(WORD fatdate
, WORD fattime
,
1216 if (!fatdate
&& !fattime
)
1217 ft
->dwLowDateTime
= ft
->dwHighDateTime
= 0;
1219 DosDateTimeToFileTime(fatdate
, fattime
, ft
);
1222 /***********************************************************************
1223 * urlcache_copy_entry (Internal)
1225 * Copies an entry from the cache index file to the Win32 structure
1228 * ERROR_SUCCESS if the buffer was big enough
1229 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1232 static DWORD
urlcache_copy_entry(
1233 cache_container
*pContainer
,
1234 const urlcache_header
*pHeader
,
1235 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1236 LPDWORD lpdwBufferSize
,
1237 const entry_url
* pUrlEntry
,
1241 DWORD dwRequiredSize
= sizeof(*lpCacheEntryInfo
);
1243 if (*lpdwBufferSize
>= dwRequiredSize
)
1245 lpCacheEntryInfo
->lpHeaderInfo
= NULL
;
1246 lpCacheEntryInfo
->lpszFileExtension
= NULL
;
1247 lpCacheEntryInfo
->lpszLocalFileName
= NULL
;
1248 lpCacheEntryInfo
->lpszSourceUrlName
= NULL
;
1249 lpCacheEntryInfo
->CacheEntryType
= pUrlEntry
->cache_entry_type
;
1250 lpCacheEntryInfo
->u
.dwExemptDelta
= pUrlEntry
->exempt_delta
;
1251 lpCacheEntryInfo
->dwHeaderInfoSize
= pUrlEntry
->header_info_size
;
1252 lpCacheEntryInfo
->dwHitRate
= pUrlEntry
->hit_rate
;
1253 lpCacheEntryInfo
->dwSizeHigh
= pUrlEntry
->size
.u
.HighPart
;
1254 lpCacheEntryInfo
->dwSizeLow
= pUrlEntry
->size
.u
.LowPart
;
1255 lpCacheEntryInfo
->dwStructSize
= sizeof(*lpCacheEntryInfo
);
1256 lpCacheEntryInfo
->dwUseCount
= pUrlEntry
->use_count
;
1257 dos_date_time_to_file_time(pUrlEntry
->expire_date
, pUrlEntry
->expire_time
, &lpCacheEntryInfo
->ExpireTime
);
1258 lpCacheEntryInfo
->LastAccessTime
= pUrlEntry
->access_time
;
1259 lpCacheEntryInfo
->LastModifiedTime
= pUrlEntry
->modification_time
;
1260 dos_date_time_to_file_time(pUrlEntry
->sync_date
, pUrlEntry
->sync_time
, &lpCacheEntryInfo
->LastSyncTime
);
1263 if ((dwRequiredSize
% 4) && (dwRequiredSize
< *lpdwBufferSize
))
1264 ZeroMemory((LPBYTE
)lpCacheEntryInfo
+ dwRequiredSize
, 4 - (dwRequiredSize
% 4));
1265 dwRequiredSize
= DWORD_ALIGN(dwRequiredSize
);
1267 lenUrl
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pUrlEntry
+ pUrlEntry
->url_off
, -1, NULL
, 0);
1269 lenUrl
= strlen((LPCSTR
)pUrlEntry
+ pUrlEntry
->url_off
);
1270 dwRequiredSize
+= (lenUrl
+ 1) * (bUnicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1272 /* FIXME: is source url optional? */
1273 if (*lpdwBufferSize
>= dwRequiredSize
)
1275 DWORD lenUrlBytes
= (lenUrl
+1) * (bUnicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1277 lpCacheEntryInfo
->lpszSourceUrlName
= (LPSTR
)lpCacheEntryInfo
+ dwRequiredSize
- lenUrlBytes
;
1279 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pUrlEntry
+ pUrlEntry
->url_off
, -1, (LPWSTR
)lpCacheEntryInfo
->lpszSourceUrlName
, lenUrl
+ 1);
1281 memcpy(lpCacheEntryInfo
->lpszSourceUrlName
, (LPCSTR
)pUrlEntry
+ pUrlEntry
->url_off
, lenUrlBytes
);
1284 if ((dwRequiredSize
% 4) && (dwRequiredSize
< *lpdwBufferSize
))
1285 ZeroMemory((LPBYTE
)lpCacheEntryInfo
+ dwRequiredSize
, 4 - (dwRequiredSize
% 4));
1286 dwRequiredSize
= DWORD_ALIGN(dwRequiredSize
);
1288 if (pUrlEntry
->local_name_off
)
1290 LONG nLocalFilePathSize
;
1291 LPSTR lpszLocalFileName
;
1292 lpszLocalFileName
= (LPSTR
)lpCacheEntryInfo
+ dwRequiredSize
;
1293 nLocalFilePathSize
= *lpdwBufferSize
- dwRequiredSize
;
1294 if ((bUnicode
&& urlcache_create_file_pathW(pContainer
, pHeader
, (LPCSTR
)pUrlEntry
+ pUrlEntry
->local_name_off
, pUrlEntry
->cache_dir
, (LPWSTR
)lpszLocalFileName
, &nLocalFilePathSize
)) ||
1295 (!bUnicode
&& urlcache_create_file_pathA(pContainer
, pHeader
, (LPCSTR
)pUrlEntry
+ pUrlEntry
->local_name_off
, pUrlEntry
->cache_dir
, lpszLocalFileName
, &nLocalFilePathSize
)))
1297 lpCacheEntryInfo
->lpszLocalFileName
= lpszLocalFileName
;
1299 dwRequiredSize
+= nLocalFilePathSize
* (bUnicode
? sizeof(WCHAR
) : sizeof(CHAR
)) ;
1301 if ((dwRequiredSize
% 4) && (dwRequiredSize
< *lpdwBufferSize
))
1302 ZeroMemory((LPBYTE
)lpCacheEntryInfo
+ dwRequiredSize
, 4 - (dwRequiredSize
% 4));
1303 dwRequiredSize
= DWORD_ALIGN(dwRequiredSize
);
1305 dwRequiredSize
+= pUrlEntry
->header_info_size
+ 1;
1307 if (*lpdwBufferSize
>= dwRequiredSize
)
1309 lpCacheEntryInfo
->lpHeaderInfo
= (LPBYTE
)lpCacheEntryInfo
+ dwRequiredSize
- pUrlEntry
->header_info_size
- 1;
1310 memcpy(lpCacheEntryInfo
->lpHeaderInfo
, (LPCSTR
)pUrlEntry
+ pUrlEntry
->header_info_off
, pUrlEntry
->header_info_size
);
1311 ((LPBYTE
)lpCacheEntryInfo
)[dwRequiredSize
- 1] = '\0';
1313 if ((dwRequiredSize
% 4) && (dwRequiredSize
< *lpdwBufferSize
))
1314 ZeroMemory((LPBYTE
)lpCacheEntryInfo
+ dwRequiredSize
, 4 - (dwRequiredSize
% 4));
1315 dwRequiredSize
= DWORD_ALIGN(dwRequiredSize
);
1317 if (pUrlEntry
->file_extension_off
)
1322 lenExtension
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pUrlEntry
+ pUrlEntry
->file_extension_off
, -1, NULL
, 0);
1324 lenExtension
= strlen((LPCSTR
)pUrlEntry
+ pUrlEntry
->file_extension_off
) + 1;
1325 dwRequiredSize
+= lenExtension
* (bUnicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1327 if (*lpdwBufferSize
>= dwRequiredSize
)
1329 lpCacheEntryInfo
->lpszFileExtension
= (LPSTR
)lpCacheEntryInfo
+ dwRequiredSize
- lenExtension
;
1331 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)pUrlEntry
+ pUrlEntry
->file_extension_off
, -1, (LPWSTR
)lpCacheEntryInfo
->lpszSourceUrlName
, lenExtension
);
1333 memcpy(lpCacheEntryInfo
->lpszFileExtension
, (LPCSTR
)pUrlEntry
+ pUrlEntry
->file_extension_off
, lenExtension
* sizeof(CHAR
));
1336 if ((dwRequiredSize
% 4) && (dwRequiredSize
< *lpdwBufferSize
))
1337 ZeroMemory((LPBYTE
)lpCacheEntryInfo
+ dwRequiredSize
, 4 - (dwRequiredSize
% 4));
1338 dwRequiredSize
= DWORD_ALIGN(dwRequiredSize
);
1341 if (dwRequiredSize
> *lpdwBufferSize
)
1343 *lpdwBufferSize
= dwRequiredSize
;
1344 return ERROR_INSUFFICIENT_BUFFER
;
1346 *lpdwBufferSize
= dwRequiredSize
;
1347 return ERROR_SUCCESS
;
1350 /***********************************************************************
1351 * urlcache_set_entry_info (Internal)
1353 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1354 * according to the flags set by field_control.
1357 * ERROR_SUCCESS if the buffer was big enough
1358 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1361 static DWORD
urlcache_set_entry_info(entry_url
*url_entry
, const INTERNET_CACHE_ENTRY_INFOA
*entry_info
, DWORD field_control
)
1363 if (field_control
& CACHE_ENTRY_ACCTIME_FC
)
1364 url_entry
->access_time
= entry_info
->LastAccessTime
;
1365 if (field_control
& CACHE_ENTRY_ATTRIBUTE_FC
)
1366 url_entry
->cache_entry_type
= entry_info
->CacheEntryType
;
1367 if (field_control
& CACHE_ENTRY_EXEMPT_DELTA_FC
)
1368 url_entry
->exempt_delta
= entry_info
->u
.dwExemptDelta
;
1369 if (field_control
& CACHE_ENTRY_EXPTIME_FC
)
1370 file_time_to_dos_date_time(&entry_info
->ExpireTime
, &url_entry
->expire_date
, &url_entry
->expire_time
);
1371 if (field_control
& CACHE_ENTRY_HEADERINFO_FC
)
1372 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1373 if (field_control
& CACHE_ENTRY_HITRATE_FC
)
1374 url_entry
->hit_rate
= entry_info
->dwHitRate
;
1375 if (field_control
& CACHE_ENTRY_MODTIME_FC
)
1376 url_entry
->modification_time
= entry_info
->LastModifiedTime
;
1377 if (field_control
& CACHE_ENTRY_SYNCTIME_FC
)
1378 file_time_to_dos_date_time(&entry_info
->LastAccessTime
, &url_entry
->sync_date
, &url_entry
->sync_time
);
1380 return ERROR_SUCCESS
;
1383 /***********************************************************************
1384 * urlcache_hash_key (Internal)
1386 * Returns the hash key for a given string
1389 * hash key for the string
1392 static DWORD
urlcache_hash_key(LPCSTR lpszKey
)
1394 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1395 * but the algorithm and result are not the same!
1397 static const unsigned char lookupTable
[256] =
1399 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1400 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1401 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1402 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1403 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1404 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1405 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1406 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1407 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1408 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1409 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1410 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1411 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1412 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1413 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1414 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1415 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1416 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1417 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1418 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1419 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1420 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1421 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1422 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1423 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1424 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1425 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1426 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1427 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1428 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1429 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1430 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1435 for (i
= 0; i
< sizeof(key
) / sizeof(key
[0]); i
++)
1436 key
[i
] = lookupTable
[(*lpszKey
+ i
) & 0xFF];
1438 for (lpszKey
++; *lpszKey
&& ((lpszKey
[0] != '/') || (lpszKey
[1] != 0)); lpszKey
++)
1440 for (i
= 0; i
< sizeof(key
) / sizeof(key
[0]); i
++)
1441 key
[i
] = lookupTable
[*lpszKey
^ key
[i
]];
1444 return *(DWORD
*)key
;
1447 static inline entry_hash_table
* urlcache_get_hash_table(const urlcache_header
*pHeader
, DWORD dwOffset
)
1451 return (entry_hash_table
*)((LPBYTE
)pHeader
+ dwOffset
);
1454 static BOOL
urlcache_find_hash_entry(const urlcache_header
*pHeader
, LPCSTR lpszUrl
, struct hash_entry
**ppHashEntry
)
1456 /* structure of hash table:
1457 * 448 entries divided into 64 blocks
1458 * each block therefore contains a chain of 7 key/offset pairs
1459 * how position in table is calculated:
1460 * 1. the url is hashed in helper function
1461 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1462 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1465 * there can be multiple hash tables in the file and the offset to
1466 * the next one is stored in the header of the hash table
1468 DWORD key
= urlcache_hash_key(lpszUrl
);
1469 DWORD offset
= (key
& (HASHTABLE_NUM_ENTRIES
-1)) * HASHTABLE_BLOCKSIZE
;
1470 entry_hash_table
* pHashEntry
;
1473 key
>>= HASHTABLE_FLAG_BITS
;
1475 for (pHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1476 pHashEntry
; pHashEntry
= urlcache_get_hash_table(pHeader
, pHashEntry
->next
))
1479 if (pHashEntry
->id
!= id
++)
1481 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry
->id
, id
);
1484 /* make sure that it is in fact a hash entry */
1485 if (pHashEntry
->header
.signature
!= HASH_SIGNATURE
)
1487 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&pHashEntry
->header
.signature
);
1491 for (i
= 0; i
< HASHTABLE_BLOCKSIZE
; i
++)
1493 struct hash_entry
*pHashElement
= &pHashEntry
->hash_table
[offset
+ i
];
1494 if (key
== pHashElement
->key
>>HASHTABLE_FLAG_BITS
)
1496 /* FIXME: we should make sure that this is the right element
1497 * before returning and claiming that it is. We can do this
1498 * by doing a simple compare between the URL we were given
1499 * and the URL stored in the entry. However, this assumes
1500 * we know the format of all the entries stored in the
1502 *ppHashEntry
= pHashElement
;
1510 static BOOL
urlcache_find_hash_entryW(const urlcache_header
*pHeader
, LPCWSTR lpszUrl
, struct hash_entry
**ppHashEntry
)
1515 urlA
= heap_strdupWtoA(lpszUrl
);
1518 SetLastError(ERROR_OUTOFMEMORY
);
1522 ret
= urlcache_find_hash_entry(pHeader
, urlA
, ppHashEntry
);
1527 /***********************************************************************
1528 * urlcache_hash_entry_set_flags (Internal)
1530 * Sets special bits in hash key
1536 static void urlcache_hash_entry_set_flags(struct hash_entry
*pHashEntry
, DWORD dwFlag
)
1538 pHashEntry
->key
= (pHashEntry
->key
>> HASHTABLE_FLAG_BITS
<< HASHTABLE_FLAG_BITS
) | dwFlag
;
1541 /***********************************************************************
1542 * urlcache_hash_entry_delete (Internal)
1544 * Searches all the hash tables in the index for the given URL and
1545 * then if found deletes the entry.
1548 * TRUE if the entry was found
1549 * FALSE if the entry could not be found
1552 static BOOL
urlcache_hash_entry_delete(struct hash_entry
*pHashEntry
)
1554 pHashEntry
->key
= HASHTABLE_DEL
;
1558 /***********************************************************************
1559 * urlcache_hash_entry_create (Internal)
1561 * Searches all the hash tables for a free slot based on the offset
1562 * generated from the hash key. If a free slot is found, the offset and
1563 * key are entered into the hash table.
1566 * ERROR_SUCCESS if the entry was added
1567 * Any other Win32 error code if the entry could not be added
1570 static DWORD
urlcache_hash_entry_create(urlcache_header
*pHeader
, LPCSTR lpszUrl
, DWORD dwOffsetEntry
, DWORD dwFieldType
)
1572 /* see urlcache_find_hash_entry for structure of hash tables */
1574 DWORD key
= urlcache_hash_key(lpszUrl
);
1575 DWORD offset
= (key
& (HASHTABLE_NUM_ENTRIES
-1)) * HASHTABLE_BLOCKSIZE
;
1576 entry_hash_table
* pHashEntry
, *pHashPrev
= NULL
;
1580 key
= ((key
>> HASHTABLE_FLAG_BITS
) << HASHTABLE_FLAG_BITS
) + dwFieldType
;
1582 for (pHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1583 pHashEntry
; pHashEntry
= urlcache_get_hash_table(pHeader
, pHashEntry
->next
))
1586 pHashPrev
= pHashEntry
;
1588 if (pHashEntry
->id
!= id
++)
1590 ERR("not right hash table number (%d) expected %d\n", pHashEntry
->id
, id
);
1593 /* make sure that it is in fact a hash entry */
1594 if (pHashEntry
->header
.signature
!= HASH_SIGNATURE
)
1596 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&pHashEntry
->header
.signature
);
1600 for (i
= 0; i
< HASHTABLE_BLOCKSIZE
; i
++)
1602 struct hash_entry
*pHashElement
= &pHashEntry
->hash_table
[offset
+ i
];
1603 if (pHashElement
->key
==HASHTABLE_FREE
|| pHashElement
->key
==HASHTABLE_DEL
) /* if the slot is free */
1605 pHashElement
->key
= key
;
1606 pHashElement
->offset
= dwOffsetEntry
;
1607 return ERROR_SUCCESS
;
1611 error
= urlcache_create_hash_table(pHeader
, pHashPrev
, &pHashEntry
);
1612 if (error
!= ERROR_SUCCESS
)
1615 pHashEntry
->hash_table
[offset
].key
= key
;
1616 pHashEntry
->hash_table
[offset
].offset
= dwOffsetEntry
;
1617 return ERROR_SUCCESS
;
1620 /***********************************************************************
1621 * urlcache_enum_hash_tables (Internal)
1623 * Enumerates the hash tables in a container.
1626 * TRUE if an entry was found
1627 * FALSE if there are no more tables to enumerate.
1630 static BOOL
urlcache_enum_hash_tables(const urlcache_header
*pHeader
, DWORD
*id
, entry_hash_table
**ppHashEntry
)
1632 for (*ppHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1633 *ppHashEntry
; *ppHashEntry
= urlcache_get_hash_table(pHeader
, (*ppHashEntry
)->next
))
1635 TRACE("looking at hash table number %d\n", (*ppHashEntry
)->id
);
1636 if ((*ppHashEntry
)->id
!= *id
)
1638 /* make sure that it is in fact a hash entry */
1639 if ((*ppHashEntry
)->header
.signature
!= HASH_SIGNATURE
)
1641 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&(*ppHashEntry
)->header
.signature
);
1646 TRACE("hash table number %d found\n", *id
);
1652 /***********************************************************************
1653 * urlcache_enum_hash_table_entries (Internal)
1655 * Enumerates entries in a hash table and returns the next non-free entry.
1658 * TRUE if an entry was found
1659 * FALSE if the hash table is empty or there are no more entries to
1663 static BOOL
urlcache_enum_hash_table_entries(const urlcache_header
*pHeader
, const entry_hash_table
*pHashEntry
,
1664 DWORD
* index
, const struct hash_entry
**ppHashEntry
)
1666 for (; *index
< HASHTABLE_SIZE
; (*index
)++)
1668 if (pHashEntry
->hash_table
[*index
].key
==HASHTABLE_FREE
|| pHashEntry
->hash_table
[*index
].key
==HASHTABLE_DEL
)
1671 *ppHashEntry
= &pHashEntry
->hash_table
[*index
];
1672 TRACE("entry found %d\n", *index
);
1675 TRACE("no more entries (%d)\n", *index
);
1679 /***********************************************************************
1680 * cache_container_delete_dir (Internal)
1682 * Erase a directory containing an URL cache.
1685 * TRUE success, FALSE failure/aborted.
1688 static BOOL
cache_container_delete_dir(LPCWSTR lpszPath
)
1691 WCHAR path
[MAX_PATH
+ 1];
1692 SHFILEOPSTRUCTW shfos
;
1695 path_len
= strlenW(lpszPath
);
1696 if (path_len
>= MAX_PATH
)
1698 strcpyW(path
, lpszPath
);
1699 path
[path_len
+ 1] = 0; /* double-NUL-terminate path */
1702 shfos
.wFunc
= FO_DELETE
;
1705 shfos
.fFlags
= FOF_NOCONFIRMATION
;
1706 shfos
.fAnyOperationsAborted
= FALSE
;
1707 ret
= SHFileOperationW(&shfos
);
1709 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path
), ret
);
1710 return !(ret
|| shfos
.fAnyOperationsAborted
);
1713 /***********************************************************************
1714 * urlcache_hash_entry_is_locked (Internal)
1716 * Checks if entry is locked. Unlocks it if possible.
1718 static BOOL
urlcache_hash_entry_is_locked(struct hash_entry
*hash_entry
, entry_url
*url_entry
)
1721 ULARGE_INTEGER acc_time
, time
;
1723 if ((hash_entry
->key
& ((1<<HASHTABLE_FLAG_BITS
)-1)) != HASHTABLE_LOCK
)
1726 GetSystemTimeAsFileTime(&cur_time
);
1727 time
.u
.LowPart
= cur_time
.dwLowDateTime
;
1728 time
.u
.HighPart
= cur_time
.dwHighDateTime
;
1730 acc_time
.u
.LowPart
= url_entry
->access_time
.dwLowDateTime
;
1731 acc_time
.u
.HighPart
= url_entry
->access_time
.dwHighDateTime
;
1733 time
.QuadPart
-= acc_time
.QuadPart
;
1735 /* check if entry was locked for at least a day */
1736 if(time
.QuadPart
> (ULONGLONG
)24*60*60*FILETIME_SECOND
) {
1737 urlcache_hash_entry_set_flags(hash_entry
, HASHTABLE_URL
);
1738 url_entry
->use_count
= 0;
1745 BOOL
urlcache_get_entry_info(const char *url
, void *entry_info
,
1746 DWORD
*size
, DWORD flags
, BOOL unicode
)
1748 urlcache_header
*header
;
1749 struct hash_entry
*hash_entry
;
1750 const entry_url
*url_entry
;
1751 cache_container
*container
;
1754 TRACE("(%s, %p, %p, %x, %x)\n", debugstr_a(url
), entry_info
, size
, flags
, unicode
);
1756 if(flags
& ~GET_INSTALLED_ENTRY
)
1757 FIXME("ignoring unsupported flags: %x\n", flags
);
1759 error
= cache_containers_find(url
, &container
);
1760 if(error
!= ERROR_SUCCESS
) {
1761 SetLastError(error
);
1765 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
1766 if(error
!= ERROR_SUCCESS
) {
1767 SetLastError(error
);
1771 if(!(header
= cache_container_lock_index(container
)))
1774 if(!urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
1775 cache_container_unlock_index(container
, header
);
1776 WARN("entry %s not found!\n", debugstr_a(url
));
1777 SetLastError(ERROR_FILE_NOT_FOUND
);
1781 url_entry
= (const entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
1782 if(url_entry
->header
.signature
!= URL_SIGNATURE
) {
1783 cache_container_unlock_index(container
, header
);
1784 FIXME("Trying to retrieve entry of unknown format %s\n",
1785 debugstr_an((LPCSTR
)&url_entry
->header
.signature
, sizeof(DWORD
)));
1786 SetLastError(ERROR_FILE_NOT_FOUND
);
1790 TRACE("Found URL: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->url_off
));
1791 TRACE("Header info: %s\n", debugstr_an((LPCSTR
)url_entry
+
1792 url_entry
->header_info_off
, url_entry
->header_info_size
));
1794 if((flags
& GET_INSTALLED_ENTRY
) && !(url_entry
->cache_entry_type
& INSTALLED_CACHE_ENTRY
)) {
1795 cache_container_unlock_index(container
, header
);
1796 SetLastError(ERROR_FILE_NOT_FOUND
);
1804 error
= urlcache_copy_entry(container
, header
, entry_info
, size
, url_entry
, unicode
);
1805 if(error
!= ERROR_SUCCESS
) {
1806 cache_container_unlock_index(container
, header
);
1807 SetLastError(error
);
1810 if(url_entry
->local_name_off
)
1811 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->local_name_off
));
1814 cache_container_unlock_index(container
, header
);
1818 /***********************************************************************
1819 * GetUrlCacheEntryInfoExA (WININET.@)
1822 BOOL WINAPI
GetUrlCacheEntryInfoExA(LPCSTR lpszUrl
,
1823 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1824 LPDWORD lpdwCacheEntryInfoBufSize
, LPSTR lpszReserved
,
1825 LPDWORD lpdwReserved
, LPVOID lpReserved
, DWORD dwFlags
)
1827 if(lpszReserved
!=NULL
|| lpdwReserved
!=NULL
|| lpReserved
!=NULL
) {
1828 ERR("Reserved value was not 0\n");
1829 SetLastError(ERROR_INVALID_PARAMETER
);
1833 return urlcache_get_entry_info(lpszUrl
, lpCacheEntryInfo
,
1834 lpdwCacheEntryInfoBufSize
, dwFlags
, FALSE
);
1837 /***********************************************************************
1838 * GetUrlCacheEntryInfoA (WININET.@)
1841 BOOL WINAPI
GetUrlCacheEntryInfoA(LPCSTR lpszUrlName
,
1842 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1843 LPDWORD lpdwCacheEntryInfoBufferSize
)
1845 return GetUrlCacheEntryInfoExA(lpszUrlName
, lpCacheEntryInfo
,
1846 lpdwCacheEntryInfoBufferSize
, NULL
, NULL
, NULL
, 0);
1849 static int urlcache_encode_url(const WCHAR
*url
, char *encoded_url
, int encoded_len
)
1852 DWORD len
, part_len
;
1855 TRACE("%s\n", debugstr_w(url
));
1857 memset(&uc
, 0, sizeof(uc
));
1858 uc
.dwStructSize
= sizeof(uc
);
1859 uc
.dwHostNameLength
= 1;
1860 if(!InternetCrackUrlW(url
, 0, 0, &uc
))
1861 uc
.nScheme
= INTERNET_SCHEME_UNKNOWN
;
1863 if(uc
.nScheme
!=INTERNET_SCHEME_HTTP
&& uc
.nScheme
!=INTERNET_SCHEME_HTTPS
)
1864 return WideCharToMultiByte(CP_UTF8
, 0, url
, -1, encoded_url
, encoded_len
, NULL
, NULL
);
1866 len
= WideCharToMultiByte(CP_UTF8
, 0, url
, uc
.lpszHostName
-url
,
1867 encoded_url
, encoded_len
, NULL
, NULL
);
1873 part_len
= IdnToAscii(0, uc
.lpszHostName
, uc
.dwHostNameLength
, NULL
, 0);
1875 SetLastError(ERROR_INTERNET_INVALID_URL
);
1879 punycode
= heap_alloc(part_len
*sizeof(WCHAR
));
1883 part_len
= IdnToAscii(0, uc
.lpszHostName
, uc
.dwHostNameLength
, punycode
, part_len
);
1885 heap_free(punycode
);
1889 part_len
= WideCharToMultiByte(CP_UTF8
, 0, punycode
, part_len
,
1890 encoded_url
? encoded_url
+len
: NULL
, encoded_len
, NULL
, NULL
);
1891 heap_free(punycode
);
1895 encoded_len
-= part_len
;
1898 part_len
= WideCharToMultiByte(CP_UTF8
, 0, uc
.lpszHostName
+uc
.dwHostNameLength
,
1899 -1, encoded_url
? encoded_url
+len
: NULL
, encoded_len
, NULL
, NULL
);
1904 TRACE("got (%d)%s\n", len
, debugstr_a(encoded_url
));
1908 static BOOL
urlcache_encode_url_alloc(const WCHAR
*url
, char **encoded_url
)
1913 encoded_len
= urlcache_encode_url(url
, NULL
, 0);
1917 ret
= heap_alloc(encoded_len
*sizeof(WCHAR
));
1921 encoded_len
= urlcache_encode_url(url
, ret
, encoded_len
);
1931 /***********************************************************************
1932 * GetUrlCacheEntryInfoExW (WININET.@)
1935 BOOL WINAPI
GetUrlCacheEntryInfoExW(LPCWSTR lpszUrl
,
1936 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
1937 LPDWORD lpdwCacheEntryInfoBufSize
, LPWSTR lpszReserved
,
1938 LPDWORD lpdwReserved
, LPVOID lpReserved
, DWORD dwFlags
)
1943 if(lpszReserved
!=NULL
|| lpdwReserved
!=NULL
|| lpReserved
!=NULL
) {
1944 ERR("Reserved value was not 0\n");
1945 SetLastError(ERROR_INVALID_PARAMETER
);
1949 /* Ignore GET_INSTALLED_ENTRY flag in unicode version of function */
1950 dwFlags
&= ~GET_INSTALLED_ENTRY
;
1952 if(!urlcache_encode_url_alloc(lpszUrl
, &url
))
1955 ret
= urlcache_get_entry_info(url
, lpCacheEntryInfo
,
1956 lpdwCacheEntryInfoBufSize
, dwFlags
, TRUE
);
1961 /***********************************************************************
1962 * GetUrlCacheEntryInfoW (WININET.@)
1965 BOOL WINAPI
GetUrlCacheEntryInfoW(LPCWSTR lpszUrl
,
1966 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
1967 LPDWORD lpdwCacheEntryInfoBufferSize
)
1969 return GetUrlCacheEntryInfoExW(lpszUrl
, lpCacheEntryInfo
,
1970 lpdwCacheEntryInfoBufferSize
, NULL
, NULL
, NULL
, 0);
1973 /***********************************************************************
1974 * SetUrlCacheEntryInfoA (WININET.@)
1976 BOOL WINAPI
SetUrlCacheEntryInfoA(LPCSTR lpszUrlName
,
1977 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1978 DWORD dwFieldControl
)
1980 urlcache_header
*pHeader
;
1981 struct hash_entry
*pHashEntry
;
1982 entry_header
*pEntry
;
1983 cache_container
*pContainer
;
1986 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName
), lpCacheEntryInfo
, dwFieldControl
);
1988 error
= cache_containers_find(lpszUrlName
, &pContainer
);
1989 if (error
!= ERROR_SUCCESS
)
1991 SetLastError(error
);
1995 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
1996 if (error
!= ERROR_SUCCESS
)
1998 SetLastError(error
);
2002 if (!(pHeader
= cache_container_lock_index(pContainer
)))
2005 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
2007 cache_container_unlock_index(pContainer
, pHeader
);
2008 WARN("entry %s not found!\n", debugstr_a(lpszUrlName
));
2009 SetLastError(ERROR_FILE_NOT_FOUND
);
2013 pEntry
= (entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2014 if (pEntry
->signature
!= URL_SIGNATURE
)
2016 cache_container_unlock_index(pContainer
, pHeader
);
2017 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR
)&pEntry
->signature
, sizeof(DWORD
)));
2018 SetLastError(ERROR_FILE_NOT_FOUND
);
2022 urlcache_set_entry_info((entry_url
*)pEntry
, lpCacheEntryInfo
, dwFieldControl
);
2024 cache_container_unlock_index(pContainer
, pHeader
);
2029 /***********************************************************************
2030 * SetUrlCacheEntryInfoW (WININET.@)
2032 BOOL WINAPI
SetUrlCacheEntryInfoW(LPCWSTR lpszUrl
,
2033 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
2034 DWORD dwFieldControl
)
2039 if(!urlcache_encode_url_alloc(lpszUrl
, &url
))
2042 ret
= SetUrlCacheEntryInfoA(url
, (INTERNET_CACHE_ENTRY_INFOA
*)lpCacheEntryInfo
, dwFieldControl
);
2047 static BOOL
urlcache_entry_get_file(const char *url
, void *entry_info
, DWORD
*size
, BOOL unicode
)
2049 urlcache_header
*header
;
2050 struct hash_entry
*hash_entry
;
2051 entry_url
*url_entry
;
2052 cache_container
*container
;
2055 TRACE("(%s, %p, %p, %x)\n", debugstr_a(url
), entry_info
, size
, unicode
);
2057 if(!url
|| !size
|| (!entry_info
&& *size
)) {
2058 SetLastError(ERROR_INVALID_PARAMETER
);
2062 error
= cache_containers_find(url
, &container
);
2063 if(error
!= ERROR_SUCCESS
) {
2064 SetLastError(error
);
2068 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2069 if (error
!= ERROR_SUCCESS
) {
2070 SetLastError(error
);
2074 if (!(header
= cache_container_lock_index(container
)))
2077 if (!urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
2078 cache_container_unlock_index(container
, header
);
2079 TRACE("entry %s not found!\n", url
);
2080 SetLastError(ERROR_FILE_NOT_FOUND
);
2084 url_entry
= (entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
2085 if(url_entry
->header
.signature
!= URL_SIGNATURE
) {
2086 cache_container_unlock_index(container
, header
);
2087 FIXME("Trying to retrieve entry of unknown format %s\n",
2088 debugstr_an((LPSTR
)&url_entry
->header
.signature
, sizeof(DWORD
)));
2089 SetLastError(ERROR_FILE_NOT_FOUND
);
2093 if(!url_entry
->local_name_off
) {
2094 cache_container_unlock_index(container
, header
);
2095 SetLastError(ERROR_INVALID_DATA
);
2099 TRACE("Found URL: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->url_off
));
2100 TRACE("Header info: %s\n", debugstr_an((LPCSTR
)url_entry
+ url_entry
->header_info_off
,
2101 url_entry
->header_info_size
));
2103 error
= urlcache_copy_entry(container
, header
, entry_info
,
2104 size
, url_entry
, unicode
);
2105 if(error
!= ERROR_SUCCESS
) {
2106 cache_container_unlock_index(container
, header
);
2107 SetLastError(error
);
2110 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->local_name_off
));
2112 url_entry
->hit_rate
++;
2113 url_entry
->use_count
++;
2114 urlcache_hash_entry_set_flags(hash_entry
, HASHTABLE_LOCK
);
2115 GetSystemTimeAsFileTime(&url_entry
->access_time
);
2117 cache_container_unlock_index(container
, header
);
2122 /***********************************************************************
2123 * RetrieveUrlCacheEntryFileA (WININET.@)
2126 BOOL WINAPI
RetrieveUrlCacheEntryFileA(LPCSTR lpszUrlName
,
2127 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
2128 LPDWORD lpdwCacheEntryInfoBufferSize
, DWORD dwReserved
)
2130 return urlcache_entry_get_file(lpszUrlName
, lpCacheEntryInfo
,
2131 lpdwCacheEntryInfoBufferSize
, FALSE
);
2134 /***********************************************************************
2135 * RetrieveUrlCacheEntryFileW (WININET.@)
2138 BOOL WINAPI
RetrieveUrlCacheEntryFileW(LPCWSTR lpszUrlName
,
2139 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
2140 LPDWORD lpdwCacheEntryInfoBufferSize
, DWORD dwReserved
)
2145 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
2148 ret
= urlcache_entry_get_file(url
, lpCacheEntryInfo
,
2149 lpdwCacheEntryInfoBufferSize
, TRUE
);
2154 static BOOL
urlcache_entry_delete(const cache_container
*pContainer
,
2155 urlcache_header
*pHeader
, struct hash_entry
*pHashEntry
)
2157 entry_header
*pEntry
;
2158 entry_url
* pUrlEntry
;
2160 pEntry
= (entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2161 if (pEntry
->signature
!= URL_SIGNATURE
)
2163 FIXME("Trying to delete entry of unknown format %s\n",
2164 debugstr_an((LPCSTR
)&pEntry
->signature
, sizeof(DWORD
)));
2165 SetLastError(ERROR_FILE_NOT_FOUND
);
2169 pUrlEntry
= (entry_url
*)pEntry
;
2170 if(urlcache_hash_entry_is_locked(pHashEntry
, pUrlEntry
))
2172 TRACE("Trying to delete locked entry\n");
2173 pUrlEntry
->cache_entry_type
|= PENDING_DELETE_CACHE_ENTRY
;
2174 SetLastError(ERROR_SHARING_VIOLATION
);
2178 if(!urlcache_delete_file(pContainer
, pHeader
, pUrlEntry
))
2180 urlcache_entry_free(pHeader
, pEntry
);
2184 /* Add entry to leaked files list */
2185 pUrlEntry
->header
.signature
= LEAK_SIGNATURE
;
2186 pUrlEntry
->exempt_delta
= pHeader
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
];
2187 pHeader
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
] = pHashEntry
->offset
;
2190 urlcache_hash_entry_delete(pHashEntry
);
2194 static HANDLE free_cache_running
;
2195 static HANDLE dll_unload_event
;
2196 static DWORD WINAPI
handle_full_cache_worker(void *param
)
2198 FreeUrlCacheSpaceW(NULL
, 20, 0);
2199 ReleaseSemaphore(free_cache_running
, 1, NULL
);
2203 static void handle_full_cache(void)
2205 if(WaitForSingleObject(free_cache_running
, 0) == WAIT_OBJECT_0
) {
2206 if(!QueueUserWorkItem(handle_full_cache_worker
, NULL
, 0))
2207 ReleaseSemaphore(free_cache_running
, 1, NULL
);
2211 /* Enumerates entries in cache, allows cache unlocking between calls. */
2212 static BOOL
urlcache_next_entry(urlcache_header
*header
, DWORD
*hash_table_off
, DWORD
*hash_table_entry
,
2213 struct hash_entry
**hash_entry
, entry_header
**entry
)
2215 entry_hash_table
*hashtable_entry
;
2220 if(!*hash_table_off
) {
2221 *hash_table_off
= header
->hash_table_off
;
2222 *hash_table_entry
= 0;
2224 hashtable_entry
= urlcache_get_hash_table(header
, *hash_table_off
);
2226 if(*hash_table_off
>= header
->size
) {
2227 *hash_table_off
= 0;
2231 hashtable_entry
= urlcache_get_hash_table(header
, *hash_table_off
);
2234 if(hashtable_entry
->header
.signature
!= HASH_SIGNATURE
) {
2235 *hash_table_off
= 0;
2240 if(*hash_table_entry
>= HASHTABLE_SIZE
) {
2241 *hash_table_off
= hashtable_entry
->next
;
2242 if(!*hash_table_off
) {
2243 *hash_table_off
= 0;
2247 hashtable_entry
= urlcache_get_hash_table(header
, *hash_table_off
);
2248 *hash_table_entry
= 0;
2251 if(hashtable_entry
->hash_table
[*hash_table_entry
].key
!= HASHTABLE_DEL
&&
2252 hashtable_entry
->hash_table
[*hash_table_entry
].key
!= HASHTABLE_FREE
) {
2253 *hash_entry
= &hashtable_entry
->hash_table
[*hash_table_entry
];
2254 *entry
= (entry_header
*)((LPBYTE
)header
+ hashtable_entry
->hash_table
[*hash_table_entry
].offset
);
2255 (*hash_table_entry
)++;
2259 (*hash_table_entry
)++;
2262 *hash_table_off
= 0;
2266 /* Rates an urlcache entry to determine if it can be deleted.
2268 * Score 0 means that entry can safely be removed, the bigger rating
2269 * the smaller chance of entry being removed.
2270 * DWORD_MAX means that entry can't be deleted at all.
2272 * Rating system is currently not fully compatible with native implementation.
2274 static DWORD
urlcache_rate_entry(entry_url
*url_entry
, FILETIME
*cur_time
)
2276 ULARGE_INTEGER time
, access_time
;
2279 access_time
.u
.LowPart
= url_entry
->access_time
.dwLowDateTime
;
2280 access_time
.u
.HighPart
= url_entry
->access_time
.dwHighDateTime
;
2282 time
.u
.LowPart
= cur_time
->dwLowDateTime
;
2283 time
.u
.HighPart
= cur_time
->dwHighDateTime
;
2285 /* Don't touch entries that were added less than 10 minutes ago */
2286 if(time
.QuadPart
< access_time
.QuadPart
+ (ULONGLONG
)10*60*FILETIME_SECOND
)
2289 if(url_entry
->cache_entry_type
& STICKY_CACHE_ENTRY
)
2290 if(time
.QuadPart
< access_time
.QuadPart
+ (ULONGLONG
)url_entry
->exempt_delta
*FILETIME_SECOND
)
2293 time
.QuadPart
= (time
.QuadPart
-access_time
.QuadPart
)/FILETIME_SECOND
;
2294 rating
= 400*60*60*24/(60*60*24+time
.QuadPart
);
2296 if(url_entry
->hit_rate
> 100)
2299 rating
+= url_entry
->hit_rate
;
2304 static int dword_cmp(const void *p1
, const void *p2
)
2306 return *(const DWORD
*)p1
- *(const DWORD
*)p2
;
2309 /***********************************************************************
2310 * FreeUrlCacheSpaceW (WININET.@)
2312 * Frees up some cache.
2315 * cache_path [I] Which volume to free up from, or NULL if you don't care.
2316 * size [I] Percentage of the cache that should be free.
2317 * filter [I] Which entries can't be deleted (CacheEntryType)
2320 * TRUE success. FALSE failure.
2323 * This implementation just retrieves the path of the cache directory, and
2324 * deletes its contents from the filesystem. The correct approach would
2325 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
2327 BOOL WINAPI
FreeUrlCacheSpaceW(LPCWSTR cache_path
, DWORD size
, DWORD filter
)
2329 cache_container
*container
;
2330 DWORD path_len
, err
;
2332 TRACE("(%s, %x, %x)\n", debugstr_w(cache_path
), size
, filter
);
2334 if(size
<1 || size
>100) {
2335 SetLastError(ERROR_INVALID_PARAMETER
);
2340 path_len
= strlenW(cache_path
);
2341 if(cache_path
[path_len
-1] == '\\')
2347 if(size
==100 && !filter
) {
2348 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
2350 /* When cache_path==NULL only clean Temporary Internet Files */
2351 if((!path_len
&& container
->cache_prefix
[0]==0) ||
2352 (path_len
&& !strncmpiW(container
->path
, cache_path
, path_len
) &&
2353 (container
->path
[path_len
]=='\0' || container
->path
[path_len
]=='\\')))
2357 WaitForSingleObject(container
->mutex
, INFINITE
);
2359 /* unlock, delete, recreate and lock cache */
2360 cache_container_close_index(container
);
2361 ret_del
= cache_container_delete_dir(container
->path
);
2362 err
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2364 ReleaseMutex(container
->mutex
);
2365 if(!ret_del
|| (err
!= ERROR_SUCCESS
))
2373 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
2375 urlcache_header
*header
;
2376 struct hash_entry
*hash_entry
;
2377 entry_header
*entry
;
2378 entry_url
*url_entry
;
2379 ULONGLONG desired_size
, cur_size
;
2380 DWORD delete_factor
, hash_table_off
, hash_table_entry
;
2381 DWORD rate
[100], rate_no
;
2384 if((path_len
|| container
->cache_prefix
[0]!=0) &&
2385 (!path_len
|| strncmpiW(container
->path
, cache_path
, path_len
) ||
2386 (container
->path
[path_len
]!='\0' && container
->path
[path_len
]!='\\')))
2389 err
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2390 if(err
!= ERROR_SUCCESS
)
2393 header
= cache_container_lock_index(container
);
2397 urlcache_clean_leaked_entries(container
, header
);
2399 desired_size
= header
->cache_limit
.QuadPart
*(100-size
)/100;
2400 cur_size
= header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
;
2401 if(cur_size
<= desired_size
)
2404 delete_factor
= (cur_size
-desired_size
)*100/cur_size
;
2406 if(!delete_factor
) {
2407 cache_container_unlock_index(container
, header
);
2412 hash_table_entry
= 0;
2414 GetSystemTimeAsFileTime(&cur_time
);
2415 while(rate_no
<sizeof(rate
)/sizeof(*rate
) &&
2416 urlcache_next_entry(header
, &hash_table_off
, &hash_table_entry
, &hash_entry
, &entry
)) {
2417 if(entry
->signature
!= URL_SIGNATURE
) {
2418 WARN("only url entries are currently supported\n");
2422 url_entry
= (entry_url
*)entry
;
2423 if(url_entry
->cache_entry_type
& filter
)
2426 rate
[rate_no
] = urlcache_rate_entry(url_entry
, &cur_time
);
2427 if(rate
[rate_no
] != -1)
2432 TRACE("nothing to delete\n");
2433 cache_container_unlock_index(container
, header
);
2437 qsort(rate
, rate_no
, sizeof(DWORD
), dword_cmp
);
2439 delete_factor
= delete_factor
*rate_no
/100;
2440 delete_factor
= rate
[delete_factor
];
2441 TRACE("deleting files with rating %d or less\n", delete_factor
);
2444 while(urlcache_next_entry(header
, &hash_table_off
, &hash_table_entry
, &hash_entry
, &entry
)) {
2445 if(entry
->signature
!= URL_SIGNATURE
)
2448 url_entry
= (entry_url
*)entry
;
2449 if(url_entry
->cache_entry_type
& filter
)
2452 if(urlcache_rate_entry(url_entry
, &cur_time
) <= delete_factor
) {
2453 TRACE("deleting file: %s\n", debugstr_a((char*)url_entry
+url_entry
->local_name_off
));
2454 urlcache_entry_delete(container
, header
, hash_entry
);
2456 if(header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
<= desired_size
)
2459 /* Allow other threads to use cache while cleaning */
2460 cache_container_unlock_index(container
, header
);
2461 if(WaitForSingleObject(dll_unload_event
, 0) == WAIT_OBJECT_0
) {
2462 TRACE("got dll_unload_event - finishing\n");
2466 header
= cache_container_lock_index(container
);
2470 TRACE("cache size after cleaning 0x%s/0x%s\n",
2471 wine_dbgstr_longlong(header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
),
2472 wine_dbgstr_longlong(header
->cache_limit
.QuadPart
));
2473 cache_container_unlock_index(container
, header
);
2479 /***********************************************************************
2480 * FreeUrlCacheSpaceA (WININET.@)
2482 * See FreeUrlCacheSpaceW.
2484 BOOL WINAPI
FreeUrlCacheSpaceA(LPCSTR lpszCachePath
, DWORD dwSize
, DWORD dwFilter
)
2487 LPWSTR path
= heap_strdupAtoW(lpszCachePath
);
2488 if (lpszCachePath
== NULL
|| path
!= NULL
)
2489 ret
= FreeUrlCacheSpaceW(path
, dwSize
, dwFilter
);
2494 /***********************************************************************
2495 * UnlockUrlCacheEntryFileA (WININET.@)
2498 BOOL WINAPI
UnlockUrlCacheEntryFileA(LPCSTR lpszUrlName
, DWORD dwReserved
)
2500 urlcache_header
*pHeader
;
2501 struct hash_entry
*pHashEntry
;
2502 entry_header
*pEntry
;
2503 entry_url
* pUrlEntry
;
2504 cache_container
*pContainer
;
2507 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName
), dwReserved
);
2511 ERR("dwReserved != 0\n");
2512 SetLastError(ERROR_INVALID_PARAMETER
);
2516 error
= cache_containers_find(lpszUrlName
, &pContainer
);
2517 if (error
!= ERROR_SUCCESS
)
2519 SetLastError(error
);
2523 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
2524 if (error
!= ERROR_SUCCESS
)
2526 SetLastError(error
);
2530 if (!(pHeader
= cache_container_lock_index(pContainer
)))
2533 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
2535 cache_container_unlock_index(pContainer
, pHeader
);
2536 TRACE("entry %s not found!\n", lpszUrlName
);
2537 SetLastError(ERROR_FILE_NOT_FOUND
);
2541 pEntry
= (entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2542 if (pEntry
->signature
!= URL_SIGNATURE
)
2544 cache_container_unlock_index(pContainer
, pHeader
);
2545 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR
)&pEntry
->signature
, sizeof(DWORD
)));
2546 SetLastError(ERROR_FILE_NOT_FOUND
);
2550 pUrlEntry
= (entry_url
*)pEntry
;
2552 if (pUrlEntry
->use_count
== 0)
2554 cache_container_unlock_index(pContainer
, pHeader
);
2557 pUrlEntry
->use_count
--;
2558 if (!pUrlEntry
->use_count
)
2560 urlcache_hash_entry_set_flags(pHashEntry
, HASHTABLE_URL
);
2561 if (pUrlEntry
->cache_entry_type
& PENDING_DELETE_CACHE_ENTRY
)
2562 urlcache_entry_delete(pContainer
, pHeader
, pHashEntry
);
2565 cache_container_unlock_index(pContainer
, pHeader
);
2570 /***********************************************************************
2571 * UnlockUrlCacheEntryFileW (WININET.@)
2574 BOOL WINAPI
UnlockUrlCacheEntryFileW(LPCWSTR lpszUrlName
, DWORD dwReserved
)
2579 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
2582 ret
= UnlockUrlCacheEntryFileA(url
, dwReserved
);
2587 /***********************************************************************
2588 * CreateUrlCacheEntryA (WININET.@)
2591 BOOL WINAPI
CreateUrlCacheEntryA(
2592 IN LPCSTR lpszUrlName
,
2593 IN DWORD dwExpectedFileSize
,
2594 IN LPCSTR lpszFileExtension
,
2595 OUT LPSTR lpszFileName
,
2600 WCHAR
*file_extension
= NULL
;
2601 WCHAR file_name
[MAX_PATH
];
2602 BOOL bSuccess
= FALSE
;
2605 TRACE("(%s %d %s %p %d)\n", debugstr_a(lpszUrlName
), dwExpectedFileSize
,
2606 debugstr_a(lpszFileExtension
), lpszFileName
, dwReserved
);
2608 if (lpszUrlName
&& (url_name
= heap_strdupAtoW(lpszUrlName
)))
2610 if (!lpszFileExtension
|| (file_extension
= heap_strdupAtoW(lpszFileExtension
)))
2612 if (CreateUrlCacheEntryW(url_name
, dwExpectedFileSize
, file_extension
, file_name
, dwReserved
))
2614 if (WideCharToMultiByte(CP_ACP
, 0, file_name
, -1, lpszFileName
, MAX_PATH
, NULL
, NULL
) < MAX_PATH
)
2620 dwError
= GetLastError();
2625 dwError
= GetLastError();
2627 heap_free(file_extension
);
2631 dwError
= GetLastError();
2633 heap_free(url_name
);
2634 if (!bSuccess
) SetLastError(dwError
);
2638 /***********************************************************************
2639 * CreateUrlCacheEntryW (WININET.@)
2642 BOOL WINAPI
CreateUrlCacheEntryW(
2643 IN LPCWSTR lpszUrlName
,
2644 IN DWORD dwExpectedFileSize
,
2645 IN LPCWSTR lpszFileExtension
,
2646 OUT LPWSTR lpszFileName
,
2650 cache_container
*pContainer
;
2651 urlcache_header
*pHeader
;
2652 CHAR szFile
[MAX_PATH
];
2653 WCHAR szExtension
[MAX_PATH
];
2654 LPCWSTR lpszUrlPart
;
2656 LPCWSTR lpszFileNameExtension
;
2657 LPWSTR lpszFileNameNoPath
;
2659 int countnoextension
;
2662 BOOL bFound
= FALSE
;
2663 BOOL generate_name
= FALSE
;
2669 static const WCHAR szWWW
[] = {'w','w','w',0};
2671 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2672 debugstr_w(lpszUrlName
),
2674 debugstr_w(lpszFileExtension
),
2679 FIXME("dwReserved 0x%08x\n", dwReserved
);
2681 lpszUrlEnd
= lpszUrlName
+ strlenW(lpszUrlName
);
2683 if (((lpszUrlEnd
- lpszUrlName
) > 1) && (*(lpszUrlEnd
- 1) == '/' || *(lpszUrlEnd
- 1) == '\\'))
2686 lpszUrlPart
= memchrW(lpszUrlName
, '?', lpszUrlEnd
- lpszUrlName
);
2688 lpszUrlPart
= memchrW(lpszUrlName
, '#', lpszUrlEnd
- lpszUrlName
);
2690 lpszUrlEnd
= lpszUrlPart
;
2692 for (lpszUrlPart
= lpszUrlEnd
;
2693 (lpszUrlPart
>= lpszUrlName
);
2696 if ((*lpszUrlPart
== '/' || *lpszUrlPart
== '\\') && ((lpszUrlEnd
- lpszUrlPart
) > 1))
2706 if (!lstrcmpW(lpszUrlPart
, szWWW
))
2708 lpszUrlPart
+= lstrlenW(szWWW
);
2711 count
= lpszUrlEnd
- lpszUrlPart
;
2713 if (bFound
&& (count
< MAX_PATH
))
2715 int len
= WideCharToMultiByte(CP_ACP
, 0, lpszUrlPart
, count
, szFile
, sizeof(szFile
) - 1, NULL
, NULL
);
2719 while(len
&& szFile
[--len
] == '/') szFile
[len
] = '\0';
2721 /* FIXME: get rid of illegal characters like \, / and : */
2722 TRACE("File name: %s\n", debugstr_a(szFile
));
2726 generate_name
= TRUE
;
2730 error
= cache_containers_findW(lpszUrlName
, &pContainer
);
2731 if (error
!= ERROR_SUCCESS
)
2733 SetLastError(error
);
2737 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
2738 if (error
!= ERROR_SUCCESS
)
2740 SetLastError(error
);
2744 if (!(pHeader
= cache_container_lock_index(pContainer
)))
2747 if(pHeader
->dirs_no
)
2748 CacheDir
= (BYTE
)(rand() % pHeader
->dirs_no
);
2750 CacheDir
= CACHE_CONTAINER_NO_SUBDIR
;
2752 lBufferSize
= MAX_PATH
* sizeof(WCHAR
);
2753 if (!urlcache_create_file_pathW(pContainer
, pHeader
, szFile
, CacheDir
, lpszFileName
, &lBufferSize
))
2755 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2756 debugstr_a(szFile
), lBufferSize
);
2757 cache_container_unlock_index(pContainer
, pHeader
);
2761 cache_container_unlock_index(pContainer
, pHeader
);
2763 for (lpszFileNameNoPath
= lpszFileName
+ lBufferSize
/ sizeof(WCHAR
) - 2;
2764 lpszFileNameNoPath
>= lpszFileName
;
2765 --lpszFileNameNoPath
)
2767 if (*lpszFileNameNoPath
== '/' || *lpszFileNameNoPath
== '\\')
2771 countnoextension
= lstrlenW(lpszFileNameNoPath
);
2772 lpszFileNameExtension
= PathFindExtensionW(lpszFileNameNoPath
);
2773 if (lpszFileNameExtension
)
2774 countnoextension
-= lstrlenW(lpszFileNameExtension
);
2775 *szExtension
= '\0';
2777 if (lpszFileExtension
)
2779 szExtension
[0] = '.';
2780 lstrcpyW(szExtension
+1, lpszFileExtension
);
2783 for (i
= 0; i
<255 && !generate_name
; i
++)
2785 static const WCHAR szFormat
[] = {'[','%','u',']','%','s',0};
2788 wsprintfW(lpszFileNameNoPath
+ countnoextension
, szFormat
, i
, szExtension
);
2789 for (p
= lpszFileNameNoPath
+ 1; *p
; p
++)
2795 case '/': case '\\':
2802 if (p
[-1] == ' ' || p
[-1] == '.') p
[-1] = '_';
2804 TRACE("Trying: %s\n", debugstr_w(lpszFileName
));
2805 hFile
= CreateFileW(lpszFileName
, GENERIC_READ
, 0, NULL
, CREATE_NEW
, 0, NULL
);
2806 if (hFile
!= INVALID_HANDLE_VALUE
)
2813 /* Try to generate random name */
2814 GetSystemTimeAsFileTime(&ft
);
2815 strcpyW(lpszFileNameNoPath
+countnoextension
+8, szExtension
);
2817 for(i
=0; i
<255; i
++)
2820 ULONGLONG n
= ft
.dwHighDateTime
;
2822 n
+= ft
.dwLowDateTime
;
2823 n
^= (ULONGLONG
)i
<<48;
2829 lpszFileNameNoPath
[countnoextension
+j
] = (r
< 10 ? '0' + r
: 'A' + r
- 10);
2832 TRACE("Trying: %s\n", debugstr_w(lpszFileName
));
2833 hFile
= CreateFileW(lpszFileName
, GENERIC_READ
, 0, NULL
, CREATE_NEW
, 0, NULL
);
2834 if (hFile
!= INVALID_HANDLE_VALUE
)
2841 WARN("Could not find a unique filename\n");
2845 /***********************************************************************
2846 * urlcache_entry_commit (Compensates for an MS bug)
2848 * The bug we are compensating for is that some drongo at Microsoft
2849 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2850 * As a consequence, CommitUrlCacheEntryA has been effectively
2851 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2852 * is still defined as LPCWSTR. The result (other than madness) is
2853 * that we always need to store lpHeaderInfo in CP_ACP rather than
2854 * in UTF16, and we need to avoid converting lpHeaderInfo in
2855 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2856 * result will lose data for arbitrary binary data.
2859 static BOOL
urlcache_entry_commit(
2860 IN LPCWSTR lpszUrlName
,
2861 IN LPCWSTR lpszLocalFileName
,
2862 IN FILETIME ExpireTime
,
2863 IN FILETIME LastModifiedTime
,
2864 IN DWORD CacheEntryType
,
2865 IN LPBYTE lpHeaderInfo
,
2866 IN DWORD dwHeaderSize
,
2867 IN LPCWSTR lpszFileExtension
,
2868 IN LPCWSTR lpszOriginalUrl
2871 cache_container
*pContainer
;
2872 urlcache_header
*pHeader
;
2873 struct hash_entry
*pHashEntry
;
2874 entry_header
*pEntry
;
2875 entry_url
* pUrlEntry
;
2876 DWORD url_entry_offset
;
2877 DWORD dwBytesNeeded
= DWORD_ALIGN(sizeof(*pUrlEntry
));
2878 DWORD dwOffsetLocalFileName
= 0;
2879 DWORD dwOffsetHeader
= 0;
2880 DWORD dwOffsetFileExtension
= 0;
2881 WIN32_FILE_ATTRIBUTE_DATA file_attr
;
2882 LARGE_INTEGER file_size
;
2884 char achFile
[MAX_PATH
];
2885 LPSTR lpszUrlNameA
= NULL
;
2886 LPSTR lpszFileExtensionA
= NULL
;
2887 char *pchLocalFileName
= 0;
2889 DWORD exempt_delta
= 0;
2892 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2893 debugstr_w(lpszUrlName
),
2894 debugstr_w(lpszLocalFileName
),
2898 debugstr_w(lpszFileExtension
),
2899 debugstr_w(lpszOriginalUrl
));
2901 if (CacheEntryType
& STICKY_CACHE_ENTRY
&& !lpszLocalFileName
)
2903 SetLastError(ERROR_INVALID_PARAMETER
);
2906 if (lpszOriginalUrl
)
2907 WARN(": lpszOriginalUrl ignored\n");
2909 memset(&file_attr
, 0, sizeof(file_attr
));
2910 if (lpszLocalFileName
)
2912 if(!GetFileAttributesExW(lpszLocalFileName
, GetFileExInfoStandard
, &file_attr
))
2915 file_size
.u
.LowPart
= file_attr
.nFileSizeLow
;
2916 file_size
.u
.HighPart
= file_attr
.nFileSizeHigh
;
2918 error
= cache_containers_findW(lpszUrlName
, &pContainer
);
2919 if (error
!= ERROR_SUCCESS
)
2921 SetLastError(error
);
2925 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
2926 if (error
!= ERROR_SUCCESS
)
2928 SetLastError(error
);
2932 if (!(pHeader
= cache_container_lock_index(pContainer
)))
2935 lpszUrlNameA
= heap_strdupWtoA(lpszUrlName
);
2938 error
= GetLastError();
2942 if (lpszFileExtension
&& !(lpszFileExtensionA
= heap_strdupWtoA(lpszFileExtension
)))
2944 error
= GetLastError();
2948 if (urlcache_find_hash_entry(pHeader
, lpszUrlNameA
, &pHashEntry
))
2950 entry_url
*pUrlEntry
= (entry_url
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2951 if (urlcache_hash_entry_is_locked(pHashEntry
, pUrlEntry
))
2953 TRACE("Trying to overwrite locked entry\n");
2954 error
= ERROR_SHARING_VIOLATION
;
2958 hit_rate
= pUrlEntry
->hit_rate
;
2959 exempt_delta
= pUrlEntry
->exempt_delta
;
2960 urlcache_entry_delete(pContainer
, pHeader
, pHashEntry
);
2963 if (pHeader
->dirs_no
)
2966 cDirectory
= CACHE_CONTAINER_NO_SUBDIR
;
2968 if (lpszLocalFileName
)
2970 BOOL bFound
= FALSE
;
2972 if (strncmpW(lpszLocalFileName
, pContainer
->path
, lstrlenW(pContainer
->path
)))
2974 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName
), debugstr_w(pContainer
->path
));
2975 error
= ERROR_INVALID_PARAMETER
;
2979 /* skip container path prefix */
2980 lpszLocalFileName
+= lstrlenW(pContainer
->path
);
2982 WideCharToMultiByte(CP_ACP
, 0, lpszLocalFileName
, -1, achFile
, MAX_PATH
, NULL
, NULL
);
2983 pchLocalFileName
= achFile
;
2985 if(pHeader
->dirs_no
)
2987 for (cDirectory
= 0; cDirectory
< pHeader
->dirs_no
; cDirectory
++)
2989 if (!strncmp(pHeader
->directory_data
[cDirectory
].name
, pchLocalFileName
, DIR_LENGTH
))
2998 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName
));
2999 error
= ERROR_INVALID_PARAMETER
;
3003 lpszLocalFileName
+= DIR_LENGTH
+ 1;
3004 pchLocalFileName
+= DIR_LENGTH
+ 1;
3008 dwBytesNeeded
= DWORD_ALIGN(dwBytesNeeded
+ strlen(lpszUrlNameA
) + 1);
3009 if (lpszLocalFileName
)
3011 dwOffsetLocalFileName
= dwBytesNeeded
;
3012 dwBytesNeeded
= DWORD_ALIGN(dwBytesNeeded
+ strlen(pchLocalFileName
) + 1);
3016 dwOffsetHeader
= dwBytesNeeded
;
3017 dwBytesNeeded
= DWORD_ALIGN(dwBytesNeeded
+ dwHeaderSize
);
3019 if (lpszFileExtensionA
)
3021 dwOffsetFileExtension
= dwBytesNeeded
;
3022 dwBytesNeeded
= DWORD_ALIGN(dwBytesNeeded
+ strlen(lpszFileExtensionA
) + 1);
3025 /* round up to next block */
3026 if (dwBytesNeeded
% BLOCKSIZE
)
3028 dwBytesNeeded
-= dwBytesNeeded
% BLOCKSIZE
;
3029 dwBytesNeeded
+= BLOCKSIZE
;
3032 error
= urlcache_entry_alloc(pHeader
, dwBytesNeeded
/ BLOCKSIZE
, &pEntry
);
3033 while (error
== ERROR_HANDLE_DISK_FULL
)
3035 error
= cache_container_clean_index(pContainer
, &pHeader
);
3036 if (error
== ERROR_SUCCESS
)
3037 error
= urlcache_entry_alloc(pHeader
, dwBytesNeeded
/ BLOCKSIZE
, &pEntry
);
3039 if (error
!= ERROR_SUCCESS
)
3042 /* FindFirstFreeEntry fills in blocks used */
3043 pUrlEntry
= (entry_url
*)pEntry
;
3044 url_entry_offset
= (LPBYTE
)pUrlEntry
- (LPBYTE
)pHeader
;
3045 pUrlEntry
->header
.signature
= URL_SIGNATURE
;
3046 pUrlEntry
->cache_dir
= cDirectory
;
3047 pUrlEntry
->cache_entry_type
= CacheEntryType
| pContainer
->default_entry_type
;
3048 pUrlEntry
->header_info_size
= dwHeaderSize
;
3049 if ((CacheEntryType
& STICKY_CACHE_ENTRY
) && !exempt_delta
)
3051 /* Sticky entries have a default exempt time of one day */
3052 exempt_delta
= 86400;
3054 pUrlEntry
->exempt_delta
= exempt_delta
;
3055 pUrlEntry
->hit_rate
= hit_rate
+1;
3056 pUrlEntry
->file_extension_off
= dwOffsetFileExtension
;
3057 pUrlEntry
->header_info_off
= dwOffsetHeader
;
3058 pUrlEntry
->local_name_off
= dwOffsetLocalFileName
;
3059 pUrlEntry
->url_off
= DWORD_ALIGN(sizeof(*pUrlEntry
));
3060 pUrlEntry
->size
.QuadPart
= file_size
.QuadPart
;
3061 pUrlEntry
->use_count
= 0;
3062 GetSystemTimeAsFileTime(&pUrlEntry
->access_time
);
3063 pUrlEntry
->modification_time
= LastModifiedTime
;
3064 file_time_to_dos_date_time(&pUrlEntry
->access_time
, &pUrlEntry
->sync_date
, &pUrlEntry
->sync_time
);
3065 file_time_to_dos_date_time(&ExpireTime
, &pUrlEntry
->expire_date
, &pUrlEntry
->expire_time
);
3066 file_time_to_dos_date_time(&file_attr
.ftLastWriteTime
, &pUrlEntry
->write_date
, &pUrlEntry
->write_time
);
3069 pUrlEntry
->unk1
= 0;
3070 pUrlEntry
->unk2
= 0;
3071 pUrlEntry
->unk3
= 0x60;
3072 pUrlEntry
->unk4
= 0;
3073 pUrlEntry
->unk5
= 0x1010;
3074 pUrlEntry
->unk7
= 0;
3075 pUrlEntry
->unk8
= 0;
3078 strcpy((LPSTR
)pUrlEntry
+ pUrlEntry
->url_off
, lpszUrlNameA
);
3079 if (dwOffsetLocalFileName
)
3080 strcpy((LPSTR
)((LPBYTE
)pUrlEntry
+ dwOffsetLocalFileName
), pchLocalFileName
);
3082 memcpy((LPBYTE
)pUrlEntry
+ dwOffsetHeader
, lpHeaderInfo
, dwHeaderSize
);
3083 if (dwOffsetFileExtension
)
3084 strcpy((LPSTR
)((LPBYTE
)pUrlEntry
+ dwOffsetFileExtension
), lpszFileExtensionA
);
3086 error
= urlcache_hash_entry_create(pHeader
, lpszUrlNameA
, url_entry_offset
, HASHTABLE_URL
);
3087 while (error
== ERROR_HANDLE_DISK_FULL
)
3089 error
= cache_container_clean_index(pContainer
, &pHeader
);
3090 if (error
== ERROR_SUCCESS
)
3092 pUrlEntry
= (entry_url
*)((LPBYTE
)pHeader
+ url_entry_offset
);
3093 error
= urlcache_hash_entry_create(pHeader
, lpszUrlNameA
,
3094 url_entry_offset
, HASHTABLE_URL
);
3097 if (error
!= ERROR_SUCCESS
)
3098 urlcache_entry_free(pHeader
, &pUrlEntry
->header
);
3101 if (pUrlEntry
->cache_dir
< pHeader
->dirs_no
)
3102 pHeader
->directory_data
[pUrlEntry
->cache_dir
].files_no
++;
3103 if (CacheEntryType
& STICKY_CACHE_ENTRY
)
3104 pHeader
->exempt_usage
.QuadPart
+= file_size
.QuadPart
;
3106 pHeader
->cache_usage
.QuadPart
+= file_size
.QuadPart
;
3107 if (pHeader
->cache_usage
.QuadPart
+ pHeader
->exempt_usage
.QuadPart
>
3108 pHeader
->cache_limit
.QuadPart
)
3109 handle_full_cache();
3113 cache_container_unlock_index(pContainer
, pHeader
);
3114 heap_free(lpszUrlNameA
);
3115 heap_free(lpszFileExtensionA
);
3117 if (error
== ERROR_SUCCESS
)
3121 SetLastError(error
);
3126 /***********************************************************************
3127 * CommitUrlCacheEntryA (WININET.@)
3130 BOOL WINAPI
CommitUrlCacheEntryA(
3131 IN LPCSTR lpszUrlName
,
3132 IN LPCSTR lpszLocalFileName
,
3133 IN FILETIME ExpireTime
,
3134 IN FILETIME LastModifiedTime
,
3135 IN DWORD CacheEntryType
,
3136 IN LPBYTE lpHeaderInfo
,
3137 IN DWORD dwHeaderSize
,
3138 IN LPCSTR lpszFileExtension
,
3139 IN LPCSTR lpszOriginalUrl
3142 WCHAR
*url_name
= NULL
;
3143 WCHAR
*local_file_name
= NULL
;
3144 WCHAR
*original_url
= NULL
;
3145 WCHAR
*file_extension
= NULL
;
3146 BOOL bSuccess
= FALSE
;
3148 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3149 debugstr_a(lpszUrlName
),
3150 debugstr_a(lpszLocalFileName
),
3154 debugstr_a(lpszFileExtension
),
3155 debugstr_a(lpszOriginalUrl
));
3157 url_name
= heap_strdupAtoW(lpszUrlName
);
3161 if (lpszLocalFileName
)
3163 local_file_name
= heap_strdupAtoW(lpszLocalFileName
);
3164 if (!local_file_name
)
3167 if (lpszFileExtension
)
3169 file_extension
= heap_strdupAtoW(lpszFileExtension
);
3170 if (!file_extension
)
3173 if (lpszOriginalUrl
)
3175 original_url
= heap_strdupAtoW(lpszOriginalUrl
);
3180 bSuccess
= urlcache_entry_commit(url_name
, local_file_name
, ExpireTime
, LastModifiedTime
,
3181 CacheEntryType
, lpHeaderInfo
, dwHeaderSize
,
3182 file_extension
, original_url
);
3185 heap_free(original_url
);
3186 heap_free(file_extension
);
3187 heap_free(local_file_name
);
3188 heap_free(url_name
);
3192 /***********************************************************************
3193 * CommitUrlCacheEntryW (WININET.@)
3196 BOOL WINAPI
CommitUrlCacheEntryW(
3197 IN LPCWSTR lpszUrlName
,
3198 IN LPCWSTR lpszLocalFileName
,
3199 IN FILETIME ExpireTime
,
3200 IN FILETIME LastModifiedTime
,
3201 IN DWORD CacheEntryType
,
3202 IN LPWSTR lpHeaderInfo
,
3203 IN DWORD dwHeaderSize
,
3204 IN LPCWSTR lpszFileExtension
,
3205 IN LPCWSTR lpszOriginalUrl
3209 BOOL bSuccess
= FALSE
;
3211 CHAR
*header_info
= NULL
;
3213 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3214 debugstr_w(lpszUrlName
),
3215 debugstr_w(lpszLocalFileName
),
3219 debugstr_w(lpszFileExtension
),
3220 debugstr_w(lpszOriginalUrl
));
3222 if (!lpHeaderInfo
|| (header_info
= heap_strdupWtoA(lpHeaderInfo
)))
3225 len
= strlen(header_info
);
3226 if (urlcache_entry_commit(lpszUrlName
, lpszLocalFileName
, ExpireTime
, LastModifiedTime
,
3227 CacheEntryType
, (LPBYTE
)header_info
, len
, lpszFileExtension
, lpszOriginalUrl
))
3233 dwError
= GetLastError();
3237 heap_free(header_info
);
3239 SetLastError(dwError
);
3245 /***********************************************************************
3246 * ReadUrlCacheEntryStream (WININET.@)
3249 BOOL WINAPI
ReadUrlCacheEntryStream(
3250 IN HANDLE hUrlCacheStream
,
3251 IN DWORD dwLocation
,
3252 IN OUT LPVOID lpBuffer
,
3253 IN OUT LPDWORD lpdwLen
,
3257 /* Get handle to file from 'stream' */
3258 stream_handle
*pStream
= (stream_handle
*)hUrlCacheStream
;
3260 if (dwReserved
!= 0)
3262 ERR("dwReserved != 0\n");
3263 SetLastError(ERROR_INVALID_PARAMETER
);
3267 if (IsBadReadPtr(pStream
, sizeof(*pStream
)) || IsBadStringPtrA(pStream
->url
, INTERNET_MAX_URL_LENGTH
))
3269 SetLastError(ERROR_INVALID_HANDLE
);
3273 if (SetFilePointer(pStream
->file
, dwLocation
, NULL
, FILE_CURRENT
) == INVALID_SET_FILE_POINTER
)
3275 return ReadFile(pStream
->file
, lpBuffer
, *lpdwLen
, lpdwLen
, NULL
);
3278 /***********************************************************************
3279 * RetrieveUrlCacheEntryStreamA (WININET.@)
3282 HANDLE WINAPI
RetrieveUrlCacheEntryStreamA(
3283 IN LPCSTR lpszUrlName
,
3284 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
3285 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
,
3286 IN BOOL fRandomRead
,
3290 /* NOTE: this is not the same as the way that the native
3291 * version allocates 'stream' handles. I did it this way
3292 * as it is much easier and no applications should depend
3293 * on this behaviour. (Native version appears to allocate
3294 * indices into a table)
3296 stream_handle
*pStream
;
3299 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName
), lpCacheEntryInfo
,
3300 lpdwCacheEntryInfoBufferSize
, fRandomRead
, dwReserved
);
3302 if (!RetrieveUrlCacheEntryFileA(lpszUrlName
,
3304 lpdwCacheEntryInfoBufferSize
,
3310 hFile
= CreateFileA(lpCacheEntryInfo
->lpszLocalFileName
,
3315 fRandomRead
? FILE_FLAG_RANDOM_ACCESS
: 0,
3317 if (hFile
== INVALID_HANDLE_VALUE
)
3320 /* allocate handle storage space */
3321 pStream
= heap_alloc(sizeof(stream_handle
) + strlen(lpszUrlName
) * sizeof(CHAR
));
3325 SetLastError(ERROR_OUTOFMEMORY
);
3329 pStream
->file
= hFile
;
3330 strcpy(pStream
->url
, lpszUrlName
);
3334 /***********************************************************************
3335 * RetrieveUrlCacheEntryStreamW (WININET.@)
3338 HANDLE WINAPI
RetrieveUrlCacheEntryStreamW(
3339 IN LPCWSTR lpszUrlName
,
3340 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
3341 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize
,
3342 IN BOOL fRandomRead
,
3348 /* NOTE: this is not the same as the way that the native
3349 * version allocates 'stream' handles. I did it this way
3350 * as it is much easier and no applications should depend
3351 * on this behaviour. (Native version appears to allocate
3352 * indices into a table)
3354 stream_handle
*pStream
;
3357 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName
), lpCacheEntryInfo
,
3358 lpdwCacheEntryInfoBufferSize
, fRandomRead
, dwReserved
);
3360 if (!RetrieveUrlCacheEntryFileW(lpszUrlName
,
3362 lpdwCacheEntryInfoBufferSize
,
3368 hFile
= CreateFileW(lpCacheEntryInfo
->lpszLocalFileName
,
3373 fRandomRead
? FILE_FLAG_RANDOM_ACCESS
: 0,
3375 if (hFile
== INVALID_HANDLE_VALUE
)
3378 /* allocate handle storage space */
3379 size
= sizeof(stream_handle
);
3380 url_len
= WideCharToMultiByte(CP_ACP
, 0, lpszUrlName
, -1, NULL
, 0, NULL
, NULL
);
3382 pStream
= heap_alloc(size
);
3386 SetLastError(ERROR_OUTOFMEMORY
);
3390 pStream
->file
= hFile
;
3391 WideCharToMultiByte(CP_ACP
, 0, lpszUrlName
, -1, pStream
->url
, url_len
, NULL
, NULL
);
3395 /***********************************************************************
3396 * UnlockUrlCacheEntryStream (WININET.@)
3399 BOOL WINAPI
UnlockUrlCacheEntryStream(
3400 IN HANDLE hUrlCacheStream
,
3404 stream_handle
*pStream
= (stream_handle
*)hUrlCacheStream
;
3406 if (dwReserved
!= 0)
3408 ERR("dwReserved != 0\n");
3409 SetLastError(ERROR_INVALID_PARAMETER
);
3413 if (IsBadReadPtr(pStream
, sizeof(*pStream
)) || IsBadStringPtrA(pStream
->url
, INTERNET_MAX_URL_LENGTH
))
3415 SetLastError(ERROR_INVALID_HANDLE
);
3419 if (!UnlockUrlCacheEntryFileA(pStream
->url
, 0))
3422 CloseHandle(pStream
->file
);
3428 /***********************************************************************
3429 * DeleteUrlCacheEntryA (WININET.@)
3432 BOOL WINAPI
DeleteUrlCacheEntryA(LPCSTR lpszUrlName
)
3434 cache_container
*pContainer
;
3435 urlcache_header
*pHeader
;
3436 struct hash_entry
*pHashEntry
;
3440 TRACE("(%s)\n", debugstr_a(lpszUrlName
));
3442 error
= cache_containers_find(lpszUrlName
, &pContainer
);
3443 if (error
!= ERROR_SUCCESS
)
3445 SetLastError(error
);
3449 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
3450 if (error
!= ERROR_SUCCESS
)
3452 SetLastError(error
);
3456 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3459 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
3461 cache_container_unlock_index(pContainer
, pHeader
);
3462 TRACE("entry %s not found!\n", lpszUrlName
);
3463 SetLastError(ERROR_FILE_NOT_FOUND
);
3467 ret
= urlcache_entry_delete(pContainer
, pHeader
, pHashEntry
);
3469 cache_container_unlock_index(pContainer
, pHeader
);
3474 /***********************************************************************
3475 * DeleteUrlCacheEntryW (WININET.@)
3478 BOOL WINAPI
DeleteUrlCacheEntryW(LPCWSTR lpszUrlName
)
3483 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
3486 ret
= DeleteUrlCacheEntryA(url
);
3491 BOOL WINAPI
DeleteUrlCacheContainerA(DWORD d1
, DWORD d2
)
3493 FIXME("(0x%08x, 0x%08x) stub\n", d1
, d2
);
3497 BOOL WINAPI
DeleteUrlCacheContainerW(DWORD d1
, DWORD d2
)
3499 FIXME("(0x%08x, 0x%08x) stub\n", d1
, d2
);
3503 /***********************************************************************
3504 * CreateCacheContainerA (WININET.@)
3506 BOOL WINAPI
CreateUrlCacheContainerA(DWORD d1
, DWORD d2
, DWORD d3
, DWORD d4
,
3507 DWORD d5
, DWORD d6
, DWORD d7
, DWORD d8
)
3509 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3510 d1
, d2
, d3
, d4
, d5
, d6
, d7
, d8
);
3514 /***********************************************************************
3515 * CreateCacheContainerW (WININET.@)
3517 BOOL WINAPI
CreateUrlCacheContainerW(DWORD d1
, DWORD d2
, DWORD d3
, DWORD d4
,
3518 DWORD d5
, DWORD d6
, DWORD d7
, DWORD d8
)
3520 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3521 d1
, d2
, d3
, d4
, d5
, d6
, d7
, d8
);
3525 /***********************************************************************
3526 * FindFirstUrlCacheContainerA (WININET.@)
3528 HANDLE WINAPI
FindFirstUrlCacheContainerA( LPVOID p1
, LPVOID p2
, LPVOID p3
, DWORD d1
)
3530 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1
, p2
, p3
, d1
);
3534 /***********************************************************************
3535 * FindFirstUrlCacheContainerW (WININET.@)
3537 HANDLE WINAPI
FindFirstUrlCacheContainerW( LPVOID p1
, LPVOID p2
, LPVOID p3
, DWORD d1
)
3539 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1
, p2
, p3
, d1
);
3543 /***********************************************************************
3544 * FindNextUrlCacheContainerA (WININET.@)
3546 BOOL WINAPI
FindNextUrlCacheContainerA( HANDLE handle
, LPVOID p1
, LPVOID p2
)
3548 FIXME("(%p, %p, %p) stub\n", handle
, p1
, p2
);
3552 /***********************************************************************
3553 * FindNextUrlCacheContainerW (WININET.@)
3555 BOOL WINAPI
FindNextUrlCacheContainerW( HANDLE handle
, LPVOID p1
, LPVOID p2
)
3557 FIXME("(%p, %p, %p) stub\n", handle
, p1
, p2
);
3561 HANDLE WINAPI
FindFirstUrlCacheEntryExA(
3562 LPCSTR lpszUrlSearchPattern
,
3566 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
,
3567 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3569 LPDWORD pcbReserved2
,
3573 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern
),
3574 dwFlags
, dwFilter
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
, lpFirstCacheEntryInfo
,
3575 lpdwFirstCacheEntryInfoBufferSize
, lpReserved
, pcbReserved2
,lpReserved3
);
3576 SetLastError(ERROR_FILE_NOT_FOUND
);
3580 HANDLE WINAPI
FindFirstUrlCacheEntryExW(
3581 LPCWSTR lpszUrlSearchPattern
,
3585 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
,
3586 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3588 LPDWORD pcbReserved2
,
3592 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern
),
3593 dwFlags
, dwFilter
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
, lpFirstCacheEntryInfo
,
3594 lpdwFirstCacheEntryInfoBufferSize
, lpReserved
, pcbReserved2
,lpReserved3
);
3595 SetLastError(ERROR_FILE_NOT_FOUND
);
3599 /***********************************************************************
3600 * FindFirstUrlCacheEntryA (WININET.@)
3603 INTERNETAPI HANDLE WINAPI
FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern
,
3604 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
, LPDWORD lpdwFirstCacheEntryInfoBufferSize
)
3606 find_handle
*pEntryHandle
;
3608 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern
), lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
);
3610 pEntryHandle
= heap_alloc(sizeof(*pEntryHandle
));
3614 pEntryHandle
->magic
= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
;
3615 if (lpszUrlSearchPattern
)
3617 pEntryHandle
->url_search_pattern
= heap_strdupA(lpszUrlSearchPattern
);
3618 if (!pEntryHandle
->url_search_pattern
)
3620 heap_free(pEntryHandle
);
3625 pEntryHandle
->url_search_pattern
= NULL
;
3626 pEntryHandle
->container_idx
= 0;
3627 pEntryHandle
->hash_table_idx
= 0;
3628 pEntryHandle
->hash_entry_idx
= 0;
3630 if (!FindNextUrlCacheEntryA(pEntryHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
))
3632 heap_free(pEntryHandle
);
3635 return pEntryHandle
;
3638 /***********************************************************************
3639 * FindFirstUrlCacheEntryW (WININET.@)
3642 INTERNETAPI HANDLE WINAPI
FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern
,
3643 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
, LPDWORD lpdwFirstCacheEntryInfoBufferSize
)
3645 find_handle
*pEntryHandle
;
3647 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern
), lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
);
3649 pEntryHandle
= heap_alloc(sizeof(*pEntryHandle
));
3653 pEntryHandle
->magic
= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
;
3654 if (lpszUrlSearchPattern
)
3656 pEntryHandle
->url_search_pattern
= heap_strdupWtoA(lpszUrlSearchPattern
);
3657 if (!pEntryHandle
->url_search_pattern
)
3659 heap_free(pEntryHandle
);
3664 pEntryHandle
->url_search_pattern
= NULL
;
3665 pEntryHandle
->container_idx
= 0;
3666 pEntryHandle
->hash_table_idx
= 0;
3667 pEntryHandle
->hash_entry_idx
= 0;
3669 if (!FindNextUrlCacheEntryW(pEntryHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
))
3671 heap_free(pEntryHandle
);
3674 return pEntryHandle
;
3677 static BOOL
urlcache_find_next_entry(
3679 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo
,
3680 LPDWORD lpdwNextCacheEntryInfoBufferSize
,
3683 find_handle
*pEntryHandle
= (find_handle
*)hEnumHandle
;
3684 cache_container
*pContainer
;
3686 if (pEntryHandle
->magic
!= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
)
3688 SetLastError(ERROR_INVALID_HANDLE
);
3692 for (; cache_containers_enum(pEntryHandle
->url_search_pattern
, pEntryHandle
->container_idx
, &pContainer
);
3693 pEntryHandle
->container_idx
++, pEntryHandle
->hash_table_idx
= 0)
3695 urlcache_header
*pHeader
;
3696 entry_hash_table
*pHashTableEntry
;
3699 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
3700 if (error
!= ERROR_SUCCESS
)
3702 SetLastError(error
);
3706 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3709 for (; urlcache_enum_hash_tables(pHeader
, &pEntryHandle
->hash_table_idx
, &pHashTableEntry
);
3710 pEntryHandle
->hash_table_idx
++, pEntryHandle
->hash_entry_idx
= 0)
3712 const struct hash_entry
*pHashEntry
= NULL
;
3713 for (; urlcache_enum_hash_table_entries(pHeader
, pHashTableEntry
, &pEntryHandle
->hash_entry_idx
, &pHashEntry
);
3714 pEntryHandle
->hash_entry_idx
++)
3716 const entry_url
*pUrlEntry
;
3717 const entry_header
*pEntry
= (const entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
3719 if (pEntry
->signature
!= URL_SIGNATURE
)
3722 pUrlEntry
= (const entry_url
*)pEntry
;
3723 TRACE("Found URL: %s\n",
3724 debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->url_off
));
3725 TRACE("Header info: %s\n",
3726 debugstr_an((LPCSTR
)pUrlEntry
+ pUrlEntry
->header_info_off
,
3727 pUrlEntry
->header_info_size
));
3729 error
= urlcache_copy_entry(
3732 lpNextCacheEntryInfo
,
3733 lpdwNextCacheEntryInfoBufferSize
,
3736 if (error
!= ERROR_SUCCESS
)
3738 cache_container_unlock_index(pContainer
, pHeader
);
3739 SetLastError(error
);
3742 if(pUrlEntry
->local_name_off
)
3743 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->local_name_off
));
3745 /* increment the current index so that next time the function
3746 * is called the next entry is returned */
3747 pEntryHandle
->hash_entry_idx
++;
3748 cache_container_unlock_index(pContainer
, pHeader
);
3753 cache_container_unlock_index(pContainer
, pHeader
);
3756 SetLastError(ERROR_NO_MORE_ITEMS
);
3760 /***********************************************************************
3761 * FindNextUrlCacheEntryA (WININET.@)
3763 BOOL WINAPI
FindNextUrlCacheEntryA(
3765 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo
,
3766 LPDWORD lpdwNextCacheEntryInfoBufferSize
)
3768 TRACE("(%p, %p, %p)\n", hEnumHandle
, lpNextCacheEntryInfo
, lpdwNextCacheEntryInfoBufferSize
);
3770 return urlcache_find_next_entry(hEnumHandle
, lpNextCacheEntryInfo
,
3771 lpdwNextCacheEntryInfoBufferSize
, FALSE
/* not UNICODE */);
3774 /***********************************************************************
3775 * FindNextUrlCacheEntryW (WININET.@)
3777 BOOL WINAPI
FindNextUrlCacheEntryW(
3779 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo
,
3780 LPDWORD lpdwNextCacheEntryInfoBufferSize
3783 TRACE("(%p, %p, %p)\n", hEnumHandle
, lpNextCacheEntryInfo
, lpdwNextCacheEntryInfoBufferSize
);
3785 return urlcache_find_next_entry(hEnumHandle
,
3786 (LPINTERNET_CACHE_ENTRY_INFOA
)lpNextCacheEntryInfo
,
3787 lpdwNextCacheEntryInfoBufferSize
, TRUE
/* UNICODE */);
3790 /***********************************************************************
3791 * FindCloseUrlCache (WININET.@)
3793 BOOL WINAPI
FindCloseUrlCache(HANDLE hEnumHandle
)
3795 find_handle
*pEntryHandle
= (find_handle
*)hEnumHandle
;
3797 TRACE("(%p)\n", hEnumHandle
);
3799 if (!pEntryHandle
|| pEntryHandle
->magic
!= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
)
3801 SetLastError(ERROR_INVALID_HANDLE
);
3805 pEntryHandle
->magic
= 0;
3806 heap_free(pEntryHandle
->url_search_pattern
);
3807 heap_free(pEntryHandle
);
3811 HANDLE WINAPI
FindFirstUrlCacheGroup( DWORD dwFlags
, DWORD dwFilter
, LPVOID lpSearchCondition
,
3812 DWORD dwSearchCondition
, GROUPID
* lpGroupId
, LPVOID lpReserved
)
3814 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags
, dwFilter
, lpSearchCondition
,
3815 dwSearchCondition
, lpGroupId
, lpReserved
);
3819 BOOL WINAPI
FindNextUrlCacheEntryExA(
3821 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
,
3822 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3824 LPDWORD pcbReserved2
,
3828 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
,
3829 lpReserved
, pcbReserved2
, lpReserved3
);
3833 BOOL WINAPI
FindNextUrlCacheEntryExW(
3835 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
,
3836 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3838 LPDWORD pcbReserved2
,
3842 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
,
3843 lpReserved
, pcbReserved2
, lpReserved3
);
3847 BOOL WINAPI
FindNextUrlCacheGroup( HANDLE hFind
, GROUPID
* lpGroupId
, LPVOID lpReserved
)
3849 FIXME("(%p, %p, %p) stub\n", hFind
, lpGroupId
, lpReserved
);
3853 /***********************************************************************
3854 * CreateUrlCacheGroup (WININET.@)
3857 INTERNETAPI GROUPID WINAPI
CreateUrlCacheGroup(DWORD dwFlags
, LPVOID lpReserved
)
3859 FIXME("(0x%08x, %p): stub\n", dwFlags
, lpReserved
);
3863 /***********************************************************************
3864 * DeleteUrlCacheGroup (WININET.@)
3867 BOOL WINAPI
DeleteUrlCacheGroup(GROUPID GroupId
, DWORD dwFlags
, LPVOID lpReserved
)
3869 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3870 (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
, dwFlags
, lpReserved
);
3874 /***********************************************************************
3875 * DeleteWpadCacheForNetworks (WININET.@)
3876 * Undocumented, added in IE8
3878 BOOL WINAPI
DeleteWpadCacheForNetworks(DWORD unk1
)
3880 FIXME("(%d) stub\n", unk1
);
3884 /***********************************************************************
3885 * SetUrlCacheEntryGroupA (WININET.@)
3888 BOOL WINAPI
SetUrlCacheEntryGroupA(LPCSTR lpszUrlName
, DWORD dwFlags
,
3889 GROUPID GroupId
, LPBYTE pbGroupAttributes
, DWORD cbGroupAttributes
,
3892 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3893 debugstr_a(lpszUrlName
), dwFlags
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
,
3894 pbGroupAttributes
, cbGroupAttributes
, lpReserved
);
3895 SetLastError(ERROR_FILE_NOT_FOUND
);
3899 /***********************************************************************
3900 * SetUrlCacheEntryGroupW (WININET.@)
3903 BOOL WINAPI
SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName
, DWORD dwFlags
,
3904 GROUPID GroupId
, LPBYTE pbGroupAttributes
, DWORD cbGroupAttributes
,
3907 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3908 debugstr_w(lpszUrlName
), dwFlags
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
,
3909 pbGroupAttributes
, cbGroupAttributes
, lpReserved
);
3910 SetLastError(ERROR_FILE_NOT_FOUND
);
3914 /***********************************************************************
3915 * GetUrlCacheConfigInfoW (WININET.@)
3917 BOOL WINAPI
GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo
, LPDWORD size
, DWORD bitmask
)
3919 FIXME("(%p, %p, %x)\n", CacheInfo
, size
, bitmask
);
3920 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
3924 /***********************************************************************
3925 * GetUrlCacheConfigInfoA (WININET.@)
3927 BOOL WINAPI
GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo
, LPDWORD size
, DWORD bitmask
)
3929 FIXME("(%p, %p, %x)\n", CacheInfo
, size
, bitmask
);
3930 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
3934 BOOL WINAPI
GetUrlCacheGroupAttributeA( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3935 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo
,
3936 LPDWORD lpdwGroupInfo
, LPVOID lpReserved
)
3938 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3939 (ULONG
)(gid
>> 32), (ULONG
)gid
, dwFlags
, dwAttributes
, lpGroupInfo
,
3940 lpdwGroupInfo
, lpReserved
);
3944 BOOL WINAPI
GetUrlCacheGroupAttributeW( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3945 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo
,
3946 LPDWORD lpdwGroupInfo
, LPVOID lpReserved
)
3948 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3949 (ULONG
)(gid
>> 32), (ULONG
)gid
, dwFlags
, dwAttributes
, lpGroupInfo
,
3950 lpdwGroupInfo
, lpReserved
);
3954 BOOL WINAPI
SetUrlCacheGroupAttributeA( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3955 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo
, LPVOID lpReserved
)
3957 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3958 (ULONG
)(gid
>> 32), (ULONG
)gid
, dwFlags
, dwAttributes
, lpGroupInfo
, lpReserved
);
3962 BOOL WINAPI
SetUrlCacheGroupAttributeW( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3963 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo
, LPVOID lpReserved
)
3965 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3966 (ULONG
)(gid
>> 32), (ULONG
)gid
, dwFlags
, dwAttributes
, lpGroupInfo
, lpReserved
);
3970 BOOL WINAPI
SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo
, DWORD dwFieldControl
)
3972 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo
, dwFieldControl
);
3976 BOOL WINAPI
SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo
, DWORD dwFieldControl
)
3978 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo
, dwFieldControl
);
3982 /***********************************************************************
3983 * DeleteIE3Cache (WININET.@)
3985 * Deletes the files used by the IE3 URL caching system.
3988 * hWnd [I] A dummy window.
3989 * hInst [I] Instance of process calling the function.
3990 * lpszCmdLine [I] Options used by function.
3991 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3993 DWORD WINAPI
DeleteIE3Cache(HWND hWnd
, HINSTANCE hInst
, LPSTR lpszCmdLine
, int nCmdShow
)
3995 FIXME("(%p, %p, %s, %d)\n", hWnd
, hInst
, debugstr_a(lpszCmdLine
), nCmdShow
);
3999 static BOOL
urlcache_entry_is_expired(const entry_url
*pUrlEntry
,
4000 FILETIME
*pftLastModified
)
4003 FILETIME now
, expired
;
4005 *pftLastModified
= pUrlEntry
->modification_time
;
4006 GetSystemTimeAsFileTime(&now
);
4007 dos_date_time_to_file_time(pUrlEntry
->expire_date
,
4008 pUrlEntry
->expire_time
, &expired
);
4009 /* If the expired time is 0, it's interpreted as not expired */
4010 if (!expired
.dwLowDateTime
&& !expired
.dwHighDateTime
)
4013 ret
= CompareFileTime(&expired
, &now
) < 0;
4017 /***********************************************************************
4018 * IsUrlCacheEntryExpiredA (WININET.@)
4022 * dwFlags [I] Unknown
4023 * pftLastModified [O] Last modified time
4025 BOOL WINAPI
IsUrlCacheEntryExpiredA( LPCSTR url
, DWORD dwFlags
, FILETIME
* pftLastModified
)
4027 urlcache_header
*pHeader
;
4028 struct hash_entry
*pHashEntry
;
4029 const entry_header
*pEntry
;
4030 const entry_url
* pUrlEntry
;
4031 cache_container
*pContainer
;
4034 TRACE("(%s, %08x, %p)\n", debugstr_a(url
), dwFlags
, pftLastModified
);
4036 if (!url
|| !pftLastModified
)
4039 FIXME("unknown flags 0x%08x\n", dwFlags
);
4041 /* Any error implies that the URL is expired, i.e. not in the cache */
4042 if (cache_containers_find(url
, &pContainer
))
4044 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
4048 if (cache_container_open_index(pContainer
, MIN_BLOCK_NO
))
4050 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
4054 if (!(pHeader
= cache_container_lock_index(pContainer
)))
4056 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
4060 if (!urlcache_find_hash_entry(pHeader
, url
, &pHashEntry
))
4062 cache_container_unlock_index(pContainer
, pHeader
);
4063 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
4064 TRACE("entry %s not found!\n", url
);
4068 pEntry
= (const entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
4069 if (pEntry
->signature
!= URL_SIGNATURE
)
4071 cache_container_unlock_index(pContainer
, pHeader
);
4072 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
4073 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR
)&pEntry
->signature
, sizeof(DWORD
)));
4077 pUrlEntry
= (const entry_url
*)pEntry
;
4078 expired
= urlcache_entry_is_expired(pUrlEntry
, pftLastModified
);
4080 cache_container_unlock_index(pContainer
, pHeader
);
4085 /***********************************************************************
4086 * IsUrlCacheEntryExpiredW (WININET.@)
4090 * dwFlags [I] Unknown
4091 * pftLastModified [O] Last modified time
4093 BOOL WINAPI
IsUrlCacheEntryExpiredW( LPCWSTR url
, DWORD dwFlags
, FILETIME
* pftLastModified
)
4095 urlcache_header
*pHeader
;
4096 struct hash_entry
*pHashEntry
;
4097 const entry_header
*pEntry
;
4098 const entry_url
* pUrlEntry
;
4099 cache_container
*pContainer
;
4102 TRACE("(%s, %08x, %p)\n", debugstr_w(url
), dwFlags
, pftLastModified
);
4104 if (!url
|| !pftLastModified
)
4107 FIXME("unknown flags 0x%08x\n", dwFlags
);
4109 /* Any error implies that the URL is expired, i.e. not in the cache */
4110 if (cache_containers_findW(url
, &pContainer
))
4112 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
4116 if (cache_container_open_index(pContainer
, MIN_BLOCK_NO
))
4118 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
4122 if (!(pHeader
= cache_container_lock_index(pContainer
)))
4124 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
4128 if (!urlcache_find_hash_entryW(pHeader
, url
, &pHashEntry
))
4130 cache_container_unlock_index(pContainer
, pHeader
);
4131 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
4132 TRACE("entry %s not found!\n", debugstr_w(url
));
4136 if (!urlcache_find_hash_entryW(pHeader
, url
, &pHashEntry
))
4138 cache_container_unlock_index(pContainer
, pHeader
);
4139 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
4140 TRACE("entry %s not found!\n", debugstr_w(url
));
4144 pEntry
= (const entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
4145 if (pEntry
->signature
!= URL_SIGNATURE
)
4147 cache_container_unlock_index(pContainer
, pHeader
);
4148 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
4149 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR
)&pEntry
->signature
, sizeof(DWORD
)));
4153 pUrlEntry
= (const entry_url
*)pEntry
;
4154 expired
= urlcache_entry_is_expired(pUrlEntry
, pftLastModified
);
4156 cache_container_unlock_index(pContainer
, pHeader
);
4161 /***********************************************************************
4162 * GetDiskInfoA (WININET.@)
4164 BOOL WINAPI
GetDiskInfoA(PCSTR path
, PDWORD cluster_size
, PDWORDLONG free
, PDWORDLONG total
)
4167 ULARGE_INTEGER bytes_free
, bytes_total
;
4169 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path
), cluster_size
, free
, total
);
4173 SetLastError(ERROR_INVALID_PARAMETER
);
4177 if ((ret
= GetDiskFreeSpaceExA(path
, NULL
, &bytes_total
, &bytes_free
)))
4179 if (cluster_size
) *cluster_size
= 1;
4180 if (free
) *free
= bytes_free
.QuadPart
;
4181 if (total
) *total
= bytes_total
.QuadPart
;
4186 /***********************************************************************
4187 * RegisterUrlCacheNotification (WININET.@)
4189 DWORD WINAPI
RegisterUrlCacheNotification(LPVOID a
, DWORD b
, DWORD c
, DWORD d
, DWORD e
, DWORD f
)
4191 FIXME("(%p %x %x %x %x %x)\n", a
, b
, c
, d
, e
, f
);
4195 /***********************************************************************
4196 * IncrementUrlCacheHeaderData (WININET.@)
4198 BOOL WINAPI
IncrementUrlCacheHeaderData(DWORD index
, LPDWORD data
)
4200 FIXME("(%u, %p)\n", index
, data
);
4204 /***********************************************************************
4205 * RunOnceUrlCache (WININET.@)
4208 DWORD WINAPI
RunOnceUrlCache(HWND hwnd
, HINSTANCE hinst
, LPSTR cmd
, int cmdshow
)
4210 FIXME("(%p, %p, %s, %d): stub\n", hwnd
, hinst
, debugstr_a(cmd
), cmdshow
);
4214 BOOL
init_urlcache(void)
4216 dll_unload_event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
4217 if(!dll_unload_event
)
4220 free_cache_running
= CreateSemaphoreW(NULL
, 1, 1, NULL
);
4221 if(!free_cache_running
) {
4222 CloseHandle(dll_unload_event
);
4226 cache_containers_init();
4230 void free_urlcache(void)
4232 SetEvent(dll_unload_event
);
4233 WaitForSingleObject(free_cache_running
, INFINITE
);
4234 ReleaseSemaphore(free_cache_running
, 1, NULL
);
4235 CloseHandle(free_cache_running
);
4236 CloseHandle(dll_unload_event
);
4238 cache_containers_free();
4241 /***********************************************************************
4242 * LoadUrlCacheContent (WININET.@)
4244 BOOL WINAPI
LoadUrlCacheContent(void)