2 * Wininet - Url Cache functions
4 * Copyright 2001,2002 CodeWeavers
5 * Copyright 2003-2008 Robert Shearman
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include "wine/port.h"
28 #define NONAMELESSUNION
29 #define NONAMELESSSTRUCT
31 #if defined(__MINGW32__) || defined (_MSC_VER)
39 #include <sys/types.h>
40 #ifdef HAVE_SYS_SOCKET_H
41 # include <sys/socket.h>
58 #include "wine/unicode.h"
59 #include "wine/debug.h"
61 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
63 static const char urlcache_ver_prefix[] = "WINE URLCache Ver ";
64 static const char urlcache_ver[] = "0.2012001";
67 #define CHAR_BIT (8 * sizeof(CHAR))
70 #define ENTRY_START_OFFSET 0x4000
72 #define MAX_DIR_NO 0x20
74 #define HASHTABLE_SIZE 448
75 #define HASHTABLE_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
76 #define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
77 #define ALLOCATION_TABLE_OFFSET 0x250
78 #define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
79 #define MIN_BLOCK_NO 0x80
80 #define MAX_BLOCK_NO (ALLOCATION_TABLE_SIZE * CHAR_BIT)
81 #define FILE_SIZE(blocks) ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
83 #define HASHTABLE_URL 0
84 #define HASHTABLE_DEL 1
85 #define HASHTABLE_LOCK 2
86 #define HASHTABLE_FREE 3
87 #define HASHTABLE_REDR 5
88 #define HASHTABLE_FLAG_BITS 6
90 #define PENDING_DELETE_CACHE_ENTRY 0x00400000
91 #define INSTALLED_CACHE_ENTRY 0x10000000
92 #define GET_INSTALLED_ENTRY 0x200
93 #define CACHE_CONTAINER_NO_SUBDIR 0xFE
95 #define CACHE_HEADER_DATA_ROOT_LEAK_OFFSET 0x16
97 #define FILETIME_SECOND 10000000
99 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
100 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
101 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
102 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
103 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
105 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
107 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
112 DWORD blocks_used; /* number of 128byte blocks used by this entry */
118 FILETIME modification_time;
119 FILETIME access_time;
120 WORD expire_date; /* expire date in dos format */
121 WORD expire_time; /* expire time in dos format */
122 DWORD unk1; /* usually zero */
123 ULARGE_INTEGER size; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
124 DWORD unk2; /* usually zero */
125 DWORD exempt_delta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
126 DWORD unk3; /* usually 0x60 */
127 DWORD url_off; /* offset of start of url from start of entry */
128 BYTE cache_dir; /* index of cache directory this url is stored in */
129 BYTE unk4; /* usually zero */
130 WORD unk5; /* usually 0x1010 */
131 DWORD local_name_off; /* offset of start of local filename from start of entry */
132 DWORD cache_entry_type; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
133 DWORD header_info_off; /* offset of start of header info from start of entry */
134 DWORD header_info_size;
135 DWORD file_extension_off; /* offset of start of file extension from start of entry */
136 WORD sync_date; /* last sync date in dos format */
137 WORD sync_time; /* last sync time in dos format */
138 DWORD hit_rate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
139 DWORD use_count; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
142 DWORD unk7; /* usually zero */
143 DWORD unk8; /* usually zero */
144 /* packing to dword align start of next field */
145 /* CHAR szSourceUrlName[]; (url) */
146 /* packing to dword align start of next field */
147 /* CHAR szLocalFileName[]; (local file name excluding path) */
148 /* packing to dword align start of next field */
149 /* CHAR szHeaderInfo[]; (header info) */
163 struct hash_entry hash_table[HASHTABLE_SIZE];
170 DWORD hash_table_off;
171 DWORD capacity_in_blocks;
174 ULARGE_INTEGER cache_limit;
175 ULARGE_INTEGER cache_usage;
176 ULARGE_INTEGER exempt_usage;
178 struct _directory_data
181 char name[DIR_LENGTH];
182 } directory_data[MAX_DIR_NO];
184 BYTE allocation_table[ALLOCATION_TABLE_SIZE];
195 struct list entry; /* part of a list */
196 char *cache_prefix; /* string that has to be prefixed for this container to be used */
197 LPWSTR path; /* path to url container directory */
198 HANDLE mapping; /* handle of file mapping */
199 DWORD file_size; /* size of file when mapping was opened */
200 HANDLE mutex; /* handle of mutex */
201 DWORD default_entry_type;
207 char *url_search_pattern;
209 DWORD hash_table_idx;
210 DWORD hash_entry_idx;
213 /* List of all containers available */
214 static struct list UrlContainers = LIST_INIT(UrlContainers);
216 static inline char *heap_strdupWtoUTF8(LPCWSTR str)
221 DWORD size = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);
222 ret = heap_alloc(size);
224 WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, size, NULL, NULL);
230 /***********************************************************************
231 * urlcache_block_is_free (Internal)
233 * Is the specified block number free?
240 static inline BYTE urlcache_block_is_free(BYTE *allocation_table, DWORD block_number)
242 BYTE mask = 1 << (block_number%CHAR_BIT);
243 return (allocation_table[block_number/CHAR_BIT] & mask) == 0;
246 /***********************************************************************
247 * urlcache_block_free (Internal)
249 * Marks the specified block as free
252 * this function is not updating used blocks count
258 static inline void urlcache_block_free(BYTE *allocation_table, DWORD block_number)
260 BYTE mask = ~(1 << (block_number%CHAR_BIT));
261 allocation_table[block_number/CHAR_BIT] &= mask;
264 /***********************************************************************
265 * urlcache_block_alloc (Internal)
267 * Marks the specified block as allocated
270 * this function is not updating used blocks count
276 static inline void urlcache_block_alloc(BYTE *allocation_table, DWORD block_number)
278 BYTE mask = 1 << (block_number%CHAR_BIT);
279 allocation_table[block_number/CHAR_BIT] |= mask;
282 /***********************************************************************
283 * urlcache_entry_alloc (Internal)
285 * Finds and allocates the first block of free space big enough and
286 * sets entry to point to it.
289 * ERROR_SUCCESS when free memory block was found
290 * Any other Win32 error code if the entry could not be added
293 static DWORD urlcache_entry_alloc(urlcache_header *header, DWORD blocks_needed, entry_header **entry)
295 DWORD block, block_size;
297 for(block=0; block<header->capacity_in_blocks; block+=block_size+1)
300 while(block_size<blocks_needed && block_size+block<header->capacity_in_blocks
301 && urlcache_block_is_free(header->allocation_table, block+block_size))
304 if(block_size == blocks_needed)
308 TRACE("Found free blocks starting at no. %d (0x%x)\n", block, ENTRY_START_OFFSET+block*BLOCKSIZE);
310 for(index=0; index<blocks_needed; index++)
311 urlcache_block_alloc(header->allocation_table, block+index);
313 *entry = (entry_header*)((BYTE*)header+ENTRY_START_OFFSET+block*BLOCKSIZE);
314 for(index=0; index<blocks_needed*BLOCKSIZE/sizeof(DWORD); index++)
315 ((DWORD*)*entry)[index] = 0xdeadbeef;
316 (*entry)->blocks_used = blocks_needed;
318 header->blocks_in_use += blocks_needed;
319 return ERROR_SUCCESS;
323 return ERROR_HANDLE_DISK_FULL;
326 /***********************************************************************
327 * urlcache_entry_free (Internal)
329 * Deletes the specified entry and frees the space allocated to it
332 * TRUE if it succeeded
336 static BOOL urlcache_entry_free(urlcache_header *header, entry_header *entry)
338 DWORD start_block, block;
340 /* update allocation table */
341 start_block = ((DWORD)((BYTE*)entry - (BYTE*)header) - ENTRY_START_OFFSET) / BLOCKSIZE;
342 for(block = start_block; block < start_block+entry->blocks_used; block++)
343 urlcache_block_free(header->allocation_table, block);
345 header->blocks_in_use -= entry->blocks_used;
349 /***********************************************************************
350 * urlcache_create_hash_table (Internal)
352 * Creates a new hash table in free space and adds it to the chain of existing
356 * ERROR_SUCCESS if the hash table was created
357 * ERROR_DISK_FULL if the hash table could not be created
360 static DWORD urlcache_create_hash_table(urlcache_header *header, entry_hash_table *hash_table_prev, entry_hash_table **hash_table)
362 DWORD dwOffset, error;
365 if((error = urlcache_entry_alloc(header, 0x20, (entry_header**)hash_table)) != ERROR_SUCCESS)
368 dwOffset = (BYTE*)*hash_table-(BYTE*)header;
371 hash_table_prev->next = dwOffset;
373 header->hash_table_off = dwOffset;
375 (*hash_table)->header.signature = HASH_SIGNATURE;
376 (*hash_table)->next = 0;
377 (*hash_table)->id = hash_table_prev ? hash_table_prev->id+1 : 0;
378 for(i = 0; i < HASHTABLE_SIZE; i++) {
379 (*hash_table)->hash_table[i].offset = HASHTABLE_FREE;
380 (*hash_table)->hash_table[i].key = HASHTABLE_FREE;
382 return ERROR_SUCCESS;
385 /***********************************************************************
386 * cache_container_create_object_name (Internal)
388 * Converts a path to a name suitable for use as a Win32 object name.
389 * Replaces '\\' characters in-place with the specified character
390 * (usually '_' or '!')
396 static void cache_container_create_object_name(LPWSTR lpszPath, WCHAR replace)
398 for (; *lpszPath; lpszPath++)
400 if (*lpszPath == '\\')
405 /* Caller must hold container lock */
406 static HANDLE cache_container_map_index(HANDLE file, const WCHAR *path, DWORD size, BOOL *validate)
408 static const WCHAR mapping_name_format[]
409 = {'%','s','i','n','d','e','x','.','d','a','t','_','%','l','u',0};
410 WCHAR mapping_name[MAX_PATH];
413 wsprintfW(mapping_name, mapping_name_format, path, size);
414 cache_container_create_object_name(mapping_name, '_');
416 mapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, mapping_name);
418 if(validate) *validate = FALSE;
422 if(validate) *validate = TRUE;
423 return CreateFileMappingW(file, NULL, PAGE_READWRITE, 0, 0, mapping_name);
426 /* Caller must hold container lock */
427 static DWORD cache_container_set_size(cache_container *container, HANDLE file, DWORD blocks_no)
429 static const WCHAR cache_content_key[] = {'S','o','f','t','w','a','r','e','\\',
430 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
431 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
432 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s','\\',
433 'C','a','c','h','e','\\','C','o','n','t','e','n','t',0};
434 static const WCHAR cache_limit[] = {'C','a','c','h','e','L','i','m','i','t',0};
436 DWORD file_size = FILE_SIZE(blocks_no);
437 WCHAR dir_path[MAX_PATH], *dir_name;
438 entry_hash_table *hashtable_entry;
439 urlcache_header *header;
445 if(SetFilePointer(file, file_size, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
446 return GetLastError();
448 if(!SetEndOfFile(file))
449 return GetLastError();
451 mapping = cache_container_map_index(file, container->path, file_size, NULL);
453 return GetLastError();
455 header = MapViewOfFile(mapping, FILE_MAP_WRITE, 0, 0, 0);
457 CloseHandle(mapping);
458 return GetLastError();
461 if(blocks_no != MIN_BLOCK_NO) {
462 if(file_size > header->size)
463 memset((char*)header+header->size, 0, file_size-header->size);
464 header->size = file_size;
465 header->capacity_in_blocks = blocks_no;
467 UnmapViewOfFile(header);
468 CloseHandle(container->mapping);
469 container->mapping = mapping;
470 container->file_size = file_size;
471 return ERROR_SUCCESS;
474 memset(header, 0, file_size);
475 /* First set some constants and defaults in the header */
476 memcpy(header->signature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1);
477 memcpy(header->signature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1);
478 header->size = file_size;
479 header->capacity_in_blocks = blocks_no;
480 /* 127MB - taken from default for Windows 2000 */
481 header->cache_limit.QuadPart = 0x07ff5400;
482 /* Copied from a Windows 2000 cache index */
483 header->dirs_no = container->default_entry_type==NORMAL_CACHE_ENTRY ? 4 : 0;
485 /* If the registry has a cache size set, use the registry value */
486 if(RegOpenKeyW(HKEY_CURRENT_USER, cache_content_key, &key) == ERROR_SUCCESS) {
487 DWORD dw, len = sizeof(dw), keytype;
489 if(RegQueryValueExW(key, cache_limit, NULL, &keytype, (BYTE*)&dw, &len) == ERROR_SUCCESS &&
490 keytype == REG_DWORD)
491 header->cache_limit.QuadPart = (ULONGLONG)dw * 1024;
495 urlcache_create_hash_table(header, NULL, &hashtable_entry);
497 /* Last step - create the directories */
498 strcpyW(dir_path, container->path);
499 dir_name = dir_path + strlenW(dir_path);
502 GetSystemTimeAsFileTime(&ft);
504 for(i=0; i<header->dirs_no; ++i) {
505 header->directory_data[i].files_no = 0;
507 ULONGLONG n = ft.dwHighDateTime;
510 /* Generate a file name to attempt to create.
511 * This algorithm will create what will appear
512 * to be random and unrelated directory names
513 * of up to 9 characters in length.
516 n += ft.dwLowDateTime;
517 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
519 for(k = 0; k < 8; ++k) {
522 /* Dividing by a prime greater than 36 helps
523 * with the appearance of randomness
528 dir_name[k] = '0' + r;
530 dir_name[k] = 'A' + (r - 10);
533 if(CreateDirectoryW(dir_path, 0)) {
534 /* The following is OK because we generated an
535 * 8 character directory name made from characters
536 * [A-Z0-9], which are equivalent for all code
537 * pages and for UTF-16
539 for (k = 0; k < 8; ++k)
540 header->directory_data[i].name[k] = dir_name[k];
543 /* Give up. The most likely cause of this
544 * is a full disk, but whatever the cause
545 * is, it should be more than apparent that
548 UnmapViewOfFile(header);
549 CloseHandle(mapping);
550 return GetLastError();
555 UnmapViewOfFile(header);
556 CloseHandle(container->mapping);
557 container->mapping = mapping;
558 container->file_size = file_size;
559 return ERROR_SUCCESS;
562 static BOOL cache_container_is_valid(urlcache_header *header, DWORD file_size)
564 DWORD allocation_size, count_bits, i;
566 if(file_size < FILE_SIZE(MIN_BLOCK_NO))
569 if(file_size != header->size)
572 if (!memcmp(header->signature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1) &&
573 memcmp(header->signature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1))
576 if(FILE_SIZE(header->capacity_in_blocks) != file_size)
580 for(i=0; i<header->capacity_in_blocks/8; i++) {
581 for(count_bits = header->allocation_table[i]; count_bits!=0; count_bits>>=1) {
586 if(allocation_size != header->blocks_in_use)
589 for(; i<ALLOCATION_TABLE_SIZE; i++) {
590 if(header->allocation_table[i])
597 /***********************************************************************
598 * cache_container_open_index (Internal)
600 * Opens the index file and saves mapping handle
603 * ERROR_SUCCESS if succeeded
604 * Any other Win32 error code if failed
607 static DWORD cache_container_open_index(cache_container *container, DWORD blocks_no)
609 static const WCHAR index_dat[] = {'i','n','d','e','x','.','d','a','t',0};
612 WCHAR index_path[MAX_PATH];
616 WaitForSingleObject(container->mutex, INFINITE);
618 if(container->mapping) {
619 ReleaseMutex(container->mutex);
620 return ERROR_SUCCESS;
623 strcpyW(index_path, container->path);
624 strcatW(index_path, index_dat);
626 file = CreateFileW(index_path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
627 if(file == INVALID_HANDLE_VALUE) {
628 /* Maybe the directory wasn't there? Try to create it */
629 if(CreateDirectoryW(container->path, 0))
630 file = CreateFileW(index_path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
632 if(file == INVALID_HANDLE_VALUE) {
633 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(index_path));
634 ReleaseMutex(container->mutex);
635 return GetLastError();
638 file_size = GetFileSize(file, NULL);
639 if(file_size == INVALID_FILE_SIZE) {
641 ReleaseMutex(container->mutex);
642 return GetLastError();
645 if(blocks_no < MIN_BLOCK_NO)
646 blocks_no = MIN_BLOCK_NO;
647 else if(blocks_no > MAX_BLOCK_NO)
648 blocks_no = MAX_BLOCK_NO;
650 if(file_size < FILE_SIZE(blocks_no)) {
651 DWORD ret = cache_container_set_size(container, file, blocks_no);
653 ReleaseMutex(container->mutex);
657 container->file_size = file_size;
658 container->mapping = cache_container_map_index(file, container->path, file_size, &validate);
660 if(container->mapping && validate) {
661 urlcache_header *header = MapViewOfFile(container->mapping, FILE_MAP_WRITE, 0, 0, 0);
663 if(header && !cache_container_is_valid(header, file_size)) {
664 WARN("detected old or broken index.dat file\n");
665 UnmapViewOfFile(header);
666 FreeUrlCacheSpaceW(container->path, 100, 0);
668 UnmapViewOfFile(header);
670 CloseHandle(container->mapping);
671 container->mapping = NULL;
675 if(!container->mapping)
677 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
678 ReleaseMutex(container->mutex);
679 return GetLastError();
682 ReleaseMutex(container->mutex);
683 return ERROR_SUCCESS;
686 /***********************************************************************
687 * cache_container_close_index (Internal)
695 static void cache_container_close_index(cache_container *pContainer)
697 CloseHandle(pContainer->mapping);
698 pContainer->mapping = NULL;
701 static BOOL cache_containers_add(LPCWSTR cache_prefix,
702 LPCWSTR path, DWORD default_entry_type, LPWSTR mutex_name)
704 cache_container *pContainer = heap_alloc(sizeof(cache_container));
705 int cache_prefix_len = strlenW(cache_prefix);
712 pContainer->mapping = NULL;
713 pContainer->file_size = 0;
714 pContainer->default_entry_type = default_entry_type;
716 pContainer->path = heap_strdupW(path);
717 if (!pContainer->path)
719 heap_free(pContainer);
723 pContainer->cache_prefix = heap_alloc((cache_prefix_len + 1) * sizeof(WCHAR));
724 if (!pContainer->cache_prefix)
726 heap_free(pContainer->path);
727 heap_free(pContainer);
731 memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
733 CharLowerW(mutex_name);
734 cache_container_create_object_name(mutex_name, '!');
736 if ((pContainer->mutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
738 ERR("couldn't create mutex (error is %d)\n", GetLastError());
739 heap_free(pContainer->path);
740 heap_free(pContainer);
744 list_add_head(&UrlContainers, &pContainer->entry);
749 static void cache_container_delete_container(cache_container *pContainer)
751 list_remove(&pContainer->entry);
753 cache_container_close_index(pContainer);
754 CloseHandle(pContainer->mutex);
755 heap_free(pContainer->path);
756 heap_free(pContainer->cache_prefix);
757 heap_free(pContainer);
760 static void cache_containers_init(void)
762 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
763 static const WCHAR UrlPrefix[] = {0};
764 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
765 static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
766 static const WCHAR CookieSuffix[] = {0};
767 static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
770 int nFolder; /* CSIDL_* constant */
771 const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
772 const WCHAR * cache_prefix; /* prefix used to reference the container */
773 DWORD default_entry_type;
774 } DefaultContainerData[] =
776 { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix, NORMAL_CACHE_ENTRY },
777 { CSIDL_HISTORY, HistorySuffix, HistoryPrefix, URLHISTORY_CACHE_ENTRY },
778 { CSIDL_COOKIES, CookieSuffix, CookiePrefix, COOKIE_CACHE_ENTRY },
782 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
784 WCHAR wszCachePath[MAX_PATH];
785 WCHAR wszMutexName[MAX_PATH];
786 int path_len, suffix_len;
789 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
791 ERR("Couldn't get path for default container %u\n", i);
794 path_len = strlenW(wszCachePath);
795 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
797 if (path_len + suffix_len + 2 > MAX_PATH)
799 ERR("Path too long\n");
803 wszCachePath[path_len] = '\\';
804 wszCachePath[path_len+1] = 0;
806 strcpyW(wszMutexName, wszCachePath);
810 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
811 wszCachePath[path_len + suffix_len + 1] = '\\';
812 wszCachePath[path_len + suffix_len + 2] = '\0';
815 if (!WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wszCachePath, path_len,
816 NULL, 0, NULL, &def_char) || def_char)
820 /* cannot convert path to ANSI code page */
821 if (!(path_len = GetShortPathNameW(wszCachePath, tmp, MAX_PATH)) ||
822 !WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, tmp, path_len,
823 NULL, 0, NULL, &def_char) || def_char)
824 ERR("Can't create container path accessible by ANSI functions\n");
826 memcpy(wszCachePath, tmp, (path_len+1)*sizeof(WCHAR));
829 cache_containers_add(DefaultContainerData[i].cache_prefix, wszCachePath,
830 DefaultContainerData[i].default_entry_type, wszMutexName);
834 static void cache_containers_free(void)
836 while(!list_empty(&UrlContainers))
837 cache_container_delete_container(
838 LIST_ENTRY(list_head(&UrlContainers), cache_container, entry)
842 static DWORD cache_containers_find(const char *url, cache_container **ret)
844 cache_container *container;
846 TRACE("searching for prefix for URL: %s\n", debugstr_a(url));
849 return ERROR_INVALID_PARAMETER;
851 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
853 int prefix_len = strlen(container->cache_prefix);
855 if(!strncmp(container->cache_prefix, url, prefix_len)) {
856 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
858 return ERROR_SUCCESS;
862 ERR("no container found\n");
863 return ERROR_FILE_NOT_FOUND;
866 static BOOL cache_containers_enum(char *search_pattern, DWORD index, cache_container **ret)
869 cache_container *container;
871 TRACE("searching for prefix: %s\n", debugstr_a(search_pattern));
873 /* non-NULL search pattern only returns one container ever */
874 if (search_pattern && index > 0)
877 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
881 if (!strcmp(container->cache_prefix, search_pattern))
883 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
892 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
902 /***********************************************************************
903 * cache_container_lock_index (Internal)
905 * Locks the index for system-wide exclusive access.
908 * Cache file header if successful
909 * NULL if failed and calls SetLastError.
911 static urlcache_header* cache_container_lock_index(cache_container *pContainer)
915 urlcache_header* pHeader;
919 WaitForSingleObject(pContainer->mutex, INFINITE);
921 pIndexData = MapViewOfFile(pContainer->mapping, FILE_MAP_WRITE, 0, 0, 0);
925 ReleaseMutex(pContainer->mutex);
926 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
929 pHeader = (urlcache_header*)pIndexData;
931 /* file has grown - we need to remap to prevent us getting
932 * access violations when we try and access beyond the end
933 * of the memory mapped file */
934 if (pHeader->size != pContainer->file_size)
936 UnmapViewOfFile( pHeader );
937 cache_container_close_index(pContainer);
938 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
939 if (error != ERROR_SUCCESS)
941 ReleaseMutex(pContainer->mutex);
945 pIndexData = MapViewOfFile(pContainer->mapping, FILE_MAP_WRITE, 0, 0, 0);
949 ReleaseMutex(pContainer->mutex);
950 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
953 pHeader = (urlcache_header*)pIndexData;
956 TRACE("Signature: %s, file size: %d bytes\n", pHeader->signature, pHeader->size);
958 for (index = 0; index < pHeader->dirs_no; index++)
960 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].name);
966 /***********************************************************************
967 * cache_container_unlock_index (Internal)
970 static BOOL cache_container_unlock_index(cache_container *pContainer, urlcache_header *pHeader)
973 ReleaseMutex(pContainer->mutex);
974 return UnmapViewOfFile(pHeader);
977 /***********************************************************************
978 * urlcache_create_file_pathW (Internal)
980 * Copies the full path to the specified buffer given the local file
981 * name and the index of the directory it is in. Always sets value in
982 * lpBufferSize to the required buffer size (in bytes).
985 * TRUE if the buffer was big enough
986 * FALSE if the buffer was too small
989 static BOOL urlcache_create_file_pathW(
990 const cache_container *pContainer,
991 const urlcache_header *pHeader,
992 LPCSTR szLocalFileName,
998 int path_len = strlenW(pContainer->path);
999 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
1000 if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->dirs_no)
1006 nRequired = (path_len + file_name_len) * sizeof(WCHAR);
1007 if(Directory != CACHE_CONTAINER_NO_SUBDIR)
1008 nRequired += (DIR_LENGTH + 1) * sizeof(WCHAR);
1009 if (nRequired <= *lpBufferSize)
1013 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
1014 if (Directory != CACHE_CONTAINER_NO_SUBDIR)
1016 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].name, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
1017 wszPath[dir_len + path_len] = '\\';
1024 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len, file_name_len);
1025 *lpBufferSize = nRequired;
1028 *lpBufferSize = nRequired;
1032 /***********************************************************************
1033 * urlcache_create_file_pathA (Internal)
1035 * Copies the full path to the specified buffer given the local file
1036 * name and the index of the directory it is in. Always sets value in
1037 * lpBufferSize to the required buffer size.
1040 * TRUE if the buffer was big enough
1041 * FALSE if the buffer was too small
1044 static BOOL urlcache_create_file_pathA(
1045 const cache_container *pContainer,
1046 const urlcache_header *pHeader,
1047 LPCSTR szLocalFileName,
1050 LPLONG lpBufferSize)
1053 int path_len, file_name_len, dir_len;
1055 if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->dirs_no)
1061 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
1062 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
1063 if (Directory!=CACHE_CONTAINER_NO_SUBDIR)
1064 dir_len = DIR_LENGTH+1;
1068 nRequired = (path_len + dir_len + file_name_len) * sizeof(char);
1069 if (nRequired <= *lpBufferSize)
1071 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
1073 memcpy(szPath+path_len, pHeader->directory_data[Directory].name, dir_len-1);
1074 szPath[path_len + dir_len-1] = '\\';
1076 memcpy(szPath + path_len + dir_len, szLocalFileName, file_name_len);
1077 *lpBufferSize = nRequired;
1080 *lpBufferSize = nRequired;
1084 /* Just like FileTimeToDosDateTime, except that it also maps the special
1085 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1087 static void file_time_to_dos_date_time(const FILETIME *ft, WORD *fatdate,
1090 if (!ft->dwLowDateTime && !ft->dwHighDateTime)
1091 *fatdate = *fattime = 0;
1093 FileTimeToDosDateTime(ft, fatdate, fattime);
1096 /***********************************************************************
1097 * urlcache_delete_file (Internal)
1099 static DWORD urlcache_delete_file(const cache_container *container,
1100 urlcache_header *header, entry_url *url_entry)
1102 WIN32_FILE_ATTRIBUTE_DATA attr;
1103 WCHAR path[MAX_PATH];
1104 LONG path_size = sizeof(path);
1108 if(!url_entry->local_name_off)
1111 if(!urlcache_create_file_pathW(container, header,
1112 (LPCSTR)url_entry+url_entry->local_name_off,
1113 url_entry->cache_dir, path, &path_size))
1116 if(!GetFileAttributesExW(path, GetFileExInfoStandard, &attr))
1118 file_time_to_dos_date_time(&attr.ftLastWriteTime, &date, &time);
1119 if(date != url_entry->write_date || time != url_entry->write_time)
1122 err = (DeleteFileW(path) ? ERROR_SUCCESS : GetLastError());
1123 if(err == ERROR_ACCESS_DENIED || err == ERROR_SHARING_VIOLATION)
1127 if (url_entry->cache_dir < header->dirs_no)
1129 if (header->directory_data[url_entry->cache_dir].files_no)
1130 header->directory_data[url_entry->cache_dir].files_no--;
1132 if (url_entry->cache_entry_type & STICKY_CACHE_ENTRY)
1134 if (url_entry->size.QuadPart < header->exempt_usage.QuadPart)
1135 header->exempt_usage.QuadPart -= url_entry->size.QuadPart;
1137 header->exempt_usage.QuadPart = 0;
1141 if (url_entry->size.QuadPart < header->cache_usage.QuadPart)
1142 header->cache_usage.QuadPart -= url_entry->size.QuadPart;
1144 header->cache_usage.QuadPart = 0;
1147 return ERROR_SUCCESS;
1150 static BOOL urlcache_clean_leaked_entries(cache_container *container, urlcache_header *header)
1155 leak_off = &header->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
1157 entry_url *url_entry = (entry_url*)((LPBYTE)header + *leak_off);
1159 if(SUCCEEDED(urlcache_delete_file(container, header, url_entry))) {
1160 *leak_off = url_entry->exempt_delta;
1161 urlcache_entry_free(header, &url_entry->header);
1164 leak_off = &url_entry->exempt_delta;
1171 /***********************************************************************
1172 * cache_container_clean_index (Internal)
1174 * This function is meant to make place in index file by removing leaked
1175 * files entries and resizing the file.
1177 * CAUTION: file view may get mapped to new memory
1180 * ERROR_SUCCESS when new memory is available
1181 * error code otherwise
1183 static DWORD cache_container_clean_index(cache_container *container, urlcache_header **file_view)
1185 urlcache_header *header = *file_view;
1188 TRACE("(%s %s)\n", debugstr_a(container->cache_prefix), debugstr_w(container->path));
1190 if(urlcache_clean_leaked_entries(container, header))
1191 return ERROR_SUCCESS;
1193 if(header->size >= ALLOCATION_TABLE_SIZE*8*BLOCKSIZE + ENTRY_START_OFFSET) {
1194 WARN("index file has maximal size\n");
1195 return ERROR_NOT_ENOUGH_MEMORY;
1198 cache_container_close_index(container);
1199 ret = cache_container_open_index(container, header->capacity_in_blocks*2);
1200 if(ret != ERROR_SUCCESS)
1202 header = MapViewOfFile(container->mapping, FILE_MAP_WRITE, 0, 0, 0);
1204 return GetLastError();
1206 UnmapViewOfFile(*file_view);
1207 *file_view = header;
1208 return ERROR_SUCCESS;
1211 /* Just like DosDateTimeToFileTime, except that it also maps the special
1212 * case of a DOS date/time of (0,0) to a filetime of (0,0).
1214 static void dos_date_time_to_file_time(WORD fatdate, WORD fattime,
1217 if (!fatdate && !fattime)
1218 ft->dwLowDateTime = ft->dwHighDateTime = 0;
1220 DosDateTimeToFileTime(fatdate, fattime, ft);
1223 static int urlcache_decode_url(const char *url, WCHAR *decoded_url, int decoded_len)
1226 DWORD len, part_len;
1229 memset(&uc, 0, sizeof(uc));
1230 uc.dwStructSize = sizeof(uc);
1231 uc.dwHostNameLength = 1;
1232 if(!InternetCrackUrlA(url, 0, 0, &uc))
1233 uc.nScheme = INTERNET_SCHEME_UNKNOWN;
1235 if(uc.nScheme!=INTERNET_SCHEME_HTTP && uc.nScheme!=INTERNET_SCHEME_HTTPS)
1236 return MultiByteToWideChar(CP_UTF8, 0, url, -1, decoded_url, decoded_len);
1241 len = MultiByteToWideChar(CP_UTF8, 0, url, uc.lpszHostName-url, decoded_url, decoded_len);
1247 host_name = heap_alloc(uc.dwHostNameLength*sizeof(WCHAR));
1250 if(!MultiByteToWideChar(CP_UTF8, 0, uc.lpszHostName, uc.dwHostNameLength,
1251 host_name, uc.dwHostNameLength)) {
1252 heap_free(host_name);
1255 part_len = IdnToUnicode(0, host_name, uc.dwHostNameLength,
1256 decoded_url ? decoded_url+len : NULL, decoded_len);
1257 heap_free(host_name);
1259 SetLastError(ERROR_INTERNET_INVALID_URL);
1264 decoded_len -= part_len;
1266 part_len = MultiByteToWideChar(CP_UTF8, 0,
1267 uc.lpszHostName+uc.dwHostNameLength,
1268 -1, decoded_url ? decoded_url+len : NULL, decoded_len);
1276 /***********************************************************************
1277 * urlcache_copy_entry (Internal)
1279 * Copies an entry from the cache index file to the Win32 structure
1282 * ERROR_SUCCESS if the buffer was big enough
1283 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1286 static DWORD urlcache_copy_entry(cache_container *container, const urlcache_header *header,
1287 INTERNET_CACHE_ENTRY_INFOA *entry_info, DWORD *info_size, const entry_url *url_entry, BOOL unicode)
1290 DWORD size = sizeof(*entry_info);
1292 if(*info_size >= size) {
1293 entry_info->lpHeaderInfo = NULL;
1294 entry_info->lpszFileExtension = NULL;
1295 entry_info->lpszLocalFileName = NULL;
1296 entry_info->lpszSourceUrlName = NULL;
1297 entry_info->CacheEntryType = url_entry->cache_entry_type;
1298 entry_info->u.dwExemptDelta = url_entry->exempt_delta;
1299 entry_info->dwHeaderInfoSize = url_entry->header_info_size;
1300 entry_info->dwHitRate = url_entry->hit_rate;
1301 entry_info->dwSizeHigh = url_entry->size.u.HighPart;
1302 entry_info->dwSizeLow = url_entry->size.u.LowPart;
1303 entry_info->dwStructSize = sizeof(*entry_info);
1304 entry_info->dwUseCount = url_entry->use_count;
1305 dos_date_time_to_file_time(url_entry->expire_date, url_entry->expire_time, &entry_info->ExpireTime);
1306 entry_info->LastAccessTime = url_entry->access_time;
1307 entry_info->LastModifiedTime = url_entry->modification_time;
1308 dos_date_time_to_file_time(url_entry->sync_date, url_entry->sync_time, &entry_info->LastSyncTime);
1311 if(size%4 && size<*info_size)
1312 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1313 size = DWORD_ALIGN(size);
1315 url_len = urlcache_decode_url((const char*)url_entry+url_entry->url_off, NULL, 0);
1317 url_len = strlen((LPCSTR)url_entry+url_entry->url_off) + 1;
1318 size += url_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1320 if(*info_size >= size) {
1321 DWORD url_size = url_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1323 entry_info->lpszSourceUrlName = (LPSTR)entry_info+size-url_size;
1325 urlcache_decode_url((const char*)url_entry+url_entry->url_off, (WCHAR*)entry_info->lpszSourceUrlName, url_len);
1327 memcpy(entry_info->lpszSourceUrlName, (LPCSTR)url_entry+url_entry->url_off, url_size);
1330 if(size%4 && size<*info_size)
1331 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1332 size = DWORD_ALIGN(size);
1334 if(url_entry->local_name_off) {
1335 LONG file_name_size;
1337 file_name = (LPSTR)entry_info+size;
1338 file_name_size = *info_size-size;
1339 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)) ||
1340 (!unicode && urlcache_create_file_pathA(container, header, (LPCSTR)url_entry+url_entry->local_name_off, url_entry->cache_dir, file_name, &file_name_size))) {
1341 entry_info->lpszLocalFileName = file_name;
1343 size += file_name_size;
1345 if(size%4 && size<*info_size)
1346 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1347 size = DWORD_ALIGN(size);
1350 if(url_entry->header_info_off) {
1354 header_len = MultiByteToWideChar(CP_UTF8, 0, (const char*)url_entry+url_entry->header_info_off,
1355 url_entry->header_info_size, NULL, 0);
1357 header_len = url_entry->header_info_size;
1358 size += header_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1360 if(*info_size >= size) {
1361 DWORD header_size = header_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1362 entry_info->lpHeaderInfo = (LPBYTE)entry_info+size-header_size;
1364 MultiByteToWideChar(CP_UTF8, 0, (const char*)url_entry+url_entry->header_info_off,
1365 url_entry->header_info_size, (LPWSTR)entry_info->lpHeaderInfo, header_len);
1367 memcpy(entry_info->lpHeaderInfo, (LPCSTR)url_entry+url_entry->header_info_off, header_len);
1369 if(size%4 && size<*info_size)
1370 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1371 size = DWORD_ALIGN(size);
1374 if(url_entry->file_extension_off) {
1378 ext_len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)url_entry+url_entry->file_extension_off, -1, NULL, 0);
1380 ext_len = strlen((LPCSTR)url_entry+url_entry->file_extension_off) + 1;
1381 size += ext_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1383 if(*info_size >= size) {
1384 DWORD ext_size = ext_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1385 entry_info->lpszFileExtension = (LPSTR)entry_info+size-ext_size;
1387 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)url_entry+url_entry->file_extension_off, -1, (LPWSTR)entry_info->lpszFileExtension, ext_len);
1389 memcpy(entry_info->lpszFileExtension, (LPCSTR)url_entry+url_entry->file_extension_off, ext_len*sizeof(CHAR));
1392 if(size%4 && size<*info_size)
1393 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1394 size = DWORD_ALIGN(size);
1397 if(size > *info_size) {
1399 return ERROR_INSUFFICIENT_BUFFER;
1402 return ERROR_SUCCESS;
1405 /***********************************************************************
1406 * urlcache_set_entry_info (Internal)
1408 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1409 * according to the flags set by field_control.
1412 * ERROR_SUCCESS if the buffer was big enough
1413 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1416 static DWORD urlcache_set_entry_info(entry_url *url_entry, const INTERNET_CACHE_ENTRY_INFOA *entry_info, DWORD field_control)
1418 if (field_control & CACHE_ENTRY_ACCTIME_FC)
1419 url_entry->access_time = entry_info->LastAccessTime;
1420 if (field_control & CACHE_ENTRY_ATTRIBUTE_FC)
1421 url_entry->cache_entry_type = entry_info->CacheEntryType;
1422 if (field_control & CACHE_ENTRY_EXEMPT_DELTA_FC)
1423 url_entry->exempt_delta = entry_info->u.dwExemptDelta;
1424 if (field_control & CACHE_ENTRY_EXPTIME_FC)
1425 file_time_to_dos_date_time(&entry_info->ExpireTime, &url_entry->expire_date, &url_entry->expire_time);
1426 if (field_control & CACHE_ENTRY_HEADERINFO_FC)
1427 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1428 if (field_control & CACHE_ENTRY_HITRATE_FC)
1429 url_entry->hit_rate = entry_info->dwHitRate;
1430 if (field_control & CACHE_ENTRY_MODTIME_FC)
1431 url_entry->modification_time = entry_info->LastModifiedTime;
1432 if (field_control & CACHE_ENTRY_SYNCTIME_FC)
1433 file_time_to_dos_date_time(&entry_info->LastAccessTime, &url_entry->sync_date, &url_entry->sync_time);
1435 return ERROR_SUCCESS;
1438 /***********************************************************************
1439 * urlcache_hash_key (Internal)
1441 * Returns the hash key for a given string
1444 * hash key for the string
1447 static DWORD urlcache_hash_key(LPCSTR lpszKey)
1449 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1450 * but the algorithm and result are not the same!
1452 static const unsigned char lookupTable[256] =
1454 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1455 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1456 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1457 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1458 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1459 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1460 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1461 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1462 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1463 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1464 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1465 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1466 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1467 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1468 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1469 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1470 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1471 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1472 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1473 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1474 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1475 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1476 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1477 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1478 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1479 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1480 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1481 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1482 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1483 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1484 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1485 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1490 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1491 key[i] = lookupTable[(*lpszKey + i) & 0xFF];
1493 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1495 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1496 key[i] = lookupTable[*lpszKey ^ key[i]];
1499 return *(DWORD *)key;
1502 static inline entry_hash_table* urlcache_get_hash_table(const urlcache_header *pHeader, DWORD dwOffset)
1506 return (entry_hash_table*)((LPBYTE)pHeader + dwOffset);
1509 static BOOL urlcache_find_hash_entry(const urlcache_header *pHeader, LPCSTR lpszUrl, struct hash_entry **ppHashEntry)
1511 /* structure of hash table:
1512 * 448 entries divided into 64 blocks
1513 * each block therefore contains a chain of 7 key/offset pairs
1514 * how position in table is calculated:
1515 * 1. the url is hashed in helper function
1516 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1517 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1520 * there can be multiple hash tables in the file and the offset to
1521 * the next one is stored in the header of the hash table
1523 DWORD key = urlcache_hash_key(lpszUrl);
1524 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1525 entry_hash_table* pHashEntry;
1528 key >>= HASHTABLE_FLAG_BITS;
1530 for (pHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1531 pHashEntry; pHashEntry = urlcache_get_hash_table(pHeader, pHashEntry->next))
1534 if (pHashEntry->id != id++)
1536 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->id, id);
1539 /* make sure that it is in fact a hash entry */
1540 if (pHashEntry->header.signature != HASH_SIGNATURE)
1542 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->header.signature);
1546 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1548 struct hash_entry *pHashElement = &pHashEntry->hash_table[offset + i];
1549 if (key == pHashElement->key>>HASHTABLE_FLAG_BITS)
1551 /* FIXME: we should make sure that this is the right element
1552 * before returning and claiming that it is. We can do this
1553 * by doing a simple compare between the URL we were given
1554 * and the URL stored in the entry. However, this assumes
1555 * we know the format of all the entries stored in the
1557 *ppHashEntry = pHashElement;
1565 /***********************************************************************
1566 * urlcache_hash_entry_set_flags (Internal)
1568 * Sets special bits in hash key
1574 static void urlcache_hash_entry_set_flags(struct hash_entry *pHashEntry, DWORD dwFlag)
1576 pHashEntry->key = (pHashEntry->key >> HASHTABLE_FLAG_BITS << HASHTABLE_FLAG_BITS) | dwFlag;
1579 /***********************************************************************
1580 * urlcache_hash_entry_delete (Internal)
1582 * Searches all the hash tables in the index for the given URL and
1583 * then if found deletes the entry.
1586 * TRUE if the entry was found
1587 * FALSE if the entry could not be found
1590 static BOOL urlcache_hash_entry_delete(struct hash_entry *pHashEntry)
1592 pHashEntry->key = HASHTABLE_DEL;
1596 /***********************************************************************
1597 * urlcache_hash_entry_create (Internal)
1599 * Searches all the hash tables for a free slot based on the offset
1600 * generated from the hash key. If a free slot is found, the offset and
1601 * key are entered into the hash table.
1604 * ERROR_SUCCESS if the entry was added
1605 * Any other Win32 error code if the entry could not be added
1608 static DWORD urlcache_hash_entry_create(urlcache_header *pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry, DWORD dwFieldType)
1610 /* see urlcache_find_hash_entry for structure of hash tables */
1612 DWORD key = urlcache_hash_key(lpszUrl);
1613 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1614 entry_hash_table* pHashEntry, *pHashPrev = NULL;
1618 key = ((key >> HASHTABLE_FLAG_BITS) << HASHTABLE_FLAG_BITS) + dwFieldType;
1620 for (pHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1621 pHashEntry; pHashEntry = urlcache_get_hash_table(pHeader, pHashEntry->next))
1624 pHashPrev = pHashEntry;
1626 if (pHashEntry->id != id++)
1628 ERR("not right hash table number (%d) expected %d\n", pHashEntry->id, id);
1631 /* make sure that it is in fact a hash entry */
1632 if (pHashEntry->header.signature != HASH_SIGNATURE)
1634 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->header.signature);
1638 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1640 struct hash_entry *pHashElement = &pHashEntry->hash_table[offset + i];
1641 if (pHashElement->key==HASHTABLE_FREE || pHashElement->key==HASHTABLE_DEL) /* if the slot is free */
1643 pHashElement->key = key;
1644 pHashElement->offset = dwOffsetEntry;
1645 return ERROR_SUCCESS;
1649 error = urlcache_create_hash_table(pHeader, pHashPrev, &pHashEntry);
1650 if (error != ERROR_SUCCESS)
1653 pHashEntry->hash_table[offset].key = key;
1654 pHashEntry->hash_table[offset].offset = dwOffsetEntry;
1655 return ERROR_SUCCESS;
1658 /***********************************************************************
1659 * urlcache_enum_hash_tables (Internal)
1661 * Enumerates the hash tables in a container.
1664 * TRUE if an entry was found
1665 * FALSE if there are no more tables to enumerate.
1668 static BOOL urlcache_enum_hash_tables(const urlcache_header *pHeader, DWORD *id, entry_hash_table **ppHashEntry)
1670 for (*ppHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1671 *ppHashEntry; *ppHashEntry = urlcache_get_hash_table(pHeader, (*ppHashEntry)->next))
1673 TRACE("looking at hash table number %d\n", (*ppHashEntry)->id);
1674 if ((*ppHashEntry)->id != *id)
1676 /* make sure that it is in fact a hash entry */
1677 if ((*ppHashEntry)->header.signature != HASH_SIGNATURE)
1679 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->header.signature);
1684 TRACE("hash table number %d found\n", *id);
1690 /***********************************************************************
1691 * urlcache_enum_hash_table_entries (Internal)
1693 * Enumerates entries in a hash table and returns the next non-free entry.
1696 * TRUE if an entry was found
1697 * FALSE if the hash table is empty or there are no more entries to
1701 static BOOL urlcache_enum_hash_table_entries(const urlcache_header *pHeader, const entry_hash_table *pHashEntry,
1702 DWORD * index, const struct hash_entry **ppHashEntry)
1704 for (; *index < HASHTABLE_SIZE ; (*index)++)
1706 if (pHashEntry->hash_table[*index].key==HASHTABLE_FREE || pHashEntry->hash_table[*index].key==HASHTABLE_DEL)
1709 *ppHashEntry = &pHashEntry->hash_table[*index];
1710 TRACE("entry found %d\n", *index);
1713 TRACE("no more entries (%d)\n", *index);
1717 /***********************************************************************
1718 * cache_container_delete_dir (Internal)
1720 * Erase a directory containing an URL cache.
1723 * TRUE success, FALSE failure/aborted.
1726 static BOOL cache_container_delete_dir(LPCWSTR lpszPath)
1729 WCHAR path[MAX_PATH + 1];
1730 SHFILEOPSTRUCTW shfos;
1733 path_len = strlenW(lpszPath);
1734 if (path_len >= MAX_PATH)
1736 strcpyW(path, lpszPath);
1737 path[path_len + 1] = 0; /* double-NUL-terminate path */
1740 shfos.wFunc = FO_DELETE;
1743 shfos.fFlags = FOF_NOCONFIRMATION;
1744 shfos.fAnyOperationsAborted = FALSE;
1745 ret = SHFileOperationW(&shfos);
1747 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path), ret);
1748 return !(ret || shfos.fAnyOperationsAborted);
1751 /***********************************************************************
1752 * urlcache_hash_entry_is_locked (Internal)
1754 * Checks if entry is locked. Unlocks it if possible.
1756 static BOOL urlcache_hash_entry_is_locked(struct hash_entry *hash_entry, entry_url *url_entry)
1759 ULARGE_INTEGER acc_time, time;
1761 if ((hash_entry->key & ((1<<HASHTABLE_FLAG_BITS)-1)) != HASHTABLE_LOCK)
1764 GetSystemTimeAsFileTime(&cur_time);
1765 time.u.LowPart = cur_time.dwLowDateTime;
1766 time.u.HighPart = cur_time.dwHighDateTime;
1768 acc_time.u.LowPart = url_entry->access_time.dwLowDateTime;
1769 acc_time.u.HighPart = url_entry->access_time.dwHighDateTime;
1771 time.QuadPart -= acc_time.QuadPart;
1773 /* check if entry was locked for at least a day */
1774 if(time.QuadPart > (ULONGLONG)24*60*60*FILETIME_SECOND) {
1775 urlcache_hash_entry_set_flags(hash_entry, HASHTABLE_URL);
1776 url_entry->use_count = 0;
1783 static BOOL urlcache_get_entry_info(const char *url, void *entry_info,
1784 DWORD *size, DWORD flags, BOOL unicode)
1786 urlcache_header *header;
1787 struct hash_entry *hash_entry;
1788 const entry_url *url_entry;
1789 cache_container *container;
1792 TRACE("(%s, %p, %p, %x, %x)\n", debugstr_a(url), entry_info, size, flags, unicode);
1794 if(flags & ~GET_INSTALLED_ENTRY)
1795 FIXME("ignoring unsupported flags: %x\n", flags);
1797 error = cache_containers_find(url, &container);
1798 if(error != ERROR_SUCCESS) {
1799 SetLastError(error);
1803 error = cache_container_open_index(container, MIN_BLOCK_NO);
1804 if(error != ERROR_SUCCESS) {
1805 SetLastError(error);
1809 if(!(header = cache_container_lock_index(container)))
1812 if(!urlcache_find_hash_entry(header, url, &hash_entry)) {
1813 cache_container_unlock_index(container, header);
1814 WARN("entry %s not found!\n", debugstr_a(url));
1815 SetLastError(ERROR_FILE_NOT_FOUND);
1819 url_entry = (const entry_url*)((LPBYTE)header + hash_entry->offset);
1820 if(url_entry->header.signature != URL_SIGNATURE) {
1821 cache_container_unlock_index(container, header);
1822 FIXME("Trying to retrieve entry of unknown format %s\n",
1823 debugstr_an((LPCSTR)&url_entry->header.signature, sizeof(DWORD)));
1824 SetLastError(ERROR_FILE_NOT_FOUND);
1828 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->url_off));
1829 TRACE("Header info: %s\n", debugstr_an((LPCSTR)url_entry +
1830 url_entry->header_info_off, url_entry->header_info_size));
1832 if((flags & GET_INSTALLED_ENTRY) && !(url_entry->cache_entry_type & INSTALLED_CACHE_ENTRY)) {
1833 cache_container_unlock_index(container, header);
1834 SetLastError(ERROR_FILE_NOT_FOUND);
1842 error = urlcache_copy_entry(container, header, entry_info, size, url_entry, unicode);
1843 if(error != ERROR_SUCCESS) {
1844 cache_container_unlock_index(container, header);
1845 SetLastError(error);
1848 if(url_entry->local_name_off)
1849 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->local_name_off));
1852 cache_container_unlock_index(container, header);
1856 /***********************************************************************
1857 * GetUrlCacheEntryInfoExA (WININET.@)
1860 BOOL WINAPI GetUrlCacheEntryInfoExA(LPCSTR lpszUrl,
1861 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1862 LPDWORD lpdwCacheEntryInfoBufSize, LPSTR lpszReserved,
1863 LPDWORD lpdwReserved, LPVOID lpReserved, DWORD dwFlags)
1865 if(lpszReserved!=NULL || lpdwReserved!=NULL || lpReserved!=NULL) {
1866 ERR("Reserved value was not 0\n");
1867 SetLastError(ERROR_INVALID_PARAMETER);
1871 return urlcache_get_entry_info(lpszUrl, lpCacheEntryInfo,
1872 lpdwCacheEntryInfoBufSize, dwFlags, FALSE);
1875 /***********************************************************************
1876 * GetUrlCacheEntryInfoA (WININET.@)
1879 BOOL WINAPI GetUrlCacheEntryInfoA(LPCSTR lpszUrlName,
1880 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1881 LPDWORD lpdwCacheEntryInfoBufferSize)
1883 return GetUrlCacheEntryInfoExA(lpszUrlName, lpCacheEntryInfo,
1884 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
1887 static int urlcache_encode_url(const WCHAR *url, char *encoded_url, int encoded_len)
1890 DWORD len, part_len;
1893 TRACE("%s\n", debugstr_w(url));
1895 memset(&uc, 0, sizeof(uc));
1896 uc.dwStructSize = sizeof(uc);
1897 uc.dwHostNameLength = 1;
1898 if(!InternetCrackUrlW(url, 0, 0, &uc))
1899 uc.nScheme = INTERNET_SCHEME_UNKNOWN;
1901 if(uc.nScheme!=INTERNET_SCHEME_HTTP && uc.nScheme!=INTERNET_SCHEME_HTTPS)
1902 return WideCharToMultiByte(CP_UTF8, 0, url, -1, encoded_url, encoded_len, NULL, NULL);
1904 len = WideCharToMultiByte(CP_UTF8, 0, url, uc.lpszHostName-url,
1905 encoded_url, encoded_len, NULL, NULL);
1911 part_len = IdnToAscii(0, uc.lpszHostName, uc.dwHostNameLength, NULL, 0);
1913 SetLastError(ERROR_INTERNET_INVALID_URL);
1917 punycode = heap_alloc(part_len*sizeof(WCHAR));
1921 part_len = IdnToAscii(0, uc.lpszHostName, uc.dwHostNameLength, punycode, part_len);
1923 heap_free(punycode);
1927 part_len = WideCharToMultiByte(CP_UTF8, 0, punycode, part_len,
1928 encoded_url ? encoded_url+len : NULL, encoded_len, NULL, NULL);
1929 heap_free(punycode);
1933 encoded_len -= part_len;
1936 part_len = WideCharToMultiByte(CP_UTF8, 0, uc.lpszHostName+uc.dwHostNameLength,
1937 -1, encoded_url ? encoded_url+len : NULL, encoded_len, NULL, NULL);
1942 TRACE("got (%d)%s\n", len, debugstr_a(encoded_url));
1946 static BOOL urlcache_encode_url_alloc(const WCHAR *url, char **encoded_url)
1951 encoded_len = urlcache_encode_url(url, NULL, 0);
1955 ret = heap_alloc(encoded_len*sizeof(WCHAR));
1959 encoded_len = urlcache_encode_url(url, ret, encoded_len);
1969 /***********************************************************************
1970 * GetUrlCacheEntryInfoExW (WININET.@)
1973 BOOL WINAPI GetUrlCacheEntryInfoExW(LPCWSTR lpszUrl,
1974 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1975 LPDWORD lpdwCacheEntryInfoBufSize, LPWSTR lpszReserved,
1976 LPDWORD lpdwReserved, LPVOID lpReserved, DWORD dwFlags)
1981 if(lpszReserved!=NULL || lpdwReserved!=NULL || lpReserved!=NULL) {
1982 ERR("Reserved value was not 0\n");
1983 SetLastError(ERROR_INVALID_PARAMETER);
1987 /* Ignore GET_INSTALLED_ENTRY flag in unicode version of function */
1988 dwFlags &= ~GET_INSTALLED_ENTRY;
1990 if(!urlcache_encode_url_alloc(lpszUrl, &url))
1993 ret = urlcache_get_entry_info(url, lpCacheEntryInfo,
1994 lpdwCacheEntryInfoBufSize, dwFlags, TRUE);
1999 /***********************************************************************
2000 * GetUrlCacheEntryInfoW (WININET.@)
2003 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
2004 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2005 LPDWORD lpdwCacheEntryInfoBufferSize)
2007 return GetUrlCacheEntryInfoExW(lpszUrl, lpCacheEntryInfo,
2008 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
2011 /***********************************************************************
2012 * SetUrlCacheEntryInfoA (WININET.@)
2014 BOOL WINAPI SetUrlCacheEntryInfoA(LPCSTR lpszUrlName,
2015 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2016 DWORD dwFieldControl)
2018 urlcache_header *pHeader;
2019 struct hash_entry *pHashEntry;
2020 entry_header *pEntry;
2021 cache_container *pContainer;
2024 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
2026 error = cache_containers_find(lpszUrlName, &pContainer);
2027 if (error != ERROR_SUCCESS)
2029 SetLastError(error);
2033 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2034 if (error != ERROR_SUCCESS)
2036 SetLastError(error);
2040 if (!(pHeader = cache_container_lock_index(pContainer)))
2043 if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
2045 cache_container_unlock_index(pContainer, pHeader);
2046 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
2047 SetLastError(ERROR_FILE_NOT_FOUND);
2051 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2052 if (pEntry->signature != URL_SIGNATURE)
2054 cache_container_unlock_index(pContainer, pHeader);
2055 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2056 SetLastError(ERROR_FILE_NOT_FOUND);
2060 urlcache_set_entry_info((entry_url*)pEntry, lpCacheEntryInfo, dwFieldControl);
2062 cache_container_unlock_index(pContainer, pHeader);
2067 /***********************************************************************
2068 * SetUrlCacheEntryInfoW (WININET.@)
2070 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
2071 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2072 DWORD dwFieldControl)
2077 if(!urlcache_encode_url_alloc(lpszUrl, &url))
2080 ret = SetUrlCacheEntryInfoA(url, (INTERNET_CACHE_ENTRY_INFOA*)lpCacheEntryInfo, dwFieldControl);
2085 static BOOL urlcache_entry_get_file(const char *url, void *entry_info, DWORD *size, BOOL unicode)
2087 urlcache_header *header;
2088 struct hash_entry *hash_entry;
2089 entry_url *url_entry;
2090 cache_container *container;
2093 TRACE("(%s, %p, %p, %x)\n", debugstr_a(url), entry_info, size, unicode);
2095 if(!url || !size || (!entry_info && *size)) {
2096 SetLastError(ERROR_INVALID_PARAMETER);
2100 error = cache_containers_find(url, &container);
2101 if(error != ERROR_SUCCESS) {
2102 SetLastError(error);
2106 error = cache_container_open_index(container, MIN_BLOCK_NO);
2107 if (error != ERROR_SUCCESS) {
2108 SetLastError(error);
2112 if (!(header = cache_container_lock_index(container)))
2115 if (!urlcache_find_hash_entry(header, url, &hash_entry)) {
2116 cache_container_unlock_index(container, header);
2117 TRACE("entry %s not found!\n", url);
2118 SetLastError(ERROR_FILE_NOT_FOUND);
2122 url_entry = (entry_url*)((LPBYTE)header + hash_entry->offset);
2123 if(url_entry->header.signature != URL_SIGNATURE) {
2124 cache_container_unlock_index(container, header);
2125 FIXME("Trying to retrieve entry of unknown format %s\n",
2126 debugstr_an((LPSTR)&url_entry->header.signature, sizeof(DWORD)));
2127 SetLastError(ERROR_FILE_NOT_FOUND);
2131 if(!url_entry->local_name_off) {
2132 cache_container_unlock_index(container, header);
2133 SetLastError(ERROR_INVALID_DATA);
2137 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->url_off));
2138 TRACE("Header info: %s\n", debugstr_an((LPCSTR)url_entry + url_entry->header_info_off,
2139 url_entry->header_info_size));
2141 error = urlcache_copy_entry(container, header, entry_info,
2142 size, url_entry, unicode);
2143 if(error != ERROR_SUCCESS) {
2144 cache_container_unlock_index(container, header);
2145 SetLastError(error);
2148 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->local_name_off));
2150 url_entry->hit_rate++;
2151 url_entry->use_count++;
2152 urlcache_hash_entry_set_flags(hash_entry, HASHTABLE_LOCK);
2153 GetSystemTimeAsFileTime(&url_entry->access_time);
2155 cache_container_unlock_index(container, header);
2160 /***********************************************************************
2161 * RetrieveUrlCacheEntryFileA (WININET.@)
2164 BOOL WINAPI RetrieveUrlCacheEntryFileA(LPCSTR lpszUrlName,
2165 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2166 LPDWORD lpdwCacheEntryInfoBufferSize, DWORD dwReserved)
2168 return urlcache_entry_get_file(lpszUrlName, lpCacheEntryInfo,
2169 lpdwCacheEntryInfoBufferSize, FALSE);
2172 /***********************************************************************
2173 * RetrieveUrlCacheEntryFileW (WININET.@)
2176 BOOL WINAPI RetrieveUrlCacheEntryFileW(LPCWSTR lpszUrlName,
2177 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2178 LPDWORD lpdwCacheEntryInfoBufferSize, DWORD dwReserved)
2183 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
2186 ret = urlcache_entry_get_file(url, lpCacheEntryInfo,
2187 lpdwCacheEntryInfoBufferSize, TRUE);
2192 static BOOL urlcache_entry_delete(const cache_container *pContainer,
2193 urlcache_header *pHeader, struct hash_entry *pHashEntry)
2195 entry_header *pEntry;
2196 entry_url * pUrlEntry;
2198 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2199 if (pEntry->signature != URL_SIGNATURE)
2201 FIXME("Trying to delete entry of unknown format %s\n",
2202 debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
2203 SetLastError(ERROR_FILE_NOT_FOUND);
2207 pUrlEntry = (entry_url *)pEntry;
2208 if(urlcache_hash_entry_is_locked(pHashEntry, pUrlEntry))
2210 TRACE("Trying to delete locked entry\n");
2211 pUrlEntry->cache_entry_type |= PENDING_DELETE_CACHE_ENTRY;
2212 SetLastError(ERROR_SHARING_VIOLATION);
2216 if(!urlcache_delete_file(pContainer, pHeader, pUrlEntry))
2218 urlcache_entry_free(pHeader, pEntry);
2222 /* Add entry to leaked files list */
2223 pUrlEntry->header.signature = LEAK_SIGNATURE;
2224 pUrlEntry->exempt_delta = pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
2225 pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET] = pHashEntry->offset;
2228 urlcache_hash_entry_delete(pHashEntry);
2232 static HANDLE free_cache_running;
2233 static HANDLE dll_unload_event;
2234 static DWORD WINAPI handle_full_cache_worker(void *param)
2236 FreeUrlCacheSpaceW(NULL, 20, 0);
2237 ReleaseSemaphore(free_cache_running, 1, NULL);
2241 static void handle_full_cache(void)
2243 if(WaitForSingleObject(free_cache_running, 0) == WAIT_OBJECT_0) {
2244 if(!QueueUserWorkItem(handle_full_cache_worker, NULL, 0))
2245 ReleaseSemaphore(free_cache_running, 1, NULL);
2249 /* Enumerates entries in cache, allows cache unlocking between calls. */
2250 static BOOL urlcache_next_entry(urlcache_header *header, DWORD *hash_table_off, DWORD *hash_table_entry,
2251 struct hash_entry **hash_entry, entry_header **entry)
2253 entry_hash_table *hashtable_entry;
2258 if(!*hash_table_off) {
2259 *hash_table_off = header->hash_table_off;
2260 *hash_table_entry = 0;
2262 hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2264 if(*hash_table_off >= header->size) {
2265 *hash_table_off = 0;
2269 hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2272 if(hashtable_entry->header.signature != HASH_SIGNATURE) {
2273 *hash_table_off = 0;
2278 if(*hash_table_entry >= HASHTABLE_SIZE) {
2279 *hash_table_off = hashtable_entry->next;
2280 if(!*hash_table_off) {
2281 *hash_table_off = 0;
2285 hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2286 *hash_table_entry = 0;
2289 if(hashtable_entry->hash_table[*hash_table_entry].key != HASHTABLE_DEL &&
2290 hashtable_entry->hash_table[*hash_table_entry].key != HASHTABLE_FREE) {
2291 *hash_entry = &hashtable_entry->hash_table[*hash_table_entry];
2292 *entry = (entry_header*)((LPBYTE)header + hashtable_entry->hash_table[*hash_table_entry].offset);
2293 (*hash_table_entry)++;
2297 (*hash_table_entry)++;
2300 *hash_table_off = 0;
2304 /* Rates an urlcache entry to determine if it can be deleted.
2306 * Score 0 means that entry can safely be removed, the bigger rating
2307 * the smaller chance of entry being removed.
2308 * DWORD_MAX means that entry can't be deleted at all.
2310 * Rating system is currently not fully compatible with native implementation.
2312 static DWORD urlcache_rate_entry(entry_url *url_entry, FILETIME *cur_time)
2314 ULARGE_INTEGER time, access_time;
2317 access_time.u.LowPart = url_entry->access_time.dwLowDateTime;
2318 access_time.u.HighPart = url_entry->access_time.dwHighDateTime;
2320 time.u.LowPart = cur_time->dwLowDateTime;
2321 time.u.HighPart = cur_time->dwHighDateTime;
2323 /* Don't touch entries that were added less than 10 minutes ago */
2324 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)10*60*FILETIME_SECOND)
2327 if(url_entry->cache_entry_type & STICKY_CACHE_ENTRY)
2328 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)url_entry->exempt_delta*FILETIME_SECOND)
2331 time.QuadPart = (time.QuadPart-access_time.QuadPart)/FILETIME_SECOND;
2332 rating = 400*60*60*24/(60*60*24+time.QuadPart);
2334 if(url_entry->hit_rate > 100)
2337 rating += url_entry->hit_rate;
2342 static int dword_cmp(const void *p1, const void *p2)
2344 return *(const DWORD*)p1 - *(const DWORD*)p2;
2347 /***********************************************************************
2348 * FreeUrlCacheSpaceW (WININET.@)
2350 * Frees up some cache.
2353 * cache_path [I] Which volume to free up from, or NULL if you don't care.
2354 * size [I] Percentage of the cache that should be free.
2355 * filter [I] Which entries can't be deleted (CacheEntryType)
2358 * TRUE success. FALSE failure.
2361 * This implementation just retrieves the path of the cache directory, and
2362 * deletes its contents from the filesystem. The correct approach would
2363 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
2365 BOOL WINAPI FreeUrlCacheSpaceW(LPCWSTR cache_path, DWORD size, DWORD filter)
2367 cache_container *container;
2368 DWORD path_len, err;
2370 TRACE("(%s, %x, %x)\n", debugstr_w(cache_path), size, filter);
2372 if(size<1 || size>100) {
2373 SetLastError(ERROR_INVALID_PARAMETER);
2378 path_len = strlenW(cache_path);
2379 if(cache_path[path_len-1] == '\\')
2385 if(size==100 && !filter) {
2386 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
2388 /* When cache_path==NULL only clean Temporary Internet Files */
2389 if((!path_len && container->cache_prefix[0]==0) ||
2390 (path_len && !strncmpiW(container->path, cache_path, path_len) &&
2391 (container->path[path_len]=='\0' || container->path[path_len]=='\\')))
2395 WaitForSingleObject(container->mutex, INFINITE);
2397 /* unlock, delete, recreate and lock cache */
2398 cache_container_close_index(container);
2399 ret_del = cache_container_delete_dir(container->path);
2400 err = cache_container_open_index(container, MIN_BLOCK_NO);
2402 ReleaseMutex(container->mutex);
2403 if(!ret_del || (err != ERROR_SUCCESS))
2411 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
2413 urlcache_header *header;
2414 struct hash_entry *hash_entry;
2415 entry_header *entry;
2416 entry_url *url_entry;
2417 ULONGLONG desired_size, cur_size;
2418 DWORD delete_factor, hash_table_off, hash_table_entry;
2419 DWORD rate[100], rate_no;
2422 if((path_len || container->cache_prefix[0]!=0) &&
2423 (!path_len || strncmpiW(container->path, cache_path, path_len) ||
2424 (container->path[path_len]!='\0' && container->path[path_len]!='\\')))
2427 err = cache_container_open_index(container, MIN_BLOCK_NO);
2428 if(err != ERROR_SUCCESS)
2431 header = cache_container_lock_index(container);
2435 urlcache_clean_leaked_entries(container, header);
2437 desired_size = header->cache_limit.QuadPart*(100-size)/100;
2438 cur_size = header->cache_usage.QuadPart+header->exempt_usage.QuadPart;
2439 if(cur_size <= desired_size)
2442 delete_factor = (cur_size-desired_size)*100/cur_size;
2444 if(!delete_factor) {
2445 cache_container_unlock_index(container, header);
2450 hash_table_entry = 0;
2452 GetSystemTimeAsFileTime(&cur_time);
2453 while(rate_no<sizeof(rate)/sizeof(*rate) &&
2454 urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2455 if(entry->signature != URL_SIGNATURE) {
2456 WARN("only url entries are currently supported\n");
2460 url_entry = (entry_url*)entry;
2461 if(url_entry->cache_entry_type & filter)
2464 rate[rate_no] = urlcache_rate_entry(url_entry, &cur_time);
2465 if(rate[rate_no] != -1)
2470 TRACE("nothing to delete\n");
2471 cache_container_unlock_index(container, header);
2475 qsort(rate, rate_no, sizeof(DWORD), dword_cmp);
2477 delete_factor = delete_factor*rate_no/100;
2478 delete_factor = rate[delete_factor];
2479 TRACE("deleting files with rating %d or less\n", delete_factor);
2482 while(urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2483 if(entry->signature != URL_SIGNATURE)
2486 url_entry = (entry_url*)entry;
2487 if(url_entry->cache_entry_type & filter)
2490 if(urlcache_rate_entry(url_entry, &cur_time) <= delete_factor) {
2491 TRACE("deleting file: %s\n", debugstr_a((char*)url_entry+url_entry->local_name_off));
2492 urlcache_entry_delete(container, header, hash_entry);
2494 if(header->cache_usage.QuadPart+header->exempt_usage.QuadPart <= desired_size)
2497 /* Allow other threads to use cache while cleaning */
2498 cache_container_unlock_index(container, header);
2499 if(WaitForSingleObject(dll_unload_event, 0) == WAIT_OBJECT_0) {
2500 TRACE("got dll_unload_event - finishing\n");
2504 header = cache_container_lock_index(container);
2508 TRACE("cache size after cleaning 0x%s/0x%s\n",
2509 wine_dbgstr_longlong(header->cache_usage.QuadPart+header->exempt_usage.QuadPart),
2510 wine_dbgstr_longlong(header->cache_limit.QuadPart));
2511 cache_container_unlock_index(container, header);
2517 /***********************************************************************
2518 * FreeUrlCacheSpaceA (WININET.@)
2520 * See FreeUrlCacheSpaceW.
2522 BOOL WINAPI FreeUrlCacheSpaceA(LPCSTR lpszCachePath, DWORD dwSize, DWORD dwFilter)
2525 LPWSTR path = heap_strdupAtoW(lpszCachePath);
2526 if (lpszCachePath == NULL || path != NULL)
2527 ret = FreeUrlCacheSpaceW(path, dwSize, dwFilter);
2532 /***********************************************************************
2533 * UnlockUrlCacheEntryFileA (WININET.@)
2536 BOOL WINAPI UnlockUrlCacheEntryFileA(LPCSTR lpszUrlName, DWORD dwReserved)
2538 urlcache_header *pHeader;
2539 struct hash_entry *pHashEntry;
2540 entry_header *pEntry;
2541 entry_url * pUrlEntry;
2542 cache_container *pContainer;
2545 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2549 ERR("dwReserved != 0\n");
2550 SetLastError(ERROR_INVALID_PARAMETER);
2554 error = cache_containers_find(lpszUrlName, &pContainer);
2555 if (error != ERROR_SUCCESS)
2557 SetLastError(error);
2561 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2562 if (error != ERROR_SUCCESS)
2564 SetLastError(error);
2568 if (!(pHeader = cache_container_lock_index(pContainer)))
2571 if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
2573 cache_container_unlock_index(pContainer, pHeader);
2574 TRACE("entry %s not found!\n", lpszUrlName);
2575 SetLastError(ERROR_FILE_NOT_FOUND);
2579 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2580 if (pEntry->signature != URL_SIGNATURE)
2582 cache_container_unlock_index(pContainer, pHeader);
2583 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2584 SetLastError(ERROR_FILE_NOT_FOUND);
2588 pUrlEntry = (entry_url *)pEntry;
2590 if (pUrlEntry->use_count == 0)
2592 cache_container_unlock_index(pContainer, pHeader);
2595 pUrlEntry->use_count--;
2596 if (!pUrlEntry->use_count)
2598 urlcache_hash_entry_set_flags(pHashEntry, HASHTABLE_URL);
2599 if (pUrlEntry->cache_entry_type & PENDING_DELETE_CACHE_ENTRY)
2600 urlcache_entry_delete(pContainer, pHeader, pHashEntry);
2603 cache_container_unlock_index(pContainer, pHeader);
2608 /***********************************************************************
2609 * UnlockUrlCacheEntryFileW (WININET.@)
2612 BOOL WINAPI UnlockUrlCacheEntryFileW(LPCWSTR lpszUrlName, DWORD dwReserved)
2617 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
2620 ret = UnlockUrlCacheEntryFileA(url, dwReserved);
2625 static BOOL urlcache_entry_create(const char *url, const char *ext, WCHAR *full_path)
2627 cache_container *container;
2628 urlcache_header *header;
2629 char file_name[MAX_PATH];
2630 WCHAR extW[MAX_PATH];
2633 BOOL generate_name = FALSE;
2640 TRACE("(%s, %s, %p)\n", debugstr_a(url), debugstr_a(ext), full_path);
2642 memset(&uc, 0, sizeof(uc));
2643 uc.dwStructSize = sizeof(uc);
2644 uc.dwUrlPathLength = 1;
2645 uc.dwExtraInfoLength = 1;
2646 if(!InternetCrackUrlA(url, 0, 0, &uc))
2647 uc.dwUrlPathLength = 0;
2649 if(!uc.dwUrlPathLength) {
2654 p = e = uc.lpszUrlPath+uc.dwUrlPathLength;
2655 while(p>uc.lpszUrlPath && *(p-1)!='/' && *(p-1)!='\\' && *(p-1)!='.')
2657 if(p>uc.lpszUrlPath && *(p-1)=='.') {
2659 while(p>uc.lpszUrlPath && *(p-1)!='/' && *(p-1)!='\\')
2663 memcpy(file_name, p, e-p);
2666 for(p=file_name; *p; p++) {
2679 generate_name = TRUE;
2681 error = cache_containers_find(url, &container);
2682 if(error != ERROR_SUCCESS) {
2683 SetLastError(error);
2687 error = cache_container_open_index(container, MIN_BLOCK_NO);
2688 if(error != ERROR_SUCCESS) {
2689 SetLastError(error);
2693 if(!(header = cache_container_lock_index(container)))
2697 cache_dir = (BYTE)(rand() % header->dirs_no);
2699 cache_dir = CACHE_CONTAINER_NO_SUBDIR;
2701 full_path_len = MAX_PATH * sizeof(WCHAR);
2702 if(!urlcache_create_file_pathW(container, header, file_name, cache_dir, full_path, &full_path_len)) {
2703 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2704 debugstr_a(file_name), full_path_len);
2705 cache_container_unlock_index(container, header);
2708 full_path_len = full_path_len/sizeof(WCHAR) - 1;
2710 cache_container_unlock_index(container, header);
2716 MultiByteToWideChar(CP_ACP, 0, ext, -1, extW+1, MAX_PATH-1);
2718 for(p=extW; *p; p++) {
2728 if(p[-1]==' ' || p[-1]=='.')
2734 for(i=0; i<255 && !generate_name; i++) {
2735 static const WCHAR format[] = {'[','%','u',']','%','s',0};
2737 wsprintfW(full_path+full_path_len, format, i, extW);
2739 TRACE("Trying: %s\n", debugstr_w(full_path));
2740 file = CreateFileW(full_path, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2741 if(file != INVALID_HANDLE_VALUE) {
2747 /* Try to generate random name */
2748 GetSystemTimeAsFileTime(&ft);
2749 strcpyW(full_path+full_path_len+8, extW);
2751 for(i=0; i<255; i++) {
2753 ULONGLONG n = ft.dwHighDateTime;
2755 n += ft.dwLowDateTime;
2756 n ^= (ULONGLONG)i<<48;
2758 for(j=0; j<8; j++) {
2761 full_path[full_path_len+j] = (r < 10 ? '0' + r : 'A' + r - 10);
2764 TRACE("Trying: %s\n", debugstr_w(full_path));
2765 file = CreateFileW(full_path, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2766 if(file != INVALID_HANDLE_VALUE) {
2772 WARN("Could not find a unique filename\n");
2776 /***********************************************************************
2777 * CreateUrlCacheEntryA (WININET.@)
2780 BOOL WINAPI CreateUrlCacheEntryA(LPCSTR lpszUrlName, DWORD dwExpectedFileSize,
2781 LPCSTR lpszFileExtension, LPSTR lpszFileName, DWORD dwReserved)
2783 WCHAR file_name[MAX_PATH];
2786 FIXME("dwReserved 0x%08x\n", dwReserved);
2788 if(!urlcache_entry_create(lpszUrlName, lpszFileExtension, file_name))
2791 if(!WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL))
2795 /***********************************************************************
2796 * CreateUrlCacheEntryW (WININET.@)
2799 BOOL WINAPI CreateUrlCacheEntryW(LPCWSTR lpszUrlName, DWORD dwExpectedFileSize,
2800 LPCWSTR lpszFileExtension, LPWSTR lpszFileName, DWORD dwReserved)
2802 char *url, *ext = NULL;
2806 FIXME("dwReserved 0x%08x\n", dwReserved);
2808 if(lpszFileExtension) {
2809 ext = heap_strdupWtoUTF8(lpszFileExtension);
2814 if(!urlcache_encode_url_alloc(lpszUrlName, &url)) {
2819 ret = urlcache_entry_create(url, ext, lpszFileName);
2825 static BOOL urlcache_entry_commit(const char *url, const WCHAR *file_name,
2826 FILETIME expire_time, FILETIME modify_time, DWORD entry_type,
2827 BYTE *header_info, DWORD header_size, const char *file_ext,
2828 const char *original_url)
2830 cache_container *container;
2831 urlcache_header *header;
2832 struct hash_entry *hash_entry;
2833 entry_header *entry;
2834 entry_url *url_entry;
2835 DWORD url_entry_offset;
2836 DWORD size = DWORD_ALIGN(sizeof(*url_entry));
2837 DWORD file_name_off = 0;
2838 DWORD header_info_off = 0;
2839 DWORD file_ext_off = 0;
2840 WIN32_FILE_ATTRIBUTE_DATA file_attr;
2841 LARGE_INTEGER file_size;
2843 char file_name_no_container[MAX_PATH];
2844 char *local_file_name = 0;
2846 DWORD exempt_delta = 0;
2849 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n", debugstr_a(url), debugstr_w(file_name),
2850 entry_type, header_info, header_size, debugstr_a(file_ext), debugstr_a(original_url));
2852 if(entry_type & STICKY_CACHE_ENTRY && !file_name) {
2853 SetLastError(ERROR_INVALID_PARAMETER);
2857 WARN(": original_url ignored\n");
2859 memset(&file_attr, 0, sizeof(file_attr));
2861 if(!GetFileAttributesExW(file_name, GetFileExInfoStandard, &file_attr))
2864 file_size.u.LowPart = file_attr.nFileSizeLow;
2865 file_size.u.HighPart = file_attr.nFileSizeHigh;
2867 error = cache_containers_find(url, &container);
2868 if(error != ERROR_SUCCESS) {
2869 SetLastError(error);
2873 error = cache_container_open_index(container, MIN_BLOCK_NO);
2874 if(error != ERROR_SUCCESS) {
2875 SetLastError(error);
2879 if(!(header = cache_container_lock_index(container)))
2882 if(urlcache_find_hash_entry(header, url, &hash_entry)) {
2883 entry_url *url_entry = (entry_url*)((LPBYTE)header + hash_entry->offset);
2885 if(urlcache_hash_entry_is_locked(hash_entry, url_entry)) {
2886 TRACE("Trying to overwrite locked entry\n");
2887 cache_container_unlock_index(container, header);
2888 SetLastError(ERROR_SHARING_VIOLATION);
2892 hit_rate = url_entry->hit_rate;
2893 exempt_delta = url_entry->exempt_delta;
2894 urlcache_entry_delete(container, header, hash_entry);
2900 dir_id = CACHE_CONTAINER_NO_SUBDIR;
2903 BOOL bFound = FALSE;
2905 if(strncmpW(file_name, container->path, lstrlenW(container->path))) {
2906 ERR("path %s must begin with cache content path %s\n", debugstr_w(file_name), debugstr_w(container->path));
2907 cache_container_unlock_index(container, header);
2908 SetLastError(ERROR_INVALID_PARAMETER);
2912 /* skip container path prefix */
2913 file_name += lstrlenW(container->path);
2915 WideCharToMultiByte(CP_ACP, 0, file_name, -1, file_name_no_container, MAX_PATH, NULL, NULL);
2916 local_file_name = file_name_no_container;
2918 if(header->dirs_no) {
2919 for(dir_id = 0; dir_id < header->dirs_no; dir_id++) {
2920 if(!strncmp(header->directory_data[dir_id].name, local_file_name, DIR_LENGTH)) {
2927 ERR("cache directory not found in path %s\n", debugstr_w(file_name));
2928 cache_container_unlock_index(container, header);
2929 SetLastError(ERROR_INVALID_PARAMETER);
2933 file_name += DIR_LENGTH + 1;
2934 local_file_name += DIR_LENGTH + 1;
2938 size = DWORD_ALIGN(size + strlen(url) + 1);
2940 file_name_off = size;
2941 size = DWORD_ALIGN(size + strlen(local_file_name) + 1);
2943 if(header_info && header_size) {
2944 header_info_off = size;
2945 size = DWORD_ALIGN(size + header_size);
2947 if(file_ext && (file_ext_off = strlen(file_ext))) {
2948 DWORD len = file_ext_off;
2950 file_ext_off = size;
2951 size = DWORD_ALIGN(size + len + 1);
2954 /* round up to next block */
2955 if(size % BLOCKSIZE) {
2956 size -= size % BLOCKSIZE;
2960 error = urlcache_entry_alloc(header, size / BLOCKSIZE, &entry);
2961 while(error == ERROR_HANDLE_DISK_FULL) {
2962 error = cache_container_clean_index(container, &header);
2963 if(error == ERROR_SUCCESS)
2964 error = urlcache_entry_alloc(header, size / BLOCKSIZE, &entry);
2966 if(error != ERROR_SUCCESS) {
2967 cache_container_unlock_index(container, header);
2968 SetLastError(error);
2972 /* FindFirstFreeEntry fills in blocks used */
2973 url_entry = (entry_url *)entry;
2974 url_entry_offset = (LPBYTE)url_entry - (LPBYTE)header;
2975 url_entry->header.signature = URL_SIGNATURE;
2976 url_entry->cache_dir = dir_id;
2977 url_entry->cache_entry_type = entry_type | container->default_entry_type;
2978 url_entry->header_info_size = header_size;
2979 if((entry_type & STICKY_CACHE_ENTRY) && !exempt_delta) {
2980 /* Sticky entries have a default exempt time of one day */
2981 exempt_delta = 86400;
2983 url_entry->exempt_delta = exempt_delta;
2984 url_entry->hit_rate = hit_rate+1;
2985 url_entry->file_extension_off = file_ext_off;
2986 url_entry->header_info_off = header_info_off;
2987 url_entry->local_name_off = file_name_off;
2988 url_entry->url_off = DWORD_ALIGN(sizeof(*url_entry));
2989 url_entry->size.QuadPart = file_size.QuadPart;
2990 url_entry->use_count = 0;
2991 GetSystemTimeAsFileTime(&url_entry->access_time);
2992 url_entry->modification_time = modify_time;
2993 file_time_to_dos_date_time(&url_entry->access_time, &url_entry->sync_date, &url_entry->sync_time);
2994 file_time_to_dos_date_time(&expire_time, &url_entry->expire_date, &url_entry->expire_time);
2995 file_time_to_dos_date_time(&file_attr.ftLastWriteTime, &url_entry->write_date, &url_entry->write_time);
2998 url_entry->unk1 = 0;
2999 url_entry->unk2 = 0;
3000 url_entry->unk3 = 0x60;
3001 url_entry->unk4 = 0;
3002 url_entry->unk5 = 0x1010;
3003 url_entry->unk7 = 0;
3004 url_entry->unk8 = 0;
3007 strcpy((LPSTR)url_entry + url_entry->url_off, url);
3009 strcpy((LPSTR)((LPBYTE)url_entry + file_name_off), local_file_name);
3011 memcpy((LPBYTE)url_entry + header_info_off, header_info, header_size);
3013 strcpy((LPSTR)((LPBYTE)url_entry + file_ext_off), file_ext);
3015 error = urlcache_hash_entry_create(header, url, url_entry_offset, HASHTABLE_URL);
3016 while(error == ERROR_HANDLE_DISK_FULL) {
3017 error = cache_container_clean_index(container, &header);
3018 if(error == ERROR_SUCCESS) {
3019 url_entry = (entry_url *)((LPBYTE)header + url_entry_offset);
3020 error = urlcache_hash_entry_create(header, url,
3021 url_entry_offset, HASHTABLE_URL);
3024 if(error != ERROR_SUCCESS) {
3025 urlcache_entry_free(header, &url_entry->header);
3026 cache_container_unlock_index(container, header);
3027 SetLastError(error);
3031 if(url_entry->cache_dir < header->dirs_no)
3032 header->directory_data[url_entry->cache_dir].files_no++;
3033 if(entry_type & STICKY_CACHE_ENTRY)
3034 header->exempt_usage.QuadPart += file_size.QuadPart;
3036 header->cache_usage.QuadPart += file_size.QuadPart;
3037 if(header->cache_usage.QuadPart+header->exempt_usage.QuadPart > header->cache_limit.QuadPart)
3038 handle_full_cache();
3040 cache_container_unlock_index(container, header);
3044 /***********************************************************************
3045 * CommitUrlCacheEntryA (WININET.@)
3047 BOOL WINAPI CommitUrlCacheEntryA(LPCSTR lpszUrlName, LPCSTR lpszLocalFileName,
3048 FILETIME ExpireTime, FILETIME LastModifiedTime, DWORD CacheEntryType,
3049 LPBYTE lpHeaderInfo, DWORD dwHeaderSize, LPCSTR lpszFileExtension, LPCSTR lpszOriginalUrl)
3051 WCHAR *file_name = NULL;
3054 if(lpszLocalFileName) {
3055 file_name = heap_strdupAtoW(lpszLocalFileName);
3060 ret = urlcache_entry_commit(lpszUrlName, file_name, ExpireTime, LastModifiedTime,
3061 CacheEntryType, lpHeaderInfo, dwHeaderSize, lpszFileExtension, lpszOriginalUrl);
3062 heap_free(file_name);
3066 /***********************************************************************
3067 * CommitUrlCacheEntryW (WININET.@)
3069 BOOL WINAPI CommitUrlCacheEntryW(LPCWSTR lpszUrlName, LPCWSTR lpszLocalFileName,
3070 FILETIME ExpireTime, FILETIME LastModifiedTime, DWORD CacheEntryType,
3071 LPWSTR lpHeaderInfo, DWORD dwHeaderSize, LPCWSTR lpszFileExtension, LPCWSTR lpszOriginalUrl)
3073 char *url, *original_url=NULL, *file_ext=NULL, *header_info=NULL;
3076 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
3080 header_info = heap_strdupWtoUTF8(lpHeaderInfo);
3085 dwHeaderSize = strlen(header_info);
3088 if(lpszFileExtension) {
3089 file_ext = heap_strdupWtoA(lpszFileExtension);
3092 heap_free(header_info);
3097 if(lpszOriginalUrl && !urlcache_encode_url_alloc(lpszOriginalUrl, &original_url)) {
3099 heap_free(header_info);
3100 heap_free(file_ext);
3103 ret = urlcache_entry_commit(url, lpszLocalFileName, ExpireTime, LastModifiedTime,
3104 CacheEntryType, (BYTE*)header_info, dwHeaderSize, file_ext, original_url);
3106 heap_free(header_info);
3107 heap_free(file_ext);
3108 heap_free(original_url);
3112 /***********************************************************************
3113 * ReadUrlCacheEntryStream (WININET.@)
3116 BOOL WINAPI ReadUrlCacheEntryStream(
3117 IN HANDLE hUrlCacheStream,
3118 IN DWORD dwLocation,
3119 IN OUT LPVOID lpBuffer,
3120 IN OUT LPDWORD lpdwLen,
3124 /* Get handle to file from 'stream' */
3125 stream_handle *pStream = (stream_handle*)hUrlCacheStream;
3127 if (dwReserved != 0)
3129 ERR("dwReserved != 0\n");
3130 SetLastError(ERROR_INVALID_PARAMETER);
3134 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->url, INTERNET_MAX_URL_LENGTH))
3136 SetLastError(ERROR_INVALID_HANDLE);
3140 if (SetFilePointer(pStream->file, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
3142 return ReadFile(pStream->file, lpBuffer, *lpdwLen, lpdwLen, NULL);
3145 /***********************************************************************
3146 * RetrieveUrlCacheEntryStreamA (WININET.@)
3149 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(LPCSTR lpszUrlName,
3150 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
3151 LPDWORD lpdwCacheEntryInfoBufferSize, BOOL fRandomRead, DWORD dwReserved)
3153 /* NOTE: this is not the same as the way that the native
3154 * version allocates 'stream' handles. I did it this way
3155 * as it is much easier and no applications should depend
3156 * on this behaviour. (Native version appears to allocate
3157 * indices into a table)
3159 stream_handle *stream;
3162 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
3163 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved);
3165 if(!RetrieveUrlCacheEntryFileA(lpszUrlName, lpCacheEntryInfo,
3166 lpdwCacheEntryInfoBufferSize, dwReserved))
3169 file = CreateFileA(lpCacheEntryInfo->lpszLocalFileName, GENERIC_READ, FILE_SHARE_READ,
3170 NULL, OPEN_EXISTING, fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0, NULL);
3171 if(file == INVALID_HANDLE_VALUE) {
3172 UnlockUrlCacheEntryFileA(lpszUrlName, 0);
3176 /* allocate handle storage space */
3177 stream = heap_alloc(sizeof(stream_handle) + strlen(lpszUrlName) * sizeof(CHAR));
3180 UnlockUrlCacheEntryFileA(lpszUrlName, 0);
3181 SetLastError(ERROR_OUTOFMEMORY);
3185 stream->file = file;
3186 strcpy(stream->url, lpszUrlName);
3190 /***********************************************************************
3191 * RetrieveUrlCacheEntryStreamW (WININET.@)
3194 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(LPCWSTR lpszUrlName,
3195 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3196 LPDWORD lpdwCacheEntryInfoBufferSize, BOOL fRandomRead, DWORD dwReserved)
3199 /* NOTE: this is not the same as the way that the native
3200 * version allocates 'stream' handles. I did it this way
3201 * as it is much easier and no applications should depend
3202 * on this behaviour. (Native version appears to allocate
3203 * indices into a table)
3205 stream_handle *stream;
3208 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3209 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved);
3211 if(!(len = urlcache_encode_url(lpszUrlName, NULL, 0)))
3214 if(!RetrieveUrlCacheEntryFileW(lpszUrlName, lpCacheEntryInfo,
3215 lpdwCacheEntryInfoBufferSize, dwReserved))
3218 file = CreateFileW(lpCacheEntryInfo->lpszLocalFileName, GENERIC_READ, FILE_SHARE_READ,
3219 NULL, OPEN_EXISTING, fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0, NULL);
3220 if(file == INVALID_HANDLE_VALUE) {
3221 UnlockUrlCacheEntryFileW(lpszUrlName, 0);
3225 /* allocate handle storage space */
3226 stream = heap_alloc(sizeof(stream_handle) + len*sizeof(WCHAR));
3229 UnlockUrlCacheEntryFileW(lpszUrlName, 0);
3230 SetLastError(ERROR_OUTOFMEMORY);
3234 stream->file = file;
3235 if(!urlcache_encode_url(lpszUrlName, stream->url, len)) {
3237 UnlockUrlCacheEntryFileW(lpszUrlName, 0);
3244 /***********************************************************************
3245 * UnlockUrlCacheEntryStream (WININET.@)
3248 BOOL WINAPI UnlockUrlCacheEntryStream(
3249 IN HANDLE hUrlCacheStream,
3253 stream_handle *pStream = (stream_handle*)hUrlCacheStream;
3255 if (dwReserved != 0)
3257 ERR("dwReserved != 0\n");
3258 SetLastError(ERROR_INVALID_PARAMETER);
3262 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->url, INTERNET_MAX_URL_LENGTH))
3264 SetLastError(ERROR_INVALID_HANDLE);
3268 if (!UnlockUrlCacheEntryFileA(pStream->url, 0))
3271 CloseHandle(pStream->file);
3277 /***********************************************************************
3278 * DeleteUrlCacheEntryA (WININET.@)
3281 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3283 cache_container *pContainer;
3284 urlcache_header *pHeader;
3285 struct hash_entry *pHashEntry;
3289 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3291 error = cache_containers_find(lpszUrlName, &pContainer);
3292 if (error != ERROR_SUCCESS)
3294 SetLastError(error);
3298 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3299 if (error != ERROR_SUCCESS)
3301 SetLastError(error);
3305 if (!(pHeader = cache_container_lock_index(pContainer)))
3308 if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
3310 cache_container_unlock_index(pContainer, pHeader);
3311 TRACE("entry %s not found!\n", lpszUrlName);
3312 SetLastError(ERROR_FILE_NOT_FOUND);
3316 ret = urlcache_entry_delete(pContainer, pHeader, pHashEntry);
3318 cache_container_unlock_index(pContainer, pHeader);
3323 /***********************************************************************
3324 * DeleteUrlCacheEntryW (WININET.@)
3327 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3332 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
3335 ret = DeleteUrlCacheEntryA(url);
3340 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3342 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3346 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3348 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3352 /***********************************************************************
3353 * CreateCacheContainerA (WININET.@)
3355 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3356 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3358 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3359 d1, d2, d3, d4, d5, d6, d7, d8);
3363 /***********************************************************************
3364 * CreateCacheContainerW (WININET.@)
3366 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3367 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3369 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3370 d1, d2, d3, d4, d5, d6, d7, d8);
3374 /***********************************************************************
3375 * FindFirstUrlCacheContainerA (WININET.@)
3377 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3379 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3383 /***********************************************************************
3384 * FindFirstUrlCacheContainerW (WININET.@)
3386 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3388 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3392 /***********************************************************************
3393 * FindNextUrlCacheContainerA (WININET.@)
3395 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3397 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3401 /***********************************************************************
3402 * FindNextUrlCacheContainerW (WININET.@)
3404 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3406 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3410 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3411 LPCSTR lpszUrlSearchPattern,
3415 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3416 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3418 LPDWORD pcbReserved2,
3422 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3423 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3424 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3425 SetLastError(ERROR_FILE_NOT_FOUND);
3429 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3430 LPCWSTR lpszUrlSearchPattern,
3434 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3435 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3437 LPDWORD pcbReserved2,
3441 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3442 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3443 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3444 SetLastError(ERROR_FILE_NOT_FOUND);
3448 /***********************************************************************
3449 * FindFirstUrlCacheEntryA (WININET.@)
3452 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3453 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3455 find_handle *pEntryHandle;
3457 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3459 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3463 pEntryHandle->magic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3464 if (lpszUrlSearchPattern)
3466 pEntryHandle->url_search_pattern = heap_strdupA(lpszUrlSearchPattern);
3467 if (!pEntryHandle->url_search_pattern)
3469 heap_free(pEntryHandle);
3474 pEntryHandle->url_search_pattern = NULL;
3475 pEntryHandle->container_idx = 0;
3476 pEntryHandle->hash_table_idx = 0;
3477 pEntryHandle->hash_entry_idx = 0;
3479 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3481 heap_free(pEntryHandle);
3484 return pEntryHandle;
3487 /***********************************************************************
3488 * FindFirstUrlCacheEntryW (WININET.@)
3491 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3492 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3494 find_handle *pEntryHandle;
3496 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3498 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3502 pEntryHandle->magic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3503 if (lpszUrlSearchPattern)
3505 pEntryHandle->url_search_pattern = heap_strdupWtoA(lpszUrlSearchPattern);
3506 if (!pEntryHandle->url_search_pattern)
3508 heap_free(pEntryHandle);
3513 pEntryHandle->url_search_pattern = NULL;
3514 pEntryHandle->container_idx = 0;
3515 pEntryHandle->hash_table_idx = 0;
3516 pEntryHandle->hash_entry_idx = 0;
3518 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3520 heap_free(pEntryHandle);
3523 return pEntryHandle;
3526 static BOOL urlcache_find_next_entry(
3528 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3529 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3532 find_handle *pEntryHandle = (find_handle*)hEnumHandle;
3533 cache_container *pContainer;
3535 if (pEntryHandle->magic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3537 SetLastError(ERROR_INVALID_HANDLE);
3541 for (; cache_containers_enum(pEntryHandle->url_search_pattern, pEntryHandle->container_idx, &pContainer);
3542 pEntryHandle->container_idx++, pEntryHandle->hash_table_idx = 0)
3544 urlcache_header *pHeader;
3545 entry_hash_table *pHashTableEntry;
3548 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3549 if (error != ERROR_SUCCESS)
3551 SetLastError(error);
3555 if (!(pHeader = cache_container_lock_index(pContainer)))
3558 for (; urlcache_enum_hash_tables(pHeader, &pEntryHandle->hash_table_idx, &pHashTableEntry);
3559 pEntryHandle->hash_table_idx++, pEntryHandle->hash_entry_idx = 0)
3561 const struct hash_entry *pHashEntry = NULL;
3562 for (; urlcache_enum_hash_table_entries(pHeader, pHashTableEntry, &pEntryHandle->hash_entry_idx, &pHashEntry);
3563 pEntryHandle->hash_entry_idx++)
3565 const entry_url *pUrlEntry;
3566 const entry_header *pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
3568 if (pEntry->signature != URL_SIGNATURE)
3571 pUrlEntry = (const entry_url *)pEntry;
3572 TRACE("Found URL: %s\n",
3573 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->url_off));
3574 TRACE("Header info: %s\n",
3575 debugstr_an((LPCSTR)pUrlEntry + pUrlEntry->header_info_off,
3576 pUrlEntry->header_info_size));
3578 error = urlcache_copy_entry(
3581 lpNextCacheEntryInfo,
3582 lpdwNextCacheEntryInfoBufferSize,
3585 if (error != ERROR_SUCCESS)
3587 cache_container_unlock_index(pContainer, pHeader);
3588 SetLastError(error);
3591 if(pUrlEntry->local_name_off)
3592 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->local_name_off));
3594 /* increment the current index so that next time the function
3595 * is called the next entry is returned */
3596 pEntryHandle->hash_entry_idx++;
3597 cache_container_unlock_index(pContainer, pHeader);
3602 cache_container_unlock_index(pContainer, pHeader);
3605 SetLastError(ERROR_NO_MORE_ITEMS);
3609 /***********************************************************************
3610 * FindNextUrlCacheEntryA (WININET.@)
3612 BOOL WINAPI FindNextUrlCacheEntryA(
3614 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3615 LPDWORD lpdwNextCacheEntryInfoBufferSize)
3617 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3619 return urlcache_find_next_entry(hEnumHandle, lpNextCacheEntryInfo,
3620 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
3623 /***********************************************************************
3624 * FindNextUrlCacheEntryW (WININET.@)
3626 BOOL WINAPI FindNextUrlCacheEntryW(
3628 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3629 LPDWORD lpdwNextCacheEntryInfoBufferSize
3632 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3634 return urlcache_find_next_entry(hEnumHandle,
3635 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
3636 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
3639 /***********************************************************************
3640 * FindCloseUrlCache (WININET.@)
3642 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3644 find_handle *pEntryHandle = (find_handle*)hEnumHandle;
3646 TRACE("(%p)\n", hEnumHandle);
3648 if (!pEntryHandle || pEntryHandle->magic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3650 SetLastError(ERROR_INVALID_HANDLE);
3654 pEntryHandle->magic = 0;
3655 heap_free(pEntryHandle->url_search_pattern);
3656 heap_free(pEntryHandle);
3660 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3661 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3663 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3664 dwSearchCondition, lpGroupId, lpReserved);
3668 BOOL WINAPI FindNextUrlCacheEntryExA(
3670 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3671 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3673 LPDWORD pcbReserved2,
3677 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3678 lpReserved, pcbReserved2, lpReserved3);
3682 BOOL WINAPI FindNextUrlCacheEntryExW(
3684 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3685 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3687 LPDWORD pcbReserved2,
3691 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3692 lpReserved, pcbReserved2, lpReserved3);
3696 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3698 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3702 /***********************************************************************
3703 * CreateUrlCacheGroup (WININET.@)
3706 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3708 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3712 /***********************************************************************
3713 * DeleteUrlCacheGroup (WININET.@)
3716 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3718 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3719 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3723 /***********************************************************************
3724 * DeleteWpadCacheForNetworks (WININET.@)
3725 * Undocumented, added in IE8
3727 BOOL WINAPI DeleteWpadCacheForNetworks(DWORD unk1)
3729 FIXME("(%d) stub\n", unk1);
3733 /***********************************************************************
3734 * SetUrlCacheEntryGroupA (WININET.@)
3737 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR 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_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3743 pbGroupAttributes, cbGroupAttributes, lpReserved);
3744 SetLastError(ERROR_FILE_NOT_FOUND);
3748 /***********************************************************************
3749 * SetUrlCacheEntryGroupW (WININET.@)
3752 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3753 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3756 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3757 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3758 pbGroupAttributes, cbGroupAttributes, lpReserved);
3759 SetLastError(ERROR_FILE_NOT_FOUND);
3763 /***********************************************************************
3764 * GetUrlCacheConfigInfoW (WININET.@)
3766 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3768 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3769 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3773 /***********************************************************************
3774 * GetUrlCacheConfigInfoA (WININET.@)
3776 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3778 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3779 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3783 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3784 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3785 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3787 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3788 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3789 lpdwGroupInfo, lpReserved);
3793 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3794 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3795 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3797 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3798 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3799 lpdwGroupInfo, lpReserved);
3803 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3804 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3806 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3807 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3811 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3812 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3814 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3815 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3819 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3821 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3825 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3827 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3831 /***********************************************************************
3832 * DeleteIE3Cache (WININET.@)
3834 * Deletes the files used by the IE3 URL caching system.
3837 * hWnd [I] A dummy window.
3838 * hInst [I] Instance of process calling the function.
3839 * lpszCmdLine [I] Options used by function.
3840 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3842 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3844 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3848 static BOOL urlcache_entry_is_expired(const entry_url *pUrlEntry,
3849 FILETIME *pftLastModified)
3852 FILETIME now, expired;
3854 *pftLastModified = pUrlEntry->modification_time;
3855 GetSystemTimeAsFileTime(&now);
3856 dos_date_time_to_file_time(pUrlEntry->expire_date,
3857 pUrlEntry->expire_time, &expired);
3858 /* If the expired time is 0, it's interpreted as not expired */
3859 if (!expired.dwLowDateTime && !expired.dwHighDateTime)
3862 ret = CompareFileTime(&expired, &now) < 0;
3866 /***********************************************************************
3867 * IsUrlCacheEntryExpiredA (WININET.@)
3871 * dwFlags [I] Unknown
3872 * pftLastModified [O] Last modified time
3874 BOOL WINAPI IsUrlCacheEntryExpiredA(LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified)
3876 urlcache_header *pHeader;
3877 struct hash_entry *pHashEntry;
3878 const entry_header *pEntry;
3879 const entry_url * pUrlEntry;
3880 cache_container *pContainer;
3883 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3885 if (!url || !pftLastModified)
3888 FIXME("unknown flags 0x%08x\n", dwFlags);
3890 /* Any error implies that the URL is expired, i.e. not in the cache */
3891 if (cache_containers_find(url, &pContainer))
3893 memset(pftLastModified, 0, sizeof(*pftLastModified));
3897 if (cache_container_open_index(pContainer, MIN_BLOCK_NO))
3899 memset(pftLastModified, 0, sizeof(*pftLastModified));
3903 if (!(pHeader = cache_container_lock_index(pContainer)))
3905 memset(pftLastModified, 0, sizeof(*pftLastModified));
3909 if (!urlcache_find_hash_entry(pHeader, url, &pHashEntry))
3911 cache_container_unlock_index(pContainer, pHeader);
3912 memset(pftLastModified, 0, sizeof(*pftLastModified));
3913 TRACE("entry %s not found!\n", url);
3917 pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
3918 if (pEntry->signature != URL_SIGNATURE)
3920 cache_container_unlock_index(pContainer, pHeader);
3921 memset(pftLastModified, 0, sizeof(*pftLastModified));
3922 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
3926 pUrlEntry = (const entry_url *)pEntry;
3927 expired = urlcache_entry_is_expired(pUrlEntry, pftLastModified);
3929 cache_container_unlock_index(pContainer, pHeader);
3934 /***********************************************************************
3935 * IsUrlCacheEntryExpiredW (WININET.@)
3939 * dwFlags [I] Unknown
3940 * pftLastModified [O] Last modified time
3942 BOOL WINAPI IsUrlCacheEntryExpiredW(LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified)
3947 if(!urlcache_encode_url_alloc(url, &encoded_url))
3950 ret = IsUrlCacheEntryExpiredA(encoded_url, dwFlags, pftLastModified);
3951 heap_free(encoded_url);
3955 /***********************************************************************
3956 * GetDiskInfoA (WININET.@)
3958 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
3961 ULARGE_INTEGER bytes_free, bytes_total;
3963 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
3967 SetLastError(ERROR_INVALID_PARAMETER);
3971 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
3973 if (cluster_size) *cluster_size = 1;
3974 if (free) *free = bytes_free.QuadPart;
3975 if (total) *total = bytes_total.QuadPart;
3980 /***********************************************************************
3981 * RegisterUrlCacheNotification (WININET.@)
3983 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
3985 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
3989 /***********************************************************************
3990 * IncrementUrlCacheHeaderData (WININET.@)
3992 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
3994 FIXME("(%u, %p)\n", index, data);
3998 /***********************************************************************
3999 * RunOnceUrlCache (WININET.@)
4002 DWORD WINAPI RunOnceUrlCache(HWND hwnd, HINSTANCE hinst, LPSTR cmd, int cmdshow)
4004 FIXME("(%p, %p, %s, %d): stub\n", hwnd, hinst, debugstr_a(cmd), cmdshow);
4008 BOOL init_urlcache(void)
4010 dll_unload_event = CreateEventW(NULL, FALSE, FALSE, NULL);
4011 if(!dll_unload_event)
4014 free_cache_running = CreateSemaphoreW(NULL, 1, 1, NULL);
4015 if(!free_cache_running) {
4016 CloseHandle(dll_unload_event);
4020 cache_containers_init();
4024 void free_urlcache(void)
4026 SetEvent(dll_unload_event);
4027 WaitForSingleObject(free_cache_running, INFINITE);
4028 ReleaseSemaphore(free_cache_running, 1, NULL);
4029 CloseHandle(free_cache_running);
4030 CloseHandle(dll_unload_event);
4032 cache_containers_free();
4035 /***********************************************************************
4036 * LoadUrlCacheContent (WININET.@)
4038 BOOL WINAPI LoadUrlCacheContent(void)