wininet: Support Punycode in DeleteUrlCacheEntryW.
[wine/multimedia.git] / dlls / wininet / urlcache.c
blob0d8c9aa4fa48bf83243b7d823d528230fbef314f
1 /*
2 * Wininet - Url Cache functions
4 * Copyright 2001,2002 CodeWeavers
5 * Copyright 2003-2008 Robert Shearman
7 * Eric Kohl
8 * Aric Stewart
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #include "config.h"
26 #include "wine/port.h"
28 #define NONAMELESSUNION
29 #define NONAMELESSSTRUCT
31 #if defined(__MINGW32__) || defined (_MSC_VER)
32 #include <ws2tcpip.h>
33 #endif
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sys/types.h>
40 #ifdef HAVE_SYS_SOCKET_H
41 # include <sys/socket.h>
42 #endif
43 #include <time.h>
45 #include "windef.h"
46 #include "winbase.h"
47 #include "winuser.h"
48 #include "wininet.h"
49 #include "winineti.h"
50 #include "winerror.h"
51 #include "winreg.h"
52 #include "shlwapi.h"
53 #include "shlobj.h"
54 #include "shellapi.h"
56 #include "internet.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";
66 #ifndef CHAR_BIT
67 #define CHAR_BIT (8 * sizeof(CHAR))
68 #endif
70 #define ENTRY_START_OFFSET 0x4000
71 #define DIR_LENGTH 8
72 #define MAX_DIR_NO 0x20
73 #define BLOCKSIZE 128
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
109 typedef struct
111 DWORD signature;
112 DWORD blocks_used; /* number of 128byte blocks used by this entry */
113 } entry_header;
115 typedef struct
117 entry_header header;
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 */
140 WORD write_date;
141 WORD write_time;
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) */
150 } entry_url;
152 struct hash_entry
154 DWORD key;
155 DWORD offset;
158 typedef struct
160 entry_header header;
161 DWORD next;
162 DWORD id;
163 struct hash_entry hash_table[HASHTABLE_SIZE];
164 } entry_hash_table;
166 typedef struct
168 char signature[28];
169 DWORD size;
170 DWORD hash_table_off;
171 DWORD capacity_in_blocks;
172 DWORD blocks_in_use;
173 DWORD unk1;
174 ULARGE_INTEGER cache_limit;
175 ULARGE_INTEGER cache_usage;
176 ULARGE_INTEGER exempt_usage;
177 DWORD dirs_no;
178 struct _directory_data
180 DWORD files_no;
181 char name[DIR_LENGTH];
182 } directory_data[MAX_DIR_NO];
183 DWORD options[0x21];
184 BYTE allocation_table[ALLOCATION_TABLE_SIZE];
185 } urlcache_header;
187 typedef struct
189 HANDLE file;
190 CHAR url[1];
191 } stream_handle;
193 typedef struct
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;
202 } cache_container;
204 typedef struct
206 DWORD magic;
207 char *url_search_pattern;
208 DWORD container_idx;
209 DWORD hash_table_idx;
210 DWORD hash_entry_idx;
211 } find_handle;
213 /* List of all containers available */
214 static struct list UrlContainers = LIST_INIT(UrlContainers);
216 /***********************************************************************
217 * urlcache_block_is_free (Internal)
219 * Is the specified block number free?
221 * RETURNS
222 * zero if free
223 * non-zero otherwise
226 static inline BYTE urlcache_block_is_free(BYTE *allocation_table, DWORD block_number)
228 BYTE mask = 1 << (block_number%CHAR_BIT);
229 return (allocation_table[block_number/CHAR_BIT] & mask) == 0;
232 /***********************************************************************
233 * urlcache_block_free (Internal)
235 * Marks the specified block as free
237 * CAUTION
238 * this function is not updating used blocks count
240 * RETURNS
241 * nothing
244 static inline void urlcache_block_free(BYTE *allocation_table, DWORD block_number)
246 BYTE mask = ~(1 << (block_number%CHAR_BIT));
247 allocation_table[block_number/CHAR_BIT] &= mask;
250 /***********************************************************************
251 * urlcache_block_alloc (Internal)
253 * Marks the specified block as allocated
255 * CAUTION
256 * this function is not updating used blocks count
258 * RETURNS
259 * nothing
262 static inline void urlcache_block_alloc(BYTE *allocation_table, DWORD block_number)
264 BYTE mask = 1 << (block_number%CHAR_BIT);
265 allocation_table[block_number/CHAR_BIT] |= mask;
268 /***********************************************************************
269 * urlcache_entry_alloc (Internal)
271 * Finds and allocates the first block of free space big enough and
272 * sets entry to point to it.
274 * RETURNS
275 * ERROR_SUCCESS when free memory block was found
276 * Any other Win32 error code if the entry could not be added
279 static DWORD urlcache_entry_alloc(urlcache_header *header, DWORD blocks_needed, entry_header **entry)
281 DWORD block, block_size;
283 for(block=0; block<header->capacity_in_blocks; block+=block_size+1)
285 block_size = 0;
286 while(block_size<blocks_needed && block_size+block<header->capacity_in_blocks
287 && urlcache_block_is_free(header->allocation_table, block+block_size))
288 block_size++;
290 if(block_size == blocks_needed)
292 DWORD index;
294 TRACE("Found free blocks starting at no. %d (0x%x)\n", block, ENTRY_START_OFFSET+block*BLOCKSIZE);
296 for(index=0; index<blocks_needed; index++)
297 urlcache_block_alloc(header->allocation_table, block+index);
299 *entry = (entry_header*)((BYTE*)header+ENTRY_START_OFFSET+block*BLOCKSIZE);
300 for(index=0; index<blocks_needed*BLOCKSIZE/sizeof(DWORD); index++)
301 ((DWORD*)*entry)[index] = 0xdeadbeef;
302 (*entry)->blocks_used = blocks_needed;
304 header->blocks_in_use += blocks_needed;
305 return ERROR_SUCCESS;
309 return ERROR_HANDLE_DISK_FULL;
312 /***********************************************************************
313 * urlcache_entry_free (Internal)
315 * Deletes the specified entry and frees the space allocated to it
317 * RETURNS
318 * TRUE if it succeeded
319 * FALSE if it failed
322 static BOOL urlcache_entry_free(urlcache_header *header, entry_header *entry)
324 DWORD start_block, block;
326 /* update allocation table */
327 start_block = ((DWORD)((BYTE*)entry - (BYTE*)header) - ENTRY_START_OFFSET) / BLOCKSIZE;
328 for(block = start_block; block < start_block+entry->blocks_used; block++)
329 urlcache_block_free(header->allocation_table, block);
331 header->blocks_in_use -= entry->blocks_used;
332 return TRUE;
335 /***********************************************************************
336 * urlcache_create_hash_table (Internal)
338 * Creates a new hash table in free space and adds it to the chain of existing
339 * hash tables.
341 * RETURNS
342 * ERROR_SUCCESS if the hash table was created
343 * ERROR_DISK_FULL if the hash table could not be created
346 static DWORD urlcache_create_hash_table(urlcache_header *header, entry_hash_table *hash_table_prev, entry_hash_table **hash_table)
348 DWORD dwOffset, error;
349 int i;
351 if((error = urlcache_entry_alloc(header, 0x20, (entry_header**)hash_table)) != ERROR_SUCCESS)
352 return error;
354 dwOffset = (BYTE*)*hash_table-(BYTE*)header;
356 if(hash_table_prev)
357 hash_table_prev->next = dwOffset;
358 else
359 header->hash_table_off = dwOffset;
361 (*hash_table)->header.signature = HASH_SIGNATURE;
362 (*hash_table)->next = 0;
363 (*hash_table)->id = hash_table_prev ? hash_table_prev->id+1 : 0;
364 for(i = 0; i < HASHTABLE_SIZE; i++) {
365 (*hash_table)->hash_table[i].offset = HASHTABLE_FREE;
366 (*hash_table)->hash_table[i].key = HASHTABLE_FREE;
368 return ERROR_SUCCESS;
371 /***********************************************************************
372 * cache_container_create_object_name (Internal)
374 * Converts a path to a name suitable for use as a Win32 object name.
375 * Replaces '\\' characters in-place with the specified character
376 * (usually '_' or '!')
378 * RETURNS
379 * nothing
382 static void cache_container_create_object_name(LPWSTR lpszPath, WCHAR replace)
384 for (; *lpszPath; lpszPath++)
386 if (*lpszPath == '\\')
387 *lpszPath = replace;
391 /* Caller must hold container lock */
392 static HANDLE cache_container_map_index(HANDLE file, const WCHAR *path, DWORD size, BOOL *validate)
394 static const WCHAR mapping_name_format[]
395 = {'%','s','i','n','d','e','x','.','d','a','t','_','%','l','u',0};
396 WCHAR mapping_name[MAX_PATH];
397 HANDLE mapping;
399 wsprintfW(mapping_name, mapping_name_format, path, size);
400 cache_container_create_object_name(mapping_name, '_');
402 mapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, mapping_name);
403 if(mapping) {
404 if(validate) *validate = FALSE;
405 return mapping;
408 if(validate) *validate = TRUE;
409 return CreateFileMappingW(file, NULL, PAGE_READWRITE, 0, 0, mapping_name);
412 /* Caller must hold container lock */
413 static DWORD cache_container_set_size(cache_container *container, HANDLE file, DWORD blocks_no)
415 static const WCHAR cache_content_key[] = {'S','o','f','t','w','a','r','e','\\',
416 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
417 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
418 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s','\\',
419 'C','a','c','h','e','\\','C','o','n','t','e','n','t',0};
420 static const WCHAR cache_limit[] = {'C','a','c','h','e','L','i','m','i','t',0};
422 DWORD file_size = FILE_SIZE(blocks_no);
423 WCHAR dir_path[MAX_PATH], *dir_name;
424 entry_hash_table *hashtable_entry;
425 urlcache_header *header;
426 HANDLE mapping;
427 FILETIME ft;
428 HKEY key;
429 int i, j;
431 if(SetFilePointer(file, file_size, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
432 return GetLastError();
434 if(!SetEndOfFile(file))
435 return GetLastError();
437 mapping = cache_container_map_index(file, container->path, file_size, NULL);
438 if(!mapping)
439 return GetLastError();
441 header = MapViewOfFile(mapping, FILE_MAP_WRITE, 0, 0, 0);
442 if(!header) {
443 CloseHandle(mapping);
444 return GetLastError();
447 if(blocks_no != MIN_BLOCK_NO) {
448 if(file_size > header->size)
449 memset((char*)header+header->size, 0, file_size-header->size);
450 header->size = file_size;
451 header->capacity_in_blocks = blocks_no;
453 UnmapViewOfFile(header);
454 CloseHandle(container->mapping);
455 container->mapping = mapping;
456 container->file_size = file_size;
457 return ERROR_SUCCESS;
460 memset(header, 0, file_size);
461 /* First set some constants and defaults in the header */
462 memcpy(header->signature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1);
463 memcpy(header->signature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1);
464 header->size = file_size;
465 header->capacity_in_blocks = blocks_no;
466 /* 127MB - taken from default for Windows 2000 */
467 header->cache_limit.QuadPart = 0x07ff5400;
468 /* Copied from a Windows 2000 cache index */
469 header->dirs_no = container->default_entry_type==NORMAL_CACHE_ENTRY ? 4 : 0;
471 /* If the registry has a cache size set, use the registry value */
472 if(RegOpenKeyW(HKEY_CURRENT_USER, cache_content_key, &key) == ERROR_SUCCESS) {
473 DWORD dw, len = sizeof(dw), keytype;
475 if(RegQueryValueExW(key, cache_limit, NULL, &keytype, (BYTE*)&dw, &len) == ERROR_SUCCESS &&
476 keytype == REG_DWORD)
477 header->cache_limit.QuadPart = (ULONGLONG)dw * 1024;
478 RegCloseKey(key);
481 urlcache_create_hash_table(header, NULL, &hashtable_entry);
483 /* Last step - create the directories */
484 strcpyW(dir_path, container->path);
485 dir_name = dir_path + strlenW(dir_path);
486 dir_name[8] = 0;
488 GetSystemTimeAsFileTime(&ft);
490 for(i=0; i<header->dirs_no; ++i) {
491 header->directory_data[i].files_no = 0;
492 for(j=0;; ++j) {
493 ULONGLONG n = ft.dwHighDateTime;
494 int k;
496 /* Generate a file name to attempt to create.
497 * This algorithm will create what will appear
498 * to be random and unrelated directory names
499 * of up to 9 characters in length.
501 n <<= 32;
502 n += ft.dwLowDateTime;
503 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
505 for(k = 0; k < 8; ++k) {
506 int r = (n % 36);
508 /* Dividing by a prime greater than 36 helps
509 * with the appearance of randomness
511 n /= 37;
513 if(r < 10)
514 dir_name[k] = '0' + r;
515 else
516 dir_name[k] = 'A' + (r - 10);
519 if(CreateDirectoryW(dir_path, 0)) {
520 /* The following is OK because we generated an
521 * 8 character directory name made from characters
522 * [A-Z0-9], which are equivalent for all code
523 * pages and for UTF-16
525 for (k = 0; k < 8; ++k)
526 header->directory_data[i].name[k] = dir_name[k];
527 break;
528 }else if(j >= 255) {
529 /* Give up. The most likely cause of this
530 * is a full disk, but whatever the cause
531 * is, it should be more than apparent that
532 * we won't succeed.
534 UnmapViewOfFile(header);
535 CloseHandle(mapping);
536 return GetLastError();
541 UnmapViewOfFile(header);
542 CloseHandle(container->mapping);
543 container->mapping = mapping;
544 container->file_size = file_size;
545 return ERROR_SUCCESS;
548 static BOOL cache_container_is_valid(urlcache_header *header, DWORD file_size)
550 DWORD allocation_size, count_bits, i;
552 if(file_size < FILE_SIZE(MIN_BLOCK_NO))
553 return FALSE;
555 if(file_size != header->size)
556 return FALSE;
558 if (!memcmp(header->signature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1) &&
559 memcmp(header->signature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1))
560 return FALSE;
562 if(FILE_SIZE(header->capacity_in_blocks) != file_size)
563 return FALSE;
565 allocation_size = 0;
566 for(i=0; i<header->capacity_in_blocks/8; i++) {
567 for(count_bits = header->allocation_table[i]; count_bits!=0; count_bits>>=1) {
568 if(count_bits & 1)
569 allocation_size++;
572 if(allocation_size != header->blocks_in_use)
573 return FALSE;
575 for(; i<ALLOCATION_TABLE_SIZE; i++) {
576 if(header->allocation_table[i])
577 return FALSE;
580 return TRUE;
583 /***********************************************************************
584 * cache_container_open_index (Internal)
586 * Opens the index file and saves mapping handle
588 * RETURNS
589 * ERROR_SUCCESS if succeeded
590 * Any other Win32 error code if failed
593 static DWORD cache_container_open_index(cache_container *container, DWORD blocks_no)
595 static const WCHAR index_dat[] = {'i','n','d','e','x','.','d','a','t',0};
597 HANDLE file;
598 WCHAR index_path[MAX_PATH];
599 DWORD file_size;
600 BOOL validate;
602 WaitForSingleObject(container->mutex, INFINITE);
604 if(container->mapping) {
605 ReleaseMutex(container->mutex);
606 return ERROR_SUCCESS;
609 strcpyW(index_path, container->path);
610 strcatW(index_path, index_dat);
612 file = CreateFileW(index_path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
613 if(file == INVALID_HANDLE_VALUE) {
614 /* Maybe the directory wasn't there? Try to create it */
615 if(CreateDirectoryW(container->path, 0))
616 file = CreateFileW(index_path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
618 if(file == INVALID_HANDLE_VALUE) {
619 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(index_path));
620 ReleaseMutex(container->mutex);
621 return GetLastError();
624 file_size = GetFileSize(file, NULL);
625 if(file_size == INVALID_FILE_SIZE) {
626 CloseHandle(file);
627 ReleaseMutex(container->mutex);
628 return GetLastError();
631 if(blocks_no < MIN_BLOCK_NO)
632 blocks_no = MIN_BLOCK_NO;
633 else if(blocks_no > MAX_BLOCK_NO)
634 blocks_no = MAX_BLOCK_NO;
636 if(file_size < FILE_SIZE(blocks_no)) {
637 DWORD ret = cache_container_set_size(container, file, blocks_no);
638 CloseHandle(file);
639 ReleaseMutex(container->mutex);
640 return ret;
643 container->file_size = file_size;
644 container->mapping = cache_container_map_index(file, container->path, file_size, &validate);
645 CloseHandle(file);
646 if(container->mapping && validate) {
647 urlcache_header *header = MapViewOfFile(container->mapping, FILE_MAP_WRITE, 0, 0, 0);
649 if(header && !cache_container_is_valid(header, file_size)) {
650 WARN("detected old or broken index.dat file\n");
651 UnmapViewOfFile(header);
652 FreeUrlCacheSpaceW(container->path, 100, 0);
653 }else if(header) {
654 UnmapViewOfFile(header);
655 }else {
656 CloseHandle(container->mapping);
657 container->mapping = NULL;
661 if(!container->mapping)
663 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
664 ReleaseMutex(container->mutex);
665 return GetLastError();
668 ReleaseMutex(container->mutex);
669 return ERROR_SUCCESS;
672 /***********************************************************************
673 * cache_container_close_index (Internal)
675 * Closes the index
677 * RETURNS
678 * nothing
681 static void cache_container_close_index(cache_container *pContainer)
683 CloseHandle(pContainer->mapping);
684 pContainer->mapping = NULL;
687 static BOOL cache_containers_add(LPCWSTR cache_prefix,
688 LPCWSTR path, DWORD default_entry_type, LPWSTR mutex_name)
690 cache_container *pContainer = heap_alloc(sizeof(cache_container));
691 int cache_prefix_len = strlenW(cache_prefix);
693 if (!pContainer)
695 return FALSE;
698 pContainer->mapping = NULL;
699 pContainer->file_size = 0;
700 pContainer->default_entry_type = default_entry_type;
702 pContainer->path = heap_strdupW(path);
703 if (!pContainer->path)
705 heap_free(pContainer);
706 return FALSE;
709 pContainer->cache_prefix = heap_alloc((cache_prefix_len + 1) * sizeof(WCHAR));
710 if (!pContainer->cache_prefix)
712 heap_free(pContainer->path);
713 heap_free(pContainer);
714 return FALSE;
717 memcpy(pContainer->cache_prefix, cache_prefix, (cache_prefix_len + 1) * sizeof(WCHAR));
719 CharLowerW(mutex_name);
720 cache_container_create_object_name(mutex_name, '!');
722 if ((pContainer->mutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
724 ERR("couldn't create mutex (error is %d)\n", GetLastError());
725 heap_free(pContainer->path);
726 heap_free(pContainer);
727 return FALSE;
730 list_add_head(&UrlContainers, &pContainer->entry);
732 return TRUE;
735 static void cache_container_delete_container(cache_container *pContainer)
737 list_remove(&pContainer->entry);
739 cache_container_close_index(pContainer);
740 CloseHandle(pContainer->mutex);
741 heap_free(pContainer->path);
742 heap_free(pContainer->cache_prefix);
743 heap_free(pContainer);
746 static void cache_containers_init(void)
748 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
749 static const WCHAR UrlPrefix[] = {0};
750 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
751 static const WCHAR HistoryPrefix[] = {'V','i','s','i','t','e','d',':',0};
752 static const WCHAR CookieSuffix[] = {0};
753 static const WCHAR CookiePrefix[] = {'C','o','o','k','i','e',':',0};
754 static const struct
756 int nFolder; /* CSIDL_* constant */
757 const WCHAR * shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
758 const WCHAR * cache_prefix; /* prefix used to reference the container */
759 DWORD default_entry_type;
760 } DefaultContainerData[] =
762 { CSIDL_INTERNET_CACHE, UrlSuffix, UrlPrefix, NORMAL_CACHE_ENTRY },
763 { CSIDL_HISTORY, HistorySuffix, HistoryPrefix, URLHISTORY_CACHE_ENTRY },
764 { CSIDL_COOKIES, CookieSuffix, CookiePrefix, COOKIE_CACHE_ENTRY },
766 DWORD i;
768 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
770 WCHAR wszCachePath[MAX_PATH];
771 WCHAR wszMutexName[MAX_PATH];
772 int path_len, suffix_len;
773 BOOL def_char;
775 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
777 ERR("Couldn't get path for default container %u\n", i);
778 continue;
780 path_len = strlenW(wszCachePath);
781 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
783 if (path_len + suffix_len + 2 > MAX_PATH)
785 ERR("Path too long\n");
786 continue;
789 wszCachePath[path_len] = '\\';
790 wszCachePath[path_len+1] = 0;
792 strcpyW(wszMutexName, wszCachePath);
794 if (suffix_len)
796 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
797 wszCachePath[path_len + suffix_len + 1] = '\\';
798 wszCachePath[path_len + suffix_len + 2] = '\0';
801 if (!WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wszCachePath, path_len,
802 NULL, 0, NULL, &def_char) || def_char)
804 WCHAR tmp[MAX_PATH];
806 /* cannot convert path to ANSI code page */
807 if (!(path_len = GetShortPathNameW(wszCachePath, tmp, MAX_PATH)) ||
808 !WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, tmp, path_len,
809 NULL, 0, NULL, &def_char) || def_char)
810 ERR("Can't create container path accessible by ANSI functions\n");
811 else
812 memcpy(wszCachePath, tmp, (path_len+1)*sizeof(WCHAR));
815 cache_containers_add(DefaultContainerData[i].cache_prefix, wszCachePath,
816 DefaultContainerData[i].default_entry_type, wszMutexName);
820 static void cache_containers_free(void)
822 while(!list_empty(&UrlContainers))
823 cache_container_delete_container(
824 LIST_ENTRY(list_head(&UrlContainers), cache_container, entry)
828 static DWORD cache_containers_find(const char *url, cache_container **ret)
830 cache_container *container;
832 TRACE("searching for prefix for URL: %s\n", debugstr_a(url));
834 if(!url)
835 return ERROR_INVALID_PARAMETER;
837 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
839 int prefix_len = strlen(container->cache_prefix);
841 if(!strncmp(container->cache_prefix, url, prefix_len)) {
842 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
843 *ret = container;
844 return ERROR_SUCCESS;
848 ERR("no container found\n");
849 return ERROR_FILE_NOT_FOUND;
852 static DWORD cache_containers_findW(LPCWSTR url, cache_container **container)
854 char *encoded_url = NULL;
855 DWORD ret;
857 if (url && !(encoded_url = heap_strdupWtoA(url)))
858 return ERROR_OUTOFMEMORY;
860 ret = cache_containers_find(encoded_url, container);
861 heap_free(encoded_url);
862 return ret;
865 static BOOL cache_containers_enum(char *search_pattern, DWORD index, cache_container **ret)
867 DWORD i = 0;
868 cache_container *container;
870 TRACE("searching for prefix: %s\n", debugstr_a(search_pattern));
872 /* non-NULL search pattern only returns one container ever */
873 if (search_pattern && index > 0)
874 return FALSE;
876 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
878 if (search_pattern)
880 if (!strcmp(container->cache_prefix, search_pattern))
882 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
883 *ret = container;
884 return TRUE;
887 else
889 if (i == index)
891 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
892 *ret = container;
893 return TRUE;
896 i++;
898 return FALSE;
901 /***********************************************************************
902 * cache_container_lock_index (Internal)
904 * Locks the index for system-wide exclusive access.
906 * RETURNS
907 * Cache file header if successful
908 * NULL if failed and calls SetLastError.
910 static urlcache_header* cache_container_lock_index(cache_container *pContainer)
912 BYTE index;
913 LPVOID pIndexData;
914 urlcache_header* pHeader;
915 DWORD error;
917 /* acquire mutex */
918 WaitForSingleObject(pContainer->mutex, INFINITE);
920 pIndexData = MapViewOfFile(pContainer->mapping, FILE_MAP_WRITE, 0, 0, 0);
922 if (!pIndexData)
924 ReleaseMutex(pContainer->mutex);
925 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
926 return NULL;
928 pHeader = (urlcache_header*)pIndexData;
930 /* file has grown - we need to remap to prevent us getting
931 * access violations when we try and access beyond the end
932 * of the memory mapped file */
933 if (pHeader->size != pContainer->file_size)
935 UnmapViewOfFile( pHeader );
936 cache_container_close_index(pContainer);
937 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
938 if (error != ERROR_SUCCESS)
940 ReleaseMutex(pContainer->mutex);
941 SetLastError(error);
942 return NULL;
944 pIndexData = MapViewOfFile(pContainer->mapping, FILE_MAP_WRITE, 0, 0, 0);
946 if (!pIndexData)
948 ReleaseMutex(pContainer->mutex);
949 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
950 return NULL;
952 pHeader = (urlcache_header*)pIndexData;
955 TRACE("Signature: %s, file size: %d bytes\n", pHeader->signature, pHeader->size);
957 for (index = 0; index < pHeader->dirs_no; index++)
959 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].name);
962 return pHeader;
965 /***********************************************************************
966 * cache_container_unlock_index (Internal)
969 static BOOL cache_container_unlock_index(cache_container *pContainer, urlcache_header *pHeader)
971 /* release mutex */
972 ReleaseMutex(pContainer->mutex);
973 return UnmapViewOfFile(pHeader);
976 /***********************************************************************
977 * urlcache_create_file_pathW (Internal)
979 * Copies the full path to the specified buffer given the local file
980 * name and the index of the directory it is in. Always sets value in
981 * lpBufferSize to the required buffer size (in bytes).
983 * RETURNS
984 * TRUE if the buffer was big enough
985 * FALSE if the buffer was too small
988 static BOOL urlcache_create_file_pathW(
989 const cache_container *pContainer,
990 const urlcache_header *pHeader,
991 LPCSTR szLocalFileName,
992 BYTE Directory,
993 LPWSTR wszPath,
994 LPLONG lpBufferSize)
996 LONG nRequired;
997 int path_len = strlenW(pContainer->path);
998 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
999 if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->dirs_no)
1001 *lpBufferSize = 0;
1002 return FALSE;
1005 nRequired = (path_len + file_name_len) * sizeof(WCHAR);
1006 if(Directory != CACHE_CONTAINER_NO_SUBDIR)
1007 nRequired += (DIR_LENGTH + 1) * sizeof(WCHAR);
1008 if (nRequired <= *lpBufferSize)
1010 int dir_len;
1012 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
1013 if (Directory != CACHE_CONTAINER_NO_SUBDIR)
1015 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].name, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
1016 wszPath[dir_len + path_len] = '\\';
1017 dir_len++;
1019 else
1021 dir_len = 0;
1023 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len, file_name_len);
1024 *lpBufferSize = nRequired;
1025 return TRUE;
1027 *lpBufferSize = nRequired;
1028 return FALSE;
1031 /***********************************************************************
1032 * urlcache_create_file_pathA (Internal)
1034 * Copies the full path to the specified buffer given the local file
1035 * name and the index of the directory it is in. Always sets value in
1036 * lpBufferSize to the required buffer size.
1038 * RETURNS
1039 * TRUE if the buffer was big enough
1040 * FALSE if the buffer was too small
1043 static BOOL urlcache_create_file_pathA(
1044 const cache_container *pContainer,
1045 const urlcache_header *pHeader,
1046 LPCSTR szLocalFileName,
1047 BYTE Directory,
1048 LPSTR szPath,
1049 LPLONG lpBufferSize)
1051 LONG nRequired;
1052 int path_len, file_name_len, dir_len;
1054 if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->dirs_no)
1056 *lpBufferSize = 0;
1057 return FALSE;
1060 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
1061 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
1062 if (Directory!=CACHE_CONTAINER_NO_SUBDIR)
1063 dir_len = DIR_LENGTH+1;
1064 else
1065 dir_len = 0;
1067 nRequired = (path_len + dir_len + file_name_len) * sizeof(char);
1068 if (nRequired <= *lpBufferSize)
1070 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
1071 if(dir_len) {
1072 memcpy(szPath+path_len, pHeader->directory_data[Directory].name, dir_len-1);
1073 szPath[path_len + dir_len-1] = '\\';
1075 memcpy(szPath + path_len + dir_len, szLocalFileName, file_name_len);
1076 *lpBufferSize = nRequired;
1077 return TRUE;
1079 *lpBufferSize = nRequired;
1080 return FALSE;
1083 /* Just like FileTimeToDosDateTime, except that it also maps the special
1084 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1086 static void file_time_to_dos_date_time(const FILETIME *ft, WORD *fatdate,
1087 WORD *fattime)
1089 if (!ft->dwLowDateTime && !ft->dwHighDateTime)
1090 *fatdate = *fattime = 0;
1091 else
1092 FileTimeToDosDateTime(ft, fatdate, fattime);
1095 /***********************************************************************
1096 * urlcache_delete_file (Internal)
1098 static DWORD urlcache_delete_file(const cache_container *container,
1099 urlcache_header *header, entry_url *url_entry)
1101 WIN32_FILE_ATTRIBUTE_DATA attr;
1102 WCHAR path[MAX_PATH];
1103 LONG path_size = sizeof(path);
1104 DWORD err;
1105 WORD date, time;
1107 if(!url_entry->local_name_off)
1108 goto succ;
1110 if(!urlcache_create_file_pathW(container, header,
1111 (LPCSTR)url_entry+url_entry->local_name_off,
1112 url_entry->cache_dir, path, &path_size))
1113 goto succ;
1115 if(!GetFileAttributesExW(path, GetFileExInfoStandard, &attr))
1116 goto succ;
1117 file_time_to_dos_date_time(&attr.ftLastWriteTime, &date, &time);
1118 if(date != url_entry->write_date || time != url_entry->write_time)
1119 goto succ;
1121 err = (DeleteFileW(path) ? ERROR_SUCCESS : GetLastError());
1122 if(err == ERROR_ACCESS_DENIED || err == ERROR_SHARING_VIOLATION)
1123 return err;
1125 succ:
1126 if (url_entry->cache_dir < header->dirs_no)
1128 if (header->directory_data[url_entry->cache_dir].files_no)
1129 header->directory_data[url_entry->cache_dir].files_no--;
1131 if (url_entry->cache_entry_type & STICKY_CACHE_ENTRY)
1133 if (url_entry->size.QuadPart < header->exempt_usage.QuadPart)
1134 header->exempt_usage.QuadPart -= url_entry->size.QuadPart;
1135 else
1136 header->exempt_usage.QuadPart = 0;
1138 else
1140 if (url_entry->size.QuadPart < header->cache_usage.QuadPart)
1141 header->cache_usage.QuadPart -= url_entry->size.QuadPart;
1142 else
1143 header->cache_usage.QuadPart = 0;
1146 return ERROR_SUCCESS;
1149 static BOOL urlcache_clean_leaked_entries(cache_container *container, urlcache_header *header)
1151 DWORD *leak_off;
1152 BOOL freed = FALSE;
1154 leak_off = &header->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
1155 while(*leak_off) {
1156 entry_url *url_entry = (entry_url*)((LPBYTE)header + *leak_off);
1158 if(SUCCEEDED(urlcache_delete_file(container, header, url_entry))) {
1159 *leak_off = url_entry->exempt_delta;
1160 urlcache_entry_free(header, &url_entry->header);
1161 freed = TRUE;
1162 }else {
1163 leak_off = &url_entry->exempt_delta;
1167 return freed;
1170 /***********************************************************************
1171 * cache_container_clean_index (Internal)
1173 * This function is meant to make place in index file by removing leaked
1174 * files entries and resizing the file.
1176 * CAUTION: file view may get mapped to new memory
1178 * RETURNS
1179 * ERROR_SUCCESS when new memory is available
1180 * error code otherwise
1182 static DWORD cache_container_clean_index(cache_container *container, urlcache_header **file_view)
1184 urlcache_header *header = *file_view;
1185 DWORD ret;
1187 TRACE("(%s %s)\n", debugstr_a(container->cache_prefix), debugstr_w(container->path));
1189 if(urlcache_clean_leaked_entries(container, header))
1190 return ERROR_SUCCESS;
1192 if(header->size >= ALLOCATION_TABLE_SIZE*8*BLOCKSIZE + ENTRY_START_OFFSET) {
1193 WARN("index file has maximal size\n");
1194 return ERROR_NOT_ENOUGH_MEMORY;
1197 cache_container_close_index(container);
1198 ret = cache_container_open_index(container, header->capacity_in_blocks*2);
1199 if(ret != ERROR_SUCCESS)
1200 return ret;
1201 header = MapViewOfFile(container->mapping, FILE_MAP_WRITE, 0, 0, 0);
1202 if(!header)
1203 return GetLastError();
1205 UnmapViewOfFile(*file_view);
1206 *file_view = header;
1207 return ERROR_SUCCESS;
1210 /* Just like DosDateTimeToFileTime, except that it also maps the special
1211 * case of a DOS date/time of (0,0) to a filetime of (0,0).
1213 static void dos_date_time_to_file_time(WORD fatdate, WORD fattime,
1214 FILETIME *ft)
1216 if (!fatdate && !fattime)
1217 ft->dwLowDateTime = ft->dwHighDateTime = 0;
1218 else
1219 DosDateTimeToFileTime(fatdate, fattime, ft);
1222 /***********************************************************************
1223 * urlcache_copy_entry (Internal)
1225 * Copies an entry from the cache index file to the Win32 structure
1227 * RETURNS
1228 * ERROR_SUCCESS if the buffer was big enough
1229 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1232 static DWORD urlcache_copy_entry(
1233 cache_container *pContainer,
1234 const urlcache_header *pHeader,
1235 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1236 LPDWORD lpdwBufferSize,
1237 const entry_url * pUrlEntry,
1238 BOOL bUnicode)
1240 int lenUrl;
1241 DWORD dwRequiredSize = sizeof(*lpCacheEntryInfo);
1243 if (*lpdwBufferSize >= dwRequiredSize)
1245 lpCacheEntryInfo->lpHeaderInfo = NULL;
1246 lpCacheEntryInfo->lpszFileExtension = NULL;
1247 lpCacheEntryInfo->lpszLocalFileName = NULL;
1248 lpCacheEntryInfo->lpszSourceUrlName = NULL;
1249 lpCacheEntryInfo->CacheEntryType = pUrlEntry->cache_entry_type;
1250 lpCacheEntryInfo->u.dwExemptDelta = pUrlEntry->exempt_delta;
1251 lpCacheEntryInfo->dwHeaderInfoSize = pUrlEntry->header_info_size;
1252 lpCacheEntryInfo->dwHitRate = pUrlEntry->hit_rate;
1253 lpCacheEntryInfo->dwSizeHigh = pUrlEntry->size.u.HighPart;
1254 lpCacheEntryInfo->dwSizeLow = pUrlEntry->size.u.LowPart;
1255 lpCacheEntryInfo->dwStructSize = sizeof(*lpCacheEntryInfo);
1256 lpCacheEntryInfo->dwUseCount = pUrlEntry->use_count;
1257 dos_date_time_to_file_time(pUrlEntry->expire_date, pUrlEntry->expire_time, &lpCacheEntryInfo->ExpireTime);
1258 lpCacheEntryInfo->LastAccessTime = pUrlEntry->access_time;
1259 lpCacheEntryInfo->LastModifiedTime = pUrlEntry->modification_time;
1260 dos_date_time_to_file_time(pUrlEntry->sync_date, pUrlEntry->sync_time, &lpCacheEntryInfo->LastSyncTime);
1263 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1264 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1265 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1266 if (bUnicode)
1267 lenUrl = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->url_off, -1, NULL, 0);
1268 else
1269 lenUrl = strlen((LPCSTR)pUrlEntry + pUrlEntry->url_off);
1270 dwRequiredSize += (lenUrl + 1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1272 /* FIXME: is source url optional? */
1273 if (*lpdwBufferSize >= dwRequiredSize)
1275 DWORD lenUrlBytes = (lenUrl+1) * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1277 lpCacheEntryInfo->lpszSourceUrlName = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenUrlBytes;
1278 if (bUnicode)
1279 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->url_off, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenUrl + 1);
1280 else
1281 memcpy(lpCacheEntryInfo->lpszSourceUrlName, (LPCSTR)pUrlEntry + pUrlEntry->url_off, lenUrlBytes);
1284 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1285 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1286 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1288 if (pUrlEntry->local_name_off)
1290 LONG nLocalFilePathSize;
1291 LPSTR lpszLocalFileName;
1292 lpszLocalFileName = (LPSTR)lpCacheEntryInfo + dwRequiredSize;
1293 nLocalFilePathSize = *lpdwBufferSize - dwRequiredSize;
1294 if ((bUnicode && urlcache_create_file_pathW(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->local_name_off, pUrlEntry->cache_dir, (LPWSTR)lpszLocalFileName, &nLocalFilePathSize)) ||
1295 (!bUnicode && urlcache_create_file_pathA(pContainer, pHeader, (LPCSTR)pUrlEntry + pUrlEntry->local_name_off, pUrlEntry->cache_dir, lpszLocalFileName, &nLocalFilePathSize)))
1297 lpCacheEntryInfo->lpszLocalFileName = lpszLocalFileName;
1299 dwRequiredSize += nLocalFilePathSize * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR)) ;
1301 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1302 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1303 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1305 dwRequiredSize += pUrlEntry->header_info_size + 1;
1307 if (*lpdwBufferSize >= dwRequiredSize)
1309 lpCacheEntryInfo->lpHeaderInfo = (LPBYTE)lpCacheEntryInfo + dwRequiredSize - pUrlEntry->header_info_size - 1;
1310 memcpy(lpCacheEntryInfo->lpHeaderInfo, (LPCSTR)pUrlEntry + pUrlEntry->header_info_off, pUrlEntry->header_info_size);
1311 ((LPBYTE)lpCacheEntryInfo)[dwRequiredSize - 1] = '\0';
1313 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1314 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1315 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1317 if (pUrlEntry->file_extension_off)
1319 int lenExtension;
1321 if (bUnicode)
1322 lenExtension = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->file_extension_off, -1, NULL, 0);
1323 else
1324 lenExtension = strlen((LPCSTR)pUrlEntry + pUrlEntry->file_extension_off) + 1;
1325 dwRequiredSize += lenExtension * (bUnicode ? sizeof(WCHAR) : sizeof(CHAR));
1327 if (*lpdwBufferSize >= dwRequiredSize)
1329 lpCacheEntryInfo->lpszFileExtension = (LPSTR)lpCacheEntryInfo + dwRequiredSize - lenExtension;
1330 if (bUnicode)
1331 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)pUrlEntry + pUrlEntry->file_extension_off, -1, (LPWSTR)lpCacheEntryInfo->lpszSourceUrlName, lenExtension);
1332 else
1333 memcpy(lpCacheEntryInfo->lpszFileExtension, (LPCSTR)pUrlEntry + pUrlEntry->file_extension_off, lenExtension * sizeof(CHAR));
1336 if ((dwRequiredSize % 4) && (dwRequiredSize < *lpdwBufferSize))
1337 ZeroMemory((LPBYTE)lpCacheEntryInfo + dwRequiredSize, 4 - (dwRequiredSize % 4));
1338 dwRequiredSize = DWORD_ALIGN(dwRequiredSize);
1341 if (dwRequiredSize > *lpdwBufferSize)
1343 *lpdwBufferSize = dwRequiredSize;
1344 return ERROR_INSUFFICIENT_BUFFER;
1346 *lpdwBufferSize = dwRequiredSize;
1347 return ERROR_SUCCESS;
1350 /***********************************************************************
1351 * urlcache_set_entry_info (Internal)
1353 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1354 * according to the flags set by field_control.
1356 * RETURNS
1357 * ERROR_SUCCESS if the buffer was big enough
1358 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1361 static DWORD urlcache_set_entry_info(entry_url *url_entry, const INTERNET_CACHE_ENTRY_INFOA *entry_info, DWORD field_control)
1363 if (field_control & CACHE_ENTRY_ACCTIME_FC)
1364 url_entry->access_time = entry_info->LastAccessTime;
1365 if (field_control & CACHE_ENTRY_ATTRIBUTE_FC)
1366 url_entry->cache_entry_type = entry_info->CacheEntryType;
1367 if (field_control & CACHE_ENTRY_EXEMPT_DELTA_FC)
1368 url_entry->exempt_delta = entry_info->u.dwExemptDelta;
1369 if (field_control & CACHE_ENTRY_EXPTIME_FC)
1370 file_time_to_dos_date_time(&entry_info->ExpireTime, &url_entry->expire_date, &url_entry->expire_time);
1371 if (field_control & CACHE_ENTRY_HEADERINFO_FC)
1372 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1373 if (field_control & CACHE_ENTRY_HITRATE_FC)
1374 url_entry->hit_rate = entry_info->dwHitRate;
1375 if (field_control & CACHE_ENTRY_MODTIME_FC)
1376 url_entry->modification_time = entry_info->LastModifiedTime;
1377 if (field_control & CACHE_ENTRY_SYNCTIME_FC)
1378 file_time_to_dos_date_time(&entry_info->LastAccessTime, &url_entry->sync_date, &url_entry->sync_time);
1380 return ERROR_SUCCESS;
1383 /***********************************************************************
1384 * urlcache_hash_key (Internal)
1386 * Returns the hash key for a given string
1388 * RETURNS
1389 * hash key for the string
1392 static DWORD urlcache_hash_key(LPCSTR lpszKey)
1394 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1395 * but the algorithm and result are not the same!
1397 static const unsigned char lookupTable[256] =
1399 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1400 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1401 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1402 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1403 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1404 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1405 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1406 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1407 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1408 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1409 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1410 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1411 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1412 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1413 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1414 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1415 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1416 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1417 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1418 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1419 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1420 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1421 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1422 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1423 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1424 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1425 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1426 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1427 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1428 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1429 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1430 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1432 BYTE key[4];
1433 DWORD i;
1435 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1436 key[i] = lookupTable[(*lpszKey + i) & 0xFF];
1438 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1440 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1441 key[i] = lookupTable[*lpszKey ^ key[i]];
1444 return *(DWORD *)key;
1447 static inline entry_hash_table* urlcache_get_hash_table(const urlcache_header *pHeader, DWORD dwOffset)
1449 if(!dwOffset)
1450 return NULL;
1451 return (entry_hash_table*)((LPBYTE)pHeader + dwOffset);
1454 static BOOL urlcache_find_hash_entry(const urlcache_header *pHeader, LPCSTR lpszUrl, struct hash_entry **ppHashEntry)
1456 /* structure of hash table:
1457 * 448 entries divided into 64 blocks
1458 * each block therefore contains a chain of 7 key/offset pairs
1459 * how position in table is calculated:
1460 * 1. the url is hashed in helper function
1461 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1462 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1464 * note:
1465 * there can be multiple hash tables in the file and the offset to
1466 * the next one is stored in the header of the hash table
1468 DWORD key = urlcache_hash_key(lpszUrl);
1469 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1470 entry_hash_table* pHashEntry;
1471 DWORD id = 0;
1473 key >>= HASHTABLE_FLAG_BITS;
1475 for (pHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1476 pHashEntry; pHashEntry = urlcache_get_hash_table(pHeader, pHashEntry->next))
1478 int i;
1479 if (pHashEntry->id != id++)
1481 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->id, id);
1482 continue;
1484 /* make sure that it is in fact a hash entry */
1485 if (pHashEntry->header.signature != HASH_SIGNATURE)
1487 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->header.signature);
1488 continue;
1491 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1493 struct hash_entry *pHashElement = &pHashEntry->hash_table[offset + i];
1494 if (key == pHashElement->key>>HASHTABLE_FLAG_BITS)
1496 /* FIXME: we should make sure that this is the right element
1497 * before returning and claiming that it is. We can do this
1498 * by doing a simple compare between the URL we were given
1499 * and the URL stored in the entry. However, this assumes
1500 * we know the format of all the entries stored in the
1501 * hash table */
1502 *ppHashEntry = pHashElement;
1503 return TRUE;
1507 return FALSE;
1510 static BOOL urlcache_find_hash_entryW(const urlcache_header *pHeader, LPCWSTR lpszUrl, struct hash_entry **ppHashEntry)
1512 LPSTR urlA;
1513 BOOL ret;
1515 urlA = heap_strdupWtoA(lpszUrl);
1516 if (!urlA)
1518 SetLastError(ERROR_OUTOFMEMORY);
1519 return FALSE;
1522 ret = urlcache_find_hash_entry(pHeader, urlA, ppHashEntry);
1523 heap_free(urlA);
1524 return ret;
1527 /***********************************************************************
1528 * urlcache_hash_entry_set_flags (Internal)
1530 * Sets special bits in hash key
1532 * RETURNS
1533 * nothing
1536 static void urlcache_hash_entry_set_flags(struct hash_entry *pHashEntry, DWORD dwFlag)
1538 pHashEntry->key = (pHashEntry->key >> HASHTABLE_FLAG_BITS << HASHTABLE_FLAG_BITS) | dwFlag;
1541 /***********************************************************************
1542 * urlcache_hash_entry_delete (Internal)
1544 * Searches all the hash tables in the index for the given URL and
1545 * then if found deletes the entry.
1547 * RETURNS
1548 * TRUE if the entry was found
1549 * FALSE if the entry could not be found
1552 static BOOL urlcache_hash_entry_delete(struct hash_entry *pHashEntry)
1554 pHashEntry->key = HASHTABLE_DEL;
1555 return TRUE;
1558 /***********************************************************************
1559 * urlcache_hash_entry_create (Internal)
1561 * Searches all the hash tables for a free slot based on the offset
1562 * generated from the hash key. If a free slot is found, the offset and
1563 * key are entered into the hash table.
1565 * RETURNS
1566 * ERROR_SUCCESS if the entry was added
1567 * Any other Win32 error code if the entry could not be added
1570 static DWORD urlcache_hash_entry_create(urlcache_header *pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry, DWORD dwFieldType)
1572 /* see urlcache_find_hash_entry for structure of hash tables */
1574 DWORD key = urlcache_hash_key(lpszUrl);
1575 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1576 entry_hash_table* pHashEntry, *pHashPrev = NULL;
1577 DWORD id = 0;
1578 DWORD error;
1580 key = ((key >> HASHTABLE_FLAG_BITS) << HASHTABLE_FLAG_BITS) + dwFieldType;
1582 for (pHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1583 pHashEntry; pHashEntry = urlcache_get_hash_table(pHeader, pHashEntry->next))
1585 int i;
1586 pHashPrev = pHashEntry;
1588 if (pHashEntry->id != id++)
1590 ERR("not right hash table number (%d) expected %d\n", pHashEntry->id, id);
1591 break;
1593 /* make sure that it is in fact a hash entry */
1594 if (pHashEntry->header.signature != HASH_SIGNATURE)
1596 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->header.signature);
1597 break;
1600 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1602 struct hash_entry *pHashElement = &pHashEntry->hash_table[offset + i];
1603 if (pHashElement->key==HASHTABLE_FREE || pHashElement->key==HASHTABLE_DEL) /* if the slot is free */
1605 pHashElement->key = key;
1606 pHashElement->offset = dwOffsetEntry;
1607 return ERROR_SUCCESS;
1611 error = urlcache_create_hash_table(pHeader, pHashPrev, &pHashEntry);
1612 if (error != ERROR_SUCCESS)
1613 return error;
1615 pHashEntry->hash_table[offset].key = key;
1616 pHashEntry->hash_table[offset].offset = dwOffsetEntry;
1617 return ERROR_SUCCESS;
1620 /***********************************************************************
1621 * urlcache_enum_hash_tables (Internal)
1623 * Enumerates the hash tables in a container.
1625 * RETURNS
1626 * TRUE if an entry was found
1627 * FALSE if there are no more tables to enumerate.
1630 static BOOL urlcache_enum_hash_tables(const urlcache_header *pHeader, DWORD *id, entry_hash_table **ppHashEntry)
1632 for (*ppHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1633 *ppHashEntry; *ppHashEntry = urlcache_get_hash_table(pHeader, (*ppHashEntry)->next))
1635 TRACE("looking at hash table number %d\n", (*ppHashEntry)->id);
1636 if ((*ppHashEntry)->id != *id)
1637 continue;
1638 /* make sure that it is in fact a hash entry */
1639 if ((*ppHashEntry)->header.signature != HASH_SIGNATURE)
1641 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->header.signature);
1642 (*id)++;
1643 continue;
1646 TRACE("hash table number %d found\n", *id);
1647 return TRUE;
1649 return FALSE;
1652 /***********************************************************************
1653 * urlcache_enum_hash_table_entries (Internal)
1655 * Enumerates entries in a hash table and returns the next non-free entry.
1657 * RETURNS
1658 * TRUE if an entry was found
1659 * FALSE if the hash table is empty or there are no more entries to
1660 * enumerate.
1663 static BOOL urlcache_enum_hash_table_entries(const urlcache_header *pHeader, const entry_hash_table *pHashEntry,
1664 DWORD * index, const struct hash_entry **ppHashEntry)
1666 for (; *index < HASHTABLE_SIZE ; (*index)++)
1668 if (pHashEntry->hash_table[*index].key==HASHTABLE_FREE || pHashEntry->hash_table[*index].key==HASHTABLE_DEL)
1669 continue;
1671 *ppHashEntry = &pHashEntry->hash_table[*index];
1672 TRACE("entry found %d\n", *index);
1673 return TRUE;
1675 TRACE("no more entries (%d)\n", *index);
1676 return FALSE;
1679 /***********************************************************************
1680 * cache_container_delete_dir (Internal)
1682 * Erase a directory containing an URL cache.
1684 * RETURNS
1685 * TRUE success, FALSE failure/aborted.
1688 static BOOL cache_container_delete_dir(LPCWSTR lpszPath)
1690 DWORD path_len;
1691 WCHAR path[MAX_PATH + 1];
1692 SHFILEOPSTRUCTW shfos;
1693 int ret;
1695 path_len = strlenW(lpszPath);
1696 if (path_len >= MAX_PATH)
1697 return FALSE;
1698 strcpyW(path, lpszPath);
1699 path[path_len + 1] = 0; /* double-NUL-terminate path */
1701 shfos.hwnd = NULL;
1702 shfos.wFunc = FO_DELETE;
1703 shfos.pFrom = path;
1704 shfos.pTo = NULL;
1705 shfos.fFlags = FOF_NOCONFIRMATION;
1706 shfos.fAnyOperationsAborted = FALSE;
1707 ret = SHFileOperationW(&shfos);
1708 if (ret)
1709 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path), ret);
1710 return !(ret || shfos.fAnyOperationsAborted);
1713 /***********************************************************************
1714 * urlcache_hash_entry_is_locked (Internal)
1716 * Checks if entry is locked. Unlocks it if possible.
1718 static BOOL urlcache_hash_entry_is_locked(struct hash_entry *hash_entry, entry_url *url_entry)
1720 FILETIME cur_time;
1721 ULARGE_INTEGER acc_time, time;
1723 if ((hash_entry->key & ((1<<HASHTABLE_FLAG_BITS)-1)) != HASHTABLE_LOCK)
1724 return FALSE;
1726 GetSystemTimeAsFileTime(&cur_time);
1727 time.u.LowPart = cur_time.dwLowDateTime;
1728 time.u.HighPart = cur_time.dwHighDateTime;
1730 acc_time.u.LowPart = url_entry->access_time.dwLowDateTime;
1731 acc_time.u.HighPart = url_entry->access_time.dwHighDateTime;
1733 time.QuadPart -= acc_time.QuadPart;
1735 /* check if entry was locked for at least a day */
1736 if(time.QuadPart > (ULONGLONG)24*60*60*FILETIME_SECOND) {
1737 urlcache_hash_entry_set_flags(hash_entry, HASHTABLE_URL);
1738 url_entry->use_count = 0;
1739 return FALSE;
1742 return TRUE;
1745 BOOL urlcache_get_entry_info(const char *url, void *entry_info,
1746 DWORD *size, DWORD flags, BOOL unicode)
1748 urlcache_header *header;
1749 struct hash_entry *hash_entry;
1750 const entry_url *url_entry;
1751 cache_container *container;
1752 DWORD error;
1754 TRACE("(%s, %p, %p, %x, %x)\n", debugstr_a(url), entry_info, size, flags, unicode);
1756 if(flags & ~GET_INSTALLED_ENTRY)
1757 FIXME("ignoring unsupported flags: %x\n", flags);
1759 error = cache_containers_find(url, &container);
1760 if(error != ERROR_SUCCESS) {
1761 SetLastError(error);
1762 return FALSE;
1765 error = cache_container_open_index(container, MIN_BLOCK_NO);
1766 if(error != ERROR_SUCCESS) {
1767 SetLastError(error);
1768 return FALSE;
1771 if(!(header = cache_container_lock_index(container)))
1772 return FALSE;
1774 if(!urlcache_find_hash_entry(header, url, &hash_entry)) {
1775 cache_container_unlock_index(container, header);
1776 WARN("entry %s not found!\n", debugstr_a(url));
1777 SetLastError(ERROR_FILE_NOT_FOUND);
1778 return FALSE;
1781 url_entry = (const entry_url*)((LPBYTE)header + hash_entry->offset);
1782 if(url_entry->header.signature != URL_SIGNATURE) {
1783 cache_container_unlock_index(container, header);
1784 FIXME("Trying to retrieve entry of unknown format %s\n",
1785 debugstr_an((LPCSTR)&url_entry->header.signature, sizeof(DWORD)));
1786 SetLastError(ERROR_FILE_NOT_FOUND);
1787 return FALSE;
1790 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->url_off));
1791 TRACE("Header info: %s\n", debugstr_an((LPCSTR)url_entry +
1792 url_entry->header_info_off, url_entry->header_info_size));
1794 if((flags & GET_INSTALLED_ENTRY) && !(url_entry->cache_entry_type & INSTALLED_CACHE_ENTRY)) {
1795 cache_container_unlock_index(container, header);
1796 SetLastError(ERROR_FILE_NOT_FOUND);
1797 return FALSE;
1800 if(size) {
1801 if(!entry_info)
1802 *size = 0;
1804 error = urlcache_copy_entry(container, header, entry_info, size, url_entry, unicode);
1805 if(error != ERROR_SUCCESS) {
1806 cache_container_unlock_index(container, header);
1807 SetLastError(error);
1808 return FALSE;
1810 if(url_entry->local_name_off)
1811 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->local_name_off));
1814 cache_container_unlock_index(container, header);
1815 return TRUE;
1818 /***********************************************************************
1819 * GetUrlCacheEntryInfoExA (WININET.@)
1822 BOOL WINAPI GetUrlCacheEntryInfoExA(LPCSTR lpszUrl,
1823 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1824 LPDWORD lpdwCacheEntryInfoBufSize, LPSTR lpszReserved,
1825 LPDWORD lpdwReserved, LPVOID lpReserved, DWORD dwFlags)
1827 if(lpszReserved!=NULL || lpdwReserved!=NULL || lpReserved!=NULL) {
1828 ERR("Reserved value was not 0\n");
1829 SetLastError(ERROR_INVALID_PARAMETER);
1830 return FALSE;
1833 return urlcache_get_entry_info(lpszUrl, lpCacheEntryInfo,
1834 lpdwCacheEntryInfoBufSize, dwFlags, FALSE);
1837 /***********************************************************************
1838 * GetUrlCacheEntryInfoA (WININET.@)
1841 BOOL WINAPI GetUrlCacheEntryInfoA(LPCSTR lpszUrlName,
1842 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1843 LPDWORD lpdwCacheEntryInfoBufferSize)
1845 return GetUrlCacheEntryInfoExA(lpszUrlName, lpCacheEntryInfo,
1846 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
1849 static int urlcache_encode_url(const WCHAR *url, char *encoded_url, int encoded_len)
1851 URL_COMPONENTSW uc;
1852 DWORD len, part_len;
1853 WCHAR *punycode;
1855 TRACE("%s\n", debugstr_w(url));
1857 memset(&uc, 0, sizeof(uc));
1858 uc.dwStructSize = sizeof(uc);
1859 uc.dwHostNameLength = 1;
1860 if(!InternetCrackUrlW(url, 0, 0, &uc))
1861 uc.nScheme = INTERNET_SCHEME_UNKNOWN;
1863 if(uc.nScheme!=INTERNET_SCHEME_HTTP && uc.nScheme!=INTERNET_SCHEME_HTTPS)
1864 return WideCharToMultiByte(CP_UTF8, 0, url, -1, encoded_url, encoded_len, NULL, NULL);
1866 len = WideCharToMultiByte(CP_UTF8, 0, url, uc.lpszHostName-url,
1867 encoded_url, encoded_len, NULL, NULL);
1868 if(!len)
1869 return 0;
1870 if(encoded_url)
1871 encoded_len -= len;
1873 part_len = IdnToAscii(0, uc.lpszHostName, uc.dwHostNameLength, NULL, 0);
1874 if(!part_len) {
1875 SetLastError(ERROR_INTERNET_INVALID_URL);
1876 return 0;
1879 punycode = heap_alloc(part_len*sizeof(WCHAR));
1880 if(!punycode)
1881 return 0;
1883 part_len = IdnToAscii(0, uc.lpszHostName, uc.dwHostNameLength, punycode, part_len);
1884 if(!part_len) {
1885 heap_free(punycode);
1886 return 0;
1889 part_len = WideCharToMultiByte(CP_UTF8, 0, punycode, part_len,
1890 encoded_url ? encoded_url+len : NULL, encoded_len, NULL, NULL);
1891 heap_free(punycode);
1892 if(!part_len)
1893 return 0;
1894 if(encoded_url)
1895 encoded_len -= part_len;
1896 len += part_len;
1898 part_len = WideCharToMultiByte(CP_UTF8, 0, uc.lpszHostName+uc.dwHostNameLength,
1899 -1, encoded_url ? encoded_url+len : NULL, encoded_len, NULL, NULL);
1900 if(!part_len)
1901 return 0;
1902 len += part_len;
1904 TRACE("got (%d)%s\n", len, debugstr_a(encoded_url));
1905 return len;
1908 static BOOL urlcache_encode_url_alloc(const WCHAR *url, char **encoded_url)
1910 DWORD encoded_len;
1911 char *ret;
1913 encoded_len = urlcache_encode_url(url, NULL, 0);
1914 if(!encoded_len)
1915 return FALSE;
1917 ret = heap_alloc(encoded_len*sizeof(WCHAR));
1918 if(!ret)
1919 return FALSE;
1921 encoded_len = urlcache_encode_url(url, ret, encoded_len);
1922 if(!encoded_len) {
1923 heap_free(ret);
1924 return FALSE;
1927 *encoded_url = ret;
1928 return TRUE;
1931 /***********************************************************************
1932 * GetUrlCacheEntryInfoExW (WININET.@)
1935 BOOL WINAPI GetUrlCacheEntryInfoExW(LPCWSTR lpszUrl,
1936 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1937 LPDWORD lpdwCacheEntryInfoBufSize, LPWSTR lpszReserved,
1938 LPDWORD lpdwReserved, LPVOID lpReserved, DWORD dwFlags)
1940 char *url;
1941 BOOL ret;
1943 if(lpszReserved!=NULL || lpdwReserved!=NULL || lpReserved!=NULL) {
1944 ERR("Reserved value was not 0\n");
1945 SetLastError(ERROR_INVALID_PARAMETER);
1946 return FALSE;
1949 /* Ignore GET_INSTALLED_ENTRY flag in unicode version of function */
1950 dwFlags &= ~GET_INSTALLED_ENTRY;
1952 if(!urlcache_encode_url_alloc(lpszUrl, &url))
1953 return FALSE;
1955 ret = urlcache_get_entry_info(url, lpCacheEntryInfo,
1956 lpdwCacheEntryInfoBufSize, dwFlags, TRUE);
1957 heap_free(url);
1958 return ret;
1961 /***********************************************************************
1962 * GetUrlCacheEntryInfoW (WININET.@)
1965 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1966 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1967 LPDWORD lpdwCacheEntryInfoBufferSize)
1969 return GetUrlCacheEntryInfoExW(lpszUrl, lpCacheEntryInfo,
1970 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
1973 /***********************************************************************
1974 * SetUrlCacheEntryInfoA (WININET.@)
1976 BOOL WINAPI SetUrlCacheEntryInfoA(LPCSTR lpszUrlName,
1977 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1978 DWORD dwFieldControl)
1980 urlcache_header *pHeader;
1981 struct hash_entry *pHashEntry;
1982 entry_header *pEntry;
1983 cache_container *pContainer;
1984 DWORD error;
1986 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1988 error = cache_containers_find(lpszUrlName, &pContainer);
1989 if (error != ERROR_SUCCESS)
1991 SetLastError(error);
1992 return FALSE;
1995 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
1996 if (error != ERROR_SUCCESS)
1998 SetLastError(error);
1999 return FALSE;
2002 if (!(pHeader = cache_container_lock_index(pContainer)))
2003 return FALSE;
2005 if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
2007 cache_container_unlock_index(pContainer, pHeader);
2008 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
2009 SetLastError(ERROR_FILE_NOT_FOUND);
2010 return FALSE;
2013 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2014 if (pEntry->signature != URL_SIGNATURE)
2016 cache_container_unlock_index(pContainer, pHeader);
2017 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2018 SetLastError(ERROR_FILE_NOT_FOUND);
2019 return FALSE;
2022 urlcache_set_entry_info((entry_url*)pEntry, lpCacheEntryInfo, dwFieldControl);
2024 cache_container_unlock_index(pContainer, pHeader);
2026 return TRUE;
2029 /***********************************************************************
2030 * SetUrlCacheEntryInfoW (WININET.@)
2032 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
2033 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2034 DWORD dwFieldControl)
2036 char *url;
2037 BOOL ret;
2039 if(!urlcache_encode_url_alloc(lpszUrl, &url))
2040 return FALSE;
2042 ret = SetUrlCacheEntryInfoA(url, (INTERNET_CACHE_ENTRY_INFOA*)lpCacheEntryInfo, dwFieldControl);
2043 heap_free(url);
2044 return ret;
2047 static BOOL urlcache_entry_get_file(const char *url, void *entry_info, DWORD *size, BOOL unicode)
2049 urlcache_header *header;
2050 struct hash_entry *hash_entry;
2051 entry_url *url_entry;
2052 cache_container *container;
2053 DWORD error;
2055 TRACE("(%s, %p, %p, %x)\n", debugstr_a(url), entry_info, size, unicode);
2057 if(!url || !size || (!entry_info && *size)) {
2058 SetLastError(ERROR_INVALID_PARAMETER);
2059 return FALSE;
2062 error = cache_containers_find(url, &container);
2063 if(error != ERROR_SUCCESS) {
2064 SetLastError(error);
2065 return FALSE;
2068 error = cache_container_open_index(container, MIN_BLOCK_NO);
2069 if (error != ERROR_SUCCESS) {
2070 SetLastError(error);
2071 return FALSE;
2074 if (!(header = cache_container_lock_index(container)))
2075 return FALSE;
2077 if (!urlcache_find_hash_entry(header, url, &hash_entry)) {
2078 cache_container_unlock_index(container, header);
2079 TRACE("entry %s not found!\n", url);
2080 SetLastError(ERROR_FILE_NOT_FOUND);
2081 return FALSE;
2084 url_entry = (entry_url*)((LPBYTE)header + hash_entry->offset);
2085 if(url_entry->header.signature != URL_SIGNATURE) {
2086 cache_container_unlock_index(container, header);
2087 FIXME("Trying to retrieve entry of unknown format %s\n",
2088 debugstr_an((LPSTR)&url_entry->header.signature, sizeof(DWORD)));
2089 SetLastError(ERROR_FILE_NOT_FOUND);
2090 return FALSE;
2093 if(!url_entry->local_name_off) {
2094 cache_container_unlock_index(container, header);
2095 SetLastError(ERROR_INVALID_DATA);
2096 return FALSE;
2099 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->url_off));
2100 TRACE("Header info: %s\n", debugstr_an((LPCSTR)url_entry + url_entry->header_info_off,
2101 url_entry->header_info_size));
2103 error = urlcache_copy_entry(container, header, entry_info,
2104 size, url_entry, unicode);
2105 if(error != ERROR_SUCCESS) {
2106 cache_container_unlock_index(container, header);
2107 SetLastError(error);
2108 return FALSE;
2110 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->local_name_off));
2112 url_entry->hit_rate++;
2113 url_entry->use_count++;
2114 urlcache_hash_entry_set_flags(hash_entry, HASHTABLE_LOCK);
2115 GetSystemTimeAsFileTime(&url_entry->access_time);
2117 cache_container_unlock_index(container, header);
2119 return TRUE;
2122 /***********************************************************************
2123 * RetrieveUrlCacheEntryFileA (WININET.@)
2126 BOOL WINAPI RetrieveUrlCacheEntryFileA(LPCSTR lpszUrlName,
2127 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2128 LPDWORD lpdwCacheEntryInfoBufferSize, DWORD dwReserved)
2130 return urlcache_entry_get_file(lpszUrlName, lpCacheEntryInfo,
2131 lpdwCacheEntryInfoBufferSize, FALSE);
2134 /***********************************************************************
2135 * RetrieveUrlCacheEntryFileW (WININET.@)
2138 BOOL WINAPI RetrieveUrlCacheEntryFileW(LPCWSTR lpszUrlName,
2139 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2140 LPDWORD lpdwCacheEntryInfoBufferSize, DWORD dwReserved)
2142 char *url;
2143 BOOL ret;
2145 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
2146 return FALSE;
2148 ret = urlcache_entry_get_file(url, lpCacheEntryInfo,
2149 lpdwCacheEntryInfoBufferSize, TRUE);
2150 heap_free(url);
2151 return ret;
2154 static BOOL urlcache_entry_delete(const cache_container *pContainer,
2155 urlcache_header *pHeader, struct hash_entry *pHashEntry)
2157 entry_header *pEntry;
2158 entry_url * pUrlEntry;
2160 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2161 if (pEntry->signature != URL_SIGNATURE)
2163 FIXME("Trying to delete entry of unknown format %s\n",
2164 debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
2165 SetLastError(ERROR_FILE_NOT_FOUND);
2166 return FALSE;
2169 pUrlEntry = (entry_url *)pEntry;
2170 if(urlcache_hash_entry_is_locked(pHashEntry, pUrlEntry))
2172 TRACE("Trying to delete locked entry\n");
2173 pUrlEntry->cache_entry_type |= PENDING_DELETE_CACHE_ENTRY;
2174 SetLastError(ERROR_SHARING_VIOLATION);
2175 return FALSE;
2178 if(!urlcache_delete_file(pContainer, pHeader, pUrlEntry))
2180 urlcache_entry_free(pHeader, pEntry);
2182 else
2184 /* Add entry to leaked files list */
2185 pUrlEntry->header.signature = LEAK_SIGNATURE;
2186 pUrlEntry->exempt_delta = pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
2187 pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET] = pHashEntry->offset;
2190 urlcache_hash_entry_delete(pHashEntry);
2191 return TRUE;
2194 static HANDLE free_cache_running;
2195 static HANDLE dll_unload_event;
2196 static DWORD WINAPI handle_full_cache_worker(void *param)
2198 FreeUrlCacheSpaceW(NULL, 20, 0);
2199 ReleaseSemaphore(free_cache_running, 1, NULL);
2200 return 0;
2203 static void handle_full_cache(void)
2205 if(WaitForSingleObject(free_cache_running, 0) == WAIT_OBJECT_0) {
2206 if(!QueueUserWorkItem(handle_full_cache_worker, NULL, 0))
2207 ReleaseSemaphore(free_cache_running, 1, NULL);
2211 /* Enumerates entries in cache, allows cache unlocking between calls. */
2212 static BOOL urlcache_next_entry(urlcache_header *header, DWORD *hash_table_off, DWORD *hash_table_entry,
2213 struct hash_entry **hash_entry, entry_header **entry)
2215 entry_hash_table *hashtable_entry;
2217 *hash_entry = NULL;
2218 *entry = NULL;
2220 if(!*hash_table_off) {
2221 *hash_table_off = header->hash_table_off;
2222 *hash_table_entry = 0;
2224 hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2225 }else {
2226 if(*hash_table_off >= header->size) {
2227 *hash_table_off = 0;
2228 return FALSE;
2231 hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2234 if(hashtable_entry->header.signature != HASH_SIGNATURE) {
2235 *hash_table_off = 0;
2236 return FALSE;
2239 while(1) {
2240 if(*hash_table_entry >= HASHTABLE_SIZE) {
2241 *hash_table_off = hashtable_entry->next;
2242 if(!*hash_table_off) {
2243 *hash_table_off = 0;
2244 return FALSE;
2247 hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2248 *hash_table_entry = 0;
2251 if(hashtable_entry->hash_table[*hash_table_entry].key != HASHTABLE_DEL &&
2252 hashtable_entry->hash_table[*hash_table_entry].key != HASHTABLE_FREE) {
2253 *hash_entry = &hashtable_entry->hash_table[*hash_table_entry];
2254 *entry = (entry_header*)((LPBYTE)header + hashtable_entry->hash_table[*hash_table_entry].offset);
2255 (*hash_table_entry)++;
2256 return TRUE;
2259 (*hash_table_entry)++;
2262 *hash_table_off = 0;
2263 return FALSE;
2266 /* Rates an urlcache entry to determine if it can be deleted.
2268 * Score 0 means that entry can safely be removed, the bigger rating
2269 * the smaller chance of entry being removed.
2270 * DWORD_MAX means that entry can't be deleted at all.
2272 * Rating system is currently not fully compatible with native implementation.
2274 static DWORD urlcache_rate_entry(entry_url *url_entry, FILETIME *cur_time)
2276 ULARGE_INTEGER time, access_time;
2277 DWORD rating;
2279 access_time.u.LowPart = url_entry->access_time.dwLowDateTime;
2280 access_time.u.HighPart = url_entry->access_time.dwHighDateTime;
2282 time.u.LowPart = cur_time->dwLowDateTime;
2283 time.u.HighPart = cur_time->dwHighDateTime;
2285 /* Don't touch entries that were added less than 10 minutes ago */
2286 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)10*60*FILETIME_SECOND)
2287 return -1;
2289 if(url_entry->cache_entry_type & STICKY_CACHE_ENTRY)
2290 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)url_entry->exempt_delta*FILETIME_SECOND)
2291 return -1;
2293 time.QuadPart = (time.QuadPart-access_time.QuadPart)/FILETIME_SECOND;
2294 rating = 400*60*60*24/(60*60*24+time.QuadPart);
2296 if(url_entry->hit_rate > 100)
2297 rating += 100;
2298 else
2299 rating += url_entry->hit_rate;
2301 return rating;
2304 static int dword_cmp(const void *p1, const void *p2)
2306 return *(const DWORD*)p1 - *(const DWORD*)p2;
2309 /***********************************************************************
2310 * FreeUrlCacheSpaceW (WININET.@)
2312 * Frees up some cache.
2314 * PARAMETERS
2315 * cache_path [I] Which volume to free up from, or NULL if you don't care.
2316 * size [I] Percentage of the cache that should be free.
2317 * filter [I] Which entries can't be deleted (CacheEntryType)
2319 * RETURNS
2320 * TRUE success. FALSE failure.
2322 * IMPLEMENTATION
2323 * This implementation just retrieves the path of the cache directory, and
2324 * deletes its contents from the filesystem. The correct approach would
2325 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
2327 BOOL WINAPI FreeUrlCacheSpaceW(LPCWSTR cache_path, DWORD size, DWORD filter)
2329 cache_container *container;
2330 DWORD path_len, err;
2332 TRACE("(%s, %x, %x)\n", debugstr_w(cache_path), size, filter);
2334 if(size<1 || size>100) {
2335 SetLastError(ERROR_INVALID_PARAMETER);
2336 return FALSE;
2339 if(cache_path) {
2340 path_len = strlenW(cache_path);
2341 if(cache_path[path_len-1] == '\\')
2342 path_len--;
2343 }else {
2344 path_len = 0;
2347 if(size==100 && !filter) {
2348 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
2350 /* When cache_path==NULL only clean Temporary Internet Files */
2351 if((!path_len && container->cache_prefix[0]==0) ||
2352 (path_len && !strncmpiW(container->path, cache_path, path_len) &&
2353 (container->path[path_len]=='\0' || container->path[path_len]=='\\')))
2355 BOOL ret_del;
2357 WaitForSingleObject(container->mutex, INFINITE);
2359 /* unlock, delete, recreate and lock cache */
2360 cache_container_close_index(container);
2361 ret_del = cache_container_delete_dir(container->path);
2362 err = cache_container_open_index(container, MIN_BLOCK_NO);
2364 ReleaseMutex(container->mutex);
2365 if(!ret_del || (err != ERROR_SUCCESS))
2366 return FALSE;
2370 return TRUE;
2373 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
2375 urlcache_header *header;
2376 struct hash_entry *hash_entry;
2377 entry_header *entry;
2378 entry_url *url_entry;
2379 ULONGLONG desired_size, cur_size;
2380 DWORD delete_factor, hash_table_off, hash_table_entry;
2381 DWORD rate[100], rate_no;
2382 FILETIME cur_time;
2384 if((path_len || container->cache_prefix[0]!=0) &&
2385 (!path_len || strncmpiW(container->path, cache_path, path_len) ||
2386 (container->path[path_len]!='\0' && container->path[path_len]!='\\')))
2387 continue;
2389 err = cache_container_open_index(container, MIN_BLOCK_NO);
2390 if(err != ERROR_SUCCESS)
2391 continue;
2393 header = cache_container_lock_index(container);
2394 if(!header)
2395 continue;
2397 urlcache_clean_leaked_entries(container, header);
2399 desired_size = header->cache_limit.QuadPart*(100-size)/100;
2400 cur_size = header->cache_usage.QuadPart+header->exempt_usage.QuadPart;
2401 if(cur_size <= desired_size)
2402 delete_factor = 0;
2403 else
2404 delete_factor = (cur_size-desired_size)*100/cur_size;
2406 if(!delete_factor) {
2407 cache_container_unlock_index(container, header);
2408 continue;
2411 hash_table_off = 0;
2412 hash_table_entry = 0;
2413 rate_no = 0;
2414 GetSystemTimeAsFileTime(&cur_time);
2415 while(rate_no<sizeof(rate)/sizeof(*rate) &&
2416 urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2417 if(entry->signature != URL_SIGNATURE) {
2418 WARN("only url entries are currently supported\n");
2419 continue;
2422 url_entry = (entry_url*)entry;
2423 if(url_entry->cache_entry_type & filter)
2424 continue;
2426 rate[rate_no] = urlcache_rate_entry(url_entry, &cur_time);
2427 if(rate[rate_no] != -1)
2428 rate_no++;
2431 if(!rate_no) {
2432 TRACE("nothing to delete\n");
2433 cache_container_unlock_index(container, header);
2434 continue;
2437 qsort(rate, rate_no, sizeof(DWORD), dword_cmp);
2439 delete_factor = delete_factor*rate_no/100;
2440 delete_factor = rate[delete_factor];
2441 TRACE("deleting files with rating %d or less\n", delete_factor);
2443 hash_table_off = 0;
2444 while(urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2445 if(entry->signature != URL_SIGNATURE)
2446 continue;
2448 url_entry = (entry_url*)entry;
2449 if(url_entry->cache_entry_type & filter)
2450 continue;
2452 if(urlcache_rate_entry(url_entry, &cur_time) <= delete_factor) {
2453 TRACE("deleting file: %s\n", debugstr_a((char*)url_entry+url_entry->local_name_off));
2454 urlcache_entry_delete(container, header, hash_entry);
2456 if(header->cache_usage.QuadPart+header->exempt_usage.QuadPart <= desired_size)
2457 break;
2459 /* Allow other threads to use cache while cleaning */
2460 cache_container_unlock_index(container, header);
2461 if(WaitForSingleObject(dll_unload_event, 0) == WAIT_OBJECT_0) {
2462 TRACE("got dll_unload_event - finishing\n");
2463 return TRUE;
2465 Sleep(0);
2466 header = cache_container_lock_index(container);
2470 TRACE("cache size after cleaning 0x%s/0x%s\n",
2471 wine_dbgstr_longlong(header->cache_usage.QuadPart+header->exempt_usage.QuadPart),
2472 wine_dbgstr_longlong(header->cache_limit.QuadPart));
2473 cache_container_unlock_index(container, header);
2476 return TRUE;
2479 /***********************************************************************
2480 * FreeUrlCacheSpaceA (WININET.@)
2482 * See FreeUrlCacheSpaceW.
2484 BOOL WINAPI FreeUrlCacheSpaceA(LPCSTR lpszCachePath, DWORD dwSize, DWORD dwFilter)
2486 BOOL ret = FALSE;
2487 LPWSTR path = heap_strdupAtoW(lpszCachePath);
2488 if (lpszCachePath == NULL || path != NULL)
2489 ret = FreeUrlCacheSpaceW(path, dwSize, dwFilter);
2490 heap_free(path);
2491 return ret;
2494 /***********************************************************************
2495 * UnlockUrlCacheEntryFileA (WININET.@)
2498 BOOL WINAPI UnlockUrlCacheEntryFileA(LPCSTR lpszUrlName, DWORD dwReserved)
2500 urlcache_header *pHeader;
2501 struct hash_entry *pHashEntry;
2502 entry_header *pEntry;
2503 entry_url * pUrlEntry;
2504 cache_container *pContainer;
2505 DWORD error;
2507 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2509 if (dwReserved)
2511 ERR("dwReserved != 0\n");
2512 SetLastError(ERROR_INVALID_PARAMETER);
2513 return FALSE;
2516 error = cache_containers_find(lpszUrlName, &pContainer);
2517 if (error != ERROR_SUCCESS)
2519 SetLastError(error);
2520 return FALSE;
2523 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2524 if (error != ERROR_SUCCESS)
2526 SetLastError(error);
2527 return FALSE;
2530 if (!(pHeader = cache_container_lock_index(pContainer)))
2531 return FALSE;
2533 if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
2535 cache_container_unlock_index(pContainer, pHeader);
2536 TRACE("entry %s not found!\n", lpszUrlName);
2537 SetLastError(ERROR_FILE_NOT_FOUND);
2538 return FALSE;
2541 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2542 if (pEntry->signature != URL_SIGNATURE)
2544 cache_container_unlock_index(pContainer, pHeader);
2545 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2546 SetLastError(ERROR_FILE_NOT_FOUND);
2547 return FALSE;
2550 pUrlEntry = (entry_url *)pEntry;
2552 if (pUrlEntry->use_count == 0)
2554 cache_container_unlock_index(pContainer, pHeader);
2555 return FALSE;
2557 pUrlEntry->use_count--;
2558 if (!pUrlEntry->use_count)
2560 urlcache_hash_entry_set_flags(pHashEntry, HASHTABLE_URL);
2561 if (pUrlEntry->cache_entry_type & PENDING_DELETE_CACHE_ENTRY)
2562 urlcache_entry_delete(pContainer, pHeader, pHashEntry);
2565 cache_container_unlock_index(pContainer, pHeader);
2567 return TRUE;
2570 /***********************************************************************
2571 * UnlockUrlCacheEntryFileW (WININET.@)
2574 BOOL WINAPI UnlockUrlCacheEntryFileW(LPCWSTR lpszUrlName, DWORD dwReserved)
2576 char *url;
2577 BOOL ret;
2579 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
2580 return FALSE;
2582 ret = UnlockUrlCacheEntryFileA(url, dwReserved);
2583 heap_free(url);
2584 return ret;
2587 /***********************************************************************
2588 * CreateUrlCacheEntryA (WININET.@)
2591 BOOL WINAPI CreateUrlCacheEntryA(
2592 IN LPCSTR lpszUrlName,
2593 IN DWORD dwExpectedFileSize,
2594 IN LPCSTR lpszFileExtension,
2595 OUT LPSTR lpszFileName,
2596 IN DWORD dwReserved
2599 WCHAR *url_name;
2600 WCHAR *file_extension = NULL;
2601 WCHAR file_name[MAX_PATH];
2602 BOOL bSuccess = FALSE;
2603 DWORD dwError = 0;
2605 TRACE("(%s %d %s %p %d)\n", debugstr_a(lpszUrlName), dwExpectedFileSize,
2606 debugstr_a(lpszFileExtension), lpszFileName, dwReserved);
2608 if (lpszUrlName && (url_name = heap_strdupAtoW(lpszUrlName)))
2610 if (!lpszFileExtension || (file_extension = heap_strdupAtoW(lpszFileExtension)))
2612 if (CreateUrlCacheEntryW(url_name, dwExpectedFileSize, file_extension, file_name, dwReserved))
2614 if (WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL) < MAX_PATH)
2616 bSuccess = TRUE;
2618 else
2620 dwError = GetLastError();
2623 else
2625 dwError = GetLastError();
2627 heap_free(file_extension);
2629 else
2631 dwError = GetLastError();
2633 heap_free(url_name);
2634 if (!bSuccess) SetLastError(dwError);
2636 return bSuccess;
2638 /***********************************************************************
2639 * CreateUrlCacheEntryW (WININET.@)
2642 BOOL WINAPI CreateUrlCacheEntryW(
2643 IN LPCWSTR lpszUrlName,
2644 IN DWORD dwExpectedFileSize,
2645 IN LPCWSTR lpszFileExtension,
2646 OUT LPWSTR lpszFileName,
2647 IN DWORD dwReserved
2650 cache_container *pContainer;
2651 urlcache_header *pHeader;
2652 CHAR szFile[MAX_PATH];
2653 WCHAR szExtension[MAX_PATH];
2654 LPCWSTR lpszUrlPart;
2655 LPCWSTR lpszUrlEnd;
2656 LPCWSTR lpszFileNameExtension;
2657 LPWSTR lpszFileNameNoPath;
2658 int i;
2659 int countnoextension;
2660 BYTE CacheDir;
2661 LONG lBufferSize;
2662 BOOL bFound = FALSE;
2663 BOOL generate_name = FALSE;
2664 int count;
2665 DWORD error;
2666 HANDLE hFile;
2667 FILETIME ft;
2669 static const WCHAR szWWW[] = {'w','w','w',0};
2671 TRACE("(%s, 0x%08x, %s, %p, 0x%08x)\n",
2672 debugstr_w(lpszUrlName),
2673 dwExpectedFileSize,
2674 debugstr_w(lpszFileExtension),
2675 lpszFileName,
2676 dwReserved);
2678 if (dwReserved)
2679 FIXME("dwReserved 0x%08x\n", dwReserved);
2681 lpszUrlEnd = lpszUrlName + strlenW(lpszUrlName);
2683 if (((lpszUrlEnd - lpszUrlName) > 1) && (*(lpszUrlEnd - 1) == '/' || *(lpszUrlEnd - 1) == '\\'))
2684 lpszUrlEnd--;
2686 lpszUrlPart = memchrW(lpszUrlName, '?', lpszUrlEnd - lpszUrlName);
2687 if (!lpszUrlPart)
2688 lpszUrlPart = memchrW(lpszUrlName, '#', lpszUrlEnd - lpszUrlName);
2689 if (lpszUrlPart)
2690 lpszUrlEnd = lpszUrlPart;
2692 for (lpszUrlPart = lpszUrlEnd;
2693 (lpszUrlPart >= lpszUrlName);
2694 lpszUrlPart--)
2696 if ((*lpszUrlPart == '/' || *lpszUrlPart == '\\') && ((lpszUrlEnd - lpszUrlPart) > 1))
2698 bFound = TRUE;
2699 lpszUrlPart++;
2700 break;
2703 if(!bFound)
2704 lpszUrlPart++;
2706 if (!lstrcmpW(lpszUrlPart, szWWW))
2708 lpszUrlPart += lstrlenW(szWWW);
2711 count = lpszUrlEnd - lpszUrlPart;
2713 if (bFound && (count < MAX_PATH))
2715 int len = WideCharToMultiByte(CP_ACP, 0, lpszUrlPart, count, szFile, sizeof(szFile) - 1, NULL, NULL);
2716 if (!len)
2717 return FALSE;
2718 szFile[len] = '\0';
2719 while(len && szFile[--len] == '/') szFile[len] = '\0';
2721 /* FIXME: get rid of illegal characters like \, / and : */
2722 TRACE("File name: %s\n", debugstr_a(szFile));
2724 else
2726 generate_name = TRUE;
2727 szFile[0] = 0;
2730 error = cache_containers_findW(lpszUrlName, &pContainer);
2731 if (error != ERROR_SUCCESS)
2733 SetLastError(error);
2734 return FALSE;
2737 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2738 if (error != ERROR_SUCCESS)
2740 SetLastError(error);
2741 return FALSE;
2744 if (!(pHeader = cache_container_lock_index(pContainer)))
2745 return FALSE;
2747 if(pHeader->dirs_no)
2748 CacheDir = (BYTE)(rand() % pHeader->dirs_no);
2749 else
2750 CacheDir = CACHE_CONTAINER_NO_SUBDIR;
2752 lBufferSize = MAX_PATH * sizeof(WCHAR);
2753 if (!urlcache_create_file_pathW(pContainer, pHeader, szFile, CacheDir, lpszFileName, &lBufferSize))
2755 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2756 debugstr_a(szFile), lBufferSize);
2757 cache_container_unlock_index(pContainer, pHeader);
2758 return FALSE;
2761 cache_container_unlock_index(pContainer, pHeader);
2763 for (lpszFileNameNoPath = lpszFileName + lBufferSize / sizeof(WCHAR) - 2;
2764 lpszFileNameNoPath >= lpszFileName;
2765 --lpszFileNameNoPath)
2767 if (*lpszFileNameNoPath == '/' || *lpszFileNameNoPath == '\\')
2768 break;
2771 countnoextension = lstrlenW(lpszFileNameNoPath);
2772 lpszFileNameExtension = PathFindExtensionW(lpszFileNameNoPath);
2773 if (lpszFileNameExtension)
2774 countnoextension -= lstrlenW(lpszFileNameExtension);
2775 *szExtension = '\0';
2777 if (lpszFileExtension)
2779 szExtension[0] = '.';
2780 lstrcpyW(szExtension+1, lpszFileExtension);
2783 for (i = 0; i<255 && !generate_name; i++)
2785 static const WCHAR szFormat[] = {'[','%','u',']','%','s',0};
2786 WCHAR *p;
2788 wsprintfW(lpszFileNameNoPath + countnoextension, szFormat, i, szExtension);
2789 for (p = lpszFileNameNoPath + 1; *p; p++)
2791 switch (*p)
2793 case '<': case '>':
2794 case ':': case '"':
2795 case '/': case '\\':
2796 case '|': case '?':
2797 case '*':
2798 *p = '_'; break;
2799 default: break;
2802 if (p[-1] == ' ' || p[-1] == '.') p[-1] = '_';
2804 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2805 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2806 if (hFile != INVALID_HANDLE_VALUE)
2808 CloseHandle(hFile);
2809 return TRUE;
2813 /* Try to generate random name */
2814 GetSystemTimeAsFileTime(&ft);
2815 strcpyW(lpszFileNameNoPath+countnoextension+8, szExtension);
2817 for(i=0; i<255; i++)
2819 int j;
2820 ULONGLONG n = ft.dwHighDateTime;
2821 n <<= 32;
2822 n += ft.dwLowDateTime;
2823 n ^= (ULONGLONG)i<<48;
2825 for(j=0; j<8; j++)
2827 int r = (n % 36);
2828 n /= 37;
2829 lpszFileNameNoPath[countnoextension+j] = (r < 10 ? '0' + r : 'A' + r - 10);
2832 TRACE("Trying: %s\n", debugstr_w(lpszFileName));
2833 hFile = CreateFileW(lpszFileName, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2834 if (hFile != INVALID_HANDLE_VALUE)
2836 CloseHandle(hFile);
2837 return TRUE;
2841 WARN("Could not find a unique filename\n");
2842 return FALSE;
2845 /***********************************************************************
2846 * urlcache_entry_commit (Compensates for an MS bug)
2848 * The bug we are compensating for is that some drongo at Microsoft
2849 * used lpHeaderInfo to pass binary data to CommitUrlCacheEntryA.
2850 * As a consequence, CommitUrlCacheEntryA has been effectively
2851 * redefined as LPBYTE rather than LPCSTR. But CommitUrlCacheEntryW
2852 * is still defined as LPCWSTR. The result (other than madness) is
2853 * that we always need to store lpHeaderInfo in CP_ACP rather than
2854 * in UTF16, and we need to avoid converting lpHeaderInfo in
2855 * CommitUrlCacheEntryA to UTF16 and then back to CP_ACP, since the
2856 * result will lose data for arbitrary binary data.
2859 static BOOL urlcache_entry_commit(
2860 IN LPCWSTR lpszUrlName,
2861 IN LPCWSTR lpszLocalFileName,
2862 IN FILETIME ExpireTime,
2863 IN FILETIME LastModifiedTime,
2864 IN DWORD CacheEntryType,
2865 IN LPBYTE lpHeaderInfo,
2866 IN DWORD dwHeaderSize,
2867 IN LPCWSTR lpszFileExtension,
2868 IN LPCWSTR lpszOriginalUrl
2871 cache_container *pContainer;
2872 urlcache_header *pHeader;
2873 struct hash_entry *pHashEntry;
2874 entry_header *pEntry;
2875 entry_url * pUrlEntry;
2876 DWORD url_entry_offset;
2877 DWORD dwBytesNeeded = DWORD_ALIGN(sizeof(*pUrlEntry));
2878 DWORD dwOffsetLocalFileName = 0;
2879 DWORD dwOffsetHeader = 0;
2880 DWORD dwOffsetFileExtension = 0;
2881 WIN32_FILE_ATTRIBUTE_DATA file_attr;
2882 LARGE_INTEGER file_size;
2883 BYTE cDirectory;
2884 char achFile[MAX_PATH];
2885 LPSTR lpszUrlNameA = NULL;
2886 LPSTR lpszFileExtensionA = NULL;
2887 char *pchLocalFileName = 0;
2888 DWORD hit_rate = 0;
2889 DWORD exempt_delta = 0;
2890 DWORD error;
2892 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
2893 debugstr_w(lpszUrlName),
2894 debugstr_w(lpszLocalFileName),
2895 CacheEntryType,
2896 lpHeaderInfo,
2897 dwHeaderSize,
2898 debugstr_w(lpszFileExtension),
2899 debugstr_w(lpszOriginalUrl));
2901 if (CacheEntryType & STICKY_CACHE_ENTRY && !lpszLocalFileName)
2903 SetLastError(ERROR_INVALID_PARAMETER);
2904 return FALSE;
2906 if (lpszOriginalUrl)
2907 WARN(": lpszOriginalUrl ignored\n");
2909 memset(&file_attr, 0, sizeof(file_attr));
2910 if (lpszLocalFileName)
2912 if(!GetFileAttributesExW(lpszLocalFileName, GetFileExInfoStandard, &file_attr))
2913 return FALSE;
2915 file_size.u.LowPart = file_attr.nFileSizeLow;
2916 file_size.u.HighPart = file_attr.nFileSizeHigh;
2918 error = cache_containers_findW(lpszUrlName, &pContainer);
2919 if (error != ERROR_SUCCESS)
2921 SetLastError(error);
2922 return FALSE;
2925 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2926 if (error != ERROR_SUCCESS)
2928 SetLastError(error);
2929 return FALSE;
2932 if (!(pHeader = cache_container_lock_index(pContainer)))
2933 return FALSE;
2935 lpszUrlNameA = heap_strdupWtoA(lpszUrlName);
2936 if (!lpszUrlNameA)
2938 error = GetLastError();
2939 goto cleanup;
2942 if (lpszFileExtension && !(lpszFileExtensionA = heap_strdupWtoA(lpszFileExtension)))
2944 error = GetLastError();
2945 goto cleanup;
2948 if (urlcache_find_hash_entry(pHeader, lpszUrlNameA, &pHashEntry))
2950 entry_url *pUrlEntry = (entry_url*)((LPBYTE)pHeader + pHashEntry->offset);
2951 if (urlcache_hash_entry_is_locked(pHashEntry, pUrlEntry))
2953 TRACE("Trying to overwrite locked entry\n");
2954 error = ERROR_SHARING_VIOLATION;
2955 goto cleanup;
2958 hit_rate = pUrlEntry->hit_rate;
2959 exempt_delta = pUrlEntry->exempt_delta;
2960 urlcache_entry_delete(pContainer, pHeader, pHashEntry);
2963 if (pHeader->dirs_no)
2964 cDirectory = 0;
2965 else
2966 cDirectory = CACHE_CONTAINER_NO_SUBDIR;
2968 if (lpszLocalFileName)
2970 BOOL bFound = FALSE;
2972 if (strncmpW(lpszLocalFileName, pContainer->path, lstrlenW(pContainer->path)))
2974 ERR("path %s must begin with cache content path %s\n", debugstr_w(lpszLocalFileName), debugstr_w(pContainer->path));
2975 error = ERROR_INVALID_PARAMETER;
2976 goto cleanup;
2979 /* skip container path prefix */
2980 lpszLocalFileName += lstrlenW(pContainer->path);
2982 WideCharToMultiByte(CP_ACP, 0, lpszLocalFileName, -1, achFile, MAX_PATH, NULL, NULL);
2983 pchLocalFileName = achFile;
2985 if(pHeader->dirs_no)
2987 for (cDirectory = 0; cDirectory < pHeader->dirs_no; cDirectory++)
2989 if (!strncmp(pHeader->directory_data[cDirectory].name, pchLocalFileName, DIR_LENGTH))
2991 bFound = TRUE;
2992 break;
2996 if (!bFound)
2998 ERR("cache directory not found in path %s\n", debugstr_w(lpszLocalFileName));
2999 error = ERROR_INVALID_PARAMETER;
3000 goto cleanup;
3003 lpszLocalFileName += DIR_LENGTH + 1;
3004 pchLocalFileName += DIR_LENGTH + 1;
3008 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszUrlNameA) + 1);
3009 if (lpszLocalFileName)
3011 dwOffsetLocalFileName = dwBytesNeeded;
3012 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(pchLocalFileName) + 1);
3014 if (lpHeaderInfo)
3016 dwOffsetHeader = dwBytesNeeded;
3017 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + dwHeaderSize);
3019 if (lpszFileExtensionA)
3021 dwOffsetFileExtension = dwBytesNeeded;
3022 dwBytesNeeded = DWORD_ALIGN(dwBytesNeeded + strlen(lpszFileExtensionA) + 1);
3025 /* round up to next block */
3026 if (dwBytesNeeded % BLOCKSIZE)
3028 dwBytesNeeded -= dwBytesNeeded % BLOCKSIZE;
3029 dwBytesNeeded += BLOCKSIZE;
3032 error = urlcache_entry_alloc(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry);
3033 while (error == ERROR_HANDLE_DISK_FULL)
3035 error = cache_container_clean_index(pContainer, &pHeader);
3036 if (error == ERROR_SUCCESS)
3037 error = urlcache_entry_alloc(pHeader, dwBytesNeeded / BLOCKSIZE, &pEntry);
3039 if (error != ERROR_SUCCESS)
3040 goto cleanup;
3042 /* FindFirstFreeEntry fills in blocks used */
3043 pUrlEntry = (entry_url *)pEntry;
3044 url_entry_offset = (LPBYTE)pUrlEntry - (LPBYTE)pHeader;
3045 pUrlEntry->header.signature = URL_SIGNATURE;
3046 pUrlEntry->cache_dir = cDirectory;
3047 pUrlEntry->cache_entry_type = CacheEntryType | pContainer->default_entry_type;
3048 pUrlEntry->header_info_size = dwHeaderSize;
3049 if ((CacheEntryType & STICKY_CACHE_ENTRY) && !exempt_delta)
3051 /* Sticky entries have a default exempt time of one day */
3052 exempt_delta = 86400;
3054 pUrlEntry->exempt_delta = exempt_delta;
3055 pUrlEntry->hit_rate = hit_rate+1;
3056 pUrlEntry->file_extension_off = dwOffsetFileExtension;
3057 pUrlEntry->header_info_off = dwOffsetHeader;
3058 pUrlEntry->local_name_off = dwOffsetLocalFileName;
3059 pUrlEntry->url_off = DWORD_ALIGN(sizeof(*pUrlEntry));
3060 pUrlEntry->size.QuadPart = file_size.QuadPart;
3061 pUrlEntry->use_count = 0;
3062 GetSystemTimeAsFileTime(&pUrlEntry->access_time);
3063 pUrlEntry->modification_time = LastModifiedTime;
3064 file_time_to_dos_date_time(&pUrlEntry->access_time, &pUrlEntry->sync_date, &pUrlEntry->sync_time);
3065 file_time_to_dos_date_time(&ExpireTime, &pUrlEntry->expire_date, &pUrlEntry->expire_time);
3066 file_time_to_dos_date_time(&file_attr.ftLastWriteTime, &pUrlEntry->write_date, &pUrlEntry->write_time);
3068 /*** Unknowns ***/
3069 pUrlEntry->unk1 = 0;
3070 pUrlEntry->unk2 = 0;
3071 pUrlEntry->unk3 = 0x60;
3072 pUrlEntry->unk4 = 0;
3073 pUrlEntry->unk5 = 0x1010;
3074 pUrlEntry->unk7 = 0;
3075 pUrlEntry->unk8 = 0;
3078 strcpy((LPSTR)pUrlEntry + pUrlEntry->url_off, lpszUrlNameA);
3079 if (dwOffsetLocalFileName)
3080 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetLocalFileName), pchLocalFileName);
3081 if (dwOffsetHeader)
3082 memcpy((LPBYTE)pUrlEntry + dwOffsetHeader, lpHeaderInfo, dwHeaderSize);
3083 if (dwOffsetFileExtension)
3084 strcpy((LPSTR)((LPBYTE)pUrlEntry + dwOffsetFileExtension), lpszFileExtensionA);
3086 error = urlcache_hash_entry_create(pHeader, lpszUrlNameA, url_entry_offset, HASHTABLE_URL);
3087 while (error == ERROR_HANDLE_DISK_FULL)
3089 error = cache_container_clean_index(pContainer, &pHeader);
3090 if (error == ERROR_SUCCESS)
3092 pUrlEntry = (entry_url *)((LPBYTE)pHeader + url_entry_offset);
3093 error = urlcache_hash_entry_create(pHeader, lpszUrlNameA,
3094 url_entry_offset, HASHTABLE_URL);
3097 if (error != ERROR_SUCCESS)
3098 urlcache_entry_free(pHeader, &pUrlEntry->header);
3099 else
3101 if (pUrlEntry->cache_dir < pHeader->dirs_no)
3102 pHeader->directory_data[pUrlEntry->cache_dir].files_no++;
3103 if (CacheEntryType & STICKY_CACHE_ENTRY)
3104 pHeader->exempt_usage.QuadPart += file_size.QuadPart;
3105 else
3106 pHeader->cache_usage.QuadPart += file_size.QuadPart;
3107 if (pHeader->cache_usage.QuadPart + pHeader->exempt_usage.QuadPart >
3108 pHeader->cache_limit.QuadPart)
3109 handle_full_cache();
3112 cleanup:
3113 cache_container_unlock_index(pContainer, pHeader);
3114 heap_free(lpszUrlNameA);
3115 heap_free(lpszFileExtensionA);
3117 if (error == ERROR_SUCCESS)
3118 return TRUE;
3119 else
3121 SetLastError(error);
3122 return FALSE;
3126 /***********************************************************************
3127 * CommitUrlCacheEntryA (WININET.@)
3130 BOOL WINAPI CommitUrlCacheEntryA(
3131 IN LPCSTR lpszUrlName,
3132 IN LPCSTR lpszLocalFileName,
3133 IN FILETIME ExpireTime,
3134 IN FILETIME LastModifiedTime,
3135 IN DWORD CacheEntryType,
3136 IN LPBYTE lpHeaderInfo,
3137 IN DWORD dwHeaderSize,
3138 IN LPCSTR lpszFileExtension,
3139 IN LPCSTR lpszOriginalUrl
3142 WCHAR *url_name = NULL;
3143 WCHAR *local_file_name = NULL;
3144 WCHAR *original_url = NULL;
3145 WCHAR *file_extension = NULL;
3146 BOOL bSuccess = FALSE;
3148 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3149 debugstr_a(lpszUrlName),
3150 debugstr_a(lpszLocalFileName),
3151 CacheEntryType,
3152 lpHeaderInfo,
3153 dwHeaderSize,
3154 debugstr_a(lpszFileExtension),
3155 debugstr_a(lpszOriginalUrl));
3157 url_name = heap_strdupAtoW(lpszUrlName);
3158 if (!url_name)
3159 goto cleanup;
3161 if (lpszLocalFileName)
3163 local_file_name = heap_strdupAtoW(lpszLocalFileName);
3164 if (!local_file_name)
3165 goto cleanup;
3167 if (lpszFileExtension)
3169 file_extension = heap_strdupAtoW(lpszFileExtension);
3170 if (!file_extension)
3171 goto cleanup;
3173 if (lpszOriginalUrl)
3175 original_url = heap_strdupAtoW(lpszOriginalUrl);
3176 if (!original_url)
3177 goto cleanup;
3180 bSuccess = urlcache_entry_commit(url_name, local_file_name, ExpireTime, LastModifiedTime,
3181 CacheEntryType, lpHeaderInfo, dwHeaderSize,
3182 file_extension, original_url);
3184 cleanup:
3185 heap_free(original_url);
3186 heap_free(file_extension);
3187 heap_free(local_file_name);
3188 heap_free(url_name);
3189 return bSuccess;
3192 /***********************************************************************
3193 * CommitUrlCacheEntryW (WININET.@)
3196 BOOL WINAPI CommitUrlCacheEntryW(
3197 IN LPCWSTR lpszUrlName,
3198 IN LPCWSTR lpszLocalFileName,
3199 IN FILETIME ExpireTime,
3200 IN FILETIME LastModifiedTime,
3201 IN DWORD CacheEntryType,
3202 IN LPWSTR lpHeaderInfo,
3203 IN DWORD dwHeaderSize,
3204 IN LPCWSTR lpszFileExtension,
3205 IN LPCWSTR lpszOriginalUrl
3208 DWORD dwError = 0;
3209 BOOL bSuccess = FALSE;
3210 DWORD len = 0;
3211 CHAR *header_info = NULL;
3213 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n",
3214 debugstr_w(lpszUrlName),
3215 debugstr_w(lpszLocalFileName),
3216 CacheEntryType,
3217 lpHeaderInfo,
3218 dwHeaderSize,
3219 debugstr_w(lpszFileExtension),
3220 debugstr_w(lpszOriginalUrl));
3222 if (!lpHeaderInfo || (header_info = heap_strdupWtoA(lpHeaderInfo)))
3224 if(header_info)
3225 len = strlen(header_info);
3226 if (urlcache_entry_commit(lpszUrlName, lpszLocalFileName, ExpireTime, LastModifiedTime,
3227 CacheEntryType, (LPBYTE)header_info, len, lpszFileExtension, lpszOriginalUrl))
3229 bSuccess = TRUE;
3231 else
3233 dwError = GetLastError();
3235 if (header_info)
3237 heap_free(header_info);
3238 if (!bSuccess)
3239 SetLastError(dwError);
3242 return bSuccess;
3245 /***********************************************************************
3246 * ReadUrlCacheEntryStream (WININET.@)
3249 BOOL WINAPI ReadUrlCacheEntryStream(
3250 IN HANDLE hUrlCacheStream,
3251 IN DWORD dwLocation,
3252 IN OUT LPVOID lpBuffer,
3253 IN OUT LPDWORD lpdwLen,
3254 IN DWORD dwReserved
3257 /* Get handle to file from 'stream' */
3258 stream_handle *pStream = (stream_handle*)hUrlCacheStream;
3260 if (dwReserved != 0)
3262 ERR("dwReserved != 0\n");
3263 SetLastError(ERROR_INVALID_PARAMETER);
3264 return FALSE;
3267 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->url, INTERNET_MAX_URL_LENGTH))
3269 SetLastError(ERROR_INVALID_HANDLE);
3270 return FALSE;
3273 if (SetFilePointer(pStream->file, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
3274 return FALSE;
3275 return ReadFile(pStream->file, lpBuffer, *lpdwLen, lpdwLen, NULL);
3278 /***********************************************************************
3279 * RetrieveUrlCacheEntryStreamA (WININET.@)
3282 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(
3283 IN LPCSTR lpszUrlName,
3284 OUT LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
3285 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3286 IN BOOL fRandomRead,
3287 IN DWORD dwReserved
3290 /* NOTE: this is not the same as the way that the native
3291 * version allocates 'stream' handles. I did it this way
3292 * as it is much easier and no applications should depend
3293 * on this behaviour. (Native version appears to allocate
3294 * indices into a table)
3296 stream_handle *pStream;
3297 HANDLE hFile;
3299 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
3300 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3302 if (!RetrieveUrlCacheEntryFileA(lpszUrlName,
3303 lpCacheEntryInfo,
3304 lpdwCacheEntryInfoBufferSize,
3305 dwReserved))
3307 return NULL;
3310 hFile = CreateFileA(lpCacheEntryInfo->lpszLocalFileName,
3311 GENERIC_READ,
3312 FILE_SHARE_READ,
3313 NULL,
3314 OPEN_EXISTING,
3315 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3316 NULL);
3317 if (hFile == INVALID_HANDLE_VALUE)
3318 return FALSE;
3320 /* allocate handle storage space */
3321 pStream = heap_alloc(sizeof(stream_handle) + strlen(lpszUrlName) * sizeof(CHAR));
3322 if (!pStream)
3324 CloseHandle(hFile);
3325 SetLastError(ERROR_OUTOFMEMORY);
3326 return FALSE;
3329 pStream->file = hFile;
3330 strcpy(pStream->url, lpszUrlName);
3331 return pStream;
3334 /***********************************************************************
3335 * RetrieveUrlCacheEntryStreamW (WININET.@)
3338 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(
3339 IN LPCWSTR lpszUrlName,
3340 OUT LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3341 IN OUT LPDWORD lpdwCacheEntryInfoBufferSize,
3342 IN BOOL fRandomRead,
3343 IN DWORD dwReserved
3346 DWORD size;
3347 int url_len;
3348 /* NOTE: this is not the same as the way that the native
3349 * version allocates 'stream' handles. I did it this way
3350 * as it is much easier and no applications should depend
3351 * on this behaviour. (Native version appears to allocate
3352 * indices into a table)
3354 stream_handle *pStream;
3355 HANDLE hFile;
3357 TRACE( "(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3358 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved );
3360 if (!RetrieveUrlCacheEntryFileW(lpszUrlName,
3361 lpCacheEntryInfo,
3362 lpdwCacheEntryInfoBufferSize,
3363 dwReserved))
3365 return NULL;
3368 hFile = CreateFileW(lpCacheEntryInfo->lpszLocalFileName,
3369 GENERIC_READ,
3370 FILE_SHARE_READ,
3371 NULL,
3372 OPEN_EXISTING,
3373 fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0,
3374 NULL);
3375 if (hFile == INVALID_HANDLE_VALUE)
3376 return FALSE;
3378 /* allocate handle storage space */
3379 size = sizeof(stream_handle);
3380 url_len = WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, NULL, 0, NULL, NULL);
3381 size += url_len;
3382 pStream = heap_alloc(size);
3383 if (!pStream)
3385 CloseHandle(hFile);
3386 SetLastError(ERROR_OUTOFMEMORY);
3387 return FALSE;
3390 pStream->file = hFile;
3391 WideCharToMultiByte(CP_ACP, 0, lpszUrlName, -1, pStream->url, url_len, NULL, NULL);
3392 return pStream;
3395 /***********************************************************************
3396 * UnlockUrlCacheEntryStream (WININET.@)
3399 BOOL WINAPI UnlockUrlCacheEntryStream(
3400 IN HANDLE hUrlCacheStream,
3401 IN DWORD dwReserved
3404 stream_handle *pStream = (stream_handle*)hUrlCacheStream;
3406 if (dwReserved != 0)
3408 ERR("dwReserved != 0\n");
3409 SetLastError(ERROR_INVALID_PARAMETER);
3410 return FALSE;
3413 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->url, INTERNET_MAX_URL_LENGTH))
3415 SetLastError(ERROR_INVALID_HANDLE);
3416 return FALSE;
3419 if (!UnlockUrlCacheEntryFileA(pStream->url, 0))
3420 return FALSE;
3422 CloseHandle(pStream->file);
3423 heap_free(pStream);
3424 return TRUE;
3428 /***********************************************************************
3429 * DeleteUrlCacheEntryA (WININET.@)
3432 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3434 cache_container *pContainer;
3435 urlcache_header *pHeader;
3436 struct hash_entry *pHashEntry;
3437 DWORD error;
3438 BOOL ret;
3440 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3442 error = cache_containers_find(lpszUrlName, &pContainer);
3443 if (error != ERROR_SUCCESS)
3445 SetLastError(error);
3446 return FALSE;
3449 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3450 if (error != ERROR_SUCCESS)
3452 SetLastError(error);
3453 return FALSE;
3456 if (!(pHeader = cache_container_lock_index(pContainer)))
3457 return FALSE;
3459 if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
3461 cache_container_unlock_index(pContainer, pHeader);
3462 TRACE("entry %s not found!\n", lpszUrlName);
3463 SetLastError(ERROR_FILE_NOT_FOUND);
3464 return FALSE;
3467 ret = urlcache_entry_delete(pContainer, pHeader, pHashEntry);
3469 cache_container_unlock_index(pContainer, pHeader);
3471 return ret;
3474 /***********************************************************************
3475 * DeleteUrlCacheEntryW (WININET.@)
3478 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3480 char *url;
3481 BOOL ret;
3483 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
3484 return FALSE;
3486 ret = DeleteUrlCacheEntryA(url);
3487 heap_free(url);
3488 return ret;
3491 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3493 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3494 return TRUE;
3497 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3499 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3500 return TRUE;
3503 /***********************************************************************
3504 * CreateCacheContainerA (WININET.@)
3506 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3507 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3509 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3510 d1, d2, d3, d4, d5, d6, d7, d8);
3511 return TRUE;
3514 /***********************************************************************
3515 * CreateCacheContainerW (WININET.@)
3517 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3518 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3520 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3521 d1, d2, d3, d4, d5, d6, d7, d8);
3522 return TRUE;
3525 /***********************************************************************
3526 * FindFirstUrlCacheContainerA (WININET.@)
3528 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3530 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3531 return NULL;
3534 /***********************************************************************
3535 * FindFirstUrlCacheContainerW (WININET.@)
3537 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3539 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3540 return NULL;
3543 /***********************************************************************
3544 * FindNextUrlCacheContainerA (WININET.@)
3546 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3548 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3549 return FALSE;
3552 /***********************************************************************
3553 * FindNextUrlCacheContainerW (WININET.@)
3555 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3557 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3558 return FALSE;
3561 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3562 LPCSTR lpszUrlSearchPattern,
3563 DWORD dwFlags,
3564 DWORD dwFilter,
3565 GROUPID GroupId,
3566 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3567 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3568 LPVOID lpReserved,
3569 LPDWORD pcbReserved2,
3570 LPVOID lpReserved3
3573 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3574 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3575 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3576 SetLastError(ERROR_FILE_NOT_FOUND);
3577 return NULL;
3580 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3581 LPCWSTR lpszUrlSearchPattern,
3582 DWORD dwFlags,
3583 DWORD dwFilter,
3584 GROUPID GroupId,
3585 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3586 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3587 LPVOID lpReserved,
3588 LPDWORD pcbReserved2,
3589 LPVOID lpReserved3
3592 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3593 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3594 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3595 SetLastError(ERROR_FILE_NOT_FOUND);
3596 return NULL;
3599 /***********************************************************************
3600 * FindFirstUrlCacheEntryA (WININET.@)
3603 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3604 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3606 find_handle *pEntryHandle;
3608 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3610 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3611 if (!pEntryHandle)
3612 return NULL;
3614 pEntryHandle->magic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3615 if (lpszUrlSearchPattern)
3617 pEntryHandle->url_search_pattern = heap_strdupA(lpszUrlSearchPattern);
3618 if (!pEntryHandle->url_search_pattern)
3620 heap_free(pEntryHandle);
3621 return NULL;
3624 else
3625 pEntryHandle->url_search_pattern = NULL;
3626 pEntryHandle->container_idx = 0;
3627 pEntryHandle->hash_table_idx = 0;
3628 pEntryHandle->hash_entry_idx = 0;
3630 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3632 heap_free(pEntryHandle);
3633 return NULL;
3635 return pEntryHandle;
3638 /***********************************************************************
3639 * FindFirstUrlCacheEntryW (WININET.@)
3642 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3643 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3645 find_handle *pEntryHandle;
3647 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3649 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3650 if (!pEntryHandle)
3651 return NULL;
3653 pEntryHandle->magic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3654 if (lpszUrlSearchPattern)
3656 pEntryHandle->url_search_pattern = heap_strdupWtoA(lpszUrlSearchPattern);
3657 if (!pEntryHandle->url_search_pattern)
3659 heap_free(pEntryHandle);
3660 return NULL;
3663 else
3664 pEntryHandle->url_search_pattern = NULL;
3665 pEntryHandle->container_idx = 0;
3666 pEntryHandle->hash_table_idx = 0;
3667 pEntryHandle->hash_entry_idx = 0;
3669 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3671 heap_free(pEntryHandle);
3672 return NULL;
3674 return pEntryHandle;
3677 static BOOL urlcache_find_next_entry(
3678 HANDLE hEnumHandle,
3679 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3680 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3681 BOOL unicode)
3683 find_handle *pEntryHandle = (find_handle*)hEnumHandle;
3684 cache_container *pContainer;
3686 if (pEntryHandle->magic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3688 SetLastError(ERROR_INVALID_HANDLE);
3689 return FALSE;
3692 for (; cache_containers_enum(pEntryHandle->url_search_pattern, pEntryHandle->container_idx, &pContainer);
3693 pEntryHandle->container_idx++, pEntryHandle->hash_table_idx = 0)
3695 urlcache_header *pHeader;
3696 entry_hash_table *pHashTableEntry;
3697 DWORD error;
3699 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3700 if (error != ERROR_SUCCESS)
3702 SetLastError(error);
3703 return FALSE;
3706 if (!(pHeader = cache_container_lock_index(pContainer)))
3707 return FALSE;
3709 for (; urlcache_enum_hash_tables(pHeader, &pEntryHandle->hash_table_idx, &pHashTableEntry);
3710 pEntryHandle->hash_table_idx++, pEntryHandle->hash_entry_idx = 0)
3712 const struct hash_entry *pHashEntry = NULL;
3713 for (; urlcache_enum_hash_table_entries(pHeader, pHashTableEntry, &pEntryHandle->hash_entry_idx, &pHashEntry);
3714 pEntryHandle->hash_entry_idx++)
3716 const entry_url *pUrlEntry;
3717 const entry_header *pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
3719 if (pEntry->signature != URL_SIGNATURE)
3720 continue;
3722 pUrlEntry = (const entry_url *)pEntry;
3723 TRACE("Found URL: %s\n",
3724 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->url_off));
3725 TRACE("Header info: %s\n",
3726 debugstr_an((LPCSTR)pUrlEntry + pUrlEntry->header_info_off,
3727 pUrlEntry->header_info_size));
3729 error = urlcache_copy_entry(
3730 pContainer,
3731 pHeader,
3732 lpNextCacheEntryInfo,
3733 lpdwNextCacheEntryInfoBufferSize,
3734 pUrlEntry,
3735 unicode);
3736 if (error != ERROR_SUCCESS)
3738 cache_container_unlock_index(pContainer, pHeader);
3739 SetLastError(error);
3740 return FALSE;
3742 if(pUrlEntry->local_name_off)
3743 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->local_name_off));
3745 /* increment the current index so that next time the function
3746 * is called the next entry is returned */
3747 pEntryHandle->hash_entry_idx++;
3748 cache_container_unlock_index(pContainer, pHeader);
3749 return TRUE;
3753 cache_container_unlock_index(pContainer, pHeader);
3756 SetLastError(ERROR_NO_MORE_ITEMS);
3757 return FALSE;
3760 /***********************************************************************
3761 * FindNextUrlCacheEntryA (WININET.@)
3763 BOOL WINAPI FindNextUrlCacheEntryA(
3764 HANDLE hEnumHandle,
3765 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3766 LPDWORD lpdwNextCacheEntryInfoBufferSize)
3768 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3770 return urlcache_find_next_entry(hEnumHandle, lpNextCacheEntryInfo,
3771 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
3774 /***********************************************************************
3775 * FindNextUrlCacheEntryW (WININET.@)
3777 BOOL WINAPI FindNextUrlCacheEntryW(
3778 HANDLE hEnumHandle,
3779 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3780 LPDWORD lpdwNextCacheEntryInfoBufferSize
3783 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3785 return urlcache_find_next_entry(hEnumHandle,
3786 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
3787 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
3790 /***********************************************************************
3791 * FindCloseUrlCache (WININET.@)
3793 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3795 find_handle *pEntryHandle = (find_handle*)hEnumHandle;
3797 TRACE("(%p)\n", hEnumHandle);
3799 if (!pEntryHandle || pEntryHandle->magic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3801 SetLastError(ERROR_INVALID_HANDLE);
3802 return FALSE;
3805 pEntryHandle->magic = 0;
3806 heap_free(pEntryHandle->url_search_pattern);
3807 heap_free(pEntryHandle);
3808 return TRUE;
3811 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3812 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3814 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3815 dwSearchCondition, lpGroupId, lpReserved);
3816 return NULL;
3819 BOOL WINAPI FindNextUrlCacheEntryExA(
3820 HANDLE hEnumHandle,
3821 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3822 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3823 LPVOID lpReserved,
3824 LPDWORD pcbReserved2,
3825 LPVOID lpReserved3
3828 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3829 lpReserved, pcbReserved2, lpReserved3);
3830 return FALSE;
3833 BOOL WINAPI FindNextUrlCacheEntryExW(
3834 HANDLE hEnumHandle,
3835 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3836 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3837 LPVOID lpReserved,
3838 LPDWORD pcbReserved2,
3839 LPVOID lpReserved3
3842 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3843 lpReserved, pcbReserved2, lpReserved3);
3844 return FALSE;
3847 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3849 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3850 return FALSE;
3853 /***********************************************************************
3854 * CreateUrlCacheGroup (WININET.@)
3857 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3859 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3860 return FALSE;
3863 /***********************************************************************
3864 * DeleteUrlCacheGroup (WININET.@)
3867 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3869 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3870 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3871 return FALSE;
3874 /***********************************************************************
3875 * DeleteWpadCacheForNetworks (WININET.@)
3876 * Undocumented, added in IE8
3878 BOOL WINAPI DeleteWpadCacheForNetworks(DWORD unk1)
3880 FIXME("(%d) stub\n", unk1);
3881 return FALSE;
3884 /***********************************************************************
3885 * SetUrlCacheEntryGroupA (WININET.@)
3888 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3889 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3890 LPVOID lpReserved)
3892 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3893 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3894 pbGroupAttributes, cbGroupAttributes, lpReserved);
3895 SetLastError(ERROR_FILE_NOT_FOUND);
3896 return FALSE;
3899 /***********************************************************************
3900 * SetUrlCacheEntryGroupW (WININET.@)
3903 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3904 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3905 LPVOID lpReserved)
3907 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3908 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3909 pbGroupAttributes, cbGroupAttributes, lpReserved);
3910 SetLastError(ERROR_FILE_NOT_FOUND);
3911 return FALSE;
3914 /***********************************************************************
3915 * GetUrlCacheConfigInfoW (WININET.@)
3917 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3919 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3920 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3921 return FALSE;
3924 /***********************************************************************
3925 * GetUrlCacheConfigInfoA (WININET.@)
3927 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3929 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3930 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3931 return FALSE;
3934 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3935 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3936 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3938 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3939 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3940 lpdwGroupInfo, lpReserved);
3941 return FALSE;
3944 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3945 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3946 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3948 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3949 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3950 lpdwGroupInfo, lpReserved);
3951 return FALSE;
3954 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3955 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3957 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3958 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3959 return TRUE;
3962 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3963 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3965 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3966 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3967 return TRUE;
3970 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3972 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3973 return TRUE;
3976 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3978 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3979 return TRUE;
3982 /***********************************************************************
3983 * DeleteIE3Cache (WININET.@)
3985 * Deletes the files used by the IE3 URL caching system.
3987 * PARAMS
3988 * hWnd [I] A dummy window.
3989 * hInst [I] Instance of process calling the function.
3990 * lpszCmdLine [I] Options used by function.
3991 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3993 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3995 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3996 return 0;
3999 static BOOL urlcache_entry_is_expired(const entry_url *pUrlEntry,
4000 FILETIME *pftLastModified)
4002 BOOL ret;
4003 FILETIME now, expired;
4005 *pftLastModified = pUrlEntry->modification_time;
4006 GetSystemTimeAsFileTime(&now);
4007 dos_date_time_to_file_time(pUrlEntry->expire_date,
4008 pUrlEntry->expire_time, &expired);
4009 /* If the expired time is 0, it's interpreted as not expired */
4010 if (!expired.dwLowDateTime && !expired.dwHighDateTime)
4011 ret = FALSE;
4012 else
4013 ret = CompareFileTime(&expired, &now) < 0;
4014 return ret;
4017 /***********************************************************************
4018 * IsUrlCacheEntryExpiredA (WININET.@)
4020 * PARAMS
4021 * url [I] Url
4022 * dwFlags [I] Unknown
4023 * pftLastModified [O] Last modified time
4025 BOOL WINAPI IsUrlCacheEntryExpiredA( LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified )
4027 urlcache_header *pHeader;
4028 struct hash_entry *pHashEntry;
4029 const entry_header *pEntry;
4030 const entry_url * pUrlEntry;
4031 cache_container *pContainer;
4032 BOOL expired;
4034 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
4036 if (!url || !pftLastModified)
4037 return TRUE;
4038 if (dwFlags)
4039 FIXME("unknown flags 0x%08x\n", dwFlags);
4041 /* Any error implies that the URL is expired, i.e. not in the cache */
4042 if (cache_containers_find(url, &pContainer))
4044 memset(pftLastModified, 0, sizeof(*pftLastModified));
4045 return TRUE;
4048 if (cache_container_open_index(pContainer, MIN_BLOCK_NO))
4050 memset(pftLastModified, 0, sizeof(*pftLastModified));
4051 return TRUE;
4054 if (!(pHeader = cache_container_lock_index(pContainer)))
4056 memset(pftLastModified, 0, sizeof(*pftLastModified));
4057 return TRUE;
4060 if (!urlcache_find_hash_entry(pHeader, url, &pHashEntry))
4062 cache_container_unlock_index(pContainer, pHeader);
4063 memset(pftLastModified, 0, sizeof(*pftLastModified));
4064 TRACE("entry %s not found!\n", url);
4065 return TRUE;
4068 pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
4069 if (pEntry->signature != URL_SIGNATURE)
4071 cache_container_unlock_index(pContainer, pHeader);
4072 memset(pftLastModified, 0, sizeof(*pftLastModified));
4073 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
4074 return TRUE;
4077 pUrlEntry = (const entry_url *)pEntry;
4078 expired = urlcache_entry_is_expired(pUrlEntry, pftLastModified);
4080 cache_container_unlock_index(pContainer, pHeader);
4082 return expired;
4085 /***********************************************************************
4086 * IsUrlCacheEntryExpiredW (WININET.@)
4088 * PARAMS
4089 * url [I] Url
4090 * dwFlags [I] Unknown
4091 * pftLastModified [O] Last modified time
4093 BOOL WINAPI IsUrlCacheEntryExpiredW( LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified )
4095 urlcache_header *pHeader;
4096 struct hash_entry *pHashEntry;
4097 const entry_header *pEntry;
4098 const entry_url * pUrlEntry;
4099 cache_container *pContainer;
4100 BOOL expired;
4102 TRACE("(%s, %08x, %p)\n", debugstr_w(url), dwFlags, pftLastModified);
4104 if (!url || !pftLastModified)
4105 return TRUE;
4106 if (dwFlags)
4107 FIXME("unknown flags 0x%08x\n", dwFlags);
4109 /* Any error implies that the URL is expired, i.e. not in the cache */
4110 if (cache_containers_findW(url, &pContainer))
4112 memset(pftLastModified, 0, sizeof(*pftLastModified));
4113 return TRUE;
4116 if (cache_container_open_index(pContainer, MIN_BLOCK_NO))
4118 memset(pftLastModified, 0, sizeof(*pftLastModified));
4119 return TRUE;
4122 if (!(pHeader = cache_container_lock_index(pContainer)))
4124 memset(pftLastModified, 0, sizeof(*pftLastModified));
4125 return TRUE;
4128 if (!urlcache_find_hash_entryW(pHeader, url, &pHashEntry))
4130 cache_container_unlock_index(pContainer, pHeader);
4131 memset(pftLastModified, 0, sizeof(*pftLastModified));
4132 TRACE("entry %s not found!\n", debugstr_w(url));
4133 return TRUE;
4136 if (!urlcache_find_hash_entryW(pHeader, url, &pHashEntry))
4138 cache_container_unlock_index(pContainer, pHeader);
4139 memset(pftLastModified, 0, sizeof(*pftLastModified));
4140 TRACE("entry %s not found!\n", debugstr_w(url));
4141 return TRUE;
4144 pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
4145 if (pEntry->signature != URL_SIGNATURE)
4147 cache_container_unlock_index(pContainer, pHeader);
4148 memset(pftLastModified, 0, sizeof(*pftLastModified));
4149 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
4150 return TRUE;
4153 pUrlEntry = (const entry_url *)pEntry;
4154 expired = urlcache_entry_is_expired(pUrlEntry, pftLastModified);
4156 cache_container_unlock_index(pContainer, pHeader);
4158 return expired;
4161 /***********************************************************************
4162 * GetDiskInfoA (WININET.@)
4164 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
4166 BOOL ret;
4167 ULARGE_INTEGER bytes_free, bytes_total;
4169 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
4171 if (!path)
4173 SetLastError(ERROR_INVALID_PARAMETER);
4174 return FALSE;
4177 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
4179 if (cluster_size) *cluster_size = 1;
4180 if (free) *free = bytes_free.QuadPart;
4181 if (total) *total = bytes_total.QuadPart;
4183 return ret;
4186 /***********************************************************************
4187 * RegisterUrlCacheNotification (WININET.@)
4189 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
4191 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
4192 return 0;
4195 /***********************************************************************
4196 * IncrementUrlCacheHeaderData (WININET.@)
4198 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
4200 FIXME("(%u, %p)\n", index, data);
4201 return FALSE;
4204 /***********************************************************************
4205 * RunOnceUrlCache (WININET.@)
4208 DWORD WINAPI RunOnceUrlCache(HWND hwnd, HINSTANCE hinst, LPSTR cmd, int cmdshow)
4210 FIXME("(%p, %p, %s, %d): stub\n", hwnd, hinst, debugstr_a(cmd), cmdshow);
4211 return 0;
4214 BOOL init_urlcache(void)
4216 dll_unload_event = CreateEventW(NULL, FALSE, FALSE, NULL);
4217 if(!dll_unload_event)
4218 return FALSE;
4220 free_cache_running = CreateSemaphoreW(NULL, 1, 1, NULL);
4221 if(!free_cache_running) {
4222 CloseHandle(dll_unload_event);
4223 return FALSE;
4226 cache_containers_init();
4227 return TRUE;
4230 void free_urlcache(void)
4232 SetEvent(dll_unload_event);
4233 WaitForSingleObject(free_cache_running, INFINITE);
4234 ReleaseSemaphore(free_cache_running, 1, NULL);
4235 CloseHandle(free_cache_running);
4236 CloseHandle(dll_unload_event);
4238 cache_containers_free();
4241 /***********************************************************************
4242 * LoadUrlCacheContent (WININET.@)
4244 BOOL WINAPI LoadUrlCacheContent(void)
4246 FIXME("stub!\n");
4247 return FALSE;