msado15: Implement Dispatch functions in _Connection.
[wine.git] / dlls / wininet / urlcache.c
blobc4ca54ac87cd978248840d0dc34540eabfcb5115
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 #define NONAMELESSUNION
26 #define NONAMELESSSTRUCT
28 #include "ws2tcpip.h"
30 #include <stdarg.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <time.h>
36 #include "windef.h"
37 #include "winbase.h"
38 #include "winuser.h"
39 #include "wininet.h"
40 #include "winineti.h"
41 #include "winerror.h"
42 #include "winreg.h"
43 #include "shlwapi.h"
44 #include "shlobj.h"
45 #include "shellapi.h"
47 #include "internet.h"
48 #include "wine/debug.h"
50 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
52 static const char urlcache_ver_prefix[] = "WINE URLCache Ver ";
53 static const char urlcache_ver[] = "0.2012001";
55 #ifndef CHAR_BIT
56 #define CHAR_BIT (8 * sizeof(CHAR))
57 #endif
59 #define ENTRY_START_OFFSET 0x4000
60 #define DIR_LENGTH 8
61 #define MAX_DIR_NO 0x20
62 #define BLOCKSIZE 128
63 #define HASHTABLE_SIZE 448
64 #define HASHTABLE_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
65 #define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
66 #define ALLOCATION_TABLE_OFFSET 0x250
67 #define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
68 #define MIN_BLOCK_NO 0x80
69 #define MAX_BLOCK_NO (ALLOCATION_TABLE_SIZE * CHAR_BIT)
70 #define FILE_SIZE(blocks) ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
72 #define HASHTABLE_URL 0
73 #define HASHTABLE_DEL 1
74 #define HASHTABLE_LOCK 2
75 #define HASHTABLE_FREE 3
76 #define HASHTABLE_REDR 5
77 #define HASHTABLE_FLAG_BITS 6
79 #define PENDING_DELETE_CACHE_ENTRY 0x00400000
80 #define INSTALLED_CACHE_ENTRY 0x10000000
81 #define GET_INSTALLED_ENTRY 0x200
82 #define CACHE_CONTAINER_NO_SUBDIR 0xFE
84 #define CACHE_HEADER_DATA_ROOT_LEAK_OFFSET 0x16
86 #define FILETIME_SECOND 10000000
88 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
89 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
90 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
91 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
92 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
94 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
96 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
98 typedef struct
100 DWORD signature;
101 DWORD blocks_used; /* number of 128byte blocks used by this entry */
102 } entry_header;
104 typedef struct
106 entry_header header;
107 FILETIME modification_time;
108 FILETIME access_time;
109 WORD expire_date; /* expire date in dos format */
110 WORD expire_time; /* expire time in dos format */
111 DWORD unk1; /* usually zero */
112 ULARGE_INTEGER size; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
113 DWORD unk2; /* usually zero */
114 DWORD exempt_delta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
115 DWORD unk3; /* usually 0x60 */
116 DWORD url_off; /* offset of start of url from start of entry */
117 BYTE cache_dir; /* index of cache directory this url is stored in */
118 BYTE unk4; /* usually zero */
119 WORD unk5; /* usually 0x1010 */
120 DWORD local_name_off; /* offset of start of local filename from start of entry */
121 DWORD cache_entry_type; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
122 DWORD header_info_off; /* offset of start of header info from start of entry */
123 DWORD header_info_size;
124 DWORD file_extension_off; /* offset of start of file extension from start of entry */
125 WORD sync_date; /* last sync date in dos format */
126 WORD sync_time; /* last sync time in dos format */
127 DWORD hit_rate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
128 DWORD use_count; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
129 WORD write_date;
130 WORD write_time;
131 DWORD unk7; /* usually zero */
132 DWORD unk8; /* usually zero */
133 /* packing to dword align start of next field */
134 /* CHAR szSourceUrlName[]; (url) */
135 /* packing to dword align start of next field */
136 /* CHAR szLocalFileName[]; (local file name excluding path) */
137 /* packing to dword align start of next field */
138 /* CHAR szHeaderInfo[]; (header info) */
139 } entry_url;
141 struct hash_entry
143 DWORD key;
144 DWORD offset;
147 typedef struct
149 entry_header header;
150 DWORD next;
151 DWORD id;
152 struct hash_entry hash_table[HASHTABLE_SIZE];
153 } entry_hash_table;
155 typedef struct
157 char signature[28];
158 DWORD size;
159 DWORD hash_table_off;
160 DWORD capacity_in_blocks;
161 DWORD blocks_in_use;
162 DWORD unk1;
163 ULARGE_INTEGER cache_limit;
164 ULARGE_INTEGER cache_usage;
165 ULARGE_INTEGER exempt_usage;
166 DWORD dirs_no;
167 struct _directory_data
169 DWORD files_no;
170 char name[DIR_LENGTH];
171 } directory_data[MAX_DIR_NO];
172 DWORD options[0x21];
173 BYTE allocation_table[ALLOCATION_TABLE_SIZE];
174 } urlcache_header;
176 typedef struct
178 HANDLE file;
179 CHAR url[1];
180 } stream_handle;
182 typedef struct
184 struct list entry; /* part of a list */
185 char *cache_prefix; /* string that has to be prefixed for this container to be used */
186 LPWSTR path; /* path to url container directory */
187 HANDLE mapping; /* handle of file mapping */
188 DWORD file_size; /* size of file when mapping was opened */
189 HANDLE mutex; /* handle of mutex */
190 DWORD default_entry_type;
191 } cache_container;
193 typedef struct
195 DWORD magic;
196 char *url_search_pattern;
197 DWORD container_idx;
198 DWORD hash_table_idx;
199 DWORD hash_entry_idx;
200 } find_handle;
202 /* List of all containers available */
203 static struct list UrlContainers = LIST_INIT(UrlContainers);
205 static inline char *heap_strdupWtoUTF8(LPCWSTR str)
207 char *ret = NULL;
209 if(str) {
210 DWORD size = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);
211 ret = heap_alloc(size);
212 if(ret)
213 WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, size, NULL, NULL);
216 return ret;
219 /***********************************************************************
220 * urlcache_block_is_free (Internal)
222 * Is the specified block number free?
224 * RETURNS
225 * zero if free
226 * non-zero otherwise
229 static inline BYTE urlcache_block_is_free(BYTE *allocation_table, DWORD block_number)
231 BYTE mask = 1 << (block_number%CHAR_BIT);
232 return (allocation_table[block_number/CHAR_BIT] & mask) == 0;
235 /***********************************************************************
236 * urlcache_block_free (Internal)
238 * Marks the specified block as free
240 * CAUTION
241 * this function is not updating used blocks count
243 * RETURNS
244 * nothing
247 static inline void urlcache_block_free(BYTE *allocation_table, DWORD block_number)
249 BYTE mask = ~(1 << (block_number%CHAR_BIT));
250 allocation_table[block_number/CHAR_BIT] &= mask;
253 /***********************************************************************
254 * urlcache_block_alloc (Internal)
256 * Marks the specified block as allocated
258 * CAUTION
259 * this function is not updating used blocks count
261 * RETURNS
262 * nothing
265 static inline void urlcache_block_alloc(BYTE *allocation_table, DWORD block_number)
267 BYTE mask = 1 << (block_number%CHAR_BIT);
268 allocation_table[block_number/CHAR_BIT] |= mask;
271 /***********************************************************************
272 * urlcache_entry_alloc (Internal)
274 * Finds and allocates the first block of free space big enough and
275 * sets entry to point to it.
277 * RETURNS
278 * ERROR_SUCCESS when free memory block was found
279 * Any other Win32 error code if the entry could not be added
282 static DWORD urlcache_entry_alloc(urlcache_header *header, DWORD blocks_needed, entry_header **entry)
284 DWORD block, block_size;
286 for(block=0; block<header->capacity_in_blocks; block+=block_size+1)
288 block_size = 0;
289 while(block_size<blocks_needed && block_size+block<header->capacity_in_blocks
290 && urlcache_block_is_free(header->allocation_table, block+block_size))
291 block_size++;
293 if(block_size == blocks_needed)
295 DWORD index;
297 TRACE("Found free blocks starting at no. %d (0x%x)\n", block, ENTRY_START_OFFSET+block*BLOCKSIZE);
299 for(index=0; index<blocks_needed; index++)
300 urlcache_block_alloc(header->allocation_table, block+index);
302 *entry = (entry_header*)((BYTE*)header+ENTRY_START_OFFSET+block*BLOCKSIZE);
303 for(index=0; index<blocks_needed*BLOCKSIZE/sizeof(DWORD); index++)
304 ((DWORD*)*entry)[index] = 0xdeadbeef;
305 (*entry)->blocks_used = blocks_needed;
307 header->blocks_in_use += blocks_needed;
308 return ERROR_SUCCESS;
312 return ERROR_HANDLE_DISK_FULL;
315 /***********************************************************************
316 * urlcache_entry_free (Internal)
318 * Deletes the specified entry and frees the space allocated to it
320 * RETURNS
321 * TRUE if it succeeded
322 * FALSE if it failed
325 static BOOL urlcache_entry_free(urlcache_header *header, entry_header *entry)
327 DWORD start_block, block;
329 /* update allocation table */
330 start_block = ((DWORD)((BYTE*)entry - (BYTE*)header) - ENTRY_START_OFFSET) / BLOCKSIZE;
331 for(block = start_block; block < start_block+entry->blocks_used; block++)
332 urlcache_block_free(header->allocation_table, block);
334 header->blocks_in_use -= entry->blocks_used;
335 return TRUE;
338 /***********************************************************************
339 * urlcache_create_hash_table (Internal)
341 * Creates a new hash table in free space and adds it to the chain of existing
342 * hash tables.
344 * RETURNS
345 * ERROR_SUCCESS if the hash table was created
346 * ERROR_DISK_FULL if the hash table could not be created
349 static DWORD urlcache_create_hash_table(urlcache_header *header, entry_hash_table *hash_table_prev, entry_hash_table **hash_table)
351 DWORD dwOffset, error;
352 int i;
354 if((error = urlcache_entry_alloc(header, 0x20, (entry_header**)hash_table)) != ERROR_SUCCESS)
355 return error;
357 dwOffset = (BYTE*)*hash_table-(BYTE*)header;
359 if(hash_table_prev)
360 hash_table_prev->next = dwOffset;
361 else
362 header->hash_table_off = dwOffset;
364 (*hash_table)->header.signature = HASH_SIGNATURE;
365 (*hash_table)->next = 0;
366 (*hash_table)->id = hash_table_prev ? hash_table_prev->id+1 : 0;
367 for(i = 0; i < HASHTABLE_SIZE; i++) {
368 (*hash_table)->hash_table[i].offset = HASHTABLE_FREE;
369 (*hash_table)->hash_table[i].key = HASHTABLE_FREE;
371 return ERROR_SUCCESS;
374 /***********************************************************************
375 * cache_container_create_object_name (Internal)
377 * Converts a path to a name suitable for use as a Win32 object name.
378 * Replaces '\\' characters in-place with the specified character
379 * (usually '_' or '!')
381 * RETURNS
382 * nothing
385 static void cache_container_create_object_name(LPWSTR lpszPath, WCHAR replace)
387 for (; *lpszPath; lpszPath++)
389 if (*lpszPath == '\\')
390 *lpszPath = replace;
394 /* Caller must hold container lock */
395 static HANDLE cache_container_map_index(HANDLE file, const WCHAR *path, DWORD size, BOOL *validate)
397 WCHAR mapping_name[MAX_PATH];
398 HANDLE mapping;
400 wsprintfW(mapping_name, L"%sindex.dat_%lu", path, size);
401 cache_container_create_object_name(mapping_name, '_');
403 mapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, mapping_name);
404 if(mapping) {
405 if(validate) *validate = FALSE;
406 return mapping;
409 if(validate) *validate = TRUE;
410 return CreateFileMappingW(file, NULL, PAGE_READWRITE, 0, 0, mapping_name);
413 /* Caller must hold container lock */
414 static DWORD cache_container_set_size(cache_container *container, HANDLE file, DWORD blocks_no)
416 DWORD file_size = FILE_SIZE(blocks_no);
417 WCHAR dir_path[MAX_PATH], *dir_name;
418 entry_hash_table *hashtable_entry;
419 urlcache_header *header;
420 HANDLE mapping;
421 FILETIME ft;
422 HKEY key;
423 int i, j;
425 if(SetFilePointer(file, file_size, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
426 return GetLastError();
428 if(!SetEndOfFile(file))
429 return GetLastError();
431 mapping = cache_container_map_index(file, container->path, file_size, NULL);
432 if(!mapping)
433 return GetLastError();
435 header = MapViewOfFile(mapping, FILE_MAP_WRITE, 0, 0, 0);
436 if(!header) {
437 CloseHandle(mapping);
438 return GetLastError();
441 if(blocks_no != MIN_BLOCK_NO) {
442 if(file_size > header->size)
443 memset((char*)header+header->size, 0, file_size-header->size);
444 header->size = file_size;
445 header->capacity_in_blocks = blocks_no;
447 UnmapViewOfFile(header);
448 CloseHandle(container->mapping);
449 container->mapping = mapping;
450 container->file_size = file_size;
451 return ERROR_SUCCESS;
454 memset(header, 0, file_size);
455 /* First set some constants and defaults in the header */
456 memcpy(header->signature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1);
457 memcpy(header->signature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1);
458 header->size = file_size;
459 header->capacity_in_blocks = blocks_no;
460 /* 127MB - taken from default for Windows 2000 */
461 header->cache_limit.QuadPart = 0x07ff5400;
462 /* Copied from a Windows 2000 cache index */
463 header->dirs_no = container->default_entry_type==NORMAL_CACHE_ENTRY ? 4 : 0;
465 /* If the registry has a cache size set, use the registry value */
466 if(RegOpenKeyW(HKEY_CURRENT_USER,
467 L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Cache\\Content", &key) == ERROR_SUCCESS) {
468 DWORD dw, len = sizeof(dw), keytype;
470 if(RegQueryValueExW(key, L"CacheLimit", NULL, &keytype, (BYTE*)&dw, &len) == ERROR_SUCCESS &&
471 keytype == REG_DWORD)
472 header->cache_limit.QuadPart = (ULONGLONG)dw * 1024;
473 RegCloseKey(key);
476 urlcache_create_hash_table(header, NULL, &hashtable_entry);
478 /* Last step - create the directories */
479 lstrcpyW(dir_path, container->path);
480 dir_name = dir_path + lstrlenW(dir_path);
481 dir_name[8] = 0;
483 GetSystemTimeAsFileTime(&ft);
485 for(i=0; i<header->dirs_no; ++i) {
486 header->directory_data[i].files_no = 0;
487 for(j=0;; ++j) {
488 ULONGLONG n = ft.dwHighDateTime;
489 int k;
491 /* Generate a file name to attempt to create.
492 * This algorithm will create what will appear
493 * to be random and unrelated directory names
494 * of up to 9 characters in length.
496 n <<= 32;
497 n += ft.dwLowDateTime;
498 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
500 for(k = 0; k < 8; ++k) {
501 int r = (n % 36);
503 /* Dividing by a prime greater than 36 helps
504 * with the appearance of randomness
506 n /= 37;
508 if(r < 10)
509 dir_name[k] = '0' + r;
510 else
511 dir_name[k] = 'A' + (r - 10);
514 if(CreateDirectoryW(dir_path, 0)) {
515 /* The following is OK because we generated an
516 * 8 character directory name made from characters
517 * [A-Z0-9], which are equivalent for all code
518 * pages and for UTF-16
520 for (k = 0; k < 8; ++k)
521 header->directory_data[i].name[k] = dir_name[k];
522 break;
523 }else if(j >= 255) {
524 /* Give up. The most likely cause of this
525 * is a full disk, but whatever the cause
526 * is, it should be more than apparent that
527 * we won't succeed.
529 UnmapViewOfFile(header);
530 CloseHandle(mapping);
531 return GetLastError();
536 UnmapViewOfFile(header);
537 CloseHandle(container->mapping);
538 container->mapping = mapping;
539 container->file_size = file_size;
540 return ERROR_SUCCESS;
543 static BOOL cache_container_is_valid(urlcache_header *header, DWORD file_size)
545 DWORD allocation_size, count_bits, i;
547 if(file_size < FILE_SIZE(MIN_BLOCK_NO))
548 return FALSE;
550 if(file_size != header->size)
551 return FALSE;
553 if (!memcmp(header->signature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1) &&
554 memcmp(header->signature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1))
555 return FALSE;
557 if(FILE_SIZE(header->capacity_in_blocks) != file_size)
558 return FALSE;
560 allocation_size = 0;
561 for(i=0; i<header->capacity_in_blocks/8; i++) {
562 for(count_bits = header->allocation_table[i]; count_bits!=0; count_bits>>=1) {
563 if(count_bits & 1)
564 allocation_size++;
567 if(allocation_size != header->blocks_in_use)
568 return FALSE;
570 for(; i<ALLOCATION_TABLE_SIZE; i++) {
571 if(header->allocation_table[i])
572 return FALSE;
575 return TRUE;
578 /***********************************************************************
579 * cache_container_open_index (Internal)
581 * Opens the index file and saves mapping handle
583 * RETURNS
584 * ERROR_SUCCESS if succeeded
585 * Any other Win32 error code if failed
588 static DWORD cache_container_open_index(cache_container *container, DWORD blocks_no)
590 HANDLE file;
591 WCHAR index_path[MAX_PATH];
592 DWORD file_size;
593 BOOL validate;
595 WaitForSingleObject(container->mutex, INFINITE);
597 if(container->mapping) {
598 ReleaseMutex(container->mutex);
599 return ERROR_SUCCESS;
602 lstrcpyW(index_path, container->path);
603 lstrcatW(index_path, L"index.dat");
605 file = CreateFileW(index_path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
606 if(file == INVALID_HANDLE_VALUE) {
607 /* Maybe the directory wasn't there? Try to create it */
608 if(CreateDirectoryW(container->path, 0))
609 file = CreateFileW(index_path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
611 if(file == INVALID_HANDLE_VALUE) {
612 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(index_path));
613 ReleaseMutex(container->mutex);
614 return GetLastError();
617 file_size = GetFileSize(file, NULL);
618 if(file_size == INVALID_FILE_SIZE) {
619 CloseHandle(file);
620 ReleaseMutex(container->mutex);
621 return GetLastError();
624 if(blocks_no < MIN_BLOCK_NO)
625 blocks_no = MIN_BLOCK_NO;
626 else if(blocks_no > MAX_BLOCK_NO)
627 blocks_no = MAX_BLOCK_NO;
629 if(file_size < FILE_SIZE(blocks_no)) {
630 DWORD ret = cache_container_set_size(container, file, blocks_no);
631 CloseHandle(file);
632 ReleaseMutex(container->mutex);
633 return ret;
636 container->file_size = file_size;
637 container->mapping = cache_container_map_index(file, container->path, file_size, &validate);
638 CloseHandle(file);
639 if(container->mapping && validate) {
640 urlcache_header *header = MapViewOfFile(container->mapping, FILE_MAP_WRITE, 0, 0, 0);
642 if(header && !cache_container_is_valid(header, file_size)) {
643 WARN("detected old or broken index.dat file\n");
644 UnmapViewOfFile(header);
645 FreeUrlCacheSpaceW(container->path, 100, 0);
646 }else if(header) {
647 UnmapViewOfFile(header);
648 }else {
649 CloseHandle(container->mapping);
650 container->mapping = NULL;
654 if(!container->mapping)
656 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
657 ReleaseMutex(container->mutex);
658 return GetLastError();
661 ReleaseMutex(container->mutex);
662 return ERROR_SUCCESS;
665 /***********************************************************************
666 * cache_container_close_index (Internal)
668 * Closes the index
670 * RETURNS
671 * nothing
674 static void cache_container_close_index(cache_container *pContainer)
676 CloseHandle(pContainer->mapping);
677 pContainer->mapping = NULL;
680 static BOOL cache_containers_add(const char *cache_prefix, LPCWSTR path,
681 DWORD default_entry_type, LPWSTR mutex_name)
683 cache_container *pContainer = heap_alloc(sizeof(cache_container));
684 int cache_prefix_len = strlen(cache_prefix);
686 if (!pContainer)
688 return FALSE;
691 pContainer->mapping = NULL;
692 pContainer->file_size = 0;
693 pContainer->default_entry_type = default_entry_type;
695 pContainer->path = heap_strdupW(path);
696 if (!pContainer->path)
698 heap_free(pContainer);
699 return FALSE;
702 pContainer->cache_prefix = heap_alloc(cache_prefix_len+1);
703 if (!pContainer->cache_prefix)
705 heap_free(pContainer->path);
706 heap_free(pContainer);
707 return FALSE;
710 memcpy(pContainer->cache_prefix, cache_prefix, cache_prefix_len+1);
712 CharLowerW(mutex_name);
713 cache_container_create_object_name(mutex_name, '!');
715 if ((pContainer->mutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
717 ERR("couldn't create mutex (error is %d)\n", GetLastError());
718 heap_free(pContainer->path);
719 heap_free(pContainer);
720 return FALSE;
723 list_add_head(&UrlContainers, &pContainer->entry);
725 return TRUE;
728 static void cache_container_delete_container(cache_container *pContainer)
730 list_remove(&pContainer->entry);
732 cache_container_close_index(pContainer);
733 CloseHandle(pContainer->mutex);
734 heap_free(pContainer->path);
735 heap_free(pContainer->cache_prefix);
736 heap_free(pContainer);
739 static void cache_containers_init(void)
741 static const struct
743 int nFolder; /* CSIDL_* constant */
744 const WCHAR *shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
745 const char *cache_prefix; /* prefix used to reference the container */
746 DWORD default_entry_type;
747 } DefaultContainerData[] =
749 { CSIDL_INTERNET_CACHE, L"Content.IE5", "", NORMAL_CACHE_ENTRY },
750 { CSIDL_HISTORY, L"History.IE5", "Visited:", URLHISTORY_CACHE_ENTRY },
751 { CSIDL_COOKIES, L"", "Cookie:", COOKIE_CACHE_ENTRY },
753 DWORD i;
755 for (i = 0; i < ARRAY_SIZE(DefaultContainerData); i++)
757 WCHAR wszCachePath[MAX_PATH];
758 WCHAR wszMutexName[MAX_PATH];
759 int path_len, suffix_len;
760 BOOL def_char;
762 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
764 ERR("Couldn't get path for default container %u\n", i);
765 continue;
767 path_len = lstrlenW(wszCachePath);
768 suffix_len = lstrlenW(DefaultContainerData[i].shpath_suffix);
770 if (path_len + suffix_len + 2 > MAX_PATH)
772 ERR("Path too long\n");
773 continue;
776 wszCachePath[path_len] = '\\';
777 wszCachePath[path_len+1] = 0;
779 lstrcpyW(wszMutexName, wszCachePath);
781 if (suffix_len)
783 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
784 wszCachePath[path_len + suffix_len + 1] = '\\';
785 wszCachePath[path_len + suffix_len + 2] = '\0';
788 if (!WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wszCachePath, path_len,
789 NULL, 0, NULL, &def_char) || def_char)
791 WCHAR tmp[MAX_PATH];
793 /* cannot convert path to ANSI code page */
794 if (!(path_len = GetShortPathNameW(wszCachePath, tmp, MAX_PATH)) ||
795 !WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, tmp, path_len,
796 NULL, 0, NULL, &def_char) || def_char)
797 ERR("Can't create container path accessible by ANSI functions\n");
798 else
799 memcpy(wszCachePath, tmp, (path_len+1)*sizeof(WCHAR));
802 cache_containers_add(DefaultContainerData[i].cache_prefix, wszCachePath,
803 DefaultContainerData[i].default_entry_type, wszMutexName);
807 static void cache_containers_free(void)
809 while(!list_empty(&UrlContainers))
810 cache_container_delete_container(
811 LIST_ENTRY(list_head(&UrlContainers), cache_container, entry)
815 static DWORD cache_containers_find(const char *url, cache_container **ret)
817 cache_container *container;
819 TRACE("searching for prefix for URL: %s\n", debugstr_a(url));
821 if(!url)
822 return ERROR_INVALID_PARAMETER;
824 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
826 int prefix_len = strlen(container->cache_prefix);
828 if(!strncmp(container->cache_prefix, url, prefix_len)) {
829 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
830 *ret = container;
831 return ERROR_SUCCESS;
835 ERR("no container found\n");
836 return ERROR_FILE_NOT_FOUND;
839 static BOOL cache_containers_enum(char *search_pattern, DWORD index, cache_container **ret)
841 DWORD i = 0;
842 cache_container *container;
844 TRACE("searching for prefix: %s\n", debugstr_a(search_pattern));
846 /* non-NULL search pattern only returns one container ever */
847 if (search_pattern && index > 0)
848 return FALSE;
850 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
852 if (search_pattern)
854 if (!strcmp(container->cache_prefix, search_pattern))
856 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
857 *ret = container;
858 return TRUE;
861 else
863 if (i == index)
865 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
866 *ret = container;
867 return TRUE;
870 i++;
872 return FALSE;
875 /***********************************************************************
876 * cache_container_lock_index (Internal)
878 * Locks the index for system-wide exclusive access.
880 * RETURNS
881 * Cache file header if successful
882 * NULL if failed and calls SetLastError.
884 static urlcache_header* cache_container_lock_index(cache_container *pContainer)
886 BYTE index;
887 LPVOID pIndexData;
888 urlcache_header* pHeader;
889 DWORD error;
891 /* acquire mutex */
892 WaitForSingleObject(pContainer->mutex, INFINITE);
894 pIndexData = MapViewOfFile(pContainer->mapping, FILE_MAP_WRITE, 0, 0, 0);
896 if (!pIndexData)
898 ReleaseMutex(pContainer->mutex);
899 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
900 return NULL;
902 pHeader = (urlcache_header*)pIndexData;
904 /* file has grown - we need to remap to prevent us getting
905 * access violations when we try and access beyond the end
906 * of the memory mapped file */
907 if (pHeader->size != pContainer->file_size)
909 UnmapViewOfFile( pHeader );
910 cache_container_close_index(pContainer);
911 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
912 if (error != ERROR_SUCCESS)
914 ReleaseMutex(pContainer->mutex);
915 SetLastError(error);
916 return NULL;
918 pIndexData = MapViewOfFile(pContainer->mapping, FILE_MAP_WRITE, 0, 0, 0);
920 if (!pIndexData)
922 ReleaseMutex(pContainer->mutex);
923 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
924 return NULL;
926 pHeader = (urlcache_header*)pIndexData;
929 TRACE("Signature: %s, file size: %d bytes\n", pHeader->signature, pHeader->size);
931 for (index = 0; index < pHeader->dirs_no; index++)
933 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].name);
936 return pHeader;
939 /***********************************************************************
940 * cache_container_unlock_index (Internal)
943 static BOOL cache_container_unlock_index(cache_container *pContainer, urlcache_header *pHeader)
945 /* release mutex */
946 ReleaseMutex(pContainer->mutex);
947 return UnmapViewOfFile(pHeader);
950 /***********************************************************************
951 * urlcache_create_file_pathW (Internal)
953 * Copies the full path to the specified buffer given the local file
954 * name and the index of the directory it is in. Always sets value in
955 * lpBufferSize to the required buffer size (in bytes).
957 * RETURNS
958 * TRUE if the buffer was big enough
959 * FALSE if the buffer was too small
962 static BOOL urlcache_create_file_pathW(
963 const cache_container *pContainer,
964 const urlcache_header *pHeader,
965 LPCSTR szLocalFileName,
966 BYTE Directory,
967 LPWSTR wszPath,
968 LPLONG lpBufferSize,
969 BOOL trunc_name)
971 LONG nRequired;
972 int path_len = lstrlenW(pContainer->path);
973 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
974 if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->dirs_no)
976 *lpBufferSize = 0;
977 return FALSE;
980 nRequired = (path_len + file_name_len) * sizeof(WCHAR);
981 if(Directory != CACHE_CONTAINER_NO_SUBDIR)
982 nRequired += (DIR_LENGTH + 1) * sizeof(WCHAR);
983 if (trunc_name && nRequired >= *lpBufferSize)
984 nRequired = *lpBufferSize;
985 if (nRequired <= *lpBufferSize)
987 int dir_len;
989 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
990 if (Directory != CACHE_CONTAINER_NO_SUBDIR)
992 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].name, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
993 wszPath[dir_len + path_len] = '\\';
994 dir_len++;
996 else
998 dir_len = 0;
1000 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len,
1001 *lpBufferSize/sizeof(WCHAR)-dir_len-path_len);
1002 wszPath[*lpBufferSize/sizeof(WCHAR)-1] = 0;
1003 *lpBufferSize = nRequired;
1004 return TRUE;
1006 *lpBufferSize = nRequired;
1007 return FALSE;
1010 /***********************************************************************
1011 * urlcache_create_file_pathA (Internal)
1013 * Copies the full path to the specified buffer given the local file
1014 * name and the index of the directory it is in. Always sets value in
1015 * lpBufferSize to the required buffer size.
1017 * RETURNS
1018 * TRUE if the buffer was big enough
1019 * FALSE if the buffer was too small
1022 static BOOL urlcache_create_file_pathA(
1023 const cache_container *pContainer,
1024 const urlcache_header *pHeader,
1025 LPCSTR szLocalFileName,
1026 BYTE Directory,
1027 LPSTR szPath,
1028 LPLONG lpBufferSize)
1030 LONG nRequired;
1031 int path_len, file_name_len, dir_len;
1033 if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->dirs_no)
1035 *lpBufferSize = 0;
1036 return FALSE;
1039 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
1040 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
1041 if (Directory!=CACHE_CONTAINER_NO_SUBDIR)
1042 dir_len = DIR_LENGTH+1;
1043 else
1044 dir_len = 0;
1046 nRequired = (path_len + dir_len + file_name_len) * sizeof(char);
1047 if (nRequired <= *lpBufferSize)
1049 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
1050 if(dir_len) {
1051 memcpy(szPath+path_len, pHeader->directory_data[Directory].name, dir_len-1);
1052 szPath[path_len + dir_len-1] = '\\';
1054 memcpy(szPath + path_len + dir_len, szLocalFileName, file_name_len);
1055 *lpBufferSize = nRequired;
1056 return TRUE;
1058 *lpBufferSize = nRequired;
1059 return FALSE;
1062 /* Just like FileTimeToDosDateTime, except that it also maps the special
1063 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1065 static void file_time_to_dos_date_time(const FILETIME *ft, WORD *fatdate,
1066 WORD *fattime)
1068 if (!ft->dwLowDateTime && !ft->dwHighDateTime)
1069 *fatdate = *fattime = 0;
1070 else
1071 FileTimeToDosDateTime(ft, fatdate, fattime);
1074 /***********************************************************************
1075 * urlcache_delete_file (Internal)
1077 static DWORD urlcache_delete_file(const cache_container *container,
1078 urlcache_header *header, entry_url *url_entry)
1080 WIN32_FILE_ATTRIBUTE_DATA attr;
1081 WCHAR path[MAX_PATH];
1082 LONG path_size = sizeof(path);
1083 DWORD err;
1084 WORD date, time;
1086 if(!url_entry->local_name_off)
1087 goto succ;
1089 if(!urlcache_create_file_pathW(container, header,
1090 (LPCSTR)url_entry+url_entry->local_name_off,
1091 url_entry->cache_dir, path, &path_size, FALSE))
1092 goto succ;
1094 if(!GetFileAttributesExW(path, GetFileExInfoStandard, &attr))
1095 goto succ;
1096 file_time_to_dos_date_time(&attr.ftLastWriteTime, &date, &time);
1097 if(date != url_entry->write_date || time != url_entry->write_time)
1098 goto succ;
1100 err = (DeleteFileW(path) ? ERROR_SUCCESS : GetLastError());
1101 if(err == ERROR_ACCESS_DENIED || err == ERROR_SHARING_VIOLATION)
1102 return err;
1104 succ:
1105 if (url_entry->cache_dir < header->dirs_no)
1107 if (header->directory_data[url_entry->cache_dir].files_no)
1108 header->directory_data[url_entry->cache_dir].files_no--;
1110 if (url_entry->cache_entry_type & STICKY_CACHE_ENTRY)
1112 if (url_entry->size.QuadPart < header->exempt_usage.QuadPart)
1113 header->exempt_usage.QuadPart -= url_entry->size.QuadPart;
1114 else
1115 header->exempt_usage.QuadPart = 0;
1117 else
1119 if (url_entry->size.QuadPart < header->cache_usage.QuadPart)
1120 header->cache_usage.QuadPart -= url_entry->size.QuadPart;
1121 else
1122 header->cache_usage.QuadPart = 0;
1125 return ERROR_SUCCESS;
1128 static BOOL urlcache_clean_leaked_entries(cache_container *container, urlcache_header *header)
1130 DWORD *leak_off;
1131 BOOL freed = FALSE;
1133 leak_off = &header->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
1134 while(*leak_off) {
1135 entry_url *url_entry = (entry_url*)((LPBYTE)header + *leak_off);
1137 if(SUCCEEDED(urlcache_delete_file(container, header, url_entry))) {
1138 *leak_off = url_entry->exempt_delta;
1139 urlcache_entry_free(header, &url_entry->header);
1140 freed = TRUE;
1141 }else {
1142 leak_off = &url_entry->exempt_delta;
1146 return freed;
1149 /***********************************************************************
1150 * cache_container_clean_index (Internal)
1152 * This function is meant to make place in index file by removing leaked
1153 * files entries and resizing the file.
1155 * CAUTION: file view may get mapped to new memory
1157 * RETURNS
1158 * ERROR_SUCCESS when new memory is available
1159 * error code otherwise
1161 static DWORD cache_container_clean_index(cache_container *container, urlcache_header **file_view)
1163 urlcache_header *header = *file_view;
1164 DWORD ret;
1166 TRACE("(%s %s)\n", debugstr_a(container->cache_prefix), debugstr_w(container->path));
1168 if(urlcache_clean_leaked_entries(container, header))
1169 return ERROR_SUCCESS;
1171 if(header->size >= ALLOCATION_TABLE_SIZE*8*BLOCKSIZE + ENTRY_START_OFFSET) {
1172 WARN("index file has maximal size\n");
1173 return ERROR_NOT_ENOUGH_MEMORY;
1176 cache_container_close_index(container);
1177 ret = cache_container_open_index(container, header->capacity_in_blocks*2);
1178 if(ret != ERROR_SUCCESS)
1179 return ret;
1180 header = MapViewOfFile(container->mapping, FILE_MAP_WRITE, 0, 0, 0);
1181 if(!header)
1182 return GetLastError();
1184 UnmapViewOfFile(*file_view);
1185 *file_view = header;
1186 return ERROR_SUCCESS;
1189 /* Just like DosDateTimeToFileTime, except that it also maps the special
1190 * case of a DOS date/time of (0,0) to a filetime of (0,0).
1192 static void dos_date_time_to_file_time(WORD fatdate, WORD fattime,
1193 FILETIME *ft)
1195 if (!fatdate && !fattime)
1196 ft->dwLowDateTime = ft->dwHighDateTime = 0;
1197 else
1198 DosDateTimeToFileTime(fatdate, fattime, ft);
1201 static int urlcache_decode_url(const char *url, WCHAR *decoded_url, int decoded_len)
1203 URL_COMPONENTSA uc;
1204 DWORD len, part_len;
1205 WCHAR *host_name;
1207 memset(&uc, 0, sizeof(uc));
1208 uc.dwStructSize = sizeof(uc);
1209 uc.dwHostNameLength = 1;
1210 if(!InternetCrackUrlA(url, 0, 0, &uc))
1211 uc.nScheme = INTERNET_SCHEME_UNKNOWN;
1213 if(uc.nScheme!=INTERNET_SCHEME_HTTP && uc.nScheme!=INTERNET_SCHEME_HTTPS)
1214 return MultiByteToWideChar(CP_UTF8, 0, url, -1, decoded_url, decoded_len);
1216 if(!decoded_url)
1217 decoded_len = 0;
1219 len = MultiByteToWideChar(CP_UTF8, 0, url, uc.lpszHostName-url, decoded_url, decoded_len);
1220 if(!len)
1221 return 0;
1222 if(decoded_url)
1223 decoded_len -= len;
1225 host_name = heap_alloc(uc.dwHostNameLength*sizeof(WCHAR));
1226 if(!host_name)
1227 return 0;
1228 if(!MultiByteToWideChar(CP_UTF8, 0, uc.lpszHostName, uc.dwHostNameLength,
1229 host_name, uc.dwHostNameLength)) {
1230 heap_free(host_name);
1231 return 0;
1233 part_len = IdnToUnicode(0, host_name, uc.dwHostNameLength,
1234 decoded_url ? decoded_url+len : NULL, decoded_len);
1235 heap_free(host_name);
1236 if(!part_len) {
1237 SetLastError(ERROR_INTERNET_INVALID_URL);
1238 return 0;
1240 len += part_len;
1241 if(decoded_url)
1242 decoded_len -= part_len;
1244 part_len = MultiByteToWideChar(CP_UTF8, 0,
1245 uc.lpszHostName+uc.dwHostNameLength,
1246 -1, decoded_url ? decoded_url+len : NULL, decoded_len);
1247 if(!part_len)
1248 return 0;
1249 len += part_len;
1251 return len;
1254 /***********************************************************************
1255 * urlcache_copy_entry (Internal)
1257 * Copies an entry from the cache index file to the Win32 structure
1259 * RETURNS
1260 * ERROR_SUCCESS if the buffer was big enough
1261 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1264 static DWORD urlcache_copy_entry(cache_container *container, const urlcache_header *header,
1265 INTERNET_CACHE_ENTRY_INFOA *entry_info, DWORD *info_size, const entry_url *url_entry, BOOL unicode)
1267 int url_len;
1268 DWORD size = sizeof(*entry_info);
1270 if(*info_size >= size) {
1271 entry_info->lpHeaderInfo = NULL;
1272 entry_info->lpszFileExtension = NULL;
1273 entry_info->lpszLocalFileName = NULL;
1274 entry_info->lpszSourceUrlName = NULL;
1275 entry_info->CacheEntryType = url_entry->cache_entry_type;
1276 entry_info->u.dwExemptDelta = url_entry->exempt_delta;
1277 entry_info->dwHeaderInfoSize = url_entry->header_info_size;
1278 entry_info->dwHitRate = url_entry->hit_rate;
1279 entry_info->dwSizeHigh = url_entry->size.u.HighPart;
1280 entry_info->dwSizeLow = url_entry->size.u.LowPart;
1281 entry_info->dwStructSize = sizeof(*entry_info);
1282 entry_info->dwUseCount = url_entry->use_count;
1283 dos_date_time_to_file_time(url_entry->expire_date, url_entry->expire_time, &entry_info->ExpireTime);
1284 entry_info->LastAccessTime = url_entry->access_time;
1285 entry_info->LastModifiedTime = url_entry->modification_time;
1286 dos_date_time_to_file_time(url_entry->sync_date, url_entry->sync_time, &entry_info->LastSyncTime);
1289 if(unicode)
1290 url_len = urlcache_decode_url((const char*)url_entry+url_entry->url_off, NULL, 0);
1291 else
1292 url_len = strlen((LPCSTR)url_entry+url_entry->url_off) + 1;
1293 size += url_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1295 if(*info_size >= size) {
1296 DWORD url_size = url_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1298 entry_info->lpszSourceUrlName = (LPSTR)entry_info+size-url_size;
1299 if(unicode)
1300 urlcache_decode_url((const char*)url_entry+url_entry->url_off, (WCHAR*)entry_info->lpszSourceUrlName, url_len);
1301 else
1302 memcpy(entry_info->lpszSourceUrlName, (LPCSTR)url_entry+url_entry->url_off, url_size);
1305 if(size%4 && size<*info_size)
1306 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1307 size = DWORD_ALIGN(size);
1309 if(url_entry->local_name_off) {
1310 LONG file_name_size;
1311 LPSTR file_name;
1312 file_name = (LPSTR)entry_info+size;
1313 file_name_size = *info_size-size;
1314 if((unicode && urlcache_create_file_pathW(container, header, (LPCSTR)url_entry+url_entry->local_name_off,
1315 url_entry->cache_dir, (LPWSTR)file_name, &file_name_size, FALSE)) ||
1316 (!unicode && urlcache_create_file_pathA(container, header, (LPCSTR)url_entry+url_entry->local_name_off,
1317 url_entry->cache_dir, file_name, &file_name_size))) {
1318 entry_info->lpszLocalFileName = file_name;
1320 size += file_name_size;
1322 if(size%4 && size<*info_size)
1323 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1324 size = DWORD_ALIGN(size);
1327 if(url_entry->header_info_off) {
1328 DWORD header_len;
1330 if(unicode)
1331 header_len = MultiByteToWideChar(CP_UTF8, 0, (const char*)url_entry+url_entry->header_info_off,
1332 url_entry->header_info_size, NULL, 0);
1333 else
1334 header_len = url_entry->header_info_size;
1335 size += header_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1337 if(*info_size >= size) {
1338 DWORD header_size = header_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1339 entry_info->lpHeaderInfo = (LPBYTE)entry_info+size-header_size;
1340 if(unicode)
1341 MultiByteToWideChar(CP_UTF8, 0, (const char*)url_entry+url_entry->header_info_off,
1342 url_entry->header_info_size, (LPWSTR)entry_info->lpHeaderInfo, header_len);
1343 else
1344 memcpy(entry_info->lpHeaderInfo, (LPCSTR)url_entry+url_entry->header_info_off, header_len);
1346 if(size%4 && size<*info_size)
1347 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1348 size = DWORD_ALIGN(size);
1351 if(url_entry->file_extension_off) {
1352 int ext_len;
1354 if(unicode)
1355 ext_len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)url_entry+url_entry->file_extension_off, -1, NULL, 0);
1356 else
1357 ext_len = strlen((LPCSTR)url_entry+url_entry->file_extension_off) + 1;
1358 size += ext_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1360 if(*info_size >= size) {
1361 DWORD ext_size = ext_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1362 entry_info->lpszFileExtension = (LPSTR)entry_info+size-ext_size;
1363 if(unicode)
1364 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)url_entry+url_entry->file_extension_off, -1, (LPWSTR)entry_info->lpszFileExtension, ext_len);
1365 else
1366 memcpy(entry_info->lpszFileExtension, (LPCSTR)url_entry+url_entry->file_extension_off, ext_len*sizeof(CHAR));
1369 if(size%4 && size<*info_size)
1370 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1371 size = DWORD_ALIGN(size);
1374 if(size > *info_size) {
1375 *info_size = size;
1376 return ERROR_INSUFFICIENT_BUFFER;
1378 *info_size = size;
1379 return ERROR_SUCCESS;
1382 /***********************************************************************
1383 * urlcache_set_entry_info (Internal)
1385 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1386 * according to the flags set by field_control.
1388 * RETURNS
1389 * ERROR_SUCCESS if the buffer was big enough
1390 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1393 static DWORD urlcache_set_entry_info(entry_url *url_entry, const INTERNET_CACHE_ENTRY_INFOA *entry_info, DWORD field_control)
1395 if (field_control & CACHE_ENTRY_ACCTIME_FC)
1396 url_entry->access_time = entry_info->LastAccessTime;
1397 if (field_control & CACHE_ENTRY_ATTRIBUTE_FC)
1398 url_entry->cache_entry_type = entry_info->CacheEntryType;
1399 if (field_control & CACHE_ENTRY_EXEMPT_DELTA_FC)
1400 url_entry->exempt_delta = entry_info->u.dwExemptDelta;
1401 if (field_control & CACHE_ENTRY_EXPTIME_FC)
1402 file_time_to_dos_date_time(&entry_info->ExpireTime, &url_entry->expire_date, &url_entry->expire_time);
1403 if (field_control & CACHE_ENTRY_HEADERINFO_FC)
1404 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1405 if (field_control & CACHE_ENTRY_HITRATE_FC)
1406 url_entry->hit_rate = entry_info->dwHitRate;
1407 if (field_control & CACHE_ENTRY_MODTIME_FC)
1408 url_entry->modification_time = entry_info->LastModifiedTime;
1409 if (field_control & CACHE_ENTRY_SYNCTIME_FC)
1410 file_time_to_dos_date_time(&entry_info->LastAccessTime, &url_entry->sync_date, &url_entry->sync_time);
1412 return ERROR_SUCCESS;
1415 /***********************************************************************
1416 * urlcache_hash_key (Internal)
1418 * Returns the hash key for a given string
1420 * RETURNS
1421 * hash key for the string
1424 static DWORD urlcache_hash_key(LPCSTR lpszKey)
1426 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1427 * but the algorithm and result are not the same!
1429 static const unsigned char lookupTable[256] =
1431 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1432 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1433 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1434 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1435 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1436 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1437 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1438 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1439 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1440 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1441 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1442 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1443 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1444 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1445 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1446 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1447 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1448 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1449 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1450 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1451 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1452 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1453 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1454 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1455 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1456 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1457 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1458 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1459 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1460 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1461 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1462 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1464 BYTE key[4];
1465 DWORD i;
1467 for (i = 0; i < ARRAY_SIZE(key); i++)
1468 key[i] = lookupTable[(*lpszKey + i) & 0xFF];
1470 for (lpszKey++; *lpszKey; lpszKey++)
1472 for (i = 0; i < ARRAY_SIZE(key); i++)
1473 key[i] = lookupTable[*lpszKey ^ key[i]];
1476 return *(DWORD *)key;
1479 static inline entry_hash_table* urlcache_get_hash_table(const urlcache_header *pHeader, DWORD dwOffset)
1481 if(!dwOffset)
1482 return NULL;
1483 return (entry_hash_table*)((LPBYTE)pHeader + dwOffset);
1486 static BOOL urlcache_find_hash_entry(const urlcache_header *pHeader, LPCSTR lpszUrl, struct hash_entry **ppHashEntry)
1488 /* structure of hash table:
1489 * 448 entries divided into 64 blocks
1490 * each block therefore contains a chain of 7 key/offset pairs
1491 * how position in table is calculated:
1492 * 1. the url is hashed in helper function
1493 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1494 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1496 * note:
1497 * there can be multiple hash tables in the file and the offset to
1498 * the next one is stored in the header of the hash table
1500 DWORD key = urlcache_hash_key(lpszUrl);
1501 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1502 entry_hash_table* pHashEntry;
1503 DWORD id = 0;
1505 key >>= HASHTABLE_FLAG_BITS;
1507 for (pHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1508 pHashEntry; pHashEntry = urlcache_get_hash_table(pHeader, pHashEntry->next))
1510 int i;
1511 if (pHashEntry->id != id++)
1513 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->id, id);
1514 continue;
1516 /* make sure that it is in fact a hash entry */
1517 if (pHashEntry->header.signature != HASH_SIGNATURE)
1519 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->header.signature);
1520 continue;
1523 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1525 struct hash_entry *pHashElement = &pHashEntry->hash_table[offset + i];
1526 if (key == pHashElement->key>>HASHTABLE_FLAG_BITS)
1528 /* FIXME: we should make sure that this is the right element
1529 * before returning and claiming that it is. We can do this
1530 * by doing a simple compare between the URL we were given
1531 * and the URL stored in the entry. However, this assumes
1532 * we know the format of all the entries stored in the
1533 * hash table */
1534 *ppHashEntry = pHashElement;
1535 return TRUE;
1539 return FALSE;
1542 /***********************************************************************
1543 * urlcache_hash_entry_set_flags (Internal)
1545 * Sets special bits in hash key
1547 * RETURNS
1548 * nothing
1551 static void urlcache_hash_entry_set_flags(struct hash_entry *pHashEntry, DWORD dwFlag)
1553 pHashEntry->key = (pHashEntry->key >> HASHTABLE_FLAG_BITS << HASHTABLE_FLAG_BITS) | dwFlag;
1556 /***********************************************************************
1557 * urlcache_hash_entry_delete (Internal)
1559 * Searches all the hash tables in the index for the given URL and
1560 * then if found deletes the entry.
1562 * RETURNS
1563 * TRUE if the entry was found
1564 * FALSE if the entry could not be found
1567 static BOOL urlcache_hash_entry_delete(struct hash_entry *pHashEntry)
1569 pHashEntry->key = HASHTABLE_DEL;
1570 return TRUE;
1573 /***********************************************************************
1574 * urlcache_hash_entry_create (Internal)
1576 * Searches all the hash tables for a free slot based on the offset
1577 * generated from the hash key. If a free slot is found, the offset and
1578 * key are entered into the hash table.
1580 * RETURNS
1581 * ERROR_SUCCESS if the entry was added
1582 * Any other Win32 error code if the entry could not be added
1585 static DWORD urlcache_hash_entry_create(urlcache_header *pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry, DWORD dwFieldType)
1587 /* see urlcache_find_hash_entry for structure of hash tables */
1589 DWORD key = urlcache_hash_key(lpszUrl);
1590 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1591 entry_hash_table* pHashEntry, *pHashPrev = NULL;
1592 DWORD id = 0;
1593 DWORD error;
1595 key = ((key >> HASHTABLE_FLAG_BITS) << HASHTABLE_FLAG_BITS) + dwFieldType;
1597 for (pHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1598 pHashEntry; pHashEntry = urlcache_get_hash_table(pHeader, pHashEntry->next))
1600 int i;
1601 pHashPrev = pHashEntry;
1603 if (pHashEntry->id != id++)
1605 ERR("not right hash table number (%d) expected %d\n", pHashEntry->id, id);
1606 break;
1608 /* make sure that it is in fact a hash entry */
1609 if (pHashEntry->header.signature != HASH_SIGNATURE)
1611 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->header.signature);
1612 break;
1615 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1617 struct hash_entry *pHashElement = &pHashEntry->hash_table[offset + i];
1618 if (pHashElement->key==HASHTABLE_FREE || pHashElement->key==HASHTABLE_DEL) /* if the slot is free */
1620 pHashElement->key = key;
1621 pHashElement->offset = dwOffsetEntry;
1622 return ERROR_SUCCESS;
1626 error = urlcache_create_hash_table(pHeader, pHashPrev, &pHashEntry);
1627 if (error != ERROR_SUCCESS)
1628 return error;
1630 pHashEntry->hash_table[offset].key = key;
1631 pHashEntry->hash_table[offset].offset = dwOffsetEntry;
1632 return ERROR_SUCCESS;
1635 /***********************************************************************
1636 * urlcache_enum_hash_tables (Internal)
1638 * Enumerates the hash tables in a container.
1640 * RETURNS
1641 * TRUE if an entry was found
1642 * FALSE if there are no more tables to enumerate.
1645 static BOOL urlcache_enum_hash_tables(const urlcache_header *pHeader, DWORD *id, entry_hash_table **ppHashEntry)
1647 for (*ppHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1648 *ppHashEntry; *ppHashEntry = urlcache_get_hash_table(pHeader, (*ppHashEntry)->next))
1650 TRACE("looking at hash table number %d\n", (*ppHashEntry)->id);
1651 if ((*ppHashEntry)->id != *id)
1652 continue;
1653 /* make sure that it is in fact a hash entry */
1654 if ((*ppHashEntry)->header.signature != HASH_SIGNATURE)
1656 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->header.signature);
1657 (*id)++;
1658 continue;
1661 TRACE("hash table number %d found\n", *id);
1662 return TRUE;
1664 return FALSE;
1667 /***********************************************************************
1668 * urlcache_enum_hash_table_entries (Internal)
1670 * Enumerates entries in a hash table and returns the next non-free entry.
1672 * RETURNS
1673 * TRUE if an entry was found
1674 * FALSE if the hash table is empty or there are no more entries to
1675 * enumerate.
1678 static BOOL urlcache_enum_hash_table_entries(const urlcache_header *pHeader, const entry_hash_table *pHashEntry,
1679 DWORD * index, const struct hash_entry **ppHashEntry)
1681 for (; *index < HASHTABLE_SIZE ; (*index)++)
1683 if (pHashEntry->hash_table[*index].key==HASHTABLE_FREE || pHashEntry->hash_table[*index].key==HASHTABLE_DEL)
1684 continue;
1686 *ppHashEntry = &pHashEntry->hash_table[*index];
1687 TRACE("entry found %d\n", *index);
1688 return TRUE;
1690 TRACE("no more entries (%d)\n", *index);
1691 return FALSE;
1694 /***********************************************************************
1695 * cache_container_delete_dir (Internal)
1697 * Erase a directory containing an URL cache.
1699 * RETURNS
1700 * TRUE success, FALSE failure/aborted.
1703 static BOOL cache_container_delete_dir(LPCWSTR lpszPath)
1705 DWORD path_len;
1706 WCHAR path[MAX_PATH + 1];
1707 SHFILEOPSTRUCTW shfos;
1708 int ret;
1710 path_len = lstrlenW(lpszPath);
1711 if (path_len >= MAX_PATH)
1712 return FALSE;
1713 lstrcpyW(path, lpszPath);
1714 path[path_len + 1] = 0; /* double-NUL-terminate path */
1716 shfos.hwnd = NULL;
1717 shfos.wFunc = FO_DELETE;
1718 shfos.pFrom = path;
1719 shfos.pTo = NULL;
1720 shfos.fFlags = FOF_NOCONFIRMATION;
1721 shfos.fAnyOperationsAborted = FALSE;
1722 ret = SHFileOperationW(&shfos);
1723 if (ret)
1724 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path), ret);
1725 return !(ret || shfos.fAnyOperationsAborted);
1728 /***********************************************************************
1729 * urlcache_hash_entry_is_locked (Internal)
1731 * Checks if entry is locked. Unlocks it if possible.
1733 static BOOL urlcache_hash_entry_is_locked(struct hash_entry *hash_entry, entry_url *url_entry)
1735 FILETIME cur_time;
1736 ULARGE_INTEGER acc_time, time;
1738 if ((hash_entry->key & ((1<<HASHTABLE_FLAG_BITS)-1)) != HASHTABLE_LOCK)
1739 return FALSE;
1741 GetSystemTimeAsFileTime(&cur_time);
1742 time.u.LowPart = cur_time.dwLowDateTime;
1743 time.u.HighPart = cur_time.dwHighDateTime;
1745 acc_time.u.LowPart = url_entry->access_time.dwLowDateTime;
1746 acc_time.u.HighPart = url_entry->access_time.dwHighDateTime;
1748 time.QuadPart -= acc_time.QuadPart;
1750 /* check if entry was locked for at least a day */
1751 if(time.QuadPart > (ULONGLONG)24*60*60*FILETIME_SECOND) {
1752 urlcache_hash_entry_set_flags(hash_entry, HASHTABLE_URL);
1753 url_entry->use_count = 0;
1754 return FALSE;
1757 return TRUE;
1760 static BOOL urlcache_get_entry_info(const char *url, void *entry_info,
1761 DWORD *size, DWORD flags, BOOL unicode)
1763 urlcache_header *header;
1764 struct hash_entry *hash_entry;
1765 const entry_url *url_entry;
1766 cache_container *container;
1767 DWORD error;
1769 TRACE("(%s, %p, %p, %x, %x)\n", debugstr_a(url), entry_info, size, flags, unicode);
1771 if(flags & ~GET_INSTALLED_ENTRY)
1772 FIXME("ignoring unsupported flags: %x\n", flags);
1774 error = cache_containers_find(url, &container);
1775 if(error != ERROR_SUCCESS) {
1776 SetLastError(error);
1777 return FALSE;
1780 error = cache_container_open_index(container, MIN_BLOCK_NO);
1781 if(error != ERROR_SUCCESS) {
1782 SetLastError(error);
1783 return FALSE;
1786 if(!(header = cache_container_lock_index(container)))
1787 return FALSE;
1789 if(!urlcache_find_hash_entry(header, url, &hash_entry)) {
1790 cache_container_unlock_index(container, header);
1791 WARN("entry %s not found!\n", debugstr_a(url));
1792 SetLastError(ERROR_FILE_NOT_FOUND);
1793 return FALSE;
1796 url_entry = (const entry_url*)((LPBYTE)header + hash_entry->offset);
1797 if(url_entry->header.signature != URL_SIGNATURE) {
1798 FIXME("Trying to retrieve entry of unknown format %s\n",
1799 debugstr_an((LPCSTR)&url_entry->header.signature, sizeof(DWORD)));
1800 cache_container_unlock_index(container, header);
1801 SetLastError(ERROR_FILE_NOT_FOUND);
1802 return FALSE;
1805 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->url_off));
1806 TRACE("Header info: %s\n", debugstr_an((LPCSTR)url_entry +
1807 url_entry->header_info_off, url_entry->header_info_size));
1809 if((flags & GET_INSTALLED_ENTRY) && !(url_entry->cache_entry_type & INSTALLED_CACHE_ENTRY)) {
1810 cache_container_unlock_index(container, header);
1811 SetLastError(ERROR_FILE_NOT_FOUND);
1812 return FALSE;
1815 if(size) {
1816 if(!entry_info)
1817 *size = 0;
1819 error = urlcache_copy_entry(container, header, entry_info, size, url_entry, unicode);
1820 if(error != ERROR_SUCCESS) {
1821 cache_container_unlock_index(container, header);
1822 SetLastError(error);
1823 return FALSE;
1825 if(url_entry->local_name_off)
1826 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->local_name_off));
1829 cache_container_unlock_index(container, header);
1830 return TRUE;
1833 /***********************************************************************
1834 * GetUrlCacheEntryInfoExA (WININET.@)
1837 BOOL WINAPI GetUrlCacheEntryInfoExA(LPCSTR lpszUrl,
1838 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1839 LPDWORD lpdwCacheEntryInfoBufSize, LPSTR lpszReserved,
1840 LPDWORD lpdwReserved, LPVOID lpReserved, DWORD dwFlags)
1842 if(lpszReserved!=NULL || lpdwReserved!=NULL || lpReserved!=NULL) {
1843 ERR("Reserved value was not 0\n");
1844 SetLastError(ERROR_INVALID_PARAMETER);
1845 return FALSE;
1848 return urlcache_get_entry_info(lpszUrl, lpCacheEntryInfo,
1849 lpdwCacheEntryInfoBufSize, dwFlags, FALSE);
1852 /***********************************************************************
1853 * GetUrlCacheEntryInfoA (WININET.@)
1856 BOOL WINAPI GetUrlCacheEntryInfoA(LPCSTR lpszUrlName,
1857 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1858 LPDWORD lpdwCacheEntryInfoBufferSize)
1860 return GetUrlCacheEntryInfoExA(lpszUrlName, lpCacheEntryInfo,
1861 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
1864 static int urlcache_encode_url(const WCHAR *url, char *encoded_url, int encoded_len)
1866 URL_COMPONENTSW uc;
1867 DWORD len, part_len;
1868 WCHAR *punycode;
1870 TRACE("%s\n", debugstr_w(url));
1872 memset(&uc, 0, sizeof(uc));
1873 uc.dwStructSize = sizeof(uc);
1874 uc.dwHostNameLength = 1;
1875 if(!InternetCrackUrlW(url, 0, 0, &uc))
1876 uc.nScheme = INTERNET_SCHEME_UNKNOWN;
1878 if(uc.nScheme!=INTERNET_SCHEME_HTTP && uc.nScheme!=INTERNET_SCHEME_HTTPS)
1879 return WideCharToMultiByte(CP_UTF8, 0, url, -1, encoded_url, encoded_len, NULL, NULL);
1881 len = WideCharToMultiByte(CP_UTF8, 0, url, uc.lpszHostName-url,
1882 encoded_url, encoded_len, NULL, NULL);
1883 if(!len)
1884 return 0;
1885 if(encoded_url)
1886 encoded_len -= len;
1888 part_len = IdnToAscii(0, uc.lpszHostName, uc.dwHostNameLength, NULL, 0);
1889 if(!part_len) {
1890 SetLastError(ERROR_INTERNET_INVALID_URL);
1891 return 0;
1894 punycode = heap_alloc(part_len*sizeof(WCHAR));
1895 if(!punycode)
1896 return 0;
1898 part_len = IdnToAscii(0, uc.lpszHostName, uc.dwHostNameLength, punycode, part_len);
1899 if(!part_len) {
1900 heap_free(punycode);
1901 return 0;
1904 part_len = WideCharToMultiByte(CP_UTF8, 0, punycode, part_len,
1905 encoded_url ? encoded_url+len : NULL, encoded_len, NULL, NULL);
1906 heap_free(punycode);
1907 if(!part_len)
1908 return 0;
1909 if(encoded_url)
1910 encoded_len -= part_len;
1911 len += part_len;
1913 part_len = WideCharToMultiByte(CP_UTF8, 0, uc.lpszHostName+uc.dwHostNameLength,
1914 -1, encoded_url ? encoded_url+len : NULL, encoded_len, NULL, NULL);
1915 if(!part_len)
1916 return 0;
1917 len += part_len;
1919 TRACE("got (%d)%s\n", len, debugstr_a(encoded_url));
1920 return len;
1923 static BOOL urlcache_encode_url_alloc(const WCHAR *url, char **encoded_url)
1925 DWORD encoded_len;
1926 char *ret;
1928 encoded_len = urlcache_encode_url(url, NULL, 0);
1929 if(!encoded_len)
1930 return FALSE;
1932 ret = heap_alloc(encoded_len*sizeof(WCHAR));
1933 if(!ret)
1934 return FALSE;
1936 encoded_len = urlcache_encode_url(url, ret, encoded_len);
1937 if(!encoded_len) {
1938 heap_free(ret);
1939 return FALSE;
1942 *encoded_url = ret;
1943 return TRUE;
1946 /***********************************************************************
1947 * GetUrlCacheEntryInfoExW (WININET.@)
1950 BOOL WINAPI GetUrlCacheEntryInfoExW(LPCWSTR lpszUrl,
1951 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1952 LPDWORD lpdwCacheEntryInfoBufSize, LPWSTR lpszReserved,
1953 LPDWORD lpdwReserved, LPVOID lpReserved, DWORD dwFlags)
1955 char *url;
1956 BOOL ret;
1958 if(lpszReserved!=NULL || lpdwReserved!=NULL || lpReserved!=NULL) {
1959 ERR("Reserved value was not 0\n");
1960 SetLastError(ERROR_INVALID_PARAMETER);
1961 return FALSE;
1964 /* Ignore GET_INSTALLED_ENTRY flag in unicode version of function */
1965 dwFlags &= ~GET_INSTALLED_ENTRY;
1967 if(!urlcache_encode_url_alloc(lpszUrl, &url))
1968 return FALSE;
1970 ret = urlcache_get_entry_info(url, lpCacheEntryInfo,
1971 lpdwCacheEntryInfoBufSize, dwFlags, TRUE);
1972 heap_free(url);
1973 return ret;
1976 /***********************************************************************
1977 * GetUrlCacheEntryInfoW (WININET.@)
1980 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1981 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1982 LPDWORD lpdwCacheEntryInfoBufferSize)
1984 return GetUrlCacheEntryInfoExW(lpszUrl, lpCacheEntryInfo,
1985 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
1988 /***********************************************************************
1989 * SetUrlCacheEntryInfoA (WININET.@)
1991 BOOL WINAPI SetUrlCacheEntryInfoA(LPCSTR lpszUrlName,
1992 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1993 DWORD dwFieldControl)
1995 urlcache_header *pHeader;
1996 struct hash_entry *pHashEntry;
1997 entry_header *pEntry;
1998 cache_container *pContainer;
1999 DWORD error;
2001 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
2003 error = cache_containers_find(lpszUrlName, &pContainer);
2004 if (error != ERROR_SUCCESS)
2006 SetLastError(error);
2007 return FALSE;
2010 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2011 if (error != ERROR_SUCCESS)
2013 SetLastError(error);
2014 return FALSE;
2017 if (!(pHeader = cache_container_lock_index(pContainer)))
2018 return FALSE;
2020 if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
2022 cache_container_unlock_index(pContainer, pHeader);
2023 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
2024 SetLastError(ERROR_FILE_NOT_FOUND);
2025 return FALSE;
2028 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2029 if (pEntry->signature != URL_SIGNATURE)
2031 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2032 cache_container_unlock_index(pContainer, pHeader);
2033 SetLastError(ERROR_FILE_NOT_FOUND);
2034 return FALSE;
2037 urlcache_set_entry_info((entry_url*)pEntry, lpCacheEntryInfo, dwFieldControl);
2039 cache_container_unlock_index(pContainer, pHeader);
2041 return TRUE;
2044 /***********************************************************************
2045 * SetUrlCacheEntryInfoW (WININET.@)
2047 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
2048 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2049 DWORD dwFieldControl)
2051 char *url;
2052 BOOL ret;
2054 if(!urlcache_encode_url_alloc(lpszUrl, &url))
2055 return FALSE;
2057 ret = SetUrlCacheEntryInfoA(url, (INTERNET_CACHE_ENTRY_INFOA*)lpCacheEntryInfo, dwFieldControl);
2058 heap_free(url);
2059 return ret;
2062 static BOOL urlcache_entry_get_file(const char *url, void *entry_info, DWORD *size, BOOL unicode)
2064 urlcache_header *header;
2065 struct hash_entry *hash_entry;
2066 entry_url *url_entry;
2067 cache_container *container;
2068 DWORD error;
2070 TRACE("(%s, %p, %p, %x)\n", debugstr_a(url), entry_info, size, unicode);
2072 if(!url || !size || (!entry_info && *size)) {
2073 SetLastError(ERROR_INVALID_PARAMETER);
2074 return FALSE;
2077 error = cache_containers_find(url, &container);
2078 if(error != ERROR_SUCCESS) {
2079 SetLastError(error);
2080 return FALSE;
2083 error = cache_container_open_index(container, MIN_BLOCK_NO);
2084 if (error != ERROR_SUCCESS) {
2085 SetLastError(error);
2086 return FALSE;
2089 if (!(header = cache_container_lock_index(container)))
2090 return FALSE;
2092 if (!urlcache_find_hash_entry(header, url, &hash_entry)) {
2093 cache_container_unlock_index(container, header);
2094 TRACE("entry %s not found!\n", debugstr_a(url));
2095 SetLastError(ERROR_FILE_NOT_FOUND);
2096 return FALSE;
2099 url_entry = (entry_url*)((LPBYTE)header + hash_entry->offset);
2100 if(url_entry->header.signature != URL_SIGNATURE) {
2101 FIXME("Trying to retrieve entry of unknown format %s\n",
2102 debugstr_an((LPSTR)&url_entry->header.signature, sizeof(DWORD)));
2103 cache_container_unlock_index(container, header);
2104 SetLastError(ERROR_FILE_NOT_FOUND);
2105 return FALSE;
2108 if(!url_entry->local_name_off) {
2109 cache_container_unlock_index(container, header);
2110 SetLastError(ERROR_INVALID_DATA);
2111 return FALSE;
2114 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->url_off));
2115 TRACE("Header info: %s\n", debugstr_an((LPCSTR)url_entry + url_entry->header_info_off,
2116 url_entry->header_info_size));
2118 error = urlcache_copy_entry(container, header, entry_info,
2119 size, url_entry, unicode);
2120 if(error != ERROR_SUCCESS) {
2121 cache_container_unlock_index(container, header);
2122 SetLastError(error);
2123 return FALSE;
2125 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->local_name_off));
2127 url_entry->hit_rate++;
2128 url_entry->use_count++;
2129 urlcache_hash_entry_set_flags(hash_entry, HASHTABLE_LOCK);
2130 GetSystemTimeAsFileTime(&url_entry->access_time);
2132 cache_container_unlock_index(container, header);
2134 return TRUE;
2137 /***********************************************************************
2138 * RetrieveUrlCacheEntryFileA (WININET.@)
2141 BOOL WINAPI RetrieveUrlCacheEntryFileA(LPCSTR lpszUrlName,
2142 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2143 LPDWORD lpdwCacheEntryInfoBufferSize, DWORD dwReserved)
2145 return urlcache_entry_get_file(lpszUrlName, lpCacheEntryInfo,
2146 lpdwCacheEntryInfoBufferSize, FALSE);
2149 /***********************************************************************
2150 * RetrieveUrlCacheEntryFileW (WININET.@)
2153 BOOL WINAPI RetrieveUrlCacheEntryFileW(LPCWSTR lpszUrlName,
2154 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2155 LPDWORD lpdwCacheEntryInfoBufferSize, DWORD dwReserved)
2157 char *url;
2158 BOOL ret;
2160 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
2161 return FALSE;
2163 ret = urlcache_entry_get_file(url, lpCacheEntryInfo,
2164 lpdwCacheEntryInfoBufferSize, TRUE);
2165 heap_free(url);
2166 return ret;
2169 static BOOL urlcache_entry_delete(const cache_container *pContainer,
2170 urlcache_header *pHeader, struct hash_entry *pHashEntry)
2172 entry_header *pEntry;
2173 entry_url * pUrlEntry;
2175 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2176 if (pEntry->signature != URL_SIGNATURE)
2178 FIXME("Trying to delete entry of unknown format %s\n",
2179 debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
2180 SetLastError(ERROR_FILE_NOT_FOUND);
2181 return FALSE;
2184 pUrlEntry = (entry_url *)pEntry;
2185 if(urlcache_hash_entry_is_locked(pHashEntry, pUrlEntry))
2187 TRACE("Trying to delete locked entry\n");
2188 pUrlEntry->cache_entry_type |= PENDING_DELETE_CACHE_ENTRY;
2189 SetLastError(ERROR_SHARING_VIOLATION);
2190 return FALSE;
2193 if(!urlcache_delete_file(pContainer, pHeader, pUrlEntry))
2195 urlcache_entry_free(pHeader, pEntry);
2197 else
2199 /* Add entry to leaked files list */
2200 pUrlEntry->header.signature = LEAK_SIGNATURE;
2201 pUrlEntry->exempt_delta = pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
2202 pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET] = pHashEntry->offset;
2205 urlcache_hash_entry_delete(pHashEntry);
2206 return TRUE;
2209 static HANDLE free_cache_running;
2210 static HANDLE dll_unload_event;
2211 static DWORD WINAPI handle_full_cache_worker(void *param)
2213 FreeUrlCacheSpaceW(NULL, 20, 0);
2214 ReleaseSemaphore(free_cache_running, 1, NULL);
2215 return 0;
2218 static void handle_full_cache(void)
2220 if(WaitForSingleObject(free_cache_running, 0) == WAIT_OBJECT_0) {
2221 if(!QueueUserWorkItem(handle_full_cache_worker, NULL, 0))
2222 ReleaseSemaphore(free_cache_running, 1, NULL);
2226 /* Enumerates entries in cache, allows cache unlocking between calls. */
2227 static BOOL urlcache_next_entry(urlcache_header *header, DWORD *hash_table_off, DWORD *hash_table_entry,
2228 struct hash_entry **hash_entry, entry_header **entry)
2230 entry_hash_table *hashtable_entry;
2232 *hash_entry = NULL;
2233 *entry = NULL;
2235 if(!*hash_table_off) {
2236 *hash_table_off = header->hash_table_off;
2237 *hash_table_entry = 0;
2239 hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2240 }else {
2241 if(*hash_table_off >= header->size) {
2242 *hash_table_off = 0;
2243 return FALSE;
2246 hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2249 if(hashtable_entry->header.signature != HASH_SIGNATURE) {
2250 *hash_table_off = 0;
2251 return FALSE;
2254 while(1) {
2255 if(*hash_table_entry >= HASHTABLE_SIZE) {
2256 *hash_table_off = hashtable_entry->next;
2257 if(!*hash_table_off) {
2258 *hash_table_off = 0;
2259 return FALSE;
2262 hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2263 *hash_table_entry = 0;
2266 if(hashtable_entry->hash_table[*hash_table_entry].key != HASHTABLE_DEL &&
2267 hashtable_entry->hash_table[*hash_table_entry].key != HASHTABLE_FREE) {
2268 *hash_entry = &hashtable_entry->hash_table[*hash_table_entry];
2269 *entry = (entry_header*)((LPBYTE)header + hashtable_entry->hash_table[*hash_table_entry].offset);
2270 (*hash_table_entry)++;
2271 return TRUE;
2274 (*hash_table_entry)++;
2277 *hash_table_off = 0;
2278 return FALSE;
2281 /* Rates an urlcache entry to determine if it can be deleted.
2283 * Score 0 means that entry can safely be removed, the bigger rating
2284 * the smaller chance of entry being removed.
2285 * DWORD_MAX means that entry can't be deleted at all.
2287 * Rating system is currently not fully compatible with native implementation.
2289 static DWORD urlcache_rate_entry(entry_url *url_entry, FILETIME *cur_time)
2291 ULARGE_INTEGER time, access_time;
2292 DWORD rating;
2294 access_time.u.LowPart = url_entry->access_time.dwLowDateTime;
2295 access_time.u.HighPart = url_entry->access_time.dwHighDateTime;
2297 time.u.LowPart = cur_time->dwLowDateTime;
2298 time.u.HighPart = cur_time->dwHighDateTime;
2300 /* Don't touch entries that were added less than 10 minutes ago */
2301 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)10*60*FILETIME_SECOND)
2302 return -1;
2304 if(url_entry->cache_entry_type & STICKY_CACHE_ENTRY)
2305 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)url_entry->exempt_delta*FILETIME_SECOND)
2306 return -1;
2308 time.QuadPart = (time.QuadPart-access_time.QuadPart)/FILETIME_SECOND;
2309 rating = 400*60*60*24/(60*60*24+time.QuadPart);
2311 if(url_entry->hit_rate > 100)
2312 rating += 100;
2313 else
2314 rating += url_entry->hit_rate;
2316 return rating;
2319 static int __cdecl dword_cmp(const void *p1, const void *p2)
2321 return *(const DWORD*)p1 - *(const DWORD*)p2;
2324 /***********************************************************************
2325 * FreeUrlCacheSpaceW (WININET.@)
2327 * Frees up some cache.
2329 * PARAMETERS
2330 * cache_path [I] Which volume to free up from, or NULL if you don't care.
2331 * size [I] Percentage of the cache that should be free.
2332 * filter [I] Which entries can't be deleted (CacheEntryType)
2334 * RETURNS
2335 * TRUE success. FALSE failure.
2337 * IMPLEMENTATION
2338 * This implementation just retrieves the path of the cache directory, and
2339 * deletes its contents from the filesystem. The correct approach would
2340 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
2342 BOOL WINAPI FreeUrlCacheSpaceW(LPCWSTR cache_path, DWORD size, DWORD filter)
2344 cache_container *container;
2345 DWORD path_len, err;
2347 TRACE("(%s, %x, %x)\n", debugstr_w(cache_path), size, filter);
2349 if(size<1 || size>100) {
2350 SetLastError(ERROR_INVALID_PARAMETER);
2351 return FALSE;
2354 if(cache_path) {
2355 path_len = lstrlenW(cache_path);
2356 if(cache_path[path_len-1] == '\\')
2357 path_len--;
2358 }else {
2359 path_len = 0;
2362 if(size==100 && !filter) {
2363 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
2365 /* When cache_path==NULL only clean Temporary Internet Files */
2366 if((!path_len && container->cache_prefix[0]==0) ||
2367 (path_len && !wcsnicmp(container->path, cache_path, path_len) &&
2368 (container->path[path_len]=='\0' || container->path[path_len]=='\\')))
2370 BOOL ret_del;
2372 WaitForSingleObject(container->mutex, INFINITE);
2374 /* unlock, delete, recreate and lock cache */
2375 cache_container_close_index(container);
2376 ret_del = cache_container_delete_dir(container->path);
2377 err = cache_container_open_index(container, MIN_BLOCK_NO);
2379 ReleaseMutex(container->mutex);
2380 if(!ret_del || (err != ERROR_SUCCESS))
2381 return FALSE;
2385 return TRUE;
2388 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
2390 urlcache_header *header;
2391 struct hash_entry *hash_entry;
2392 entry_header *entry;
2393 entry_url *url_entry;
2394 ULONGLONG desired_size, cur_size;
2395 DWORD delete_factor, hash_table_off, hash_table_entry;
2396 DWORD rate[100], rate_no;
2397 FILETIME cur_time;
2399 if((path_len || container->cache_prefix[0]!=0) &&
2400 (!path_len || wcsnicmp(container->path, cache_path, path_len) ||
2401 (container->path[path_len]!='\0' && container->path[path_len]!='\\')))
2402 continue;
2404 err = cache_container_open_index(container, MIN_BLOCK_NO);
2405 if(err != ERROR_SUCCESS)
2406 continue;
2408 header = cache_container_lock_index(container);
2409 if(!header)
2410 continue;
2412 urlcache_clean_leaked_entries(container, header);
2414 desired_size = header->cache_limit.QuadPart*(100-size)/100;
2415 cur_size = header->cache_usage.QuadPart+header->exempt_usage.QuadPart;
2416 if(cur_size <= desired_size)
2417 delete_factor = 0;
2418 else
2419 delete_factor = (cur_size-desired_size)*100/cur_size;
2421 if(!delete_factor) {
2422 cache_container_unlock_index(container, header);
2423 continue;
2426 hash_table_off = 0;
2427 hash_table_entry = 0;
2428 rate_no = 0;
2429 GetSystemTimeAsFileTime(&cur_time);
2430 while(rate_no < ARRAY_SIZE(rate) &&
2431 urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2432 if(entry->signature != URL_SIGNATURE) {
2433 WARN("only url entries are currently supported\n");
2434 continue;
2437 url_entry = (entry_url*)entry;
2438 if(url_entry->cache_entry_type & filter)
2439 continue;
2441 rate[rate_no] = urlcache_rate_entry(url_entry, &cur_time);
2442 if(rate[rate_no] != -1)
2443 rate_no++;
2446 if(!rate_no) {
2447 TRACE("nothing to delete\n");
2448 cache_container_unlock_index(container, header);
2449 continue;
2452 qsort(rate, rate_no, sizeof(DWORD), dword_cmp);
2454 delete_factor = delete_factor*rate_no/100;
2455 delete_factor = rate[delete_factor];
2456 TRACE("deleting files with rating %d or less\n", delete_factor);
2458 hash_table_off = 0;
2459 while(urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2460 if(entry->signature != URL_SIGNATURE)
2461 continue;
2463 url_entry = (entry_url*)entry;
2464 if(url_entry->cache_entry_type & filter)
2465 continue;
2467 if(urlcache_rate_entry(url_entry, &cur_time) <= delete_factor) {
2468 TRACE("deleting file: %s\n", debugstr_a((char*)url_entry+url_entry->local_name_off));
2469 urlcache_entry_delete(container, header, hash_entry);
2471 if(header->cache_usage.QuadPart+header->exempt_usage.QuadPart <= desired_size)
2472 break;
2474 /* Allow other threads to use cache while cleaning */
2475 cache_container_unlock_index(container, header);
2476 if(WaitForSingleObject(dll_unload_event, 0) == WAIT_OBJECT_0) {
2477 TRACE("got dll_unload_event - finishing\n");
2478 return TRUE;
2480 Sleep(0);
2481 header = cache_container_lock_index(container);
2485 TRACE("cache size after cleaning 0x%s/0x%s\n",
2486 wine_dbgstr_longlong(header->cache_usage.QuadPart+header->exempt_usage.QuadPart),
2487 wine_dbgstr_longlong(header->cache_limit.QuadPart));
2488 cache_container_unlock_index(container, header);
2491 return TRUE;
2494 /***********************************************************************
2495 * FreeUrlCacheSpaceA (WININET.@)
2497 * See FreeUrlCacheSpaceW.
2499 BOOL WINAPI FreeUrlCacheSpaceA(LPCSTR lpszCachePath, DWORD dwSize, DWORD dwFilter)
2501 BOOL ret = FALSE;
2502 LPWSTR path = heap_strdupAtoW(lpszCachePath);
2503 if (lpszCachePath == NULL || path != NULL)
2504 ret = FreeUrlCacheSpaceW(path, dwSize, dwFilter);
2505 heap_free(path);
2506 return ret;
2509 /***********************************************************************
2510 * UnlockUrlCacheEntryFileA (WININET.@)
2513 BOOL WINAPI UnlockUrlCacheEntryFileA(LPCSTR lpszUrlName, DWORD dwReserved)
2515 urlcache_header *pHeader;
2516 struct hash_entry *pHashEntry;
2517 entry_header *pEntry;
2518 entry_url * pUrlEntry;
2519 cache_container *pContainer;
2520 DWORD error;
2522 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2524 if (dwReserved)
2526 ERR("dwReserved != 0\n");
2527 SetLastError(ERROR_INVALID_PARAMETER);
2528 return FALSE;
2531 error = cache_containers_find(lpszUrlName, &pContainer);
2532 if (error != ERROR_SUCCESS)
2534 SetLastError(error);
2535 return FALSE;
2538 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2539 if (error != ERROR_SUCCESS)
2541 SetLastError(error);
2542 return FALSE;
2545 if (!(pHeader = cache_container_lock_index(pContainer)))
2546 return FALSE;
2548 if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
2550 cache_container_unlock_index(pContainer, pHeader);
2551 TRACE("entry %s not found!\n", debugstr_a(lpszUrlName));
2552 SetLastError(ERROR_FILE_NOT_FOUND);
2553 return FALSE;
2556 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2557 if (pEntry->signature != URL_SIGNATURE)
2559 cache_container_unlock_index(pContainer, pHeader);
2560 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2561 SetLastError(ERROR_FILE_NOT_FOUND);
2562 return FALSE;
2565 pUrlEntry = (entry_url *)pEntry;
2567 if (pUrlEntry->use_count == 0)
2569 cache_container_unlock_index(pContainer, pHeader);
2570 return FALSE;
2572 pUrlEntry->use_count--;
2573 if (!pUrlEntry->use_count)
2575 urlcache_hash_entry_set_flags(pHashEntry, HASHTABLE_URL);
2576 if (pUrlEntry->cache_entry_type & PENDING_DELETE_CACHE_ENTRY)
2577 urlcache_entry_delete(pContainer, pHeader, pHashEntry);
2580 cache_container_unlock_index(pContainer, pHeader);
2582 return TRUE;
2585 /***********************************************************************
2586 * UnlockUrlCacheEntryFileW (WININET.@)
2589 BOOL WINAPI UnlockUrlCacheEntryFileW(LPCWSTR lpszUrlName, DWORD dwReserved)
2591 char *url;
2592 BOOL ret;
2594 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
2595 return FALSE;
2597 ret = UnlockUrlCacheEntryFileA(url, dwReserved);
2598 heap_free(url);
2599 return ret;
2602 static BOOL urlcache_entry_create(const char *url, const char *ext, WCHAR *full_path)
2604 cache_container *container;
2605 urlcache_header *header;
2606 char file_name[MAX_PATH];
2607 WCHAR extW[MAX_PATH];
2608 BYTE cache_dir;
2609 LONG full_path_len, ext_len = 0;
2610 BOOL generate_name = FALSE;
2611 DWORD error;
2612 HANDLE file;
2613 FILETIME ft;
2614 URL_COMPONENTSA uc;
2615 int i;
2617 TRACE("(%s, %s, %p)\n", debugstr_a(url), debugstr_a(ext), full_path);
2619 memset(&uc, 0, sizeof(uc));
2620 uc.dwStructSize = sizeof(uc);
2621 uc.dwUrlPathLength = 1;
2622 uc.dwExtraInfoLength = 1;
2623 if(!InternetCrackUrlA(url, 0, 0, &uc))
2624 uc.dwUrlPathLength = 0;
2626 if(!uc.dwUrlPathLength) {
2627 file_name[0] = 0;
2628 }else {
2629 char *p, *e;
2631 p = e = uc.lpszUrlPath+uc.dwUrlPathLength;
2632 while(p>uc.lpszUrlPath && *(p-1)!='/' && *(p-1)!='\\' && *(p-1)!='.')
2633 p--;
2634 if(p>uc.lpszUrlPath && *(p-1)=='.') {
2635 e = p-1;
2636 while(p>uc.lpszUrlPath && *(p-1)!='/' && *(p-1)!='\\')
2637 p--;
2640 if(e-p >= MAX_PATH)
2641 e = p+MAX_PATH-1;
2642 memcpy(file_name, p, e-p);
2643 file_name[e-p] = 0;
2645 for(p=file_name; *p; p++) {
2646 switch(*p) {
2647 case '<': case '>':
2648 case ':': case '"':
2649 case '|': case '?':
2650 case '*':
2651 *p = '_'; break;
2652 default: break;
2657 if(!file_name[0])
2658 generate_name = TRUE;
2660 error = cache_containers_find(url, &container);
2661 if(error != ERROR_SUCCESS) {
2662 SetLastError(error);
2663 return FALSE;
2666 error = cache_container_open_index(container, MIN_BLOCK_NO);
2667 if(error != ERROR_SUCCESS) {
2668 SetLastError(error);
2669 return FALSE;
2672 if(!(header = cache_container_lock_index(container)))
2673 return FALSE;
2675 if(header->dirs_no)
2676 cache_dir = (BYTE)(rand() % header->dirs_no);
2677 else
2678 cache_dir = CACHE_CONTAINER_NO_SUBDIR;
2680 full_path_len = MAX_PATH * sizeof(WCHAR);
2681 if(!urlcache_create_file_pathW(container, header, file_name, cache_dir, full_path, &full_path_len, TRUE)) {
2682 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2683 debugstr_a(file_name), full_path_len);
2684 cache_container_unlock_index(container, header);
2685 return FALSE;
2687 full_path_len = full_path_len/sizeof(WCHAR) - 1;
2689 cache_container_unlock_index(container, header);
2691 if(ext) {
2692 WCHAR *p;
2694 extW[0] = '.';
2695 ext_len = MultiByteToWideChar(CP_ACP, 0, ext, -1, extW+1, MAX_PATH-1);
2697 for(p=extW; *p; p++) {
2698 switch(*p) {
2699 case '<': case '>':
2700 case ':': case '"':
2701 case '|': case '?':
2702 case '*':
2703 *p = '_'; break;
2704 default: break;
2707 if(p[-1]==' ' || p[-1]=='.')
2708 p[-1] = '_';
2709 }else {
2710 extW[0] = '\0';
2713 if(!generate_name && full_path_len+5+ext_len>=MAX_PATH) { /* strlen("[255]") = 5 */
2714 full_path_len = MAX_PATH-5-ext_len-1;
2717 for(i=0; i<255 && !generate_name; i++) {
2718 wsprintfW(full_path+full_path_len, L"[%u]%s", i, extW);
2720 TRACE("Trying: %s\n", debugstr_w(full_path));
2721 file = CreateFileW(full_path, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2722 if(file != INVALID_HANDLE_VALUE) {
2723 CloseHandle(file);
2724 return TRUE;
2728 if(full_path_len+8+ext_len >= MAX_PATH)
2729 full_path_len = MAX_PATH-8-ext_len-1;
2731 /* Try to generate random name */
2732 GetSystemTimeAsFileTime(&ft);
2733 lstrcpyW(full_path+full_path_len+8, extW);
2735 for(i=0; i<255; i++) {
2736 int j;
2737 ULONGLONG n = ft.dwHighDateTime;
2738 n <<= 32;
2739 n += ft.dwLowDateTime;
2740 n ^= (ULONGLONG)i<<48;
2742 for(j=0; j<8; j++) {
2743 int r = (n % 36);
2744 n /= 37;
2745 full_path[full_path_len+j] = (r < 10 ? '0' + r : 'A' + r - 10);
2748 TRACE("Trying: %s\n", debugstr_w(full_path));
2749 file = CreateFileW(full_path, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2750 if(file != INVALID_HANDLE_VALUE) {
2751 CloseHandle(file);
2752 return TRUE;
2756 WARN("Could not find a unique filename\n");
2757 return FALSE;
2760 /***********************************************************************
2761 * CreateUrlCacheEntryA (WININET.@)
2764 BOOL WINAPI CreateUrlCacheEntryA(LPCSTR lpszUrlName, DWORD dwExpectedFileSize,
2765 LPCSTR lpszFileExtension, LPSTR lpszFileName, DWORD dwReserved)
2767 WCHAR file_name[MAX_PATH];
2769 if(dwReserved)
2770 FIXME("dwReserved 0x%08x\n", dwReserved);
2772 if(!urlcache_entry_create(lpszUrlName, lpszFileExtension, file_name))
2773 return FALSE;
2775 if(!WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL))
2776 return FALSE;
2777 return TRUE;
2779 /***********************************************************************
2780 * CreateUrlCacheEntryW (WININET.@)
2783 BOOL WINAPI CreateUrlCacheEntryW(LPCWSTR lpszUrlName, DWORD dwExpectedFileSize,
2784 LPCWSTR lpszFileExtension, LPWSTR lpszFileName, DWORD dwReserved)
2786 char *url, *ext = NULL;
2787 BOOL ret;
2789 if(dwReserved)
2790 FIXME("dwReserved 0x%08x\n", dwReserved);
2792 if(lpszFileExtension) {
2793 ext = heap_strdupWtoUTF8(lpszFileExtension);
2794 if(!ext)
2795 return FALSE;
2798 if(!urlcache_encode_url_alloc(lpszUrlName, &url)) {
2799 heap_free(ext);
2800 return FALSE;
2803 ret = urlcache_entry_create(url, ext, lpszFileName);
2804 heap_free(ext);
2805 heap_free(url);
2806 return ret;
2809 static BOOL urlcache_entry_commit(const char *url, const WCHAR *file_name,
2810 FILETIME expire_time, FILETIME modify_time, DWORD entry_type,
2811 BYTE *header_info, DWORD header_size, const char *file_ext,
2812 const char *original_url)
2814 cache_container *container;
2815 urlcache_header *header;
2816 struct hash_entry *hash_entry;
2817 entry_header *entry;
2818 entry_url *url_entry;
2819 DWORD url_entry_offset;
2820 DWORD size = DWORD_ALIGN(sizeof(*url_entry));
2821 DWORD file_name_off = 0;
2822 DWORD header_info_off = 0;
2823 DWORD file_ext_off = 0;
2824 WIN32_FILE_ATTRIBUTE_DATA file_attr;
2825 LARGE_INTEGER file_size;
2826 BYTE dir_id;
2827 char file_name_no_container[MAX_PATH];
2828 char *local_file_name = 0;
2829 DWORD hit_rate = 0;
2830 DWORD exempt_delta = 0;
2831 DWORD error;
2833 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n", debugstr_a(url), debugstr_w(file_name),
2834 entry_type, header_info, header_size, debugstr_a(file_ext), debugstr_a(original_url));
2836 if(entry_type & STICKY_CACHE_ENTRY && !file_name) {
2837 SetLastError(ERROR_INVALID_PARAMETER);
2838 return FALSE;
2840 if(original_url)
2841 WARN(": original_url ignored\n");
2843 memset(&file_attr, 0, sizeof(file_attr));
2844 if(file_name) {
2845 if(!GetFileAttributesExW(file_name, GetFileExInfoStandard, &file_attr))
2846 return FALSE;
2848 file_size.u.LowPart = file_attr.nFileSizeLow;
2849 file_size.u.HighPart = file_attr.nFileSizeHigh;
2851 error = cache_containers_find(url, &container);
2852 if(error != ERROR_SUCCESS) {
2853 SetLastError(error);
2854 return FALSE;
2857 error = cache_container_open_index(container, MIN_BLOCK_NO);
2858 if(error != ERROR_SUCCESS) {
2859 SetLastError(error);
2860 return FALSE;
2863 if(!(header = cache_container_lock_index(container)))
2864 return FALSE;
2866 if(urlcache_find_hash_entry(header, url, &hash_entry)) {
2867 entry_url *url_entry = (entry_url*)((LPBYTE)header + hash_entry->offset);
2869 if(urlcache_hash_entry_is_locked(hash_entry, url_entry)) {
2870 TRACE("Trying to overwrite locked entry\n");
2871 cache_container_unlock_index(container, header);
2872 SetLastError(ERROR_SHARING_VIOLATION);
2873 return FALSE;
2876 hit_rate = url_entry->hit_rate;
2877 exempt_delta = url_entry->exempt_delta;
2878 urlcache_entry_delete(container, header, hash_entry);
2881 if(header->dirs_no)
2882 dir_id = 0;
2883 else
2884 dir_id = CACHE_CONTAINER_NO_SUBDIR;
2886 if(file_name) {
2887 BOOL bFound = FALSE;
2889 if(wcsncmp(file_name, container->path, lstrlenW(container->path))) {
2890 ERR("path %s must begin with cache content path %s\n", debugstr_w(file_name), debugstr_w(container->path));
2891 cache_container_unlock_index(container, header);
2892 SetLastError(ERROR_INVALID_PARAMETER);
2893 return FALSE;
2896 /* skip container path prefix */
2897 file_name += lstrlenW(container->path);
2899 WideCharToMultiByte(CP_ACP, 0, file_name, -1, file_name_no_container, MAX_PATH, NULL, NULL);
2900 local_file_name = file_name_no_container;
2902 if(header->dirs_no) {
2903 for(dir_id = 0; dir_id < header->dirs_no; dir_id++) {
2904 if(!strncmp(header->directory_data[dir_id].name, local_file_name, DIR_LENGTH)) {
2905 bFound = TRUE;
2906 break;
2910 if(!bFound) {
2911 ERR("cache directory not found in path %s\n", debugstr_w(file_name));
2912 cache_container_unlock_index(container, header);
2913 SetLastError(ERROR_INVALID_PARAMETER);
2914 return FALSE;
2917 file_name += DIR_LENGTH + 1;
2918 local_file_name += DIR_LENGTH + 1;
2922 size = DWORD_ALIGN(size + strlen(url) + 1);
2923 if(file_name) {
2924 file_name_off = size;
2925 size = DWORD_ALIGN(size + strlen(local_file_name) + 1);
2927 if(header_info && header_size) {
2928 header_info_off = size;
2929 size = DWORD_ALIGN(size + header_size);
2931 if(file_ext && (file_ext_off = strlen(file_ext))) {
2932 DWORD len = file_ext_off;
2934 file_ext_off = size;
2935 size = DWORD_ALIGN(size + len + 1);
2938 /* round up to next block */
2939 if(size % BLOCKSIZE) {
2940 size -= size % BLOCKSIZE;
2941 size += BLOCKSIZE;
2944 error = urlcache_entry_alloc(header, size / BLOCKSIZE, &entry);
2945 while(error == ERROR_HANDLE_DISK_FULL) {
2946 error = cache_container_clean_index(container, &header);
2947 if(error == ERROR_SUCCESS)
2948 error = urlcache_entry_alloc(header, size / BLOCKSIZE, &entry);
2950 if(error != ERROR_SUCCESS) {
2951 cache_container_unlock_index(container, header);
2952 SetLastError(error);
2953 return FALSE;
2956 /* FindFirstFreeEntry fills in blocks used */
2957 url_entry = (entry_url *)entry;
2958 url_entry_offset = (LPBYTE)url_entry - (LPBYTE)header;
2959 url_entry->header.signature = URL_SIGNATURE;
2960 url_entry->cache_dir = dir_id;
2961 url_entry->cache_entry_type = entry_type | container->default_entry_type;
2962 url_entry->header_info_size = header_size;
2963 if((entry_type & STICKY_CACHE_ENTRY) && !exempt_delta) {
2964 /* Sticky entries have a default exempt time of one day */
2965 exempt_delta = 86400;
2967 url_entry->exempt_delta = exempt_delta;
2968 url_entry->hit_rate = hit_rate+1;
2969 url_entry->file_extension_off = file_ext_off;
2970 url_entry->header_info_off = header_info_off;
2971 url_entry->local_name_off = file_name_off;
2972 url_entry->url_off = DWORD_ALIGN(sizeof(*url_entry));
2973 url_entry->size.QuadPart = file_size.QuadPart;
2974 url_entry->use_count = 0;
2975 GetSystemTimeAsFileTime(&url_entry->access_time);
2976 url_entry->modification_time = modify_time;
2977 file_time_to_dos_date_time(&url_entry->access_time, &url_entry->sync_date, &url_entry->sync_time);
2978 file_time_to_dos_date_time(&expire_time, &url_entry->expire_date, &url_entry->expire_time);
2979 file_time_to_dos_date_time(&file_attr.ftLastWriteTime, &url_entry->write_date, &url_entry->write_time);
2981 /*** Unknowns ***/
2982 url_entry->unk1 = 0;
2983 url_entry->unk2 = 0;
2984 url_entry->unk3 = 0x60;
2985 url_entry->unk4 = 0;
2986 url_entry->unk5 = 0x1010;
2987 url_entry->unk7 = 0;
2988 url_entry->unk8 = 0;
2991 strcpy((LPSTR)url_entry + url_entry->url_off, url);
2992 if(file_name_off)
2993 strcpy((LPSTR)((LPBYTE)url_entry + file_name_off), local_file_name);
2994 if(header_info_off)
2995 memcpy((LPBYTE)url_entry + header_info_off, header_info, header_size);
2996 if(file_ext_off)
2997 strcpy((LPSTR)((LPBYTE)url_entry + file_ext_off), file_ext);
2999 error = urlcache_hash_entry_create(header, url, url_entry_offset, HASHTABLE_URL);
3000 while(error == ERROR_HANDLE_DISK_FULL) {
3001 error = cache_container_clean_index(container, &header);
3002 if(error == ERROR_SUCCESS) {
3003 url_entry = (entry_url *)((LPBYTE)header + url_entry_offset);
3004 error = urlcache_hash_entry_create(header, url,
3005 url_entry_offset, HASHTABLE_URL);
3008 if(error != ERROR_SUCCESS) {
3009 urlcache_entry_free(header, &url_entry->header);
3010 cache_container_unlock_index(container, header);
3011 SetLastError(error);
3012 return FALSE;
3015 if(url_entry->cache_dir < header->dirs_no)
3016 header->directory_data[url_entry->cache_dir].files_no++;
3017 if(entry_type & STICKY_CACHE_ENTRY)
3018 header->exempt_usage.QuadPart += file_size.QuadPart;
3019 else
3020 header->cache_usage.QuadPart += file_size.QuadPart;
3021 if(header->cache_usage.QuadPart+header->exempt_usage.QuadPart > header->cache_limit.QuadPart)
3022 handle_full_cache();
3024 cache_container_unlock_index(container, header);
3025 return TRUE;
3028 /***********************************************************************
3029 * CommitUrlCacheEntryA (WININET.@)
3031 BOOL WINAPI CommitUrlCacheEntryA(LPCSTR lpszUrlName, LPCSTR lpszLocalFileName,
3032 FILETIME ExpireTime, FILETIME LastModifiedTime, DWORD CacheEntryType,
3033 LPBYTE lpHeaderInfo, DWORD dwHeaderSize, LPCSTR lpszFileExtension, LPCSTR lpszOriginalUrl)
3035 WCHAR *file_name = NULL;
3036 BOOL ret;
3038 if(lpszLocalFileName) {
3039 file_name = heap_strdupAtoW(lpszLocalFileName);
3040 if(!file_name)
3041 return FALSE;
3044 ret = urlcache_entry_commit(lpszUrlName, file_name, ExpireTime, LastModifiedTime,
3045 CacheEntryType, lpHeaderInfo, dwHeaderSize, lpszFileExtension, lpszOriginalUrl);
3046 heap_free(file_name);
3047 return ret;
3050 /***********************************************************************
3051 * CommitUrlCacheEntryW (WININET.@)
3053 BOOL WINAPI CommitUrlCacheEntryW(LPCWSTR lpszUrlName, LPCWSTR lpszLocalFileName,
3054 FILETIME ExpireTime, FILETIME LastModifiedTime, DWORD CacheEntryType,
3055 LPWSTR lpHeaderInfo, DWORD dwHeaderSize, LPCWSTR lpszFileExtension, LPCWSTR lpszOriginalUrl)
3057 char *url, *original_url=NULL, *file_ext=NULL, *header_info=NULL;
3058 BOOL ret;
3060 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
3061 return FALSE;
3063 if(lpHeaderInfo) {
3064 header_info = heap_strdupWtoUTF8(lpHeaderInfo);
3065 if(!header_info) {
3066 heap_free(url);
3067 return FALSE;
3069 dwHeaderSize = strlen(header_info);
3072 if(lpszFileExtension) {
3073 file_ext = heap_strdupWtoA(lpszFileExtension);
3074 if(!file_ext) {
3075 heap_free(url);
3076 heap_free(header_info);
3077 return FALSE;
3081 if(lpszOriginalUrl && !urlcache_encode_url_alloc(lpszOriginalUrl, &original_url)) {
3082 heap_free(url);
3083 heap_free(header_info);
3084 heap_free(file_ext);
3085 return FALSE;
3088 ret = urlcache_entry_commit(url, lpszLocalFileName, ExpireTime, LastModifiedTime,
3089 CacheEntryType, (BYTE*)header_info, dwHeaderSize, file_ext, original_url);
3090 heap_free(url);
3091 heap_free(header_info);
3092 heap_free(file_ext);
3093 heap_free(original_url);
3094 return ret;
3097 /***********************************************************************
3098 * ReadUrlCacheEntryStream (WININET.@)
3101 BOOL WINAPI ReadUrlCacheEntryStream(
3102 IN HANDLE hUrlCacheStream,
3103 IN DWORD dwLocation,
3104 IN OUT LPVOID lpBuffer,
3105 IN OUT LPDWORD lpdwLen,
3106 IN DWORD dwReserved
3109 /* Get handle to file from 'stream' */
3110 stream_handle *pStream = (stream_handle*)hUrlCacheStream;
3112 if (dwReserved != 0)
3114 ERR("dwReserved != 0\n");
3115 SetLastError(ERROR_INVALID_PARAMETER);
3116 return FALSE;
3119 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->url, INTERNET_MAX_URL_LENGTH))
3121 SetLastError(ERROR_INVALID_HANDLE);
3122 return FALSE;
3125 if (SetFilePointer(pStream->file, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
3126 return FALSE;
3127 return ReadFile(pStream->file, lpBuffer, *lpdwLen, lpdwLen, NULL);
3130 /***********************************************************************
3131 * RetrieveUrlCacheEntryStreamA (WININET.@)
3134 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(LPCSTR lpszUrlName,
3135 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
3136 LPDWORD lpdwCacheEntryInfoBufferSize, BOOL fRandomRead, DWORD dwReserved)
3138 /* NOTE: this is not the same as the way that the native
3139 * version allocates 'stream' handles. I did it this way
3140 * as it is much easier and no applications should depend
3141 * on this behaviour. (Native version appears to allocate
3142 * indices into a table)
3144 stream_handle *stream;
3145 HANDLE file;
3147 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
3148 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved);
3150 if(!RetrieveUrlCacheEntryFileA(lpszUrlName, lpCacheEntryInfo,
3151 lpdwCacheEntryInfoBufferSize, dwReserved))
3152 return NULL;
3154 file = CreateFileA(lpCacheEntryInfo->lpszLocalFileName, GENERIC_READ, FILE_SHARE_READ,
3155 NULL, OPEN_EXISTING, fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0, NULL);
3156 if(file == INVALID_HANDLE_VALUE) {
3157 UnlockUrlCacheEntryFileA(lpszUrlName, 0);
3158 return NULL;
3161 /* allocate handle storage space */
3162 stream = heap_alloc(sizeof(stream_handle) + strlen(lpszUrlName) * sizeof(CHAR));
3163 if(!stream) {
3164 CloseHandle(file);
3165 UnlockUrlCacheEntryFileA(lpszUrlName, 0);
3166 SetLastError(ERROR_OUTOFMEMORY);
3167 return NULL;
3170 stream->file = file;
3171 strcpy(stream->url, lpszUrlName);
3172 return stream;
3175 /***********************************************************************
3176 * RetrieveUrlCacheEntryStreamW (WININET.@)
3179 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(LPCWSTR lpszUrlName,
3180 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3181 LPDWORD lpdwCacheEntryInfoBufferSize, BOOL fRandomRead, DWORD dwReserved)
3183 DWORD len;
3184 /* NOTE: this is not the same as the way that the native
3185 * version allocates 'stream' handles. I did it this way
3186 * as it is much easier and no applications should depend
3187 * on this behaviour. (Native version appears to allocate
3188 * indices into a table)
3190 stream_handle *stream;
3191 HANDLE file;
3193 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3194 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved);
3196 if(!(len = urlcache_encode_url(lpszUrlName, NULL, 0)))
3197 return NULL;
3199 if(!RetrieveUrlCacheEntryFileW(lpszUrlName, lpCacheEntryInfo,
3200 lpdwCacheEntryInfoBufferSize, dwReserved))
3201 return NULL;
3203 file = CreateFileW(lpCacheEntryInfo->lpszLocalFileName, GENERIC_READ, FILE_SHARE_READ,
3204 NULL, OPEN_EXISTING, fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0, NULL);
3205 if(file == INVALID_HANDLE_VALUE) {
3206 UnlockUrlCacheEntryFileW(lpszUrlName, 0);
3207 return NULL;
3210 /* allocate handle storage space */
3211 stream = heap_alloc(sizeof(stream_handle) + len*sizeof(WCHAR));
3212 if(!stream) {
3213 CloseHandle(file);
3214 UnlockUrlCacheEntryFileW(lpszUrlName, 0);
3215 SetLastError(ERROR_OUTOFMEMORY);
3216 return NULL;
3219 stream->file = file;
3220 if(!urlcache_encode_url(lpszUrlName, stream->url, len)) {
3221 CloseHandle(file);
3222 UnlockUrlCacheEntryFileW(lpszUrlName, 0);
3223 heap_free(stream);
3224 return NULL;
3226 return stream;
3229 /***********************************************************************
3230 * UnlockUrlCacheEntryStream (WININET.@)
3233 BOOL WINAPI UnlockUrlCacheEntryStream(
3234 IN HANDLE hUrlCacheStream,
3235 IN DWORD dwReserved
3238 stream_handle *pStream = (stream_handle*)hUrlCacheStream;
3240 if (dwReserved != 0)
3242 ERR("dwReserved != 0\n");
3243 SetLastError(ERROR_INVALID_PARAMETER);
3244 return FALSE;
3247 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->url, INTERNET_MAX_URL_LENGTH))
3249 SetLastError(ERROR_INVALID_HANDLE);
3250 return FALSE;
3253 if (!UnlockUrlCacheEntryFileA(pStream->url, 0))
3254 return FALSE;
3256 CloseHandle(pStream->file);
3257 heap_free(pStream);
3258 return TRUE;
3262 /***********************************************************************
3263 * DeleteUrlCacheEntryA (WININET.@)
3266 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3268 cache_container *pContainer;
3269 urlcache_header *pHeader;
3270 struct hash_entry *pHashEntry;
3271 DWORD error;
3272 BOOL ret;
3274 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3276 error = cache_containers_find(lpszUrlName, &pContainer);
3277 if (error != ERROR_SUCCESS)
3279 SetLastError(error);
3280 return FALSE;
3283 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3284 if (error != ERROR_SUCCESS)
3286 SetLastError(error);
3287 return FALSE;
3290 if (!(pHeader = cache_container_lock_index(pContainer)))
3291 return FALSE;
3293 if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
3295 cache_container_unlock_index(pContainer, pHeader);
3296 TRACE("entry %s not found!\n", debugstr_a(lpszUrlName));
3297 SetLastError(ERROR_FILE_NOT_FOUND);
3298 return FALSE;
3301 ret = urlcache_entry_delete(pContainer, pHeader, pHashEntry);
3303 cache_container_unlock_index(pContainer, pHeader);
3305 return ret;
3308 /***********************************************************************
3309 * DeleteUrlCacheEntryW (WININET.@)
3312 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3314 char *url;
3315 BOOL ret;
3317 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
3318 return FALSE;
3320 ret = DeleteUrlCacheEntryA(url);
3321 heap_free(url);
3322 return ret;
3325 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3327 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3328 return TRUE;
3331 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3333 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3334 return TRUE;
3337 /***********************************************************************
3338 * CreateCacheContainerA (WININET.@)
3340 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3341 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3343 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3344 d1, d2, d3, d4, d5, d6, d7, d8);
3345 return TRUE;
3348 /***********************************************************************
3349 * CreateCacheContainerW (WININET.@)
3351 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3352 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3354 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3355 d1, d2, d3, d4, d5, d6, d7, d8);
3356 return TRUE;
3359 /***********************************************************************
3360 * FindFirstUrlCacheContainerA (WININET.@)
3362 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3364 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3365 return NULL;
3368 /***********************************************************************
3369 * FindFirstUrlCacheContainerW (WININET.@)
3371 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3373 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3374 return NULL;
3377 /***********************************************************************
3378 * FindNextUrlCacheContainerA (WININET.@)
3380 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3382 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3383 return FALSE;
3386 /***********************************************************************
3387 * FindNextUrlCacheContainerW (WININET.@)
3389 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3391 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3392 return FALSE;
3395 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3396 LPCSTR lpszUrlSearchPattern,
3397 DWORD dwFlags,
3398 DWORD dwFilter,
3399 GROUPID GroupId,
3400 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3401 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3402 LPVOID lpReserved,
3403 LPDWORD pcbReserved2,
3404 LPVOID lpReserved3
3407 FIXME("(%s, 0x%08x, 0x%08x, 0x%s, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3408 dwFlags, dwFilter, wine_dbgstr_longlong(GroupId), lpFirstCacheEntryInfo,
3409 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3410 SetLastError(ERROR_FILE_NOT_FOUND);
3411 return NULL;
3414 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3415 LPCWSTR lpszUrlSearchPattern,
3416 DWORD dwFlags,
3417 DWORD dwFilter,
3418 GROUPID GroupId,
3419 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3420 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3421 LPVOID lpReserved,
3422 LPDWORD pcbReserved2,
3423 LPVOID lpReserved3
3426 FIXME("(%s, 0x%08x, 0x%08x, 0x%s, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3427 dwFlags, dwFilter, wine_dbgstr_longlong(GroupId), lpFirstCacheEntryInfo,
3428 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3429 SetLastError(ERROR_FILE_NOT_FOUND);
3430 return NULL;
3433 /***********************************************************************
3434 * FindFirstUrlCacheEntryA (WININET.@)
3437 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3438 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3440 find_handle *pEntryHandle;
3442 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3444 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3445 if (!pEntryHandle)
3446 return NULL;
3448 pEntryHandle->magic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3449 if (lpszUrlSearchPattern)
3451 pEntryHandle->url_search_pattern = heap_strdupA(lpszUrlSearchPattern);
3452 if (!pEntryHandle->url_search_pattern)
3454 heap_free(pEntryHandle);
3455 return NULL;
3458 else
3459 pEntryHandle->url_search_pattern = NULL;
3460 pEntryHandle->container_idx = 0;
3461 pEntryHandle->hash_table_idx = 0;
3462 pEntryHandle->hash_entry_idx = 0;
3464 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3466 heap_free(pEntryHandle);
3467 return NULL;
3469 return pEntryHandle;
3472 /***********************************************************************
3473 * FindFirstUrlCacheEntryW (WININET.@)
3476 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3477 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3479 find_handle *pEntryHandle;
3481 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3483 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3484 if (!pEntryHandle)
3485 return NULL;
3487 pEntryHandle->magic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3488 if (lpszUrlSearchPattern)
3490 pEntryHandle->url_search_pattern = heap_strdupWtoA(lpszUrlSearchPattern);
3491 if (!pEntryHandle->url_search_pattern)
3493 heap_free(pEntryHandle);
3494 return NULL;
3497 else
3498 pEntryHandle->url_search_pattern = NULL;
3499 pEntryHandle->container_idx = 0;
3500 pEntryHandle->hash_table_idx = 0;
3501 pEntryHandle->hash_entry_idx = 0;
3503 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3505 heap_free(pEntryHandle);
3506 return NULL;
3508 return pEntryHandle;
3511 static BOOL urlcache_find_next_entry(
3512 HANDLE hEnumHandle,
3513 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3514 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3515 BOOL unicode)
3517 find_handle *pEntryHandle = (find_handle*)hEnumHandle;
3518 cache_container *pContainer;
3520 if (pEntryHandle->magic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3522 SetLastError(ERROR_INVALID_HANDLE);
3523 return FALSE;
3526 for (; cache_containers_enum(pEntryHandle->url_search_pattern, pEntryHandle->container_idx, &pContainer);
3527 pEntryHandle->container_idx++, pEntryHandle->hash_table_idx = 0)
3529 urlcache_header *pHeader;
3530 entry_hash_table *pHashTableEntry;
3531 DWORD error;
3533 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3534 if (error != ERROR_SUCCESS)
3536 SetLastError(error);
3537 return FALSE;
3540 if (!(pHeader = cache_container_lock_index(pContainer)))
3541 return FALSE;
3543 for (; urlcache_enum_hash_tables(pHeader, &pEntryHandle->hash_table_idx, &pHashTableEntry);
3544 pEntryHandle->hash_table_idx++, pEntryHandle->hash_entry_idx = 0)
3546 const struct hash_entry *pHashEntry = NULL;
3547 for (; urlcache_enum_hash_table_entries(pHeader, pHashTableEntry, &pEntryHandle->hash_entry_idx, &pHashEntry);
3548 pEntryHandle->hash_entry_idx++)
3550 const entry_url *pUrlEntry;
3551 const entry_header *pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
3553 if (pEntry->signature != URL_SIGNATURE)
3554 continue;
3556 pUrlEntry = (const entry_url *)pEntry;
3557 TRACE("Found URL: %s\n",
3558 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->url_off));
3559 TRACE("Header info: %s\n",
3560 debugstr_an((LPCSTR)pUrlEntry + pUrlEntry->header_info_off,
3561 pUrlEntry->header_info_size));
3563 error = urlcache_copy_entry(
3564 pContainer,
3565 pHeader,
3566 lpNextCacheEntryInfo,
3567 lpdwNextCacheEntryInfoBufferSize,
3568 pUrlEntry,
3569 unicode);
3570 if (error != ERROR_SUCCESS)
3572 cache_container_unlock_index(pContainer, pHeader);
3573 SetLastError(error);
3574 return FALSE;
3576 if(pUrlEntry->local_name_off)
3577 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->local_name_off));
3579 /* increment the current index so that next time the function
3580 * is called the next entry is returned */
3581 pEntryHandle->hash_entry_idx++;
3582 cache_container_unlock_index(pContainer, pHeader);
3583 return TRUE;
3587 cache_container_unlock_index(pContainer, pHeader);
3590 SetLastError(ERROR_NO_MORE_ITEMS);
3591 return FALSE;
3594 /***********************************************************************
3595 * FindNextUrlCacheEntryA (WININET.@)
3597 BOOL WINAPI FindNextUrlCacheEntryA(
3598 HANDLE hEnumHandle,
3599 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3600 LPDWORD lpdwNextCacheEntryInfoBufferSize)
3602 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3604 return urlcache_find_next_entry(hEnumHandle, lpNextCacheEntryInfo,
3605 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
3608 /***********************************************************************
3609 * FindNextUrlCacheEntryW (WININET.@)
3611 BOOL WINAPI FindNextUrlCacheEntryW(
3612 HANDLE hEnumHandle,
3613 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3614 LPDWORD lpdwNextCacheEntryInfoBufferSize
3617 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3619 return urlcache_find_next_entry(hEnumHandle,
3620 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
3621 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
3624 /***********************************************************************
3625 * FindCloseUrlCache (WININET.@)
3627 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3629 find_handle *pEntryHandle = (find_handle*)hEnumHandle;
3631 TRACE("(%p)\n", hEnumHandle);
3633 if (!pEntryHandle || pEntryHandle->magic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3635 SetLastError(ERROR_INVALID_HANDLE);
3636 return FALSE;
3639 pEntryHandle->magic = 0;
3640 heap_free(pEntryHandle->url_search_pattern);
3641 heap_free(pEntryHandle);
3642 return TRUE;
3645 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3646 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3648 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3649 dwSearchCondition, lpGroupId, lpReserved);
3650 return NULL;
3653 BOOL WINAPI FindNextUrlCacheEntryExA(
3654 HANDLE hEnumHandle,
3655 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3656 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3657 LPVOID lpReserved,
3658 LPDWORD pcbReserved2,
3659 LPVOID lpReserved3
3662 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3663 lpReserved, pcbReserved2, lpReserved3);
3664 return FALSE;
3667 BOOL WINAPI FindNextUrlCacheEntryExW(
3668 HANDLE hEnumHandle,
3669 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3670 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3671 LPVOID lpReserved,
3672 LPDWORD pcbReserved2,
3673 LPVOID lpReserved3
3676 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3677 lpReserved, pcbReserved2, lpReserved3);
3678 return FALSE;
3681 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3683 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3684 return FALSE;
3687 /***********************************************************************
3688 * CreateUrlCacheGroup (WININET.@)
3691 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3693 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3694 return FALSE;
3697 /***********************************************************************
3698 * DeleteUrlCacheGroup (WININET.@)
3701 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3703 FIXME("(0x%s, 0x%08x, %p) stub\n",
3704 wine_dbgstr_longlong(GroupId), dwFlags, lpReserved);
3705 return FALSE;
3708 /***********************************************************************
3709 * DeleteWpadCacheForNetworks (WININET.@)
3710 * Undocumented, added in IE8
3712 BOOL WINAPI DeleteWpadCacheForNetworks(DWORD unk1)
3714 FIXME("(%d) stub\n", unk1);
3715 return FALSE;
3718 /***********************************************************************
3719 * SetUrlCacheEntryGroupA (WININET.@)
3722 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3723 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3724 LPVOID lpReserved)
3726 FIXME("(%s, 0x%08x, 0x%s, %p, 0x%08x, %p) stub\n",
3727 debugstr_a(lpszUrlName), dwFlags, wine_dbgstr_longlong(GroupId),
3728 pbGroupAttributes, cbGroupAttributes, lpReserved);
3729 SetLastError(ERROR_FILE_NOT_FOUND);
3730 return FALSE;
3733 /***********************************************************************
3734 * SetUrlCacheEntryGroupW (WININET.@)
3737 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3738 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3739 LPVOID lpReserved)
3741 FIXME("(%s, 0x%08x, 0x%s, %p, 0x%08x, %p) stub\n",
3742 debugstr_w(lpszUrlName), dwFlags, wine_dbgstr_longlong(GroupId),
3743 pbGroupAttributes, cbGroupAttributes, lpReserved);
3744 SetLastError(ERROR_FILE_NOT_FOUND);
3745 return FALSE;
3748 static cache_container *find_container(DWORD flags)
3750 cache_container *container;
3752 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
3754 switch (flags & (CACHE_CONFIG_CONTENT_PATHS_FC | CACHE_CONFIG_COOKIES_PATHS_FC | CACHE_CONFIG_HISTORY_PATHS_FC))
3756 case 0:
3757 case CACHE_CONFIG_CONTENT_PATHS_FC:
3758 if (container->default_entry_type == NORMAL_CACHE_ENTRY)
3759 return container;
3760 break;
3762 case CACHE_CONFIG_COOKIES_PATHS_FC:
3763 if (container->default_entry_type == COOKIE_CACHE_ENTRY)
3764 return container;
3765 break;
3767 case CACHE_CONFIG_HISTORY_PATHS_FC:
3768 if (container->default_entry_type == URLHISTORY_CACHE_ENTRY)
3769 return container;
3770 break;
3772 default:
3773 FIXME("flags %08x not handled\n", flags);
3774 break;
3778 return NULL;
3781 /***********************************************************************
3782 * GetUrlCacheConfigInfoW (WININET.@)
3784 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW info, LPDWORD size, DWORD flags)
3786 cache_container *container;
3787 DWORD error;
3789 FIXME("(%p, %p, %x): semi-stub\n", info, size, flags);
3791 if (!info || !(container = find_container(flags)))
3793 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3794 return FALSE;
3797 error = cache_container_open_index(container, MIN_BLOCK_NO);
3798 if (error != ERROR_SUCCESS)
3800 INTERNET_SetLastError(error);
3801 return FALSE;
3804 info->dwContainer = 0;
3805 info->dwQuota = FILE_SIZE(MAX_BLOCK_NO) / 1024;
3806 info->dwReserved4 = 0;
3807 info->fPerUser = TRUE;
3808 info->dwSyncMode = 0;
3809 info->dwNumCachePaths = 1;
3810 info->dwNormalUsage = 0;
3811 info->dwExemptUsage = 0;
3812 info->u.s.dwCacheSize = container->file_size / 1024;
3813 lstrcpynW(info->u.s.CachePath, container->path, MAX_PATH);
3815 cache_container_close_index(container);
3817 TRACE("CachePath %s\n", debugstr_w(info->u.s.CachePath));
3819 return TRUE;
3822 /***********************************************************************
3823 * GetUrlCacheConfigInfoA (WININET.@)
3825 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA info, LPDWORD size, DWORD flags)
3827 INTERNET_CACHE_CONFIG_INFOW infoW;
3829 TRACE("(%p, %p, %x)\n", info, size, flags);
3831 if (!info)
3833 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3834 return FALSE;
3837 infoW.dwStructSize = sizeof(infoW);
3838 if (!GetUrlCacheConfigInfoW(&infoW, size, flags))
3839 return FALSE;
3841 info->dwContainer = infoW.dwContainer;
3842 info->dwQuota = infoW.dwQuota;
3843 info->dwReserved4 = infoW.dwReserved4;
3844 info->fPerUser = infoW.fPerUser;
3845 info->dwSyncMode = infoW.dwSyncMode;
3846 info->dwNumCachePaths = infoW.dwNumCachePaths;
3847 info->dwNormalUsage = infoW.dwNormalUsage;
3848 info->dwExemptUsage = infoW.dwExemptUsage;
3849 info->u.s.dwCacheSize = infoW.u.s.dwCacheSize;
3850 WideCharToMultiByte(CP_ACP, 0, infoW.u.s.CachePath, -1, info->u.s.CachePath, MAX_PATH, NULL, NULL);
3852 return TRUE;
3855 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3856 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3857 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3859 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3860 wine_dbgstr_longlong(gid), dwFlags, dwAttributes, lpGroupInfo,
3861 lpdwGroupInfo, lpReserved);
3862 return FALSE;
3865 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3866 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3867 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3869 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3870 wine_dbgstr_longlong(gid), dwFlags, dwAttributes, lpGroupInfo,
3871 lpdwGroupInfo, lpReserved);
3872 return FALSE;
3875 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3876 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3878 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p) stub\n",
3879 wine_dbgstr_longlong(gid), dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3880 return TRUE;
3883 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3884 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3886 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p) stub\n",
3887 wine_dbgstr_longlong(gid), dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3888 return TRUE;
3891 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3893 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3894 return TRUE;
3897 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3899 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3900 return TRUE;
3903 /***********************************************************************
3904 * DeleteIE3Cache (WININET.@)
3906 * Deletes the files used by the IE3 URL caching system.
3908 * PARAMS
3909 * hWnd [I] A dummy window.
3910 * hInst [I] Instance of process calling the function.
3911 * lpszCmdLine [I] Options used by function.
3912 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3914 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3916 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3917 return 0;
3920 static BOOL urlcache_entry_is_expired(const entry_url *pUrlEntry,
3921 FILETIME *pftLastModified)
3923 BOOL ret;
3924 FILETIME now, expired;
3926 *pftLastModified = pUrlEntry->modification_time;
3927 GetSystemTimeAsFileTime(&now);
3928 dos_date_time_to_file_time(pUrlEntry->expire_date,
3929 pUrlEntry->expire_time, &expired);
3930 /* If the expired time is 0, it's interpreted as not expired */
3931 if (!expired.dwLowDateTime && !expired.dwHighDateTime)
3932 ret = FALSE;
3933 else
3934 ret = CompareFileTime(&expired, &now) < 0;
3935 return ret;
3938 /***********************************************************************
3939 * IsUrlCacheEntryExpiredA (WININET.@)
3941 * PARAMS
3942 * url [I] Url
3943 * dwFlags [I] Unknown
3944 * pftLastModified [O] Last modified time
3946 BOOL WINAPI IsUrlCacheEntryExpiredA(LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified)
3948 urlcache_header *pHeader;
3949 struct hash_entry *pHashEntry;
3950 const entry_header *pEntry;
3951 const entry_url * pUrlEntry;
3952 cache_container *pContainer;
3953 BOOL expired;
3955 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3957 if (!url || !pftLastModified)
3958 return TRUE;
3959 if (dwFlags)
3960 FIXME("unknown flags 0x%08x\n", dwFlags);
3962 /* Any error implies that the URL is expired, i.e. not in the cache */
3963 if (cache_containers_find(url, &pContainer))
3965 memset(pftLastModified, 0, sizeof(*pftLastModified));
3966 return TRUE;
3969 if (cache_container_open_index(pContainer, MIN_BLOCK_NO))
3971 memset(pftLastModified, 0, sizeof(*pftLastModified));
3972 return TRUE;
3975 if (!(pHeader = cache_container_lock_index(pContainer)))
3977 memset(pftLastModified, 0, sizeof(*pftLastModified));
3978 return TRUE;
3981 if (!urlcache_find_hash_entry(pHeader, url, &pHashEntry))
3983 cache_container_unlock_index(pContainer, pHeader);
3984 memset(pftLastModified, 0, sizeof(*pftLastModified));
3985 TRACE("entry %s not found!\n", url);
3986 return TRUE;
3989 pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
3990 if (pEntry->signature != URL_SIGNATURE)
3992 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
3993 cache_container_unlock_index(pContainer, pHeader);
3994 memset(pftLastModified, 0, sizeof(*pftLastModified));
3995 return TRUE;
3998 pUrlEntry = (const entry_url *)pEntry;
3999 expired = urlcache_entry_is_expired(pUrlEntry, pftLastModified);
4001 cache_container_unlock_index(pContainer, pHeader);
4003 return expired;
4006 /***********************************************************************
4007 * IsUrlCacheEntryExpiredW (WININET.@)
4009 * PARAMS
4010 * url [I] Url
4011 * dwFlags [I] Unknown
4012 * pftLastModified [O] Last modified time
4014 BOOL WINAPI IsUrlCacheEntryExpiredW(LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified)
4016 char *encoded_url;
4017 BOOL ret;
4019 if(!urlcache_encode_url_alloc(url, &encoded_url))
4020 return FALSE;
4022 ret = IsUrlCacheEntryExpiredA(encoded_url, dwFlags, pftLastModified);
4023 heap_free(encoded_url);
4024 return ret;
4027 /***********************************************************************
4028 * GetDiskInfoA (WININET.@)
4030 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
4032 BOOL ret;
4033 ULARGE_INTEGER bytes_free, bytes_total;
4035 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
4037 if (!path)
4039 SetLastError(ERROR_INVALID_PARAMETER);
4040 return FALSE;
4043 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
4045 if (cluster_size) *cluster_size = 1;
4046 if (free) *free = bytes_free.QuadPart;
4047 if (total) *total = bytes_total.QuadPart;
4049 return ret;
4052 /***********************************************************************
4053 * RegisterUrlCacheNotification (WININET.@)
4055 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
4057 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
4058 return 0;
4061 /***********************************************************************
4062 * IncrementUrlCacheHeaderData (WININET.@)
4064 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
4066 FIXME("(%u, %p)\n", index, data);
4067 return FALSE;
4070 /***********************************************************************
4071 * RunOnceUrlCache (WININET.@)
4074 DWORD WINAPI RunOnceUrlCache(HWND hwnd, HINSTANCE hinst, LPSTR cmd, int cmdshow)
4076 FIXME("(%p, %p, %s, %d): stub\n", hwnd, hinst, debugstr_a(cmd), cmdshow);
4077 return 0;
4080 BOOL init_urlcache(void)
4082 dll_unload_event = CreateEventW(NULL, FALSE, FALSE, NULL);
4083 if(!dll_unload_event)
4084 return FALSE;
4086 free_cache_running = CreateSemaphoreW(NULL, 1, 1, NULL);
4087 if(!free_cache_running) {
4088 CloseHandle(dll_unload_event);
4089 return FALSE;
4092 cache_containers_init();
4093 return TRUE;
4096 void free_urlcache(void)
4098 SetEvent(dll_unload_event);
4099 WaitForSingleObject(free_cache_running, INFINITE);
4100 ReleaseSemaphore(free_cache_running, 1, NULL);
4101 CloseHandle(free_cache_running);
4102 CloseHandle(dll_unload_event);
4104 cache_containers_free();
4107 /***********************************************************************
4108 * LoadUrlCacheContent (WININET.@)
4110 BOOL WINAPI LoadUrlCacheContent(void)
4112 FIXME("stub!\n");
4113 return FALSE;