2 * Wininet - Url Cache functions
4 * Copyright 2001,2002 CodeWeavers
5 * Copyright 2003-2008 Robert Shearman
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #define NONAMELESSUNION
48 #include "wine/unicode.h"
49 #include "wine/debug.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(wininet
);
53 static const char urlcache_ver_prefix
[] = "WINE URLCache Ver ";
54 static const char urlcache_ver
[] = "0.2012001";
57 #define CHAR_BIT (8 * sizeof(CHAR))
60 #define ENTRY_START_OFFSET 0x4000
62 #define MAX_DIR_NO 0x20
64 #define HASHTABLE_SIZE 448
65 #define HASHTABLE_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
66 #define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
67 #define ALLOCATION_TABLE_OFFSET 0x250
68 #define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
69 #define MIN_BLOCK_NO 0x80
70 #define MAX_BLOCK_NO (ALLOCATION_TABLE_SIZE * CHAR_BIT)
71 #define FILE_SIZE(blocks) ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
73 #define HASHTABLE_URL 0
74 #define HASHTABLE_DEL 1
75 #define HASHTABLE_LOCK 2
76 #define HASHTABLE_FREE 3
77 #define HASHTABLE_REDR 5
78 #define HASHTABLE_FLAG_BITS 6
80 #define PENDING_DELETE_CACHE_ENTRY 0x00400000
81 #define INSTALLED_CACHE_ENTRY 0x10000000
82 #define GET_INSTALLED_ENTRY 0x200
83 #define CACHE_CONTAINER_NO_SUBDIR 0xFE
85 #define CACHE_HEADER_DATA_ROOT_LEAK_OFFSET 0x16
87 #define FILETIME_SECOND 10000000
89 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
90 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
91 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
92 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
93 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
95 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
97 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
102 DWORD blocks_used
; /* number of 128byte blocks used by this entry */
108 FILETIME modification_time
;
109 FILETIME access_time
;
110 WORD expire_date
; /* expire date in dos format */
111 WORD expire_time
; /* expire time in dos format */
112 DWORD unk1
; /* usually zero */
113 ULARGE_INTEGER size
; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
114 DWORD unk2
; /* usually zero */
115 DWORD exempt_delta
; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
116 DWORD unk3
; /* usually 0x60 */
117 DWORD url_off
; /* offset of start of url from start of entry */
118 BYTE cache_dir
; /* index of cache directory this url is stored in */
119 BYTE unk4
; /* usually zero */
120 WORD unk5
; /* usually 0x1010 */
121 DWORD local_name_off
; /* offset of start of local filename from start of entry */
122 DWORD cache_entry_type
; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
123 DWORD header_info_off
; /* offset of start of header info from start of entry */
124 DWORD header_info_size
;
125 DWORD file_extension_off
; /* offset of start of file extension from start of entry */
126 WORD sync_date
; /* last sync date in dos format */
127 WORD sync_time
; /* last sync time in dos format */
128 DWORD hit_rate
; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
129 DWORD use_count
; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
132 DWORD unk7
; /* usually zero */
133 DWORD unk8
; /* usually zero */
134 /* packing to dword align start of next field */
135 /* CHAR szSourceUrlName[]; (url) */
136 /* packing to dword align start of next field */
137 /* CHAR szLocalFileName[]; (local file name excluding path) */
138 /* packing to dword align start of next field */
139 /* CHAR szHeaderInfo[]; (header info) */
153 struct hash_entry hash_table
[HASHTABLE_SIZE
];
160 DWORD hash_table_off
;
161 DWORD capacity_in_blocks
;
164 ULARGE_INTEGER cache_limit
;
165 ULARGE_INTEGER cache_usage
;
166 ULARGE_INTEGER exempt_usage
;
168 struct _directory_data
171 char name
[DIR_LENGTH
];
172 } directory_data
[MAX_DIR_NO
];
174 BYTE allocation_table
[ALLOCATION_TABLE_SIZE
];
185 struct list entry
; /* part of a list */
186 char *cache_prefix
; /* string that has to be prefixed for this container to be used */
187 LPWSTR path
; /* path to url container directory */
188 HANDLE mapping
; /* handle of file mapping */
189 DWORD file_size
; /* size of file when mapping was opened */
190 HANDLE mutex
; /* handle of mutex */
191 DWORD default_entry_type
;
197 char *url_search_pattern
;
199 DWORD hash_table_idx
;
200 DWORD hash_entry_idx
;
203 /* List of all containers available */
204 static struct list UrlContainers
= LIST_INIT(UrlContainers
);
206 static inline char *heap_strdupWtoUTF8(LPCWSTR str
)
211 DWORD size
= WideCharToMultiByte(CP_UTF8
, 0, str
, -1, NULL
, 0, NULL
, NULL
);
212 ret
= heap_alloc(size
);
214 WideCharToMultiByte(CP_UTF8
, 0, str
, -1, ret
, size
, NULL
, NULL
);
220 /***********************************************************************
221 * urlcache_block_is_free (Internal)
223 * Is the specified block number free?
230 static inline BYTE
urlcache_block_is_free(BYTE
*allocation_table
, DWORD block_number
)
232 BYTE mask
= 1 << (block_number
%CHAR_BIT
);
233 return (allocation_table
[block_number
/CHAR_BIT
] & mask
) == 0;
236 /***********************************************************************
237 * urlcache_block_free (Internal)
239 * Marks the specified block as free
242 * this function is not updating used blocks count
248 static inline void urlcache_block_free(BYTE
*allocation_table
, DWORD block_number
)
250 BYTE mask
= ~(1 << (block_number
%CHAR_BIT
));
251 allocation_table
[block_number
/CHAR_BIT
] &= mask
;
254 /***********************************************************************
255 * urlcache_block_alloc (Internal)
257 * Marks the specified block as allocated
260 * this function is not updating used blocks count
266 static inline void urlcache_block_alloc(BYTE
*allocation_table
, DWORD block_number
)
268 BYTE mask
= 1 << (block_number
%CHAR_BIT
);
269 allocation_table
[block_number
/CHAR_BIT
] |= mask
;
272 /***********************************************************************
273 * urlcache_entry_alloc (Internal)
275 * Finds and allocates the first block of free space big enough and
276 * sets entry to point to it.
279 * ERROR_SUCCESS when free memory block was found
280 * Any other Win32 error code if the entry could not be added
283 static DWORD
urlcache_entry_alloc(urlcache_header
*header
, DWORD blocks_needed
, entry_header
**entry
)
285 DWORD block
, block_size
;
287 for(block
=0; block
<header
->capacity_in_blocks
; block
+=block_size
+1)
290 while(block_size
<blocks_needed
&& block_size
+block
<header
->capacity_in_blocks
291 && urlcache_block_is_free(header
->allocation_table
, block
+block_size
))
294 if(block_size
== blocks_needed
)
298 TRACE("Found free blocks starting at no. %d (0x%x)\n", block
, ENTRY_START_OFFSET
+block
*BLOCKSIZE
);
300 for(index
=0; index
<blocks_needed
; index
++)
301 urlcache_block_alloc(header
->allocation_table
, block
+index
);
303 *entry
= (entry_header
*)((BYTE
*)header
+ENTRY_START_OFFSET
+block
*BLOCKSIZE
);
304 for(index
=0; index
<blocks_needed
*BLOCKSIZE
/sizeof(DWORD
); index
++)
305 ((DWORD
*)*entry
)[index
] = 0xdeadbeef;
306 (*entry
)->blocks_used
= blocks_needed
;
308 header
->blocks_in_use
+= blocks_needed
;
309 return ERROR_SUCCESS
;
313 return ERROR_HANDLE_DISK_FULL
;
316 /***********************************************************************
317 * urlcache_entry_free (Internal)
319 * Deletes the specified entry and frees the space allocated to it
322 * TRUE if it succeeded
326 static BOOL
urlcache_entry_free(urlcache_header
*header
, entry_header
*entry
)
328 DWORD start_block
, block
;
330 /* update allocation table */
331 start_block
= ((DWORD
)((BYTE
*)entry
- (BYTE
*)header
) - ENTRY_START_OFFSET
) / BLOCKSIZE
;
332 for(block
= start_block
; block
< start_block
+entry
->blocks_used
; block
++)
333 urlcache_block_free(header
->allocation_table
, block
);
335 header
->blocks_in_use
-= entry
->blocks_used
;
339 /***********************************************************************
340 * urlcache_create_hash_table (Internal)
342 * Creates a new hash table in free space and adds it to the chain of existing
346 * ERROR_SUCCESS if the hash table was created
347 * ERROR_DISK_FULL if the hash table could not be created
350 static DWORD
urlcache_create_hash_table(urlcache_header
*header
, entry_hash_table
*hash_table_prev
, entry_hash_table
**hash_table
)
352 DWORD dwOffset
, error
;
355 if((error
= urlcache_entry_alloc(header
, 0x20, (entry_header
**)hash_table
)) != ERROR_SUCCESS
)
358 dwOffset
= (BYTE
*)*hash_table
-(BYTE
*)header
;
361 hash_table_prev
->next
= dwOffset
;
363 header
->hash_table_off
= dwOffset
;
365 (*hash_table
)->header
.signature
= HASH_SIGNATURE
;
366 (*hash_table
)->next
= 0;
367 (*hash_table
)->id
= hash_table_prev
? hash_table_prev
->id
+1 : 0;
368 for(i
= 0; i
< HASHTABLE_SIZE
; i
++) {
369 (*hash_table
)->hash_table
[i
].offset
= HASHTABLE_FREE
;
370 (*hash_table
)->hash_table
[i
].key
= HASHTABLE_FREE
;
372 return ERROR_SUCCESS
;
375 /***********************************************************************
376 * cache_container_create_object_name (Internal)
378 * Converts a path to a name suitable for use as a Win32 object name.
379 * Replaces '\\' characters in-place with the specified character
380 * (usually '_' or '!')
386 static void cache_container_create_object_name(LPWSTR lpszPath
, WCHAR replace
)
388 for (; *lpszPath
; lpszPath
++)
390 if (*lpszPath
== '\\')
395 /* Caller must hold container lock */
396 static HANDLE
cache_container_map_index(HANDLE file
, const WCHAR
*path
, DWORD size
, BOOL
*validate
)
398 static const WCHAR mapping_name_format
[]
399 = {'%','s','i','n','d','e','x','.','d','a','t','_','%','l','u',0};
400 WCHAR mapping_name
[MAX_PATH
];
403 wsprintfW(mapping_name
, mapping_name_format
, path
, size
);
404 cache_container_create_object_name(mapping_name
, '_');
406 mapping
= OpenFileMappingW(FILE_MAP_WRITE
, FALSE
, mapping_name
);
408 if(validate
) *validate
= FALSE
;
412 if(validate
) *validate
= TRUE
;
413 return CreateFileMappingW(file
, NULL
, PAGE_READWRITE
, 0, 0, mapping_name
);
416 /* Caller must hold container lock */
417 static DWORD
cache_container_set_size(cache_container
*container
, HANDLE file
, DWORD blocks_no
)
419 static const WCHAR cache_content_key
[] = {'S','o','f','t','w','a','r','e','\\',
420 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
421 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
422 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s','\\',
423 'C','a','c','h','e','\\','C','o','n','t','e','n','t',0};
424 static const WCHAR cache_limit
[] = {'C','a','c','h','e','L','i','m','i','t',0};
426 DWORD file_size
= FILE_SIZE(blocks_no
);
427 WCHAR dir_path
[MAX_PATH
], *dir_name
;
428 entry_hash_table
*hashtable_entry
;
429 urlcache_header
*header
;
435 if(SetFilePointer(file
, file_size
, NULL
, FILE_BEGIN
) == INVALID_SET_FILE_POINTER
)
436 return GetLastError();
438 if(!SetEndOfFile(file
))
439 return GetLastError();
441 mapping
= cache_container_map_index(file
, container
->path
, file_size
, NULL
);
443 return GetLastError();
445 header
= MapViewOfFile(mapping
, FILE_MAP_WRITE
, 0, 0, 0);
447 CloseHandle(mapping
);
448 return GetLastError();
451 if(blocks_no
!= MIN_BLOCK_NO
) {
452 if(file_size
> header
->size
)
453 memset((char*)header
+header
->size
, 0, file_size
-header
->size
);
454 header
->size
= file_size
;
455 header
->capacity_in_blocks
= blocks_no
;
457 UnmapViewOfFile(header
);
458 CloseHandle(container
->mapping
);
459 container
->mapping
= mapping
;
460 container
->file_size
= file_size
;
461 return ERROR_SUCCESS
;
464 memset(header
, 0, file_size
);
465 /* First set some constants and defaults in the header */
466 memcpy(header
->signature
, urlcache_ver_prefix
, sizeof(urlcache_ver_prefix
)-1);
467 memcpy(header
->signature
+sizeof(urlcache_ver_prefix
)-1, urlcache_ver
, sizeof(urlcache_ver
)-1);
468 header
->size
= file_size
;
469 header
->capacity_in_blocks
= blocks_no
;
470 /* 127MB - taken from default for Windows 2000 */
471 header
->cache_limit
.QuadPart
= 0x07ff5400;
472 /* Copied from a Windows 2000 cache index */
473 header
->dirs_no
= container
->default_entry_type
==NORMAL_CACHE_ENTRY
? 4 : 0;
475 /* If the registry has a cache size set, use the registry value */
476 if(RegOpenKeyW(HKEY_CURRENT_USER
, cache_content_key
, &key
) == ERROR_SUCCESS
) {
477 DWORD dw
, len
= sizeof(dw
), keytype
;
479 if(RegQueryValueExW(key
, cache_limit
, NULL
, &keytype
, (BYTE
*)&dw
, &len
) == ERROR_SUCCESS
&&
480 keytype
== REG_DWORD
)
481 header
->cache_limit
.QuadPart
= (ULONGLONG
)dw
* 1024;
485 urlcache_create_hash_table(header
, NULL
, &hashtable_entry
);
487 /* Last step - create the directories */
488 strcpyW(dir_path
, container
->path
);
489 dir_name
= dir_path
+ strlenW(dir_path
);
492 GetSystemTimeAsFileTime(&ft
);
494 for(i
=0; i
<header
->dirs_no
; ++i
) {
495 header
->directory_data
[i
].files_no
= 0;
497 ULONGLONG n
= ft
.dwHighDateTime
;
500 /* Generate a file name to attempt to create.
501 * This algorithm will create what will appear
502 * to be random and unrelated directory names
503 * of up to 9 characters in length.
506 n
+= ft
.dwLowDateTime
;
507 n
^= ((ULONGLONG
) i
<< 56) | ((ULONGLONG
) j
<< 48);
509 for(k
= 0; k
< 8; ++k
) {
512 /* Dividing by a prime greater than 36 helps
513 * with the appearance of randomness
518 dir_name
[k
] = '0' + r
;
520 dir_name
[k
] = 'A' + (r
- 10);
523 if(CreateDirectoryW(dir_path
, 0)) {
524 /* The following is OK because we generated an
525 * 8 character directory name made from characters
526 * [A-Z0-9], which are equivalent for all code
527 * pages and for UTF-16
529 for (k
= 0; k
< 8; ++k
)
530 header
->directory_data
[i
].name
[k
] = dir_name
[k
];
533 /* Give up. The most likely cause of this
534 * is a full disk, but whatever the cause
535 * is, it should be more than apparent that
538 UnmapViewOfFile(header
);
539 CloseHandle(mapping
);
540 return GetLastError();
545 UnmapViewOfFile(header
);
546 CloseHandle(container
->mapping
);
547 container
->mapping
= mapping
;
548 container
->file_size
= file_size
;
549 return ERROR_SUCCESS
;
552 static BOOL
cache_container_is_valid(urlcache_header
*header
, DWORD file_size
)
554 DWORD allocation_size
, count_bits
, i
;
556 if(file_size
< FILE_SIZE(MIN_BLOCK_NO
))
559 if(file_size
!= header
->size
)
562 if (!memcmp(header
->signature
, urlcache_ver_prefix
, sizeof(urlcache_ver_prefix
)-1) &&
563 memcmp(header
->signature
+sizeof(urlcache_ver_prefix
)-1, urlcache_ver
, sizeof(urlcache_ver
)-1))
566 if(FILE_SIZE(header
->capacity_in_blocks
) != file_size
)
570 for(i
=0; i
<header
->capacity_in_blocks
/8; i
++) {
571 for(count_bits
= header
->allocation_table
[i
]; count_bits
!=0; count_bits
>>=1) {
576 if(allocation_size
!= header
->blocks_in_use
)
579 for(; i
<ALLOCATION_TABLE_SIZE
; i
++) {
580 if(header
->allocation_table
[i
])
587 /***********************************************************************
588 * cache_container_open_index (Internal)
590 * Opens the index file and saves mapping handle
593 * ERROR_SUCCESS if succeeded
594 * Any other Win32 error code if failed
597 static DWORD
cache_container_open_index(cache_container
*container
, DWORD blocks_no
)
599 static const WCHAR index_dat
[] = {'i','n','d','e','x','.','d','a','t',0};
602 WCHAR index_path
[MAX_PATH
];
606 WaitForSingleObject(container
->mutex
, INFINITE
);
608 if(container
->mapping
) {
609 ReleaseMutex(container
->mutex
);
610 return ERROR_SUCCESS
;
613 strcpyW(index_path
, container
->path
);
614 strcatW(index_path
, index_dat
);
616 file
= CreateFileW(index_path
, GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_ALWAYS
, 0, NULL
);
617 if(file
== INVALID_HANDLE_VALUE
) {
618 /* Maybe the directory wasn't there? Try to create it */
619 if(CreateDirectoryW(container
->path
, 0))
620 file
= CreateFileW(index_path
, GENERIC_READ
|GENERIC_WRITE
, FILE_SHARE_READ
|FILE_SHARE_WRITE
, NULL
, OPEN_ALWAYS
, 0, NULL
);
622 if(file
== INVALID_HANDLE_VALUE
) {
623 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(index_path
));
624 ReleaseMutex(container
->mutex
);
625 return GetLastError();
628 file_size
= GetFileSize(file
, NULL
);
629 if(file_size
== INVALID_FILE_SIZE
) {
631 ReleaseMutex(container
->mutex
);
632 return GetLastError();
635 if(blocks_no
< MIN_BLOCK_NO
)
636 blocks_no
= MIN_BLOCK_NO
;
637 else if(blocks_no
> MAX_BLOCK_NO
)
638 blocks_no
= MAX_BLOCK_NO
;
640 if(file_size
< FILE_SIZE(blocks_no
)) {
641 DWORD ret
= cache_container_set_size(container
, file
, blocks_no
);
643 ReleaseMutex(container
->mutex
);
647 container
->file_size
= file_size
;
648 container
->mapping
= cache_container_map_index(file
, container
->path
, file_size
, &validate
);
650 if(container
->mapping
&& validate
) {
651 urlcache_header
*header
= MapViewOfFile(container
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
653 if(header
&& !cache_container_is_valid(header
, file_size
)) {
654 WARN("detected old or broken index.dat file\n");
655 UnmapViewOfFile(header
);
656 FreeUrlCacheSpaceW(container
->path
, 100, 0);
658 UnmapViewOfFile(header
);
660 CloseHandle(container
->mapping
);
661 container
->mapping
= NULL
;
665 if(!container
->mapping
)
667 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
668 ReleaseMutex(container
->mutex
);
669 return GetLastError();
672 ReleaseMutex(container
->mutex
);
673 return ERROR_SUCCESS
;
676 /***********************************************************************
677 * cache_container_close_index (Internal)
685 static void cache_container_close_index(cache_container
*pContainer
)
687 CloseHandle(pContainer
->mapping
);
688 pContainer
->mapping
= NULL
;
691 static BOOL
cache_containers_add(const char *cache_prefix
, LPCWSTR path
,
692 DWORD default_entry_type
, LPWSTR mutex_name
)
694 cache_container
*pContainer
= heap_alloc(sizeof(cache_container
));
695 int cache_prefix_len
= strlen(cache_prefix
);
702 pContainer
->mapping
= NULL
;
703 pContainer
->file_size
= 0;
704 pContainer
->default_entry_type
= default_entry_type
;
706 pContainer
->path
= heap_strdupW(path
);
707 if (!pContainer
->path
)
709 heap_free(pContainer
);
713 pContainer
->cache_prefix
= heap_alloc(cache_prefix_len
+1);
714 if (!pContainer
->cache_prefix
)
716 heap_free(pContainer
->path
);
717 heap_free(pContainer
);
721 memcpy(pContainer
->cache_prefix
, cache_prefix
, cache_prefix_len
+1);
723 CharLowerW(mutex_name
);
724 cache_container_create_object_name(mutex_name
, '!');
726 if ((pContainer
->mutex
= CreateMutexW(NULL
, FALSE
, mutex_name
)) == NULL
)
728 ERR("couldn't create mutex (error is %d)\n", GetLastError());
729 heap_free(pContainer
->path
);
730 heap_free(pContainer
);
734 list_add_head(&UrlContainers
, &pContainer
->entry
);
739 static void cache_container_delete_container(cache_container
*pContainer
)
741 list_remove(&pContainer
->entry
);
743 cache_container_close_index(pContainer
);
744 CloseHandle(pContainer
->mutex
);
745 heap_free(pContainer
->path
);
746 heap_free(pContainer
->cache_prefix
);
747 heap_free(pContainer
);
750 static void cache_containers_init(void)
752 static const WCHAR UrlSuffix
[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
753 static const WCHAR HistorySuffix
[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
754 static const WCHAR CookieSuffix
[] = {0};
757 int nFolder
; /* CSIDL_* constant */
758 const WCHAR
*shpath_suffix
; /* suffix on path returned by SHGetSpecialFolderPath */
759 const char *cache_prefix
; /* prefix used to reference the container */
760 DWORD default_entry_type
;
761 } DefaultContainerData
[] =
763 { CSIDL_INTERNET_CACHE
, UrlSuffix
, "", NORMAL_CACHE_ENTRY
},
764 { CSIDL_HISTORY
, HistorySuffix
, "Visited:", URLHISTORY_CACHE_ENTRY
},
765 { CSIDL_COOKIES
, CookieSuffix
, "Cookie:", COOKIE_CACHE_ENTRY
},
769 for (i
= 0; i
< sizeof(DefaultContainerData
) / sizeof(DefaultContainerData
[0]); i
++)
771 WCHAR wszCachePath
[MAX_PATH
];
772 WCHAR wszMutexName
[MAX_PATH
];
773 int path_len
, suffix_len
;
776 if (!SHGetSpecialFolderPathW(NULL
, wszCachePath
, DefaultContainerData
[i
].nFolder
, TRUE
))
778 ERR("Couldn't get path for default container %u\n", i
);
781 path_len
= strlenW(wszCachePath
);
782 suffix_len
= strlenW(DefaultContainerData
[i
].shpath_suffix
);
784 if (path_len
+ suffix_len
+ 2 > MAX_PATH
)
786 ERR("Path too long\n");
790 wszCachePath
[path_len
] = '\\';
791 wszCachePath
[path_len
+1] = 0;
793 strcpyW(wszMutexName
, wszCachePath
);
797 memcpy(wszCachePath
+ path_len
+ 1, DefaultContainerData
[i
].shpath_suffix
, (suffix_len
+ 1) * sizeof(WCHAR
));
798 wszCachePath
[path_len
+ suffix_len
+ 1] = '\\';
799 wszCachePath
[path_len
+ suffix_len
+ 2] = '\0';
802 if (!WideCharToMultiByte(CP_ACP
, WC_NO_BEST_FIT_CHARS
, wszCachePath
, path_len
,
803 NULL
, 0, NULL
, &def_char
) || def_char
)
807 /* cannot convert path to ANSI code page */
808 if (!(path_len
= GetShortPathNameW(wszCachePath
, tmp
, MAX_PATH
)) ||
809 !WideCharToMultiByte(CP_ACP
, WC_NO_BEST_FIT_CHARS
, tmp
, path_len
,
810 NULL
, 0, NULL
, &def_char
) || def_char
)
811 ERR("Can't create container path accessible by ANSI functions\n");
813 memcpy(wszCachePath
, tmp
, (path_len
+1)*sizeof(WCHAR
));
816 cache_containers_add(DefaultContainerData
[i
].cache_prefix
, wszCachePath
,
817 DefaultContainerData
[i
].default_entry_type
, wszMutexName
);
821 static void cache_containers_free(void)
823 while(!list_empty(&UrlContainers
))
824 cache_container_delete_container(
825 LIST_ENTRY(list_head(&UrlContainers
), cache_container
, entry
)
829 static DWORD
cache_containers_find(const char *url
, cache_container
**ret
)
831 cache_container
*container
;
833 TRACE("searching for prefix for URL: %s\n", debugstr_a(url
));
836 return ERROR_INVALID_PARAMETER
;
838 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
840 int prefix_len
= strlen(container
->cache_prefix
);
842 if(!strncmp(container
->cache_prefix
, url
, prefix_len
)) {
843 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
845 return ERROR_SUCCESS
;
849 ERR("no container found\n");
850 return ERROR_FILE_NOT_FOUND
;
853 static BOOL
cache_containers_enum(char *search_pattern
, DWORD index
, cache_container
**ret
)
856 cache_container
*container
;
858 TRACE("searching for prefix: %s\n", debugstr_a(search_pattern
));
860 /* non-NULL search pattern only returns one container ever */
861 if (search_pattern
&& index
> 0)
864 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
868 if (!strcmp(container
->cache_prefix
, search_pattern
))
870 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
879 TRACE("found container with prefix %s\n", debugstr_a(container
->cache_prefix
));
889 /***********************************************************************
890 * cache_container_lock_index (Internal)
892 * Locks the index for system-wide exclusive access.
895 * Cache file header if successful
896 * NULL if failed and calls SetLastError.
898 static urlcache_header
* cache_container_lock_index(cache_container
*pContainer
)
902 urlcache_header
* pHeader
;
906 WaitForSingleObject(pContainer
->mutex
, INFINITE
);
908 pIndexData
= MapViewOfFile(pContainer
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
912 ReleaseMutex(pContainer
->mutex
);
913 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
916 pHeader
= (urlcache_header
*)pIndexData
;
918 /* file has grown - we need to remap to prevent us getting
919 * access violations when we try and access beyond the end
920 * of the memory mapped file */
921 if (pHeader
->size
!= pContainer
->file_size
)
923 UnmapViewOfFile( pHeader
);
924 cache_container_close_index(pContainer
);
925 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
926 if (error
!= ERROR_SUCCESS
)
928 ReleaseMutex(pContainer
->mutex
);
932 pIndexData
= MapViewOfFile(pContainer
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
936 ReleaseMutex(pContainer
->mutex
);
937 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
940 pHeader
= (urlcache_header
*)pIndexData
;
943 TRACE("Signature: %s, file size: %d bytes\n", pHeader
->signature
, pHeader
->size
);
945 for (index
= 0; index
< pHeader
->dirs_no
; index
++)
947 TRACE("Directory[%d] = \"%.8s\"\n", index
, pHeader
->directory_data
[index
].name
);
953 /***********************************************************************
954 * cache_container_unlock_index (Internal)
957 static BOOL
cache_container_unlock_index(cache_container
*pContainer
, urlcache_header
*pHeader
)
960 ReleaseMutex(pContainer
->mutex
);
961 return UnmapViewOfFile(pHeader
);
964 /***********************************************************************
965 * urlcache_create_file_pathW (Internal)
967 * Copies the full path to the specified buffer given the local file
968 * name and the index of the directory it is in. Always sets value in
969 * lpBufferSize to the required buffer size (in bytes).
972 * TRUE if the buffer was big enough
973 * FALSE if the buffer was too small
976 static BOOL
urlcache_create_file_pathW(
977 const cache_container
*pContainer
,
978 const urlcache_header
*pHeader
,
979 LPCSTR szLocalFileName
,
985 int path_len
= strlenW(pContainer
->path
);
986 int file_name_len
= MultiByteToWideChar(CP_ACP
, 0, szLocalFileName
, -1, NULL
, 0);
987 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
&& Directory
>=pHeader
->dirs_no
)
993 nRequired
= (path_len
+ file_name_len
) * sizeof(WCHAR
);
994 if(Directory
!= CACHE_CONTAINER_NO_SUBDIR
)
995 nRequired
+= (DIR_LENGTH
+ 1) * sizeof(WCHAR
);
996 if (nRequired
<= *lpBufferSize
)
1000 memcpy(wszPath
, pContainer
->path
, path_len
* sizeof(WCHAR
));
1001 if (Directory
!= CACHE_CONTAINER_NO_SUBDIR
)
1003 dir_len
= MultiByteToWideChar(CP_ACP
, 0, pHeader
->directory_data
[Directory
].name
, DIR_LENGTH
, wszPath
+ path_len
, DIR_LENGTH
);
1004 wszPath
[dir_len
+ path_len
] = '\\';
1011 MultiByteToWideChar(CP_ACP
, 0, szLocalFileName
, -1, wszPath
+ dir_len
+ path_len
, file_name_len
);
1012 *lpBufferSize
= nRequired
;
1015 *lpBufferSize
= nRequired
;
1019 /***********************************************************************
1020 * urlcache_create_file_pathA (Internal)
1022 * Copies the full path to the specified buffer given the local file
1023 * name and the index of the directory it is in. Always sets value in
1024 * lpBufferSize to the required buffer size.
1027 * TRUE if the buffer was big enough
1028 * FALSE if the buffer was too small
1031 static BOOL
urlcache_create_file_pathA(
1032 const cache_container
*pContainer
,
1033 const urlcache_header
*pHeader
,
1034 LPCSTR szLocalFileName
,
1037 LPLONG lpBufferSize
)
1040 int path_len
, file_name_len
, dir_len
;
1042 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
&& Directory
>=pHeader
->dirs_no
)
1048 path_len
= WideCharToMultiByte(CP_ACP
, 0, pContainer
->path
, -1, NULL
, 0, NULL
, NULL
) - 1;
1049 file_name_len
= strlen(szLocalFileName
) + 1 /* for nul-terminator */;
1050 if (Directory
!=CACHE_CONTAINER_NO_SUBDIR
)
1051 dir_len
= DIR_LENGTH
+1;
1055 nRequired
= (path_len
+ dir_len
+ file_name_len
) * sizeof(char);
1056 if (nRequired
<= *lpBufferSize
)
1058 WideCharToMultiByte(CP_ACP
, 0, pContainer
->path
, -1, szPath
, path_len
, NULL
, NULL
);
1060 memcpy(szPath
+path_len
, pHeader
->directory_data
[Directory
].name
, dir_len
-1);
1061 szPath
[path_len
+ dir_len
-1] = '\\';
1063 memcpy(szPath
+ path_len
+ dir_len
, szLocalFileName
, file_name_len
);
1064 *lpBufferSize
= nRequired
;
1067 *lpBufferSize
= nRequired
;
1071 /* Just like FileTimeToDosDateTime, except that it also maps the special
1072 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1074 static void file_time_to_dos_date_time(const FILETIME
*ft
, WORD
*fatdate
,
1077 if (!ft
->dwLowDateTime
&& !ft
->dwHighDateTime
)
1078 *fatdate
= *fattime
= 0;
1080 FileTimeToDosDateTime(ft
, fatdate
, fattime
);
1083 /***********************************************************************
1084 * urlcache_delete_file (Internal)
1086 static DWORD
urlcache_delete_file(const cache_container
*container
,
1087 urlcache_header
*header
, entry_url
*url_entry
)
1089 WIN32_FILE_ATTRIBUTE_DATA attr
;
1090 WCHAR path
[MAX_PATH
];
1091 LONG path_size
= sizeof(path
);
1095 if(!url_entry
->local_name_off
)
1098 if(!urlcache_create_file_pathW(container
, header
,
1099 (LPCSTR
)url_entry
+url_entry
->local_name_off
,
1100 url_entry
->cache_dir
, path
, &path_size
))
1103 if(!GetFileAttributesExW(path
, GetFileExInfoStandard
, &attr
))
1105 file_time_to_dos_date_time(&attr
.ftLastWriteTime
, &date
, &time
);
1106 if(date
!= url_entry
->write_date
|| time
!= url_entry
->write_time
)
1109 err
= (DeleteFileW(path
) ? ERROR_SUCCESS
: GetLastError());
1110 if(err
== ERROR_ACCESS_DENIED
|| err
== ERROR_SHARING_VIOLATION
)
1114 if (url_entry
->cache_dir
< header
->dirs_no
)
1116 if (header
->directory_data
[url_entry
->cache_dir
].files_no
)
1117 header
->directory_data
[url_entry
->cache_dir
].files_no
--;
1119 if (url_entry
->cache_entry_type
& STICKY_CACHE_ENTRY
)
1121 if (url_entry
->size
.QuadPart
< header
->exempt_usage
.QuadPart
)
1122 header
->exempt_usage
.QuadPart
-= url_entry
->size
.QuadPart
;
1124 header
->exempt_usage
.QuadPart
= 0;
1128 if (url_entry
->size
.QuadPart
< header
->cache_usage
.QuadPart
)
1129 header
->cache_usage
.QuadPart
-= url_entry
->size
.QuadPart
;
1131 header
->cache_usage
.QuadPart
= 0;
1134 return ERROR_SUCCESS
;
1137 static BOOL
urlcache_clean_leaked_entries(cache_container
*container
, urlcache_header
*header
)
1142 leak_off
= &header
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
];
1144 entry_url
*url_entry
= (entry_url
*)((LPBYTE
)header
+ *leak_off
);
1146 if(SUCCEEDED(urlcache_delete_file(container
, header
, url_entry
))) {
1147 *leak_off
= url_entry
->exempt_delta
;
1148 urlcache_entry_free(header
, &url_entry
->header
);
1151 leak_off
= &url_entry
->exempt_delta
;
1158 /***********************************************************************
1159 * cache_container_clean_index (Internal)
1161 * This function is meant to make place in index file by removing leaked
1162 * files entries and resizing the file.
1164 * CAUTION: file view may get mapped to new memory
1167 * ERROR_SUCCESS when new memory is available
1168 * error code otherwise
1170 static DWORD
cache_container_clean_index(cache_container
*container
, urlcache_header
**file_view
)
1172 urlcache_header
*header
= *file_view
;
1175 TRACE("(%s %s)\n", debugstr_a(container
->cache_prefix
), debugstr_w(container
->path
));
1177 if(urlcache_clean_leaked_entries(container
, header
))
1178 return ERROR_SUCCESS
;
1180 if(header
->size
>= ALLOCATION_TABLE_SIZE
*8*BLOCKSIZE
+ ENTRY_START_OFFSET
) {
1181 WARN("index file has maximal size\n");
1182 return ERROR_NOT_ENOUGH_MEMORY
;
1185 cache_container_close_index(container
);
1186 ret
= cache_container_open_index(container
, header
->capacity_in_blocks
*2);
1187 if(ret
!= ERROR_SUCCESS
)
1189 header
= MapViewOfFile(container
->mapping
, FILE_MAP_WRITE
, 0, 0, 0);
1191 return GetLastError();
1193 UnmapViewOfFile(*file_view
);
1194 *file_view
= header
;
1195 return ERROR_SUCCESS
;
1198 /* Just like DosDateTimeToFileTime, except that it also maps the special
1199 * case of a DOS date/time of (0,0) to a filetime of (0,0).
1201 static void dos_date_time_to_file_time(WORD fatdate
, WORD fattime
,
1204 if (!fatdate
&& !fattime
)
1205 ft
->dwLowDateTime
= ft
->dwHighDateTime
= 0;
1207 DosDateTimeToFileTime(fatdate
, fattime
, ft
);
1210 static int urlcache_decode_url(const char *url
, WCHAR
*decoded_url
, int decoded_len
)
1213 DWORD len
, part_len
;
1216 memset(&uc
, 0, sizeof(uc
));
1217 uc
.dwStructSize
= sizeof(uc
);
1218 uc
.dwHostNameLength
= 1;
1219 if(!InternetCrackUrlA(url
, 0, 0, &uc
))
1220 uc
.nScheme
= INTERNET_SCHEME_UNKNOWN
;
1222 if(uc
.nScheme
!=INTERNET_SCHEME_HTTP
&& uc
.nScheme
!=INTERNET_SCHEME_HTTPS
)
1223 return MultiByteToWideChar(CP_UTF8
, 0, url
, -1, decoded_url
, decoded_len
);
1228 len
= MultiByteToWideChar(CP_UTF8
, 0, url
, uc
.lpszHostName
-url
, decoded_url
, decoded_len
);
1234 host_name
= heap_alloc(uc
.dwHostNameLength
*sizeof(WCHAR
));
1237 if(!MultiByteToWideChar(CP_UTF8
, 0, uc
.lpszHostName
, uc
.dwHostNameLength
,
1238 host_name
, uc
.dwHostNameLength
)) {
1239 heap_free(host_name
);
1242 part_len
= IdnToUnicode(0, host_name
, uc
.dwHostNameLength
,
1243 decoded_url
? decoded_url
+len
: NULL
, decoded_len
);
1244 heap_free(host_name
);
1246 SetLastError(ERROR_INTERNET_INVALID_URL
);
1251 decoded_len
-= part_len
;
1253 part_len
= MultiByteToWideChar(CP_UTF8
, 0,
1254 uc
.lpszHostName
+uc
.dwHostNameLength
,
1255 -1, decoded_url
? decoded_url
+len
: NULL
, decoded_len
);
1263 /***********************************************************************
1264 * urlcache_copy_entry (Internal)
1266 * Copies an entry from the cache index file to the Win32 structure
1269 * ERROR_SUCCESS if the buffer was big enough
1270 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1273 static DWORD
urlcache_copy_entry(cache_container
*container
, const urlcache_header
*header
,
1274 INTERNET_CACHE_ENTRY_INFOA
*entry_info
, DWORD
*info_size
, const entry_url
*url_entry
, BOOL unicode
)
1277 DWORD size
= sizeof(*entry_info
);
1279 if(*info_size
>= size
) {
1280 entry_info
->lpHeaderInfo
= NULL
;
1281 entry_info
->lpszFileExtension
= NULL
;
1282 entry_info
->lpszLocalFileName
= NULL
;
1283 entry_info
->lpszSourceUrlName
= NULL
;
1284 entry_info
->CacheEntryType
= url_entry
->cache_entry_type
;
1285 entry_info
->u
.dwExemptDelta
= url_entry
->exempt_delta
;
1286 entry_info
->dwHeaderInfoSize
= url_entry
->header_info_size
;
1287 entry_info
->dwHitRate
= url_entry
->hit_rate
;
1288 entry_info
->dwSizeHigh
= url_entry
->size
.u
.HighPart
;
1289 entry_info
->dwSizeLow
= url_entry
->size
.u
.LowPart
;
1290 entry_info
->dwStructSize
= sizeof(*entry_info
);
1291 entry_info
->dwUseCount
= url_entry
->use_count
;
1292 dos_date_time_to_file_time(url_entry
->expire_date
, url_entry
->expire_time
, &entry_info
->ExpireTime
);
1293 entry_info
->LastAccessTime
= url_entry
->access_time
;
1294 entry_info
->LastModifiedTime
= url_entry
->modification_time
;
1295 dos_date_time_to_file_time(url_entry
->sync_date
, url_entry
->sync_time
, &entry_info
->LastSyncTime
);
1299 url_len
= urlcache_decode_url((const char*)url_entry
+url_entry
->url_off
, NULL
, 0);
1301 url_len
= strlen((LPCSTR
)url_entry
+url_entry
->url_off
) + 1;
1302 size
+= url_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1304 if(*info_size
>= size
) {
1305 DWORD url_size
= url_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1307 entry_info
->lpszSourceUrlName
= (LPSTR
)entry_info
+size
-url_size
;
1309 urlcache_decode_url((const char*)url_entry
+url_entry
->url_off
, (WCHAR
*)entry_info
->lpszSourceUrlName
, url_len
);
1311 memcpy(entry_info
->lpszSourceUrlName
, (LPCSTR
)url_entry
+url_entry
->url_off
, url_size
);
1314 if(size
%4 && size
<*info_size
)
1315 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1316 size
= DWORD_ALIGN(size
);
1318 if(url_entry
->local_name_off
) {
1319 LONG file_name_size
;
1321 file_name
= (LPSTR
)entry_info
+size
;
1322 file_name_size
= *info_size
-size
;
1323 if((unicode
&& urlcache_create_file_pathW(container
, header
, (LPCSTR
)url_entry
+url_entry
->local_name_off
, url_entry
->cache_dir
, (LPWSTR
)file_name
, &file_name_size
)) ||
1324 (!unicode
&& urlcache_create_file_pathA(container
, header
, (LPCSTR
)url_entry
+url_entry
->local_name_off
, url_entry
->cache_dir
, file_name
, &file_name_size
))) {
1325 entry_info
->lpszLocalFileName
= file_name
;
1327 size
+= file_name_size
;
1329 if(size
%4 && size
<*info_size
)
1330 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1331 size
= DWORD_ALIGN(size
);
1334 if(url_entry
->header_info_off
) {
1338 header_len
= MultiByteToWideChar(CP_UTF8
, 0, (const char*)url_entry
+url_entry
->header_info_off
,
1339 url_entry
->header_info_size
, NULL
, 0);
1341 header_len
= url_entry
->header_info_size
;
1342 size
+= header_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1344 if(*info_size
>= size
) {
1345 DWORD header_size
= header_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1346 entry_info
->lpHeaderInfo
= (LPBYTE
)entry_info
+size
-header_size
;
1348 MultiByteToWideChar(CP_UTF8
, 0, (const char*)url_entry
+url_entry
->header_info_off
,
1349 url_entry
->header_info_size
, (LPWSTR
)entry_info
->lpHeaderInfo
, header_len
);
1351 memcpy(entry_info
->lpHeaderInfo
, (LPCSTR
)url_entry
+url_entry
->header_info_off
, header_len
);
1353 if(size
%4 && size
<*info_size
)
1354 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1355 size
= DWORD_ALIGN(size
);
1358 if(url_entry
->file_extension_off
) {
1362 ext_len
= MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)url_entry
+url_entry
->file_extension_off
, -1, NULL
, 0);
1364 ext_len
= strlen((LPCSTR
)url_entry
+url_entry
->file_extension_off
) + 1;
1365 size
+= ext_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1367 if(*info_size
>= size
) {
1368 DWORD ext_size
= ext_len
* (unicode
? sizeof(WCHAR
) : sizeof(CHAR
));
1369 entry_info
->lpszFileExtension
= (LPSTR
)entry_info
+size
-ext_size
;
1371 MultiByteToWideChar(CP_ACP
, 0, (LPCSTR
)url_entry
+url_entry
->file_extension_off
, -1, (LPWSTR
)entry_info
->lpszFileExtension
, ext_len
);
1373 memcpy(entry_info
->lpszFileExtension
, (LPCSTR
)url_entry
+url_entry
->file_extension_off
, ext_len
*sizeof(CHAR
));
1376 if(size
%4 && size
<*info_size
)
1377 ZeroMemory((LPBYTE
)entry_info
+size
, 4-size
%4);
1378 size
= DWORD_ALIGN(size
);
1381 if(size
> *info_size
) {
1383 return ERROR_INSUFFICIENT_BUFFER
;
1386 return ERROR_SUCCESS
;
1389 /***********************************************************************
1390 * urlcache_set_entry_info (Internal)
1392 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1393 * according to the flags set by field_control.
1396 * ERROR_SUCCESS if the buffer was big enough
1397 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1400 static DWORD
urlcache_set_entry_info(entry_url
*url_entry
, const INTERNET_CACHE_ENTRY_INFOA
*entry_info
, DWORD field_control
)
1402 if (field_control
& CACHE_ENTRY_ACCTIME_FC
)
1403 url_entry
->access_time
= entry_info
->LastAccessTime
;
1404 if (field_control
& CACHE_ENTRY_ATTRIBUTE_FC
)
1405 url_entry
->cache_entry_type
= entry_info
->CacheEntryType
;
1406 if (field_control
& CACHE_ENTRY_EXEMPT_DELTA_FC
)
1407 url_entry
->exempt_delta
= entry_info
->u
.dwExemptDelta
;
1408 if (field_control
& CACHE_ENTRY_EXPTIME_FC
)
1409 file_time_to_dos_date_time(&entry_info
->ExpireTime
, &url_entry
->expire_date
, &url_entry
->expire_time
);
1410 if (field_control
& CACHE_ENTRY_HEADERINFO_FC
)
1411 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1412 if (field_control
& CACHE_ENTRY_HITRATE_FC
)
1413 url_entry
->hit_rate
= entry_info
->dwHitRate
;
1414 if (field_control
& CACHE_ENTRY_MODTIME_FC
)
1415 url_entry
->modification_time
= entry_info
->LastModifiedTime
;
1416 if (field_control
& CACHE_ENTRY_SYNCTIME_FC
)
1417 file_time_to_dos_date_time(&entry_info
->LastAccessTime
, &url_entry
->sync_date
, &url_entry
->sync_time
);
1419 return ERROR_SUCCESS
;
1422 /***********************************************************************
1423 * urlcache_hash_key (Internal)
1425 * Returns the hash key for a given string
1428 * hash key for the string
1431 static DWORD
urlcache_hash_key(LPCSTR lpszKey
)
1433 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1434 * but the algorithm and result are not the same!
1436 static const unsigned char lookupTable
[256] =
1438 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1439 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1440 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1441 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1442 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1443 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1444 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1445 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1446 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1447 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1448 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1449 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1450 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1451 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1452 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1453 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1454 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1455 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1456 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1457 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1458 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1459 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1460 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1461 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1462 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1463 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1464 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1465 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1466 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1467 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1468 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1469 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1474 for (i
= 0; i
< sizeof(key
) / sizeof(key
[0]); i
++)
1475 key
[i
] = lookupTable
[(*lpszKey
+ i
) & 0xFF];
1477 for (lpszKey
++; *lpszKey
; lpszKey
++)
1479 for (i
= 0; i
< sizeof(key
) / sizeof(key
[0]); i
++)
1480 key
[i
] = lookupTable
[*lpszKey
^ key
[i
]];
1483 return *(DWORD
*)key
;
1486 static inline entry_hash_table
* urlcache_get_hash_table(const urlcache_header
*pHeader
, DWORD dwOffset
)
1490 return (entry_hash_table
*)((LPBYTE
)pHeader
+ dwOffset
);
1493 static BOOL
urlcache_find_hash_entry(const urlcache_header
*pHeader
, LPCSTR lpszUrl
, struct hash_entry
**ppHashEntry
)
1495 /* structure of hash table:
1496 * 448 entries divided into 64 blocks
1497 * each block therefore contains a chain of 7 key/offset pairs
1498 * how position in table is calculated:
1499 * 1. the url is hashed in helper function
1500 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1501 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1504 * there can be multiple hash tables in the file and the offset to
1505 * the next one is stored in the header of the hash table
1507 DWORD key
= urlcache_hash_key(lpszUrl
);
1508 DWORD offset
= (key
& (HASHTABLE_NUM_ENTRIES
-1)) * HASHTABLE_BLOCKSIZE
;
1509 entry_hash_table
* pHashEntry
;
1512 key
>>= HASHTABLE_FLAG_BITS
;
1514 for (pHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1515 pHashEntry
; pHashEntry
= urlcache_get_hash_table(pHeader
, pHashEntry
->next
))
1518 if (pHashEntry
->id
!= id
++)
1520 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry
->id
, id
);
1523 /* make sure that it is in fact a hash entry */
1524 if (pHashEntry
->header
.signature
!= HASH_SIGNATURE
)
1526 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&pHashEntry
->header
.signature
);
1530 for (i
= 0; i
< HASHTABLE_BLOCKSIZE
; i
++)
1532 struct hash_entry
*pHashElement
= &pHashEntry
->hash_table
[offset
+ i
];
1533 if (key
== pHashElement
->key
>>HASHTABLE_FLAG_BITS
)
1535 /* FIXME: we should make sure that this is the right element
1536 * before returning and claiming that it is. We can do this
1537 * by doing a simple compare between the URL we were given
1538 * and the URL stored in the entry. However, this assumes
1539 * we know the format of all the entries stored in the
1541 *ppHashEntry
= pHashElement
;
1549 /***********************************************************************
1550 * urlcache_hash_entry_set_flags (Internal)
1552 * Sets special bits in hash key
1558 static void urlcache_hash_entry_set_flags(struct hash_entry
*pHashEntry
, DWORD dwFlag
)
1560 pHashEntry
->key
= (pHashEntry
->key
>> HASHTABLE_FLAG_BITS
<< HASHTABLE_FLAG_BITS
) | dwFlag
;
1563 /***********************************************************************
1564 * urlcache_hash_entry_delete (Internal)
1566 * Searches all the hash tables in the index for the given URL and
1567 * then if found deletes the entry.
1570 * TRUE if the entry was found
1571 * FALSE if the entry could not be found
1574 static BOOL
urlcache_hash_entry_delete(struct hash_entry
*pHashEntry
)
1576 pHashEntry
->key
= HASHTABLE_DEL
;
1580 /***********************************************************************
1581 * urlcache_hash_entry_create (Internal)
1583 * Searches all the hash tables for a free slot based on the offset
1584 * generated from the hash key. If a free slot is found, the offset and
1585 * key are entered into the hash table.
1588 * ERROR_SUCCESS if the entry was added
1589 * Any other Win32 error code if the entry could not be added
1592 static DWORD
urlcache_hash_entry_create(urlcache_header
*pHeader
, LPCSTR lpszUrl
, DWORD dwOffsetEntry
, DWORD dwFieldType
)
1594 /* see urlcache_find_hash_entry for structure of hash tables */
1596 DWORD key
= urlcache_hash_key(lpszUrl
);
1597 DWORD offset
= (key
& (HASHTABLE_NUM_ENTRIES
-1)) * HASHTABLE_BLOCKSIZE
;
1598 entry_hash_table
* pHashEntry
, *pHashPrev
= NULL
;
1602 key
= ((key
>> HASHTABLE_FLAG_BITS
) << HASHTABLE_FLAG_BITS
) + dwFieldType
;
1604 for (pHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1605 pHashEntry
; pHashEntry
= urlcache_get_hash_table(pHeader
, pHashEntry
->next
))
1608 pHashPrev
= pHashEntry
;
1610 if (pHashEntry
->id
!= id
++)
1612 ERR("not right hash table number (%d) expected %d\n", pHashEntry
->id
, id
);
1615 /* make sure that it is in fact a hash entry */
1616 if (pHashEntry
->header
.signature
!= HASH_SIGNATURE
)
1618 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&pHashEntry
->header
.signature
);
1622 for (i
= 0; i
< HASHTABLE_BLOCKSIZE
; i
++)
1624 struct hash_entry
*pHashElement
= &pHashEntry
->hash_table
[offset
+ i
];
1625 if (pHashElement
->key
==HASHTABLE_FREE
|| pHashElement
->key
==HASHTABLE_DEL
) /* if the slot is free */
1627 pHashElement
->key
= key
;
1628 pHashElement
->offset
= dwOffsetEntry
;
1629 return ERROR_SUCCESS
;
1633 error
= urlcache_create_hash_table(pHeader
, pHashPrev
, &pHashEntry
);
1634 if (error
!= ERROR_SUCCESS
)
1637 pHashEntry
->hash_table
[offset
].key
= key
;
1638 pHashEntry
->hash_table
[offset
].offset
= dwOffsetEntry
;
1639 return ERROR_SUCCESS
;
1642 /***********************************************************************
1643 * urlcache_enum_hash_tables (Internal)
1645 * Enumerates the hash tables in a container.
1648 * TRUE if an entry was found
1649 * FALSE if there are no more tables to enumerate.
1652 static BOOL
urlcache_enum_hash_tables(const urlcache_header
*pHeader
, DWORD
*id
, entry_hash_table
**ppHashEntry
)
1654 for (*ppHashEntry
= urlcache_get_hash_table(pHeader
, pHeader
->hash_table_off
);
1655 *ppHashEntry
; *ppHashEntry
= urlcache_get_hash_table(pHeader
, (*ppHashEntry
)->next
))
1657 TRACE("looking at hash table number %d\n", (*ppHashEntry
)->id
);
1658 if ((*ppHashEntry
)->id
!= *id
)
1660 /* make sure that it is in fact a hash entry */
1661 if ((*ppHashEntry
)->header
.signature
!= HASH_SIGNATURE
)
1663 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR
)&(*ppHashEntry
)->header
.signature
);
1668 TRACE("hash table number %d found\n", *id
);
1674 /***********************************************************************
1675 * urlcache_enum_hash_table_entries (Internal)
1677 * Enumerates entries in a hash table and returns the next non-free entry.
1680 * TRUE if an entry was found
1681 * FALSE if the hash table is empty or there are no more entries to
1685 static BOOL
urlcache_enum_hash_table_entries(const urlcache_header
*pHeader
, const entry_hash_table
*pHashEntry
,
1686 DWORD
* index
, const struct hash_entry
**ppHashEntry
)
1688 for (; *index
< HASHTABLE_SIZE
; (*index
)++)
1690 if (pHashEntry
->hash_table
[*index
].key
==HASHTABLE_FREE
|| pHashEntry
->hash_table
[*index
].key
==HASHTABLE_DEL
)
1693 *ppHashEntry
= &pHashEntry
->hash_table
[*index
];
1694 TRACE("entry found %d\n", *index
);
1697 TRACE("no more entries (%d)\n", *index
);
1701 /***********************************************************************
1702 * cache_container_delete_dir (Internal)
1704 * Erase a directory containing an URL cache.
1707 * TRUE success, FALSE failure/aborted.
1710 static BOOL
cache_container_delete_dir(LPCWSTR lpszPath
)
1713 WCHAR path
[MAX_PATH
+ 1];
1714 SHFILEOPSTRUCTW shfos
;
1717 path_len
= strlenW(lpszPath
);
1718 if (path_len
>= MAX_PATH
)
1720 strcpyW(path
, lpszPath
);
1721 path
[path_len
+ 1] = 0; /* double-NUL-terminate path */
1724 shfos
.wFunc
= FO_DELETE
;
1727 shfos
.fFlags
= FOF_NOCONFIRMATION
;
1728 shfos
.fAnyOperationsAborted
= FALSE
;
1729 ret
= SHFileOperationW(&shfos
);
1731 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path
), ret
);
1732 return !(ret
|| shfos
.fAnyOperationsAborted
);
1735 /***********************************************************************
1736 * urlcache_hash_entry_is_locked (Internal)
1738 * Checks if entry is locked. Unlocks it if possible.
1740 static BOOL
urlcache_hash_entry_is_locked(struct hash_entry
*hash_entry
, entry_url
*url_entry
)
1743 ULARGE_INTEGER acc_time
, time
;
1745 if ((hash_entry
->key
& ((1<<HASHTABLE_FLAG_BITS
)-1)) != HASHTABLE_LOCK
)
1748 GetSystemTimeAsFileTime(&cur_time
);
1749 time
.u
.LowPart
= cur_time
.dwLowDateTime
;
1750 time
.u
.HighPart
= cur_time
.dwHighDateTime
;
1752 acc_time
.u
.LowPart
= url_entry
->access_time
.dwLowDateTime
;
1753 acc_time
.u
.HighPart
= url_entry
->access_time
.dwHighDateTime
;
1755 time
.QuadPart
-= acc_time
.QuadPart
;
1757 /* check if entry was locked for at least a day */
1758 if(time
.QuadPart
> (ULONGLONG
)24*60*60*FILETIME_SECOND
) {
1759 urlcache_hash_entry_set_flags(hash_entry
, HASHTABLE_URL
);
1760 url_entry
->use_count
= 0;
1767 static BOOL
urlcache_get_entry_info(const char *url
, void *entry_info
,
1768 DWORD
*size
, DWORD flags
, BOOL unicode
)
1770 urlcache_header
*header
;
1771 struct hash_entry
*hash_entry
;
1772 const entry_url
*url_entry
;
1773 cache_container
*container
;
1776 TRACE("(%s, %p, %p, %x, %x)\n", debugstr_a(url
), entry_info
, size
, flags
, unicode
);
1778 if(flags
& ~GET_INSTALLED_ENTRY
)
1779 FIXME("ignoring unsupported flags: %x\n", flags
);
1781 error
= cache_containers_find(url
, &container
);
1782 if(error
!= ERROR_SUCCESS
) {
1783 SetLastError(error
);
1787 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
1788 if(error
!= ERROR_SUCCESS
) {
1789 SetLastError(error
);
1793 if(!(header
= cache_container_lock_index(container
)))
1796 if(!urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
1797 cache_container_unlock_index(container
, header
);
1798 WARN("entry %s not found!\n", debugstr_a(url
));
1799 SetLastError(ERROR_FILE_NOT_FOUND
);
1803 url_entry
= (const entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
1804 if(url_entry
->header
.signature
!= URL_SIGNATURE
) {
1805 cache_container_unlock_index(container
, header
);
1806 FIXME("Trying to retrieve entry of unknown format %s\n",
1807 debugstr_an((LPCSTR
)&url_entry
->header
.signature
, sizeof(DWORD
)));
1808 SetLastError(ERROR_FILE_NOT_FOUND
);
1812 TRACE("Found URL: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->url_off
));
1813 TRACE("Header info: %s\n", debugstr_an((LPCSTR
)url_entry
+
1814 url_entry
->header_info_off
, url_entry
->header_info_size
));
1816 if((flags
& GET_INSTALLED_ENTRY
) && !(url_entry
->cache_entry_type
& INSTALLED_CACHE_ENTRY
)) {
1817 cache_container_unlock_index(container
, header
);
1818 SetLastError(ERROR_FILE_NOT_FOUND
);
1826 error
= urlcache_copy_entry(container
, header
, entry_info
, size
, url_entry
, unicode
);
1827 if(error
!= ERROR_SUCCESS
) {
1828 cache_container_unlock_index(container
, header
);
1829 SetLastError(error
);
1832 if(url_entry
->local_name_off
)
1833 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->local_name_off
));
1836 cache_container_unlock_index(container
, header
);
1840 /***********************************************************************
1841 * GetUrlCacheEntryInfoExA (WININET.@)
1844 BOOL WINAPI
GetUrlCacheEntryInfoExA(LPCSTR lpszUrl
,
1845 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1846 LPDWORD lpdwCacheEntryInfoBufSize
, LPSTR lpszReserved
,
1847 LPDWORD lpdwReserved
, LPVOID lpReserved
, DWORD dwFlags
)
1849 if(lpszReserved
!=NULL
|| lpdwReserved
!=NULL
|| lpReserved
!=NULL
) {
1850 ERR("Reserved value was not 0\n");
1851 SetLastError(ERROR_INVALID_PARAMETER
);
1855 return urlcache_get_entry_info(lpszUrl
, lpCacheEntryInfo
,
1856 lpdwCacheEntryInfoBufSize
, dwFlags
, FALSE
);
1859 /***********************************************************************
1860 * GetUrlCacheEntryInfoA (WININET.@)
1863 BOOL WINAPI
GetUrlCacheEntryInfoA(LPCSTR lpszUrlName
,
1864 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
1865 LPDWORD lpdwCacheEntryInfoBufferSize
)
1867 return GetUrlCacheEntryInfoExA(lpszUrlName
, lpCacheEntryInfo
,
1868 lpdwCacheEntryInfoBufferSize
, NULL
, NULL
, NULL
, 0);
1871 static int urlcache_encode_url(const WCHAR
*url
, char *encoded_url
, int encoded_len
)
1874 DWORD len
, part_len
;
1877 TRACE("%s\n", debugstr_w(url
));
1879 memset(&uc
, 0, sizeof(uc
));
1880 uc
.dwStructSize
= sizeof(uc
);
1881 uc
.dwHostNameLength
= 1;
1882 if(!InternetCrackUrlW(url
, 0, 0, &uc
))
1883 uc
.nScheme
= INTERNET_SCHEME_UNKNOWN
;
1885 if(uc
.nScheme
!=INTERNET_SCHEME_HTTP
&& uc
.nScheme
!=INTERNET_SCHEME_HTTPS
)
1886 return WideCharToMultiByte(CP_UTF8
, 0, url
, -1, encoded_url
, encoded_len
, NULL
, NULL
);
1888 len
= WideCharToMultiByte(CP_UTF8
, 0, url
, uc
.lpszHostName
-url
,
1889 encoded_url
, encoded_len
, NULL
, NULL
);
1895 part_len
= IdnToAscii(0, uc
.lpszHostName
, uc
.dwHostNameLength
, NULL
, 0);
1897 SetLastError(ERROR_INTERNET_INVALID_URL
);
1901 punycode
= heap_alloc(part_len
*sizeof(WCHAR
));
1905 part_len
= IdnToAscii(0, uc
.lpszHostName
, uc
.dwHostNameLength
, punycode
, part_len
);
1907 heap_free(punycode
);
1911 part_len
= WideCharToMultiByte(CP_UTF8
, 0, punycode
, part_len
,
1912 encoded_url
? encoded_url
+len
: NULL
, encoded_len
, NULL
, NULL
);
1913 heap_free(punycode
);
1917 encoded_len
-= part_len
;
1920 part_len
= WideCharToMultiByte(CP_UTF8
, 0, uc
.lpszHostName
+uc
.dwHostNameLength
,
1921 -1, encoded_url
? encoded_url
+len
: NULL
, encoded_len
, NULL
, NULL
);
1926 TRACE("got (%d)%s\n", len
, debugstr_a(encoded_url
));
1930 static BOOL
urlcache_encode_url_alloc(const WCHAR
*url
, char **encoded_url
)
1935 encoded_len
= urlcache_encode_url(url
, NULL
, 0);
1939 ret
= heap_alloc(encoded_len
*sizeof(WCHAR
));
1943 encoded_len
= urlcache_encode_url(url
, ret
, encoded_len
);
1953 /***********************************************************************
1954 * GetUrlCacheEntryInfoExW (WININET.@)
1957 BOOL WINAPI
GetUrlCacheEntryInfoExW(LPCWSTR lpszUrl
,
1958 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
1959 LPDWORD lpdwCacheEntryInfoBufSize
, LPWSTR lpszReserved
,
1960 LPDWORD lpdwReserved
, LPVOID lpReserved
, DWORD dwFlags
)
1965 if(lpszReserved
!=NULL
|| lpdwReserved
!=NULL
|| lpReserved
!=NULL
) {
1966 ERR("Reserved value was not 0\n");
1967 SetLastError(ERROR_INVALID_PARAMETER
);
1971 /* Ignore GET_INSTALLED_ENTRY flag in unicode version of function */
1972 dwFlags
&= ~GET_INSTALLED_ENTRY
;
1974 if(!urlcache_encode_url_alloc(lpszUrl
, &url
))
1977 ret
= urlcache_get_entry_info(url
, lpCacheEntryInfo
,
1978 lpdwCacheEntryInfoBufSize
, dwFlags
, TRUE
);
1983 /***********************************************************************
1984 * GetUrlCacheEntryInfoW (WININET.@)
1987 BOOL WINAPI
GetUrlCacheEntryInfoW(LPCWSTR lpszUrl
,
1988 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
1989 LPDWORD lpdwCacheEntryInfoBufferSize
)
1991 return GetUrlCacheEntryInfoExW(lpszUrl
, lpCacheEntryInfo
,
1992 lpdwCacheEntryInfoBufferSize
, NULL
, NULL
, NULL
, 0);
1995 /***********************************************************************
1996 * SetUrlCacheEntryInfoA (WININET.@)
1998 BOOL WINAPI
SetUrlCacheEntryInfoA(LPCSTR lpszUrlName
,
1999 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
2000 DWORD dwFieldControl
)
2002 urlcache_header
*pHeader
;
2003 struct hash_entry
*pHashEntry
;
2004 entry_header
*pEntry
;
2005 cache_container
*pContainer
;
2008 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName
), lpCacheEntryInfo
, dwFieldControl
);
2010 error
= cache_containers_find(lpszUrlName
, &pContainer
);
2011 if (error
!= ERROR_SUCCESS
)
2013 SetLastError(error
);
2017 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
2018 if (error
!= ERROR_SUCCESS
)
2020 SetLastError(error
);
2024 if (!(pHeader
= cache_container_lock_index(pContainer
)))
2027 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
2029 cache_container_unlock_index(pContainer
, pHeader
);
2030 WARN("entry %s not found!\n", debugstr_a(lpszUrlName
));
2031 SetLastError(ERROR_FILE_NOT_FOUND
);
2035 pEntry
= (entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2036 if (pEntry
->signature
!= URL_SIGNATURE
)
2038 cache_container_unlock_index(pContainer
, pHeader
);
2039 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR
)&pEntry
->signature
, sizeof(DWORD
)));
2040 SetLastError(ERROR_FILE_NOT_FOUND
);
2044 urlcache_set_entry_info((entry_url
*)pEntry
, lpCacheEntryInfo
, dwFieldControl
);
2046 cache_container_unlock_index(pContainer
, pHeader
);
2051 /***********************************************************************
2052 * SetUrlCacheEntryInfoW (WININET.@)
2054 BOOL WINAPI
SetUrlCacheEntryInfoW(LPCWSTR lpszUrl
,
2055 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
2056 DWORD dwFieldControl
)
2061 if(!urlcache_encode_url_alloc(lpszUrl
, &url
))
2064 ret
= SetUrlCacheEntryInfoA(url
, (INTERNET_CACHE_ENTRY_INFOA
*)lpCacheEntryInfo
, dwFieldControl
);
2069 static BOOL
urlcache_entry_get_file(const char *url
, void *entry_info
, DWORD
*size
, BOOL unicode
)
2071 urlcache_header
*header
;
2072 struct hash_entry
*hash_entry
;
2073 entry_url
*url_entry
;
2074 cache_container
*container
;
2077 TRACE("(%s, %p, %p, %x)\n", debugstr_a(url
), entry_info
, size
, unicode
);
2079 if(!url
|| !size
|| (!entry_info
&& *size
)) {
2080 SetLastError(ERROR_INVALID_PARAMETER
);
2084 error
= cache_containers_find(url
, &container
);
2085 if(error
!= ERROR_SUCCESS
) {
2086 SetLastError(error
);
2090 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2091 if (error
!= ERROR_SUCCESS
) {
2092 SetLastError(error
);
2096 if (!(header
= cache_container_lock_index(container
)))
2099 if (!urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
2100 cache_container_unlock_index(container
, header
);
2101 TRACE("entry %s not found!\n", url
);
2102 SetLastError(ERROR_FILE_NOT_FOUND
);
2106 url_entry
= (entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
2107 if(url_entry
->header
.signature
!= URL_SIGNATURE
) {
2108 cache_container_unlock_index(container
, header
);
2109 FIXME("Trying to retrieve entry of unknown format %s\n",
2110 debugstr_an((LPSTR
)&url_entry
->header
.signature
, sizeof(DWORD
)));
2111 SetLastError(ERROR_FILE_NOT_FOUND
);
2115 if(!url_entry
->local_name_off
) {
2116 cache_container_unlock_index(container
, header
);
2117 SetLastError(ERROR_INVALID_DATA
);
2121 TRACE("Found URL: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->url_off
));
2122 TRACE("Header info: %s\n", debugstr_an((LPCSTR
)url_entry
+ url_entry
->header_info_off
,
2123 url_entry
->header_info_size
));
2125 error
= urlcache_copy_entry(container
, header
, entry_info
,
2126 size
, url_entry
, unicode
);
2127 if(error
!= ERROR_SUCCESS
) {
2128 cache_container_unlock_index(container
, header
);
2129 SetLastError(error
);
2132 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)url_entry
+ url_entry
->local_name_off
));
2134 url_entry
->hit_rate
++;
2135 url_entry
->use_count
++;
2136 urlcache_hash_entry_set_flags(hash_entry
, HASHTABLE_LOCK
);
2137 GetSystemTimeAsFileTime(&url_entry
->access_time
);
2139 cache_container_unlock_index(container
, header
);
2144 /***********************************************************************
2145 * RetrieveUrlCacheEntryFileA (WININET.@)
2148 BOOL WINAPI
RetrieveUrlCacheEntryFileA(LPCSTR lpszUrlName
,
2149 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
2150 LPDWORD lpdwCacheEntryInfoBufferSize
, DWORD dwReserved
)
2152 return urlcache_entry_get_file(lpszUrlName
, lpCacheEntryInfo
,
2153 lpdwCacheEntryInfoBufferSize
, FALSE
);
2156 /***********************************************************************
2157 * RetrieveUrlCacheEntryFileW (WININET.@)
2160 BOOL WINAPI
RetrieveUrlCacheEntryFileW(LPCWSTR lpszUrlName
,
2161 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
2162 LPDWORD lpdwCacheEntryInfoBufferSize
, DWORD dwReserved
)
2167 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
2170 ret
= urlcache_entry_get_file(url
, lpCacheEntryInfo
,
2171 lpdwCacheEntryInfoBufferSize
, TRUE
);
2176 static BOOL
urlcache_entry_delete(const cache_container
*pContainer
,
2177 urlcache_header
*pHeader
, struct hash_entry
*pHashEntry
)
2179 entry_header
*pEntry
;
2180 entry_url
* pUrlEntry
;
2182 pEntry
= (entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2183 if (pEntry
->signature
!= URL_SIGNATURE
)
2185 FIXME("Trying to delete entry of unknown format %s\n",
2186 debugstr_an((LPCSTR
)&pEntry
->signature
, sizeof(DWORD
)));
2187 SetLastError(ERROR_FILE_NOT_FOUND
);
2191 pUrlEntry
= (entry_url
*)pEntry
;
2192 if(urlcache_hash_entry_is_locked(pHashEntry
, pUrlEntry
))
2194 TRACE("Trying to delete locked entry\n");
2195 pUrlEntry
->cache_entry_type
|= PENDING_DELETE_CACHE_ENTRY
;
2196 SetLastError(ERROR_SHARING_VIOLATION
);
2200 if(!urlcache_delete_file(pContainer
, pHeader
, pUrlEntry
))
2202 urlcache_entry_free(pHeader
, pEntry
);
2206 /* Add entry to leaked files list */
2207 pUrlEntry
->header
.signature
= LEAK_SIGNATURE
;
2208 pUrlEntry
->exempt_delta
= pHeader
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
];
2209 pHeader
->options
[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET
] = pHashEntry
->offset
;
2212 urlcache_hash_entry_delete(pHashEntry
);
2216 static HANDLE free_cache_running
;
2217 static HANDLE dll_unload_event
;
2218 static DWORD WINAPI
handle_full_cache_worker(void *param
)
2220 FreeUrlCacheSpaceW(NULL
, 20, 0);
2221 ReleaseSemaphore(free_cache_running
, 1, NULL
);
2225 static void handle_full_cache(void)
2227 if(WaitForSingleObject(free_cache_running
, 0) == WAIT_OBJECT_0
) {
2228 if(!QueueUserWorkItem(handle_full_cache_worker
, NULL
, 0))
2229 ReleaseSemaphore(free_cache_running
, 1, NULL
);
2233 /* Enumerates entries in cache, allows cache unlocking between calls. */
2234 static BOOL
urlcache_next_entry(urlcache_header
*header
, DWORD
*hash_table_off
, DWORD
*hash_table_entry
,
2235 struct hash_entry
**hash_entry
, entry_header
**entry
)
2237 entry_hash_table
*hashtable_entry
;
2242 if(!*hash_table_off
) {
2243 *hash_table_off
= header
->hash_table_off
;
2244 *hash_table_entry
= 0;
2246 hashtable_entry
= urlcache_get_hash_table(header
, *hash_table_off
);
2248 if(*hash_table_off
>= header
->size
) {
2249 *hash_table_off
= 0;
2253 hashtable_entry
= urlcache_get_hash_table(header
, *hash_table_off
);
2256 if(hashtable_entry
->header
.signature
!= HASH_SIGNATURE
) {
2257 *hash_table_off
= 0;
2262 if(*hash_table_entry
>= HASHTABLE_SIZE
) {
2263 *hash_table_off
= hashtable_entry
->next
;
2264 if(!*hash_table_off
) {
2265 *hash_table_off
= 0;
2269 hashtable_entry
= urlcache_get_hash_table(header
, *hash_table_off
);
2270 *hash_table_entry
= 0;
2273 if(hashtable_entry
->hash_table
[*hash_table_entry
].key
!= HASHTABLE_DEL
&&
2274 hashtable_entry
->hash_table
[*hash_table_entry
].key
!= HASHTABLE_FREE
) {
2275 *hash_entry
= &hashtable_entry
->hash_table
[*hash_table_entry
];
2276 *entry
= (entry_header
*)((LPBYTE
)header
+ hashtable_entry
->hash_table
[*hash_table_entry
].offset
);
2277 (*hash_table_entry
)++;
2281 (*hash_table_entry
)++;
2284 *hash_table_off
= 0;
2288 /* Rates an urlcache entry to determine if it can be deleted.
2290 * Score 0 means that entry can safely be removed, the bigger rating
2291 * the smaller chance of entry being removed.
2292 * DWORD_MAX means that entry can't be deleted at all.
2294 * Rating system is currently not fully compatible with native implementation.
2296 static DWORD
urlcache_rate_entry(entry_url
*url_entry
, FILETIME
*cur_time
)
2298 ULARGE_INTEGER time
, access_time
;
2301 access_time
.u
.LowPart
= url_entry
->access_time
.dwLowDateTime
;
2302 access_time
.u
.HighPart
= url_entry
->access_time
.dwHighDateTime
;
2304 time
.u
.LowPart
= cur_time
->dwLowDateTime
;
2305 time
.u
.HighPart
= cur_time
->dwHighDateTime
;
2307 /* Don't touch entries that were added less than 10 minutes ago */
2308 if(time
.QuadPart
< access_time
.QuadPart
+ (ULONGLONG
)10*60*FILETIME_SECOND
)
2311 if(url_entry
->cache_entry_type
& STICKY_CACHE_ENTRY
)
2312 if(time
.QuadPart
< access_time
.QuadPart
+ (ULONGLONG
)url_entry
->exempt_delta
*FILETIME_SECOND
)
2315 time
.QuadPart
= (time
.QuadPart
-access_time
.QuadPart
)/FILETIME_SECOND
;
2316 rating
= 400*60*60*24/(60*60*24+time
.QuadPart
);
2318 if(url_entry
->hit_rate
> 100)
2321 rating
+= url_entry
->hit_rate
;
2326 static int dword_cmp(const void *p1
, const void *p2
)
2328 return *(const DWORD
*)p1
- *(const DWORD
*)p2
;
2331 /***********************************************************************
2332 * FreeUrlCacheSpaceW (WININET.@)
2334 * Frees up some cache.
2337 * cache_path [I] Which volume to free up from, or NULL if you don't care.
2338 * size [I] Percentage of the cache that should be free.
2339 * filter [I] Which entries can't be deleted (CacheEntryType)
2342 * TRUE success. FALSE failure.
2345 * This implementation just retrieves the path of the cache directory, and
2346 * deletes its contents from the filesystem. The correct approach would
2347 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
2349 BOOL WINAPI
FreeUrlCacheSpaceW(LPCWSTR cache_path
, DWORD size
, DWORD filter
)
2351 cache_container
*container
;
2352 DWORD path_len
, err
;
2354 TRACE("(%s, %x, %x)\n", debugstr_w(cache_path
), size
, filter
);
2356 if(size
<1 || size
>100) {
2357 SetLastError(ERROR_INVALID_PARAMETER
);
2362 path_len
= strlenW(cache_path
);
2363 if(cache_path
[path_len
-1] == '\\')
2369 if(size
==100 && !filter
) {
2370 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
2372 /* When cache_path==NULL only clean Temporary Internet Files */
2373 if((!path_len
&& container
->cache_prefix
[0]==0) ||
2374 (path_len
&& !strncmpiW(container
->path
, cache_path
, path_len
) &&
2375 (container
->path
[path_len
]=='\0' || container
->path
[path_len
]=='\\')))
2379 WaitForSingleObject(container
->mutex
, INFINITE
);
2381 /* unlock, delete, recreate and lock cache */
2382 cache_container_close_index(container
);
2383 ret_del
= cache_container_delete_dir(container
->path
);
2384 err
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2386 ReleaseMutex(container
->mutex
);
2387 if(!ret_del
|| (err
!= ERROR_SUCCESS
))
2395 LIST_FOR_EACH_ENTRY(container
, &UrlContainers
, cache_container
, entry
)
2397 urlcache_header
*header
;
2398 struct hash_entry
*hash_entry
;
2399 entry_header
*entry
;
2400 entry_url
*url_entry
;
2401 ULONGLONG desired_size
, cur_size
;
2402 DWORD delete_factor
, hash_table_off
, hash_table_entry
;
2403 DWORD rate
[100], rate_no
;
2406 if((path_len
|| container
->cache_prefix
[0]!=0) &&
2407 (!path_len
|| strncmpiW(container
->path
, cache_path
, path_len
) ||
2408 (container
->path
[path_len
]!='\0' && container
->path
[path_len
]!='\\')))
2411 err
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2412 if(err
!= ERROR_SUCCESS
)
2415 header
= cache_container_lock_index(container
);
2419 urlcache_clean_leaked_entries(container
, header
);
2421 desired_size
= header
->cache_limit
.QuadPart
*(100-size
)/100;
2422 cur_size
= header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
;
2423 if(cur_size
<= desired_size
)
2426 delete_factor
= (cur_size
-desired_size
)*100/cur_size
;
2428 if(!delete_factor
) {
2429 cache_container_unlock_index(container
, header
);
2434 hash_table_entry
= 0;
2436 GetSystemTimeAsFileTime(&cur_time
);
2437 while(rate_no
<sizeof(rate
)/sizeof(*rate
) &&
2438 urlcache_next_entry(header
, &hash_table_off
, &hash_table_entry
, &hash_entry
, &entry
)) {
2439 if(entry
->signature
!= URL_SIGNATURE
) {
2440 WARN("only url entries are currently supported\n");
2444 url_entry
= (entry_url
*)entry
;
2445 if(url_entry
->cache_entry_type
& filter
)
2448 rate
[rate_no
] = urlcache_rate_entry(url_entry
, &cur_time
);
2449 if(rate
[rate_no
] != -1)
2454 TRACE("nothing to delete\n");
2455 cache_container_unlock_index(container
, header
);
2459 qsort(rate
, rate_no
, sizeof(DWORD
), dword_cmp
);
2461 delete_factor
= delete_factor
*rate_no
/100;
2462 delete_factor
= rate
[delete_factor
];
2463 TRACE("deleting files with rating %d or less\n", delete_factor
);
2466 while(urlcache_next_entry(header
, &hash_table_off
, &hash_table_entry
, &hash_entry
, &entry
)) {
2467 if(entry
->signature
!= URL_SIGNATURE
)
2470 url_entry
= (entry_url
*)entry
;
2471 if(url_entry
->cache_entry_type
& filter
)
2474 if(urlcache_rate_entry(url_entry
, &cur_time
) <= delete_factor
) {
2475 TRACE("deleting file: %s\n", debugstr_a((char*)url_entry
+url_entry
->local_name_off
));
2476 urlcache_entry_delete(container
, header
, hash_entry
);
2478 if(header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
<= desired_size
)
2481 /* Allow other threads to use cache while cleaning */
2482 cache_container_unlock_index(container
, header
);
2483 if(WaitForSingleObject(dll_unload_event
, 0) == WAIT_OBJECT_0
) {
2484 TRACE("got dll_unload_event - finishing\n");
2488 header
= cache_container_lock_index(container
);
2492 TRACE("cache size after cleaning 0x%s/0x%s\n",
2493 wine_dbgstr_longlong(header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
),
2494 wine_dbgstr_longlong(header
->cache_limit
.QuadPart
));
2495 cache_container_unlock_index(container
, header
);
2501 /***********************************************************************
2502 * FreeUrlCacheSpaceA (WININET.@)
2504 * See FreeUrlCacheSpaceW.
2506 BOOL WINAPI
FreeUrlCacheSpaceA(LPCSTR lpszCachePath
, DWORD dwSize
, DWORD dwFilter
)
2509 LPWSTR path
= heap_strdupAtoW(lpszCachePath
);
2510 if (lpszCachePath
== NULL
|| path
!= NULL
)
2511 ret
= FreeUrlCacheSpaceW(path
, dwSize
, dwFilter
);
2516 /***********************************************************************
2517 * UnlockUrlCacheEntryFileA (WININET.@)
2520 BOOL WINAPI
UnlockUrlCacheEntryFileA(LPCSTR lpszUrlName
, DWORD dwReserved
)
2522 urlcache_header
*pHeader
;
2523 struct hash_entry
*pHashEntry
;
2524 entry_header
*pEntry
;
2525 entry_url
* pUrlEntry
;
2526 cache_container
*pContainer
;
2529 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName
), dwReserved
);
2533 ERR("dwReserved != 0\n");
2534 SetLastError(ERROR_INVALID_PARAMETER
);
2538 error
= cache_containers_find(lpszUrlName
, &pContainer
);
2539 if (error
!= ERROR_SUCCESS
)
2541 SetLastError(error
);
2545 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
2546 if (error
!= ERROR_SUCCESS
)
2548 SetLastError(error
);
2552 if (!(pHeader
= cache_container_lock_index(pContainer
)))
2555 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
2557 cache_container_unlock_index(pContainer
, pHeader
);
2558 TRACE("entry %s not found!\n", lpszUrlName
);
2559 SetLastError(ERROR_FILE_NOT_FOUND
);
2563 pEntry
= (entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
2564 if (pEntry
->signature
!= URL_SIGNATURE
)
2566 cache_container_unlock_index(pContainer
, pHeader
);
2567 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR
)&pEntry
->signature
, sizeof(DWORD
)));
2568 SetLastError(ERROR_FILE_NOT_FOUND
);
2572 pUrlEntry
= (entry_url
*)pEntry
;
2574 if (pUrlEntry
->use_count
== 0)
2576 cache_container_unlock_index(pContainer
, pHeader
);
2579 pUrlEntry
->use_count
--;
2580 if (!pUrlEntry
->use_count
)
2582 urlcache_hash_entry_set_flags(pHashEntry
, HASHTABLE_URL
);
2583 if (pUrlEntry
->cache_entry_type
& PENDING_DELETE_CACHE_ENTRY
)
2584 urlcache_entry_delete(pContainer
, pHeader
, pHashEntry
);
2587 cache_container_unlock_index(pContainer
, pHeader
);
2592 /***********************************************************************
2593 * UnlockUrlCacheEntryFileW (WININET.@)
2596 BOOL WINAPI
UnlockUrlCacheEntryFileW(LPCWSTR lpszUrlName
, DWORD dwReserved
)
2601 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
2604 ret
= UnlockUrlCacheEntryFileA(url
, dwReserved
);
2609 static BOOL
urlcache_entry_create(const char *url
, const char *ext
, WCHAR
*full_path
)
2611 cache_container
*container
;
2612 urlcache_header
*header
;
2613 char file_name
[MAX_PATH
];
2614 WCHAR extW
[MAX_PATH
];
2617 BOOL generate_name
= FALSE
;
2624 TRACE("(%s, %s, %p)\n", debugstr_a(url
), debugstr_a(ext
), full_path
);
2626 memset(&uc
, 0, sizeof(uc
));
2627 uc
.dwStructSize
= sizeof(uc
);
2628 uc
.dwUrlPathLength
= 1;
2629 uc
.dwExtraInfoLength
= 1;
2630 if(!InternetCrackUrlA(url
, 0, 0, &uc
))
2631 uc
.dwUrlPathLength
= 0;
2633 if(!uc
.dwUrlPathLength
) {
2638 p
= e
= uc
.lpszUrlPath
+uc
.dwUrlPathLength
;
2639 while(p
>uc
.lpszUrlPath
&& *(p
-1)!='/' && *(p
-1)!='\\' && *(p
-1)!='.')
2641 if(p
>uc
.lpszUrlPath
&& *(p
-1)=='.') {
2643 while(p
>uc
.lpszUrlPath
&& *(p
-1)!='/' && *(p
-1)!='\\')
2647 memcpy(file_name
, p
, e
-p
);
2650 for(p
=file_name
; *p
; p
++) {
2663 generate_name
= TRUE
;
2665 error
= cache_containers_find(url
, &container
);
2666 if(error
!= ERROR_SUCCESS
) {
2667 SetLastError(error
);
2671 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2672 if(error
!= ERROR_SUCCESS
) {
2673 SetLastError(error
);
2677 if(!(header
= cache_container_lock_index(container
)))
2681 cache_dir
= (BYTE
)(rand() % header
->dirs_no
);
2683 cache_dir
= CACHE_CONTAINER_NO_SUBDIR
;
2685 full_path_len
= MAX_PATH
* sizeof(WCHAR
);
2686 if(!urlcache_create_file_pathW(container
, header
, file_name
, cache_dir
, full_path
, &full_path_len
)) {
2687 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2688 debugstr_a(file_name
), full_path_len
);
2689 cache_container_unlock_index(container
, header
);
2692 full_path_len
= full_path_len
/sizeof(WCHAR
) - 1;
2694 cache_container_unlock_index(container
, header
);
2700 MultiByteToWideChar(CP_ACP
, 0, ext
, -1, extW
+1, MAX_PATH
-1);
2702 for(p
=extW
; *p
; p
++) {
2712 if(p
[-1]==' ' || p
[-1]=='.')
2718 for(i
=0; i
<255 && !generate_name
; i
++) {
2719 static const WCHAR format
[] = {'[','%','u',']','%','s',0};
2721 wsprintfW(full_path
+full_path_len
, format
, i
, extW
);
2723 TRACE("Trying: %s\n", debugstr_w(full_path
));
2724 file
= CreateFileW(full_path
, GENERIC_READ
, 0, NULL
, CREATE_NEW
, 0, NULL
);
2725 if(file
!= INVALID_HANDLE_VALUE
) {
2731 /* Try to generate random name */
2732 GetSystemTimeAsFileTime(&ft
);
2733 strcpyW(full_path
+full_path_len
+8, extW
);
2735 for(i
=0; i
<255; i
++) {
2737 ULONGLONG n
= ft
.dwHighDateTime
;
2739 n
+= ft
.dwLowDateTime
;
2740 n
^= (ULONGLONG
)i
<<48;
2742 for(j
=0; j
<8; j
++) {
2745 full_path
[full_path_len
+j
] = (r
< 10 ? '0' + r
: 'A' + r
- 10);
2748 TRACE("Trying: %s\n", debugstr_w(full_path
));
2749 file
= CreateFileW(full_path
, GENERIC_READ
, 0, NULL
, CREATE_NEW
, 0, NULL
);
2750 if(file
!= INVALID_HANDLE_VALUE
) {
2756 WARN("Could not find a unique filename\n");
2760 /***********************************************************************
2761 * CreateUrlCacheEntryA (WININET.@)
2764 BOOL WINAPI
CreateUrlCacheEntryA(LPCSTR lpszUrlName
, DWORD dwExpectedFileSize
,
2765 LPCSTR lpszFileExtension
, LPSTR lpszFileName
, DWORD dwReserved
)
2767 WCHAR file_name
[MAX_PATH
];
2770 FIXME("dwReserved 0x%08x\n", dwReserved
);
2772 if(!urlcache_entry_create(lpszUrlName
, lpszFileExtension
, file_name
))
2775 if(!WideCharToMultiByte(CP_ACP
, 0, file_name
, -1, lpszFileName
, MAX_PATH
, NULL
, NULL
))
2779 /***********************************************************************
2780 * CreateUrlCacheEntryW (WININET.@)
2783 BOOL WINAPI
CreateUrlCacheEntryW(LPCWSTR lpszUrlName
, DWORD dwExpectedFileSize
,
2784 LPCWSTR lpszFileExtension
, LPWSTR lpszFileName
, DWORD dwReserved
)
2786 char *url
, *ext
= NULL
;
2790 FIXME("dwReserved 0x%08x\n", dwReserved
);
2792 if(lpszFileExtension
) {
2793 ext
= heap_strdupWtoUTF8(lpszFileExtension
);
2798 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
)) {
2803 ret
= urlcache_entry_create(url
, ext
, lpszFileName
);
2809 static BOOL
urlcache_entry_commit(const char *url
, const WCHAR
*file_name
,
2810 FILETIME expire_time
, FILETIME modify_time
, DWORD entry_type
,
2811 BYTE
*header_info
, DWORD header_size
, const char *file_ext
,
2812 const char *original_url
)
2814 cache_container
*container
;
2815 urlcache_header
*header
;
2816 struct hash_entry
*hash_entry
;
2817 entry_header
*entry
;
2818 entry_url
*url_entry
;
2819 DWORD url_entry_offset
;
2820 DWORD size
= DWORD_ALIGN(sizeof(*url_entry
));
2821 DWORD file_name_off
= 0;
2822 DWORD header_info_off
= 0;
2823 DWORD file_ext_off
= 0;
2824 WIN32_FILE_ATTRIBUTE_DATA file_attr
;
2825 LARGE_INTEGER file_size
;
2827 char file_name_no_container
[MAX_PATH
];
2828 char *local_file_name
= 0;
2830 DWORD exempt_delta
= 0;
2833 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n", debugstr_a(url
), debugstr_w(file_name
),
2834 entry_type
, header_info
, header_size
, debugstr_a(file_ext
), debugstr_a(original_url
));
2836 if(entry_type
& STICKY_CACHE_ENTRY
&& !file_name
) {
2837 SetLastError(ERROR_INVALID_PARAMETER
);
2841 WARN(": original_url ignored\n");
2843 memset(&file_attr
, 0, sizeof(file_attr
));
2845 if(!GetFileAttributesExW(file_name
, GetFileExInfoStandard
, &file_attr
))
2848 file_size
.u
.LowPart
= file_attr
.nFileSizeLow
;
2849 file_size
.u
.HighPart
= file_attr
.nFileSizeHigh
;
2851 error
= cache_containers_find(url
, &container
);
2852 if(error
!= ERROR_SUCCESS
) {
2853 SetLastError(error
);
2857 error
= cache_container_open_index(container
, MIN_BLOCK_NO
);
2858 if(error
!= ERROR_SUCCESS
) {
2859 SetLastError(error
);
2863 if(!(header
= cache_container_lock_index(container
)))
2866 if(urlcache_find_hash_entry(header
, url
, &hash_entry
)) {
2867 entry_url
*url_entry
= (entry_url
*)((LPBYTE
)header
+ hash_entry
->offset
);
2869 if(urlcache_hash_entry_is_locked(hash_entry
, url_entry
)) {
2870 TRACE("Trying to overwrite locked entry\n");
2871 cache_container_unlock_index(container
, header
);
2872 SetLastError(ERROR_SHARING_VIOLATION
);
2876 hit_rate
= url_entry
->hit_rate
;
2877 exempt_delta
= url_entry
->exempt_delta
;
2878 urlcache_entry_delete(container
, header
, hash_entry
);
2884 dir_id
= CACHE_CONTAINER_NO_SUBDIR
;
2887 BOOL bFound
= FALSE
;
2889 if(strncmpW(file_name
, container
->path
, lstrlenW(container
->path
))) {
2890 ERR("path %s must begin with cache content path %s\n", debugstr_w(file_name
), debugstr_w(container
->path
));
2891 cache_container_unlock_index(container
, header
);
2892 SetLastError(ERROR_INVALID_PARAMETER
);
2896 /* skip container path prefix */
2897 file_name
+= lstrlenW(container
->path
);
2899 WideCharToMultiByte(CP_ACP
, 0, file_name
, -1, file_name_no_container
, MAX_PATH
, NULL
, NULL
);
2900 local_file_name
= file_name_no_container
;
2902 if(header
->dirs_no
) {
2903 for(dir_id
= 0; dir_id
< header
->dirs_no
; dir_id
++) {
2904 if(!strncmp(header
->directory_data
[dir_id
].name
, local_file_name
, DIR_LENGTH
)) {
2911 ERR("cache directory not found in path %s\n", debugstr_w(file_name
));
2912 cache_container_unlock_index(container
, header
);
2913 SetLastError(ERROR_INVALID_PARAMETER
);
2917 file_name
+= DIR_LENGTH
+ 1;
2918 local_file_name
+= DIR_LENGTH
+ 1;
2922 size
= DWORD_ALIGN(size
+ strlen(url
) + 1);
2924 file_name_off
= size
;
2925 size
= DWORD_ALIGN(size
+ strlen(local_file_name
) + 1);
2927 if(header_info
&& header_size
) {
2928 header_info_off
= size
;
2929 size
= DWORD_ALIGN(size
+ header_size
);
2931 if(file_ext
&& (file_ext_off
= strlen(file_ext
))) {
2932 DWORD len
= file_ext_off
;
2934 file_ext_off
= size
;
2935 size
= DWORD_ALIGN(size
+ len
+ 1);
2938 /* round up to next block */
2939 if(size
% BLOCKSIZE
) {
2940 size
-= size
% BLOCKSIZE
;
2944 error
= urlcache_entry_alloc(header
, size
/ BLOCKSIZE
, &entry
);
2945 while(error
== ERROR_HANDLE_DISK_FULL
) {
2946 error
= cache_container_clean_index(container
, &header
);
2947 if(error
== ERROR_SUCCESS
)
2948 error
= urlcache_entry_alloc(header
, size
/ BLOCKSIZE
, &entry
);
2950 if(error
!= ERROR_SUCCESS
) {
2951 cache_container_unlock_index(container
, header
);
2952 SetLastError(error
);
2956 /* FindFirstFreeEntry fills in blocks used */
2957 url_entry
= (entry_url
*)entry
;
2958 url_entry_offset
= (LPBYTE
)url_entry
- (LPBYTE
)header
;
2959 url_entry
->header
.signature
= URL_SIGNATURE
;
2960 url_entry
->cache_dir
= dir_id
;
2961 url_entry
->cache_entry_type
= entry_type
| container
->default_entry_type
;
2962 url_entry
->header_info_size
= header_size
;
2963 if((entry_type
& STICKY_CACHE_ENTRY
) && !exempt_delta
) {
2964 /* Sticky entries have a default exempt time of one day */
2965 exempt_delta
= 86400;
2967 url_entry
->exempt_delta
= exempt_delta
;
2968 url_entry
->hit_rate
= hit_rate
+1;
2969 url_entry
->file_extension_off
= file_ext_off
;
2970 url_entry
->header_info_off
= header_info_off
;
2971 url_entry
->local_name_off
= file_name_off
;
2972 url_entry
->url_off
= DWORD_ALIGN(sizeof(*url_entry
));
2973 url_entry
->size
.QuadPart
= file_size
.QuadPart
;
2974 url_entry
->use_count
= 0;
2975 GetSystemTimeAsFileTime(&url_entry
->access_time
);
2976 url_entry
->modification_time
= modify_time
;
2977 file_time_to_dos_date_time(&url_entry
->access_time
, &url_entry
->sync_date
, &url_entry
->sync_time
);
2978 file_time_to_dos_date_time(&expire_time
, &url_entry
->expire_date
, &url_entry
->expire_time
);
2979 file_time_to_dos_date_time(&file_attr
.ftLastWriteTime
, &url_entry
->write_date
, &url_entry
->write_time
);
2982 url_entry
->unk1
= 0;
2983 url_entry
->unk2
= 0;
2984 url_entry
->unk3
= 0x60;
2985 url_entry
->unk4
= 0;
2986 url_entry
->unk5
= 0x1010;
2987 url_entry
->unk7
= 0;
2988 url_entry
->unk8
= 0;
2991 strcpy((LPSTR
)url_entry
+ url_entry
->url_off
, url
);
2993 strcpy((LPSTR
)((LPBYTE
)url_entry
+ file_name_off
), local_file_name
);
2995 memcpy((LPBYTE
)url_entry
+ header_info_off
, header_info
, header_size
);
2997 strcpy((LPSTR
)((LPBYTE
)url_entry
+ file_ext_off
), file_ext
);
2999 error
= urlcache_hash_entry_create(header
, url
, url_entry_offset
, HASHTABLE_URL
);
3000 while(error
== ERROR_HANDLE_DISK_FULL
) {
3001 error
= cache_container_clean_index(container
, &header
);
3002 if(error
== ERROR_SUCCESS
) {
3003 url_entry
= (entry_url
*)((LPBYTE
)header
+ url_entry_offset
);
3004 error
= urlcache_hash_entry_create(header
, url
,
3005 url_entry_offset
, HASHTABLE_URL
);
3008 if(error
!= ERROR_SUCCESS
) {
3009 urlcache_entry_free(header
, &url_entry
->header
);
3010 cache_container_unlock_index(container
, header
);
3011 SetLastError(error
);
3015 if(url_entry
->cache_dir
< header
->dirs_no
)
3016 header
->directory_data
[url_entry
->cache_dir
].files_no
++;
3017 if(entry_type
& STICKY_CACHE_ENTRY
)
3018 header
->exempt_usage
.QuadPart
+= file_size
.QuadPart
;
3020 header
->cache_usage
.QuadPart
+= file_size
.QuadPart
;
3021 if(header
->cache_usage
.QuadPart
+header
->exempt_usage
.QuadPart
> header
->cache_limit
.QuadPart
)
3022 handle_full_cache();
3024 cache_container_unlock_index(container
, header
);
3028 /***********************************************************************
3029 * CommitUrlCacheEntryA (WININET.@)
3031 BOOL WINAPI
CommitUrlCacheEntryA(LPCSTR lpszUrlName
, LPCSTR lpszLocalFileName
,
3032 FILETIME ExpireTime
, FILETIME LastModifiedTime
, DWORD CacheEntryType
,
3033 LPBYTE lpHeaderInfo
, DWORD dwHeaderSize
, LPCSTR lpszFileExtension
, LPCSTR lpszOriginalUrl
)
3035 WCHAR
*file_name
= NULL
;
3038 if(lpszLocalFileName
) {
3039 file_name
= heap_strdupAtoW(lpszLocalFileName
);
3044 ret
= urlcache_entry_commit(lpszUrlName
, file_name
, ExpireTime
, LastModifiedTime
,
3045 CacheEntryType
, lpHeaderInfo
, dwHeaderSize
, lpszFileExtension
, lpszOriginalUrl
);
3046 heap_free(file_name
);
3050 /***********************************************************************
3051 * CommitUrlCacheEntryW (WININET.@)
3053 BOOL WINAPI
CommitUrlCacheEntryW(LPCWSTR lpszUrlName
, LPCWSTR lpszLocalFileName
,
3054 FILETIME ExpireTime
, FILETIME LastModifiedTime
, DWORD CacheEntryType
,
3055 LPWSTR lpHeaderInfo
, DWORD dwHeaderSize
, LPCWSTR lpszFileExtension
, LPCWSTR lpszOriginalUrl
)
3057 char *url
, *original_url
=NULL
, *file_ext
=NULL
, *header_info
=NULL
;
3060 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
3064 header_info
= heap_strdupWtoUTF8(lpHeaderInfo
);
3069 dwHeaderSize
= strlen(header_info
);
3072 if(lpszFileExtension
) {
3073 file_ext
= heap_strdupWtoA(lpszFileExtension
);
3076 heap_free(header_info
);
3081 if(lpszOriginalUrl
&& !urlcache_encode_url_alloc(lpszOriginalUrl
, &original_url
)) {
3083 heap_free(header_info
);
3084 heap_free(file_ext
);
3088 ret
= urlcache_entry_commit(url
, lpszLocalFileName
, ExpireTime
, LastModifiedTime
,
3089 CacheEntryType
, (BYTE
*)header_info
, dwHeaderSize
, file_ext
, original_url
);
3091 heap_free(header_info
);
3092 heap_free(file_ext
);
3093 heap_free(original_url
);
3097 /***********************************************************************
3098 * ReadUrlCacheEntryStream (WININET.@)
3101 BOOL WINAPI
ReadUrlCacheEntryStream(
3102 IN HANDLE hUrlCacheStream
,
3103 IN DWORD dwLocation
,
3104 IN OUT LPVOID lpBuffer
,
3105 IN OUT LPDWORD lpdwLen
,
3109 /* Get handle to file from 'stream' */
3110 stream_handle
*pStream
= (stream_handle
*)hUrlCacheStream
;
3112 if (dwReserved
!= 0)
3114 ERR("dwReserved != 0\n");
3115 SetLastError(ERROR_INVALID_PARAMETER
);
3119 if (IsBadReadPtr(pStream
, sizeof(*pStream
)) || IsBadStringPtrA(pStream
->url
, INTERNET_MAX_URL_LENGTH
))
3121 SetLastError(ERROR_INVALID_HANDLE
);
3125 if (SetFilePointer(pStream
->file
, dwLocation
, NULL
, FILE_CURRENT
) == INVALID_SET_FILE_POINTER
)
3127 return ReadFile(pStream
->file
, lpBuffer
, *lpdwLen
, lpdwLen
, NULL
);
3130 /***********************************************************************
3131 * RetrieveUrlCacheEntryStreamA (WININET.@)
3134 HANDLE WINAPI
RetrieveUrlCacheEntryStreamA(LPCSTR lpszUrlName
,
3135 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo
,
3136 LPDWORD lpdwCacheEntryInfoBufferSize
, BOOL fRandomRead
, DWORD dwReserved
)
3138 /* NOTE: this is not the same as the way that the native
3139 * version allocates 'stream' handles. I did it this way
3140 * as it is much easier and no applications should depend
3141 * on this behaviour. (Native version appears to allocate
3142 * indices into a table)
3144 stream_handle
*stream
;
3147 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName
), lpCacheEntryInfo
,
3148 lpdwCacheEntryInfoBufferSize
, fRandomRead
, dwReserved
);
3150 if(!RetrieveUrlCacheEntryFileA(lpszUrlName
, lpCacheEntryInfo
,
3151 lpdwCacheEntryInfoBufferSize
, dwReserved
))
3154 file
= CreateFileA(lpCacheEntryInfo
->lpszLocalFileName
, GENERIC_READ
, FILE_SHARE_READ
,
3155 NULL
, OPEN_EXISTING
, fRandomRead
? FILE_FLAG_RANDOM_ACCESS
: 0, NULL
);
3156 if(file
== INVALID_HANDLE_VALUE
) {
3157 UnlockUrlCacheEntryFileA(lpszUrlName
, 0);
3161 /* allocate handle storage space */
3162 stream
= heap_alloc(sizeof(stream_handle
) + strlen(lpszUrlName
) * sizeof(CHAR
));
3165 UnlockUrlCacheEntryFileA(lpszUrlName
, 0);
3166 SetLastError(ERROR_OUTOFMEMORY
);
3170 stream
->file
= file
;
3171 strcpy(stream
->url
, lpszUrlName
);
3175 /***********************************************************************
3176 * RetrieveUrlCacheEntryStreamW (WININET.@)
3179 HANDLE WINAPI
RetrieveUrlCacheEntryStreamW(LPCWSTR lpszUrlName
,
3180 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo
,
3181 LPDWORD lpdwCacheEntryInfoBufferSize
, BOOL fRandomRead
, DWORD dwReserved
)
3184 /* NOTE: this is not the same as the way that the native
3185 * version allocates 'stream' handles. I did it this way
3186 * as it is much easier and no applications should depend
3187 * on this behaviour. (Native version appears to allocate
3188 * indices into a table)
3190 stream_handle
*stream
;
3193 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName
), lpCacheEntryInfo
,
3194 lpdwCacheEntryInfoBufferSize
, fRandomRead
, dwReserved
);
3196 if(!(len
= urlcache_encode_url(lpszUrlName
, NULL
, 0)))
3199 if(!RetrieveUrlCacheEntryFileW(lpszUrlName
, lpCacheEntryInfo
,
3200 lpdwCacheEntryInfoBufferSize
, dwReserved
))
3203 file
= CreateFileW(lpCacheEntryInfo
->lpszLocalFileName
, GENERIC_READ
, FILE_SHARE_READ
,
3204 NULL
, OPEN_EXISTING
, fRandomRead
? FILE_FLAG_RANDOM_ACCESS
: 0, NULL
);
3205 if(file
== INVALID_HANDLE_VALUE
) {
3206 UnlockUrlCacheEntryFileW(lpszUrlName
, 0);
3210 /* allocate handle storage space */
3211 stream
= heap_alloc(sizeof(stream_handle
) + len
*sizeof(WCHAR
));
3214 UnlockUrlCacheEntryFileW(lpszUrlName
, 0);
3215 SetLastError(ERROR_OUTOFMEMORY
);
3219 stream
->file
= file
;
3220 if(!urlcache_encode_url(lpszUrlName
, stream
->url
, len
)) {
3222 UnlockUrlCacheEntryFileW(lpszUrlName
, 0);
3229 /***********************************************************************
3230 * UnlockUrlCacheEntryStream (WININET.@)
3233 BOOL WINAPI
UnlockUrlCacheEntryStream(
3234 IN HANDLE hUrlCacheStream
,
3238 stream_handle
*pStream
= (stream_handle
*)hUrlCacheStream
;
3240 if (dwReserved
!= 0)
3242 ERR("dwReserved != 0\n");
3243 SetLastError(ERROR_INVALID_PARAMETER
);
3247 if (IsBadReadPtr(pStream
, sizeof(*pStream
)) || IsBadStringPtrA(pStream
->url
, INTERNET_MAX_URL_LENGTH
))
3249 SetLastError(ERROR_INVALID_HANDLE
);
3253 if (!UnlockUrlCacheEntryFileA(pStream
->url
, 0))
3256 CloseHandle(pStream
->file
);
3262 /***********************************************************************
3263 * DeleteUrlCacheEntryA (WININET.@)
3266 BOOL WINAPI
DeleteUrlCacheEntryA(LPCSTR lpszUrlName
)
3268 cache_container
*pContainer
;
3269 urlcache_header
*pHeader
;
3270 struct hash_entry
*pHashEntry
;
3274 TRACE("(%s)\n", debugstr_a(lpszUrlName
));
3276 error
= cache_containers_find(lpszUrlName
, &pContainer
);
3277 if (error
!= ERROR_SUCCESS
)
3279 SetLastError(error
);
3283 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
3284 if (error
!= ERROR_SUCCESS
)
3286 SetLastError(error
);
3290 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3293 if (!urlcache_find_hash_entry(pHeader
, lpszUrlName
, &pHashEntry
))
3295 cache_container_unlock_index(pContainer
, pHeader
);
3296 TRACE("entry %s not found!\n", lpszUrlName
);
3297 SetLastError(ERROR_FILE_NOT_FOUND
);
3301 ret
= urlcache_entry_delete(pContainer
, pHeader
, pHashEntry
);
3303 cache_container_unlock_index(pContainer
, pHeader
);
3308 /***********************************************************************
3309 * DeleteUrlCacheEntryW (WININET.@)
3312 BOOL WINAPI
DeleteUrlCacheEntryW(LPCWSTR lpszUrlName
)
3317 if(!urlcache_encode_url_alloc(lpszUrlName
, &url
))
3320 ret
= DeleteUrlCacheEntryA(url
);
3325 BOOL WINAPI
DeleteUrlCacheContainerA(DWORD d1
, DWORD d2
)
3327 FIXME("(0x%08x, 0x%08x) stub\n", d1
, d2
);
3331 BOOL WINAPI
DeleteUrlCacheContainerW(DWORD d1
, DWORD d2
)
3333 FIXME("(0x%08x, 0x%08x) stub\n", d1
, d2
);
3337 /***********************************************************************
3338 * CreateCacheContainerA (WININET.@)
3340 BOOL WINAPI
CreateUrlCacheContainerA(DWORD d1
, DWORD d2
, DWORD d3
, DWORD d4
,
3341 DWORD d5
, DWORD d6
, DWORD d7
, DWORD d8
)
3343 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3344 d1
, d2
, d3
, d4
, d5
, d6
, d7
, d8
);
3348 /***********************************************************************
3349 * CreateCacheContainerW (WININET.@)
3351 BOOL WINAPI
CreateUrlCacheContainerW(DWORD d1
, DWORD d2
, DWORD d3
, DWORD d4
,
3352 DWORD d5
, DWORD d6
, DWORD d7
, DWORD d8
)
3354 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3355 d1
, d2
, d3
, d4
, d5
, d6
, d7
, d8
);
3359 /***********************************************************************
3360 * FindFirstUrlCacheContainerA (WININET.@)
3362 HANDLE WINAPI
FindFirstUrlCacheContainerA( LPVOID p1
, LPVOID p2
, LPVOID p3
, DWORD d1
)
3364 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1
, p2
, p3
, d1
);
3368 /***********************************************************************
3369 * FindFirstUrlCacheContainerW (WININET.@)
3371 HANDLE WINAPI
FindFirstUrlCacheContainerW( LPVOID p1
, LPVOID p2
, LPVOID p3
, DWORD d1
)
3373 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1
, p2
, p3
, d1
);
3377 /***********************************************************************
3378 * FindNextUrlCacheContainerA (WININET.@)
3380 BOOL WINAPI
FindNextUrlCacheContainerA( HANDLE handle
, LPVOID p1
, LPVOID p2
)
3382 FIXME("(%p, %p, %p) stub\n", handle
, p1
, p2
);
3386 /***********************************************************************
3387 * FindNextUrlCacheContainerW (WININET.@)
3389 BOOL WINAPI
FindNextUrlCacheContainerW( HANDLE handle
, LPVOID p1
, LPVOID p2
)
3391 FIXME("(%p, %p, %p) stub\n", handle
, p1
, p2
);
3395 HANDLE WINAPI
FindFirstUrlCacheEntryExA(
3396 LPCSTR lpszUrlSearchPattern
,
3400 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
,
3401 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3403 LPDWORD pcbReserved2
,
3407 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern
),
3408 dwFlags
, dwFilter
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
, lpFirstCacheEntryInfo
,
3409 lpdwFirstCacheEntryInfoBufferSize
, lpReserved
, pcbReserved2
,lpReserved3
);
3410 SetLastError(ERROR_FILE_NOT_FOUND
);
3414 HANDLE WINAPI
FindFirstUrlCacheEntryExW(
3415 LPCWSTR lpszUrlSearchPattern
,
3419 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
,
3420 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3422 LPDWORD pcbReserved2
,
3426 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern
),
3427 dwFlags
, dwFilter
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
, lpFirstCacheEntryInfo
,
3428 lpdwFirstCacheEntryInfoBufferSize
, lpReserved
, pcbReserved2
,lpReserved3
);
3429 SetLastError(ERROR_FILE_NOT_FOUND
);
3433 /***********************************************************************
3434 * FindFirstUrlCacheEntryA (WININET.@)
3437 INTERNETAPI HANDLE WINAPI
FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern
,
3438 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
, LPDWORD lpdwFirstCacheEntryInfoBufferSize
)
3440 find_handle
*pEntryHandle
;
3442 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern
), lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
);
3444 pEntryHandle
= heap_alloc(sizeof(*pEntryHandle
));
3448 pEntryHandle
->magic
= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
;
3449 if (lpszUrlSearchPattern
)
3451 pEntryHandle
->url_search_pattern
= heap_strdupA(lpszUrlSearchPattern
);
3452 if (!pEntryHandle
->url_search_pattern
)
3454 heap_free(pEntryHandle
);
3459 pEntryHandle
->url_search_pattern
= NULL
;
3460 pEntryHandle
->container_idx
= 0;
3461 pEntryHandle
->hash_table_idx
= 0;
3462 pEntryHandle
->hash_entry_idx
= 0;
3464 if (!FindNextUrlCacheEntryA(pEntryHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
))
3466 heap_free(pEntryHandle
);
3469 return pEntryHandle
;
3472 /***********************************************************************
3473 * FindFirstUrlCacheEntryW (WININET.@)
3476 INTERNETAPI HANDLE WINAPI
FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern
,
3477 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
, LPDWORD lpdwFirstCacheEntryInfoBufferSize
)
3479 find_handle
*pEntryHandle
;
3481 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern
), lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
);
3483 pEntryHandle
= heap_alloc(sizeof(*pEntryHandle
));
3487 pEntryHandle
->magic
= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
;
3488 if (lpszUrlSearchPattern
)
3490 pEntryHandle
->url_search_pattern
= heap_strdupWtoA(lpszUrlSearchPattern
);
3491 if (!pEntryHandle
->url_search_pattern
)
3493 heap_free(pEntryHandle
);
3498 pEntryHandle
->url_search_pattern
= NULL
;
3499 pEntryHandle
->container_idx
= 0;
3500 pEntryHandle
->hash_table_idx
= 0;
3501 pEntryHandle
->hash_entry_idx
= 0;
3503 if (!FindNextUrlCacheEntryW(pEntryHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
))
3505 heap_free(pEntryHandle
);
3508 return pEntryHandle
;
3511 static BOOL
urlcache_find_next_entry(
3513 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo
,
3514 LPDWORD lpdwNextCacheEntryInfoBufferSize
,
3517 find_handle
*pEntryHandle
= (find_handle
*)hEnumHandle
;
3518 cache_container
*pContainer
;
3520 if (pEntryHandle
->magic
!= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
)
3522 SetLastError(ERROR_INVALID_HANDLE
);
3526 for (; cache_containers_enum(pEntryHandle
->url_search_pattern
, pEntryHandle
->container_idx
, &pContainer
);
3527 pEntryHandle
->container_idx
++, pEntryHandle
->hash_table_idx
= 0)
3529 urlcache_header
*pHeader
;
3530 entry_hash_table
*pHashTableEntry
;
3533 error
= cache_container_open_index(pContainer
, MIN_BLOCK_NO
);
3534 if (error
!= ERROR_SUCCESS
)
3536 SetLastError(error
);
3540 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3543 for (; urlcache_enum_hash_tables(pHeader
, &pEntryHandle
->hash_table_idx
, &pHashTableEntry
);
3544 pEntryHandle
->hash_table_idx
++, pEntryHandle
->hash_entry_idx
= 0)
3546 const struct hash_entry
*pHashEntry
= NULL
;
3547 for (; urlcache_enum_hash_table_entries(pHeader
, pHashTableEntry
, &pEntryHandle
->hash_entry_idx
, &pHashEntry
);
3548 pEntryHandle
->hash_entry_idx
++)
3550 const entry_url
*pUrlEntry
;
3551 const entry_header
*pEntry
= (const entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
3553 if (pEntry
->signature
!= URL_SIGNATURE
)
3556 pUrlEntry
= (const entry_url
*)pEntry
;
3557 TRACE("Found URL: %s\n",
3558 debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->url_off
));
3559 TRACE("Header info: %s\n",
3560 debugstr_an((LPCSTR
)pUrlEntry
+ pUrlEntry
->header_info_off
,
3561 pUrlEntry
->header_info_size
));
3563 error
= urlcache_copy_entry(
3566 lpNextCacheEntryInfo
,
3567 lpdwNextCacheEntryInfoBufferSize
,
3570 if (error
!= ERROR_SUCCESS
)
3572 cache_container_unlock_index(pContainer
, pHeader
);
3573 SetLastError(error
);
3576 if(pUrlEntry
->local_name_off
)
3577 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR
)pUrlEntry
+ pUrlEntry
->local_name_off
));
3579 /* increment the current index so that next time the function
3580 * is called the next entry is returned */
3581 pEntryHandle
->hash_entry_idx
++;
3582 cache_container_unlock_index(pContainer
, pHeader
);
3587 cache_container_unlock_index(pContainer
, pHeader
);
3590 SetLastError(ERROR_NO_MORE_ITEMS
);
3594 /***********************************************************************
3595 * FindNextUrlCacheEntryA (WININET.@)
3597 BOOL WINAPI
FindNextUrlCacheEntryA(
3599 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo
,
3600 LPDWORD lpdwNextCacheEntryInfoBufferSize
)
3602 TRACE("(%p, %p, %p)\n", hEnumHandle
, lpNextCacheEntryInfo
, lpdwNextCacheEntryInfoBufferSize
);
3604 return urlcache_find_next_entry(hEnumHandle
, lpNextCacheEntryInfo
,
3605 lpdwNextCacheEntryInfoBufferSize
, FALSE
/* not UNICODE */);
3608 /***********************************************************************
3609 * FindNextUrlCacheEntryW (WININET.@)
3611 BOOL WINAPI
FindNextUrlCacheEntryW(
3613 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo
,
3614 LPDWORD lpdwNextCacheEntryInfoBufferSize
3617 TRACE("(%p, %p, %p)\n", hEnumHandle
, lpNextCacheEntryInfo
, lpdwNextCacheEntryInfoBufferSize
);
3619 return urlcache_find_next_entry(hEnumHandle
,
3620 (LPINTERNET_CACHE_ENTRY_INFOA
)lpNextCacheEntryInfo
,
3621 lpdwNextCacheEntryInfoBufferSize
, TRUE
/* UNICODE */);
3624 /***********************************************************************
3625 * FindCloseUrlCache (WININET.@)
3627 BOOL WINAPI
FindCloseUrlCache(HANDLE hEnumHandle
)
3629 find_handle
*pEntryHandle
= (find_handle
*)hEnumHandle
;
3631 TRACE("(%p)\n", hEnumHandle
);
3633 if (!pEntryHandle
|| pEntryHandle
->magic
!= URLCACHE_FIND_ENTRY_HANDLE_MAGIC
)
3635 SetLastError(ERROR_INVALID_HANDLE
);
3639 pEntryHandle
->magic
= 0;
3640 heap_free(pEntryHandle
->url_search_pattern
);
3641 heap_free(pEntryHandle
);
3645 HANDLE WINAPI
FindFirstUrlCacheGroup( DWORD dwFlags
, DWORD dwFilter
, LPVOID lpSearchCondition
,
3646 DWORD dwSearchCondition
, GROUPID
* lpGroupId
, LPVOID lpReserved
)
3648 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags
, dwFilter
, lpSearchCondition
,
3649 dwSearchCondition
, lpGroupId
, lpReserved
);
3653 BOOL WINAPI
FindNextUrlCacheEntryExA(
3655 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo
,
3656 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3658 LPDWORD pcbReserved2
,
3662 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
,
3663 lpReserved
, pcbReserved2
, lpReserved3
);
3667 BOOL WINAPI
FindNextUrlCacheEntryExW(
3669 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo
,
3670 LPDWORD lpdwFirstCacheEntryInfoBufferSize
,
3672 LPDWORD pcbReserved2
,
3676 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle
, lpFirstCacheEntryInfo
, lpdwFirstCacheEntryInfoBufferSize
,
3677 lpReserved
, pcbReserved2
, lpReserved3
);
3681 BOOL WINAPI
FindNextUrlCacheGroup( HANDLE hFind
, GROUPID
* lpGroupId
, LPVOID lpReserved
)
3683 FIXME("(%p, %p, %p) stub\n", hFind
, lpGroupId
, lpReserved
);
3687 /***********************************************************************
3688 * CreateUrlCacheGroup (WININET.@)
3691 INTERNETAPI GROUPID WINAPI
CreateUrlCacheGroup(DWORD dwFlags
, LPVOID lpReserved
)
3693 FIXME("(0x%08x, %p): stub\n", dwFlags
, lpReserved
);
3697 /***********************************************************************
3698 * DeleteUrlCacheGroup (WININET.@)
3701 BOOL WINAPI
DeleteUrlCacheGroup(GROUPID GroupId
, DWORD dwFlags
, LPVOID lpReserved
)
3703 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3704 (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
, dwFlags
, lpReserved
);
3708 /***********************************************************************
3709 * DeleteWpadCacheForNetworks (WININET.@)
3710 * Undocumented, added in IE8
3712 BOOL WINAPI
DeleteWpadCacheForNetworks(DWORD unk1
)
3714 FIXME("(%d) stub\n", unk1
);
3718 /***********************************************************************
3719 * SetUrlCacheEntryGroupA (WININET.@)
3722 BOOL WINAPI
SetUrlCacheEntryGroupA(LPCSTR lpszUrlName
, DWORD dwFlags
,
3723 GROUPID GroupId
, LPBYTE pbGroupAttributes
, DWORD cbGroupAttributes
,
3726 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3727 debugstr_a(lpszUrlName
), dwFlags
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
,
3728 pbGroupAttributes
, cbGroupAttributes
, lpReserved
);
3729 SetLastError(ERROR_FILE_NOT_FOUND
);
3733 /***********************************************************************
3734 * SetUrlCacheEntryGroupW (WININET.@)
3737 BOOL WINAPI
SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName
, DWORD dwFlags
,
3738 GROUPID GroupId
, LPBYTE pbGroupAttributes
, DWORD cbGroupAttributes
,
3741 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3742 debugstr_w(lpszUrlName
), dwFlags
, (ULONG
)(GroupId
>> 32), (ULONG
)GroupId
,
3743 pbGroupAttributes
, cbGroupAttributes
, lpReserved
);
3744 SetLastError(ERROR_FILE_NOT_FOUND
);
3748 /***********************************************************************
3749 * GetUrlCacheConfigInfoW (WININET.@)
3751 BOOL WINAPI
GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo
, LPDWORD size
, DWORD bitmask
)
3753 FIXME("(%p, %p, %x)\n", CacheInfo
, size
, bitmask
);
3754 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
3758 /***********************************************************************
3759 * GetUrlCacheConfigInfoA (WININET.@)
3761 BOOL WINAPI
GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo
, LPDWORD size
, DWORD bitmask
)
3763 FIXME("(%p, %p, %x)\n", CacheInfo
, size
, bitmask
);
3764 INTERNET_SetLastError(ERROR_INVALID_PARAMETER
);
3768 BOOL WINAPI
GetUrlCacheGroupAttributeA( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3769 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo
,
3770 LPDWORD lpdwGroupInfo
, LPVOID lpReserved
)
3772 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3773 (ULONG
)(gid
>> 32), (ULONG
)gid
, dwFlags
, dwAttributes
, lpGroupInfo
,
3774 lpdwGroupInfo
, lpReserved
);
3778 BOOL WINAPI
GetUrlCacheGroupAttributeW( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3779 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo
,
3780 LPDWORD lpdwGroupInfo
, LPVOID lpReserved
)
3782 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3783 (ULONG
)(gid
>> 32), (ULONG
)gid
, dwFlags
, dwAttributes
, lpGroupInfo
,
3784 lpdwGroupInfo
, lpReserved
);
3788 BOOL WINAPI
SetUrlCacheGroupAttributeA( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3789 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo
, LPVOID lpReserved
)
3791 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3792 (ULONG
)(gid
>> 32), (ULONG
)gid
, dwFlags
, dwAttributes
, lpGroupInfo
, lpReserved
);
3796 BOOL WINAPI
SetUrlCacheGroupAttributeW( GROUPID gid
, DWORD dwFlags
, DWORD dwAttributes
,
3797 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo
, LPVOID lpReserved
)
3799 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3800 (ULONG
)(gid
>> 32), (ULONG
)gid
, dwFlags
, dwAttributes
, lpGroupInfo
, lpReserved
);
3804 BOOL WINAPI
SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo
, DWORD dwFieldControl
)
3806 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo
, dwFieldControl
);
3810 BOOL WINAPI
SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo
, DWORD dwFieldControl
)
3812 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo
, dwFieldControl
);
3816 /***********************************************************************
3817 * DeleteIE3Cache (WININET.@)
3819 * Deletes the files used by the IE3 URL caching system.
3822 * hWnd [I] A dummy window.
3823 * hInst [I] Instance of process calling the function.
3824 * lpszCmdLine [I] Options used by function.
3825 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3827 DWORD WINAPI
DeleteIE3Cache(HWND hWnd
, HINSTANCE hInst
, LPSTR lpszCmdLine
, int nCmdShow
)
3829 FIXME("(%p, %p, %s, %d)\n", hWnd
, hInst
, debugstr_a(lpszCmdLine
), nCmdShow
);
3833 static BOOL
urlcache_entry_is_expired(const entry_url
*pUrlEntry
,
3834 FILETIME
*pftLastModified
)
3837 FILETIME now
, expired
;
3839 *pftLastModified
= pUrlEntry
->modification_time
;
3840 GetSystemTimeAsFileTime(&now
);
3841 dos_date_time_to_file_time(pUrlEntry
->expire_date
,
3842 pUrlEntry
->expire_time
, &expired
);
3843 /* If the expired time is 0, it's interpreted as not expired */
3844 if (!expired
.dwLowDateTime
&& !expired
.dwHighDateTime
)
3847 ret
= CompareFileTime(&expired
, &now
) < 0;
3851 /***********************************************************************
3852 * IsUrlCacheEntryExpiredA (WININET.@)
3856 * dwFlags [I] Unknown
3857 * pftLastModified [O] Last modified time
3859 BOOL WINAPI
IsUrlCacheEntryExpiredA(LPCSTR url
, DWORD dwFlags
, FILETIME
* pftLastModified
)
3861 urlcache_header
*pHeader
;
3862 struct hash_entry
*pHashEntry
;
3863 const entry_header
*pEntry
;
3864 const entry_url
* pUrlEntry
;
3865 cache_container
*pContainer
;
3868 TRACE("(%s, %08x, %p)\n", debugstr_a(url
), dwFlags
, pftLastModified
);
3870 if (!url
|| !pftLastModified
)
3873 FIXME("unknown flags 0x%08x\n", dwFlags
);
3875 /* Any error implies that the URL is expired, i.e. not in the cache */
3876 if (cache_containers_find(url
, &pContainer
))
3878 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3882 if (cache_container_open_index(pContainer
, MIN_BLOCK_NO
))
3884 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3888 if (!(pHeader
= cache_container_lock_index(pContainer
)))
3890 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3894 if (!urlcache_find_hash_entry(pHeader
, url
, &pHashEntry
))
3896 cache_container_unlock_index(pContainer
, pHeader
);
3897 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3898 TRACE("entry %s not found!\n", url
);
3902 pEntry
= (const entry_header
*)((LPBYTE
)pHeader
+ pHashEntry
->offset
);
3903 if (pEntry
->signature
!= URL_SIGNATURE
)
3905 cache_container_unlock_index(pContainer
, pHeader
);
3906 memset(pftLastModified
, 0, sizeof(*pftLastModified
));
3907 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR
)&pEntry
->signature
, sizeof(DWORD
)));
3911 pUrlEntry
= (const entry_url
*)pEntry
;
3912 expired
= urlcache_entry_is_expired(pUrlEntry
, pftLastModified
);
3914 cache_container_unlock_index(pContainer
, pHeader
);
3919 /***********************************************************************
3920 * IsUrlCacheEntryExpiredW (WININET.@)
3924 * dwFlags [I] Unknown
3925 * pftLastModified [O] Last modified time
3927 BOOL WINAPI
IsUrlCacheEntryExpiredW(LPCWSTR url
, DWORD dwFlags
, FILETIME
* pftLastModified
)
3932 if(!urlcache_encode_url_alloc(url
, &encoded_url
))
3935 ret
= IsUrlCacheEntryExpiredA(encoded_url
, dwFlags
, pftLastModified
);
3936 heap_free(encoded_url
);
3940 /***********************************************************************
3941 * GetDiskInfoA (WININET.@)
3943 BOOL WINAPI
GetDiskInfoA(PCSTR path
, PDWORD cluster_size
, PDWORDLONG free
, PDWORDLONG total
)
3946 ULARGE_INTEGER bytes_free
, bytes_total
;
3948 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path
), cluster_size
, free
, total
);
3952 SetLastError(ERROR_INVALID_PARAMETER
);
3956 if ((ret
= GetDiskFreeSpaceExA(path
, NULL
, &bytes_total
, &bytes_free
)))
3958 if (cluster_size
) *cluster_size
= 1;
3959 if (free
) *free
= bytes_free
.QuadPart
;
3960 if (total
) *total
= bytes_total
.QuadPart
;
3965 /***********************************************************************
3966 * RegisterUrlCacheNotification (WININET.@)
3968 DWORD WINAPI
RegisterUrlCacheNotification(LPVOID a
, DWORD b
, DWORD c
, DWORD d
, DWORD e
, DWORD f
)
3970 FIXME("(%p %x %x %x %x %x)\n", a
, b
, c
, d
, e
, f
);
3974 /***********************************************************************
3975 * IncrementUrlCacheHeaderData (WININET.@)
3977 BOOL WINAPI
IncrementUrlCacheHeaderData(DWORD index
, LPDWORD data
)
3979 FIXME("(%u, %p)\n", index
, data
);
3983 /***********************************************************************
3984 * RunOnceUrlCache (WININET.@)
3987 DWORD WINAPI
RunOnceUrlCache(HWND hwnd
, HINSTANCE hinst
, LPSTR cmd
, int cmdshow
)
3989 FIXME("(%p, %p, %s, %d): stub\n", hwnd
, hinst
, debugstr_a(cmd
), cmdshow
);
3993 BOOL
init_urlcache(void)
3995 dll_unload_event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
3996 if(!dll_unload_event
)
3999 free_cache_running
= CreateSemaphoreW(NULL
, 1, 1, NULL
);
4000 if(!free_cache_running
) {
4001 CloseHandle(dll_unload_event
);
4005 cache_containers_init();
4009 void free_urlcache(void)
4011 SetEvent(dll_unload_event
);
4012 WaitForSingleObject(free_cache_running
, INFINITE
);
4013 ReleaseSemaphore(free_cache_running
, 1, NULL
);
4014 CloseHandle(free_cache_running
);
4015 CloseHandle(dll_unload_event
);
4017 cache_containers_free();
4020 /***********************************************************************
4021 * LoadUrlCacheContent (WININET.@)
4023 BOOL WINAPI
LoadUrlCacheContent(void)