ntdll: Add a helper for platform-specific threading initialization.
[wine.git] / dlls / wininet / urlcache.c
blob3275cb19e6a014b35223ff074da1c6c9404946a7
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 static const WCHAR mapping_name_format[]
398 = {'%','s','i','n','d','e','x','.','d','a','t','_','%','l','u',0};
399 WCHAR mapping_name[MAX_PATH];
400 HANDLE mapping;
402 wsprintfW(mapping_name, mapping_name_format, path, size);
403 cache_container_create_object_name(mapping_name, '_');
405 mapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, mapping_name);
406 if(mapping) {
407 if(validate) *validate = FALSE;
408 return mapping;
411 if(validate) *validate = TRUE;
412 return CreateFileMappingW(file, NULL, PAGE_READWRITE, 0, 0, mapping_name);
415 /* Caller must hold container lock */
416 static DWORD cache_container_set_size(cache_container *container, HANDLE file, DWORD blocks_no)
418 static const WCHAR cache_content_key[] = {'S','o','f','t','w','a','r','e','\\',
419 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
420 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
421 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s','\\',
422 'C','a','c','h','e','\\','C','o','n','t','e','n','t',0};
423 static const WCHAR cache_limit[] = {'C','a','c','h','e','L','i','m','i','t',0};
425 DWORD file_size = FILE_SIZE(blocks_no);
426 WCHAR dir_path[MAX_PATH], *dir_name;
427 entry_hash_table *hashtable_entry;
428 urlcache_header *header;
429 HANDLE mapping;
430 FILETIME ft;
431 HKEY key;
432 int i, j;
434 if(SetFilePointer(file, file_size, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
435 return GetLastError();
437 if(!SetEndOfFile(file))
438 return GetLastError();
440 mapping = cache_container_map_index(file, container->path, file_size, NULL);
441 if(!mapping)
442 return GetLastError();
444 header = MapViewOfFile(mapping, FILE_MAP_WRITE, 0, 0, 0);
445 if(!header) {
446 CloseHandle(mapping);
447 return GetLastError();
450 if(blocks_no != MIN_BLOCK_NO) {
451 if(file_size > header->size)
452 memset((char*)header+header->size, 0, file_size-header->size);
453 header->size = file_size;
454 header->capacity_in_blocks = blocks_no;
456 UnmapViewOfFile(header);
457 CloseHandle(container->mapping);
458 container->mapping = mapping;
459 container->file_size = file_size;
460 return ERROR_SUCCESS;
463 memset(header, 0, file_size);
464 /* First set some constants and defaults in the header */
465 memcpy(header->signature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1);
466 memcpy(header->signature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1);
467 header->size = file_size;
468 header->capacity_in_blocks = blocks_no;
469 /* 127MB - taken from default for Windows 2000 */
470 header->cache_limit.QuadPart = 0x07ff5400;
471 /* Copied from a Windows 2000 cache index */
472 header->dirs_no = container->default_entry_type==NORMAL_CACHE_ENTRY ? 4 : 0;
474 /* If the registry has a cache size set, use the registry value */
475 if(RegOpenKeyW(HKEY_CURRENT_USER, cache_content_key, &key) == ERROR_SUCCESS) {
476 DWORD dw, len = sizeof(dw), keytype;
478 if(RegQueryValueExW(key, cache_limit, NULL, &keytype, (BYTE*)&dw, &len) == ERROR_SUCCESS &&
479 keytype == REG_DWORD)
480 header->cache_limit.QuadPart = (ULONGLONG)dw * 1024;
481 RegCloseKey(key);
484 urlcache_create_hash_table(header, NULL, &hashtable_entry);
486 /* Last step - create the directories */
487 lstrcpyW(dir_path, container->path);
488 dir_name = dir_path + lstrlenW(dir_path);
489 dir_name[8] = 0;
491 GetSystemTimeAsFileTime(&ft);
493 for(i=0; i<header->dirs_no; ++i) {
494 header->directory_data[i].files_no = 0;
495 for(j=0;; ++j) {
496 ULONGLONG n = ft.dwHighDateTime;
497 int k;
499 /* Generate a file name to attempt to create.
500 * This algorithm will create what will appear
501 * to be random and unrelated directory names
502 * of up to 9 characters in length.
504 n <<= 32;
505 n += ft.dwLowDateTime;
506 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
508 for(k = 0; k < 8; ++k) {
509 int r = (n % 36);
511 /* Dividing by a prime greater than 36 helps
512 * with the appearance of randomness
514 n /= 37;
516 if(r < 10)
517 dir_name[k] = '0' + r;
518 else
519 dir_name[k] = 'A' + (r - 10);
522 if(CreateDirectoryW(dir_path, 0)) {
523 /* The following is OK because we generated an
524 * 8 character directory name made from characters
525 * [A-Z0-9], which are equivalent for all code
526 * pages and for UTF-16
528 for (k = 0; k < 8; ++k)
529 header->directory_data[i].name[k] = dir_name[k];
530 break;
531 }else if(j >= 255) {
532 /* Give up. The most likely cause of this
533 * is a full disk, but whatever the cause
534 * is, it should be more than apparent that
535 * we won't succeed.
537 UnmapViewOfFile(header);
538 CloseHandle(mapping);
539 return GetLastError();
544 UnmapViewOfFile(header);
545 CloseHandle(container->mapping);
546 container->mapping = mapping;
547 container->file_size = file_size;
548 return ERROR_SUCCESS;
551 static BOOL cache_container_is_valid(urlcache_header *header, DWORD file_size)
553 DWORD allocation_size, count_bits, i;
555 if(file_size < FILE_SIZE(MIN_BLOCK_NO))
556 return FALSE;
558 if(file_size != header->size)
559 return FALSE;
561 if (!memcmp(header->signature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1) &&
562 memcmp(header->signature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1))
563 return FALSE;
565 if(FILE_SIZE(header->capacity_in_blocks) != file_size)
566 return FALSE;
568 allocation_size = 0;
569 for(i=0; i<header->capacity_in_blocks/8; i++) {
570 for(count_bits = header->allocation_table[i]; count_bits!=0; count_bits>>=1) {
571 if(count_bits & 1)
572 allocation_size++;
575 if(allocation_size != header->blocks_in_use)
576 return FALSE;
578 for(; i<ALLOCATION_TABLE_SIZE; i++) {
579 if(header->allocation_table[i])
580 return FALSE;
583 return TRUE;
586 /***********************************************************************
587 * cache_container_open_index (Internal)
589 * Opens the index file and saves mapping handle
591 * RETURNS
592 * ERROR_SUCCESS if succeeded
593 * Any other Win32 error code if failed
596 static DWORD cache_container_open_index(cache_container *container, DWORD blocks_no)
598 static const WCHAR index_dat[] = {'i','n','d','e','x','.','d','a','t',0};
600 HANDLE file;
601 WCHAR index_path[MAX_PATH];
602 DWORD file_size;
603 BOOL validate;
605 WaitForSingleObject(container->mutex, INFINITE);
607 if(container->mapping) {
608 ReleaseMutex(container->mutex);
609 return ERROR_SUCCESS;
612 lstrcpyW(index_path, container->path);
613 lstrcatW(index_path, index_dat);
615 file = CreateFileW(index_path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
616 if(file == INVALID_HANDLE_VALUE) {
617 /* Maybe the directory wasn't there? Try to create it */
618 if(CreateDirectoryW(container->path, 0))
619 file = CreateFileW(index_path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
621 if(file == INVALID_HANDLE_VALUE) {
622 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(index_path));
623 ReleaseMutex(container->mutex);
624 return GetLastError();
627 file_size = GetFileSize(file, NULL);
628 if(file_size == INVALID_FILE_SIZE) {
629 CloseHandle(file);
630 ReleaseMutex(container->mutex);
631 return GetLastError();
634 if(blocks_no < MIN_BLOCK_NO)
635 blocks_no = MIN_BLOCK_NO;
636 else if(blocks_no > MAX_BLOCK_NO)
637 blocks_no = MAX_BLOCK_NO;
639 if(file_size < FILE_SIZE(blocks_no)) {
640 DWORD ret = cache_container_set_size(container, file, blocks_no);
641 CloseHandle(file);
642 ReleaseMutex(container->mutex);
643 return ret;
646 container->file_size = file_size;
647 container->mapping = cache_container_map_index(file, container->path, file_size, &validate);
648 CloseHandle(file);
649 if(container->mapping && validate) {
650 urlcache_header *header = MapViewOfFile(container->mapping, FILE_MAP_WRITE, 0, 0, 0);
652 if(header && !cache_container_is_valid(header, file_size)) {
653 WARN("detected old or broken index.dat file\n");
654 UnmapViewOfFile(header);
655 FreeUrlCacheSpaceW(container->path, 100, 0);
656 }else if(header) {
657 UnmapViewOfFile(header);
658 }else {
659 CloseHandle(container->mapping);
660 container->mapping = NULL;
664 if(!container->mapping)
666 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
667 ReleaseMutex(container->mutex);
668 return GetLastError();
671 ReleaseMutex(container->mutex);
672 return ERROR_SUCCESS;
675 /***********************************************************************
676 * cache_container_close_index (Internal)
678 * Closes the index
680 * RETURNS
681 * nothing
684 static void cache_container_close_index(cache_container *pContainer)
686 CloseHandle(pContainer->mapping);
687 pContainer->mapping = NULL;
690 static BOOL cache_containers_add(const char *cache_prefix, LPCWSTR path,
691 DWORD default_entry_type, LPWSTR mutex_name)
693 cache_container *pContainer = heap_alloc(sizeof(cache_container));
694 int cache_prefix_len = strlen(cache_prefix);
696 if (!pContainer)
698 return FALSE;
701 pContainer->mapping = NULL;
702 pContainer->file_size = 0;
703 pContainer->default_entry_type = default_entry_type;
705 pContainer->path = heap_strdupW(path);
706 if (!pContainer->path)
708 heap_free(pContainer);
709 return FALSE;
712 pContainer->cache_prefix = heap_alloc(cache_prefix_len+1);
713 if (!pContainer->cache_prefix)
715 heap_free(pContainer->path);
716 heap_free(pContainer);
717 return FALSE;
720 memcpy(pContainer->cache_prefix, cache_prefix, cache_prefix_len+1);
722 CharLowerW(mutex_name);
723 cache_container_create_object_name(mutex_name, '!');
725 if ((pContainer->mutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
727 ERR("couldn't create mutex (error is %d)\n", GetLastError());
728 heap_free(pContainer->path);
729 heap_free(pContainer);
730 return FALSE;
733 list_add_head(&UrlContainers, &pContainer->entry);
735 return TRUE;
738 static void cache_container_delete_container(cache_container *pContainer)
740 list_remove(&pContainer->entry);
742 cache_container_close_index(pContainer);
743 CloseHandle(pContainer->mutex);
744 heap_free(pContainer->path);
745 heap_free(pContainer->cache_prefix);
746 heap_free(pContainer);
749 static void cache_containers_init(void)
751 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
752 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
753 static const WCHAR CookieSuffix[] = {0};
754 static const struct
756 int nFolder; /* CSIDL_* constant */
757 const WCHAR *shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
758 const char *cache_prefix; /* prefix used to reference the container */
759 DWORD default_entry_type;
760 } DefaultContainerData[] =
762 { CSIDL_INTERNET_CACHE, UrlSuffix, "", NORMAL_CACHE_ENTRY },
763 { CSIDL_HISTORY, HistorySuffix, "Visited:", URLHISTORY_CACHE_ENTRY },
764 { CSIDL_COOKIES, CookieSuffix, "Cookie:", COOKIE_CACHE_ENTRY },
766 DWORD i;
768 for (i = 0; i < ARRAY_SIZE(DefaultContainerData); i++)
770 WCHAR wszCachePath[MAX_PATH];
771 WCHAR wszMutexName[MAX_PATH];
772 int path_len, suffix_len;
773 BOOL def_char;
775 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
777 ERR("Couldn't get path for default container %u\n", i);
778 continue;
780 path_len = lstrlenW(wszCachePath);
781 suffix_len = lstrlenW(DefaultContainerData[i].shpath_suffix);
783 if (path_len + suffix_len + 2 > MAX_PATH)
785 ERR("Path too long\n");
786 continue;
789 wszCachePath[path_len] = '\\';
790 wszCachePath[path_len+1] = 0;
792 lstrcpyW(wszMutexName, wszCachePath);
794 if (suffix_len)
796 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
797 wszCachePath[path_len + suffix_len + 1] = '\\';
798 wszCachePath[path_len + suffix_len + 2] = '\0';
801 if (!WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wszCachePath, path_len,
802 NULL, 0, NULL, &def_char) || def_char)
804 WCHAR tmp[MAX_PATH];
806 /* cannot convert path to ANSI code page */
807 if (!(path_len = GetShortPathNameW(wszCachePath, tmp, MAX_PATH)) ||
808 !WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, tmp, path_len,
809 NULL, 0, NULL, &def_char) || def_char)
810 ERR("Can't create container path accessible by ANSI functions\n");
811 else
812 memcpy(wszCachePath, tmp, (path_len+1)*sizeof(WCHAR));
815 cache_containers_add(DefaultContainerData[i].cache_prefix, wszCachePath,
816 DefaultContainerData[i].default_entry_type, wszMutexName);
820 static void cache_containers_free(void)
822 while(!list_empty(&UrlContainers))
823 cache_container_delete_container(
824 LIST_ENTRY(list_head(&UrlContainers), cache_container, entry)
828 static DWORD cache_containers_find(const char *url, cache_container **ret)
830 cache_container *container;
832 TRACE("searching for prefix for URL: %s\n", debugstr_a(url));
834 if(!url)
835 return ERROR_INVALID_PARAMETER;
837 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
839 int prefix_len = strlen(container->cache_prefix);
841 if(!strncmp(container->cache_prefix, url, prefix_len)) {
842 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
843 *ret = container;
844 return ERROR_SUCCESS;
848 ERR("no container found\n");
849 return ERROR_FILE_NOT_FOUND;
852 static BOOL cache_containers_enum(char *search_pattern, DWORD index, cache_container **ret)
854 DWORD i = 0;
855 cache_container *container;
857 TRACE("searching for prefix: %s\n", debugstr_a(search_pattern));
859 /* non-NULL search pattern only returns one container ever */
860 if (search_pattern && index > 0)
861 return FALSE;
863 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
865 if (search_pattern)
867 if (!strcmp(container->cache_prefix, search_pattern))
869 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
870 *ret = container;
871 return TRUE;
874 else
876 if (i == index)
878 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
879 *ret = container;
880 return TRUE;
883 i++;
885 return FALSE;
888 /***********************************************************************
889 * cache_container_lock_index (Internal)
891 * Locks the index for system-wide exclusive access.
893 * RETURNS
894 * Cache file header if successful
895 * NULL if failed and calls SetLastError.
897 static urlcache_header* cache_container_lock_index(cache_container *pContainer)
899 BYTE index;
900 LPVOID pIndexData;
901 urlcache_header* pHeader;
902 DWORD error;
904 /* acquire mutex */
905 WaitForSingleObject(pContainer->mutex, INFINITE);
907 pIndexData = MapViewOfFile(pContainer->mapping, FILE_MAP_WRITE, 0, 0, 0);
909 if (!pIndexData)
911 ReleaseMutex(pContainer->mutex);
912 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
913 return NULL;
915 pHeader = (urlcache_header*)pIndexData;
917 /* file has grown - we need to remap to prevent us getting
918 * access violations when we try and access beyond the end
919 * of the memory mapped file */
920 if (pHeader->size != pContainer->file_size)
922 UnmapViewOfFile( pHeader );
923 cache_container_close_index(pContainer);
924 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
925 if (error != ERROR_SUCCESS)
927 ReleaseMutex(pContainer->mutex);
928 SetLastError(error);
929 return NULL;
931 pIndexData = MapViewOfFile(pContainer->mapping, FILE_MAP_WRITE, 0, 0, 0);
933 if (!pIndexData)
935 ReleaseMutex(pContainer->mutex);
936 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
937 return NULL;
939 pHeader = (urlcache_header*)pIndexData;
942 TRACE("Signature: %s, file size: %d bytes\n", pHeader->signature, pHeader->size);
944 for (index = 0; index < pHeader->dirs_no; index++)
946 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].name);
949 return pHeader;
952 /***********************************************************************
953 * cache_container_unlock_index (Internal)
956 static BOOL cache_container_unlock_index(cache_container *pContainer, urlcache_header *pHeader)
958 /* release mutex */
959 ReleaseMutex(pContainer->mutex);
960 return UnmapViewOfFile(pHeader);
963 /***********************************************************************
964 * urlcache_create_file_pathW (Internal)
966 * Copies the full path to the specified buffer given the local file
967 * name and the index of the directory it is in. Always sets value in
968 * lpBufferSize to the required buffer size (in bytes).
970 * RETURNS
971 * TRUE if the buffer was big enough
972 * FALSE if the buffer was too small
975 static BOOL urlcache_create_file_pathW(
976 const cache_container *pContainer,
977 const urlcache_header *pHeader,
978 LPCSTR szLocalFileName,
979 BYTE Directory,
980 LPWSTR wszPath,
981 LPLONG lpBufferSize,
982 BOOL trunc_name)
984 LONG nRequired;
985 int path_len = lstrlenW(pContainer->path);
986 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
987 if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->dirs_no)
989 *lpBufferSize = 0;
990 return FALSE;
993 nRequired = (path_len + file_name_len) * sizeof(WCHAR);
994 if(Directory != CACHE_CONTAINER_NO_SUBDIR)
995 nRequired += (DIR_LENGTH + 1) * sizeof(WCHAR);
996 if (trunc_name && nRequired >= *lpBufferSize)
997 nRequired = *lpBufferSize;
998 if (nRequired <= *lpBufferSize)
1000 int dir_len;
1002 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
1003 if (Directory != CACHE_CONTAINER_NO_SUBDIR)
1005 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].name, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
1006 wszPath[dir_len + path_len] = '\\';
1007 dir_len++;
1009 else
1011 dir_len = 0;
1013 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len,
1014 *lpBufferSize/sizeof(WCHAR)-dir_len-path_len);
1015 wszPath[*lpBufferSize/sizeof(WCHAR)-1] = 0;
1016 *lpBufferSize = nRequired;
1017 return TRUE;
1019 *lpBufferSize = nRequired;
1020 return FALSE;
1023 /***********************************************************************
1024 * urlcache_create_file_pathA (Internal)
1026 * Copies the full path to the specified buffer given the local file
1027 * name and the index of the directory it is in. Always sets value in
1028 * lpBufferSize to the required buffer size.
1030 * RETURNS
1031 * TRUE if the buffer was big enough
1032 * FALSE if the buffer was too small
1035 static BOOL urlcache_create_file_pathA(
1036 const cache_container *pContainer,
1037 const urlcache_header *pHeader,
1038 LPCSTR szLocalFileName,
1039 BYTE Directory,
1040 LPSTR szPath,
1041 LPLONG lpBufferSize)
1043 LONG nRequired;
1044 int path_len, file_name_len, dir_len;
1046 if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->dirs_no)
1048 *lpBufferSize = 0;
1049 return FALSE;
1052 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
1053 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
1054 if (Directory!=CACHE_CONTAINER_NO_SUBDIR)
1055 dir_len = DIR_LENGTH+1;
1056 else
1057 dir_len = 0;
1059 nRequired = (path_len + dir_len + file_name_len) * sizeof(char);
1060 if (nRequired <= *lpBufferSize)
1062 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
1063 if(dir_len) {
1064 memcpy(szPath+path_len, pHeader->directory_data[Directory].name, dir_len-1);
1065 szPath[path_len + dir_len-1] = '\\';
1067 memcpy(szPath + path_len + dir_len, szLocalFileName, file_name_len);
1068 *lpBufferSize = nRequired;
1069 return TRUE;
1071 *lpBufferSize = nRequired;
1072 return FALSE;
1075 /* Just like FileTimeToDosDateTime, except that it also maps the special
1076 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1078 static void file_time_to_dos_date_time(const FILETIME *ft, WORD *fatdate,
1079 WORD *fattime)
1081 if (!ft->dwLowDateTime && !ft->dwHighDateTime)
1082 *fatdate = *fattime = 0;
1083 else
1084 FileTimeToDosDateTime(ft, fatdate, fattime);
1087 /***********************************************************************
1088 * urlcache_delete_file (Internal)
1090 static DWORD urlcache_delete_file(const cache_container *container,
1091 urlcache_header *header, entry_url *url_entry)
1093 WIN32_FILE_ATTRIBUTE_DATA attr;
1094 WCHAR path[MAX_PATH];
1095 LONG path_size = sizeof(path);
1096 DWORD err;
1097 WORD date, time;
1099 if(!url_entry->local_name_off)
1100 goto succ;
1102 if(!urlcache_create_file_pathW(container, header,
1103 (LPCSTR)url_entry+url_entry->local_name_off,
1104 url_entry->cache_dir, path, &path_size, FALSE))
1105 goto succ;
1107 if(!GetFileAttributesExW(path, GetFileExInfoStandard, &attr))
1108 goto succ;
1109 file_time_to_dos_date_time(&attr.ftLastWriteTime, &date, &time);
1110 if(date != url_entry->write_date || time != url_entry->write_time)
1111 goto succ;
1113 err = (DeleteFileW(path) ? ERROR_SUCCESS : GetLastError());
1114 if(err == ERROR_ACCESS_DENIED || err == ERROR_SHARING_VIOLATION)
1115 return err;
1117 succ:
1118 if (url_entry->cache_dir < header->dirs_no)
1120 if (header->directory_data[url_entry->cache_dir].files_no)
1121 header->directory_data[url_entry->cache_dir].files_no--;
1123 if (url_entry->cache_entry_type & STICKY_CACHE_ENTRY)
1125 if (url_entry->size.QuadPart < header->exempt_usage.QuadPart)
1126 header->exempt_usage.QuadPart -= url_entry->size.QuadPart;
1127 else
1128 header->exempt_usage.QuadPart = 0;
1130 else
1132 if (url_entry->size.QuadPart < header->cache_usage.QuadPart)
1133 header->cache_usage.QuadPart -= url_entry->size.QuadPart;
1134 else
1135 header->cache_usage.QuadPart = 0;
1138 return ERROR_SUCCESS;
1141 static BOOL urlcache_clean_leaked_entries(cache_container *container, urlcache_header *header)
1143 DWORD *leak_off;
1144 BOOL freed = FALSE;
1146 leak_off = &header->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
1147 while(*leak_off) {
1148 entry_url *url_entry = (entry_url*)((LPBYTE)header + *leak_off);
1150 if(SUCCEEDED(urlcache_delete_file(container, header, url_entry))) {
1151 *leak_off = url_entry->exempt_delta;
1152 urlcache_entry_free(header, &url_entry->header);
1153 freed = TRUE;
1154 }else {
1155 leak_off = &url_entry->exempt_delta;
1159 return freed;
1162 /***********************************************************************
1163 * cache_container_clean_index (Internal)
1165 * This function is meant to make place in index file by removing leaked
1166 * files entries and resizing the file.
1168 * CAUTION: file view may get mapped to new memory
1170 * RETURNS
1171 * ERROR_SUCCESS when new memory is available
1172 * error code otherwise
1174 static DWORD cache_container_clean_index(cache_container *container, urlcache_header **file_view)
1176 urlcache_header *header = *file_view;
1177 DWORD ret;
1179 TRACE("(%s %s)\n", debugstr_a(container->cache_prefix), debugstr_w(container->path));
1181 if(urlcache_clean_leaked_entries(container, header))
1182 return ERROR_SUCCESS;
1184 if(header->size >= ALLOCATION_TABLE_SIZE*8*BLOCKSIZE + ENTRY_START_OFFSET) {
1185 WARN("index file has maximal size\n");
1186 return ERROR_NOT_ENOUGH_MEMORY;
1189 cache_container_close_index(container);
1190 ret = cache_container_open_index(container, header->capacity_in_blocks*2);
1191 if(ret != ERROR_SUCCESS)
1192 return ret;
1193 header = MapViewOfFile(container->mapping, FILE_MAP_WRITE, 0, 0, 0);
1194 if(!header)
1195 return GetLastError();
1197 UnmapViewOfFile(*file_view);
1198 *file_view = header;
1199 return ERROR_SUCCESS;
1202 /* Just like DosDateTimeToFileTime, except that it also maps the special
1203 * case of a DOS date/time of (0,0) to a filetime of (0,0).
1205 static void dos_date_time_to_file_time(WORD fatdate, WORD fattime,
1206 FILETIME *ft)
1208 if (!fatdate && !fattime)
1209 ft->dwLowDateTime = ft->dwHighDateTime = 0;
1210 else
1211 DosDateTimeToFileTime(fatdate, fattime, ft);
1214 static int urlcache_decode_url(const char *url, WCHAR *decoded_url, int decoded_len)
1216 URL_COMPONENTSA uc;
1217 DWORD len, part_len;
1218 WCHAR *host_name;
1220 memset(&uc, 0, sizeof(uc));
1221 uc.dwStructSize = sizeof(uc);
1222 uc.dwHostNameLength = 1;
1223 if(!InternetCrackUrlA(url, 0, 0, &uc))
1224 uc.nScheme = INTERNET_SCHEME_UNKNOWN;
1226 if(uc.nScheme!=INTERNET_SCHEME_HTTP && uc.nScheme!=INTERNET_SCHEME_HTTPS)
1227 return MultiByteToWideChar(CP_UTF8, 0, url, -1, decoded_url, decoded_len);
1229 if(!decoded_url)
1230 decoded_len = 0;
1232 len = MultiByteToWideChar(CP_UTF8, 0, url, uc.lpszHostName-url, decoded_url, decoded_len);
1233 if(!len)
1234 return 0;
1235 if(decoded_url)
1236 decoded_len -= len;
1238 host_name = heap_alloc(uc.dwHostNameLength*sizeof(WCHAR));
1239 if(!host_name)
1240 return 0;
1241 if(!MultiByteToWideChar(CP_UTF8, 0, uc.lpszHostName, uc.dwHostNameLength,
1242 host_name, uc.dwHostNameLength)) {
1243 heap_free(host_name);
1244 return 0;
1246 part_len = IdnToUnicode(0, host_name, uc.dwHostNameLength,
1247 decoded_url ? decoded_url+len : NULL, decoded_len);
1248 heap_free(host_name);
1249 if(!part_len) {
1250 SetLastError(ERROR_INTERNET_INVALID_URL);
1251 return 0;
1253 len += part_len;
1254 if(decoded_url)
1255 decoded_len -= part_len;
1257 part_len = MultiByteToWideChar(CP_UTF8, 0,
1258 uc.lpszHostName+uc.dwHostNameLength,
1259 -1, decoded_url ? decoded_url+len : NULL, decoded_len);
1260 if(!part_len)
1261 return 0;
1262 len += part_len;
1264 return len;
1267 /***********************************************************************
1268 * urlcache_copy_entry (Internal)
1270 * Copies an entry from the cache index file to the Win32 structure
1272 * RETURNS
1273 * ERROR_SUCCESS if the buffer was big enough
1274 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1277 static DWORD urlcache_copy_entry(cache_container *container, const urlcache_header *header,
1278 INTERNET_CACHE_ENTRY_INFOA *entry_info, DWORD *info_size, const entry_url *url_entry, BOOL unicode)
1280 int url_len;
1281 DWORD size = sizeof(*entry_info);
1283 if(*info_size >= size) {
1284 entry_info->lpHeaderInfo = NULL;
1285 entry_info->lpszFileExtension = NULL;
1286 entry_info->lpszLocalFileName = NULL;
1287 entry_info->lpszSourceUrlName = NULL;
1288 entry_info->CacheEntryType = url_entry->cache_entry_type;
1289 entry_info->u.dwExemptDelta = url_entry->exempt_delta;
1290 entry_info->dwHeaderInfoSize = url_entry->header_info_size;
1291 entry_info->dwHitRate = url_entry->hit_rate;
1292 entry_info->dwSizeHigh = url_entry->size.u.HighPart;
1293 entry_info->dwSizeLow = url_entry->size.u.LowPart;
1294 entry_info->dwStructSize = sizeof(*entry_info);
1295 entry_info->dwUseCount = url_entry->use_count;
1296 dos_date_time_to_file_time(url_entry->expire_date, url_entry->expire_time, &entry_info->ExpireTime);
1297 entry_info->LastAccessTime = url_entry->access_time;
1298 entry_info->LastModifiedTime = url_entry->modification_time;
1299 dos_date_time_to_file_time(url_entry->sync_date, url_entry->sync_time, &entry_info->LastSyncTime);
1302 if(unicode)
1303 url_len = urlcache_decode_url((const char*)url_entry+url_entry->url_off, NULL, 0);
1304 else
1305 url_len = strlen((LPCSTR)url_entry+url_entry->url_off) + 1;
1306 size += url_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1308 if(*info_size >= size) {
1309 DWORD url_size = url_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1311 entry_info->lpszSourceUrlName = (LPSTR)entry_info+size-url_size;
1312 if(unicode)
1313 urlcache_decode_url((const char*)url_entry+url_entry->url_off, (WCHAR*)entry_info->lpszSourceUrlName, url_len);
1314 else
1315 memcpy(entry_info->lpszSourceUrlName, (LPCSTR)url_entry+url_entry->url_off, url_size);
1318 if(size%4 && size<*info_size)
1319 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1320 size = DWORD_ALIGN(size);
1322 if(url_entry->local_name_off) {
1323 LONG file_name_size;
1324 LPSTR file_name;
1325 file_name = (LPSTR)entry_info+size;
1326 file_name_size = *info_size-size;
1327 if((unicode && urlcache_create_file_pathW(container, header, (LPCSTR)url_entry+url_entry->local_name_off,
1328 url_entry->cache_dir, (LPWSTR)file_name, &file_name_size, FALSE)) ||
1329 (!unicode && urlcache_create_file_pathA(container, header, (LPCSTR)url_entry+url_entry->local_name_off,
1330 url_entry->cache_dir, file_name, &file_name_size))) {
1331 entry_info->lpszLocalFileName = file_name;
1333 size += file_name_size;
1335 if(size%4 && size<*info_size)
1336 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1337 size = DWORD_ALIGN(size);
1340 if(url_entry->header_info_off) {
1341 DWORD header_len;
1343 if(unicode)
1344 header_len = MultiByteToWideChar(CP_UTF8, 0, (const char*)url_entry+url_entry->header_info_off,
1345 url_entry->header_info_size, NULL, 0);
1346 else
1347 header_len = url_entry->header_info_size;
1348 size += header_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1350 if(*info_size >= size) {
1351 DWORD header_size = header_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1352 entry_info->lpHeaderInfo = (LPBYTE)entry_info+size-header_size;
1353 if(unicode)
1354 MultiByteToWideChar(CP_UTF8, 0, (const char*)url_entry+url_entry->header_info_off,
1355 url_entry->header_info_size, (LPWSTR)entry_info->lpHeaderInfo, header_len);
1356 else
1357 memcpy(entry_info->lpHeaderInfo, (LPCSTR)url_entry+url_entry->header_info_off, header_len);
1359 if(size%4 && size<*info_size)
1360 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1361 size = DWORD_ALIGN(size);
1364 if(url_entry->file_extension_off) {
1365 int ext_len;
1367 if(unicode)
1368 ext_len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)url_entry+url_entry->file_extension_off, -1, NULL, 0);
1369 else
1370 ext_len = strlen((LPCSTR)url_entry+url_entry->file_extension_off) + 1;
1371 size += ext_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1373 if(*info_size >= size) {
1374 DWORD ext_size = ext_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1375 entry_info->lpszFileExtension = (LPSTR)entry_info+size-ext_size;
1376 if(unicode)
1377 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)url_entry+url_entry->file_extension_off, -1, (LPWSTR)entry_info->lpszFileExtension, ext_len);
1378 else
1379 memcpy(entry_info->lpszFileExtension, (LPCSTR)url_entry+url_entry->file_extension_off, ext_len*sizeof(CHAR));
1382 if(size%4 && size<*info_size)
1383 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1384 size = DWORD_ALIGN(size);
1387 if(size > *info_size) {
1388 *info_size = size;
1389 return ERROR_INSUFFICIENT_BUFFER;
1391 *info_size = size;
1392 return ERROR_SUCCESS;
1395 /***********************************************************************
1396 * urlcache_set_entry_info (Internal)
1398 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1399 * according to the flags set by field_control.
1401 * RETURNS
1402 * ERROR_SUCCESS if the buffer was big enough
1403 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1406 static DWORD urlcache_set_entry_info(entry_url *url_entry, const INTERNET_CACHE_ENTRY_INFOA *entry_info, DWORD field_control)
1408 if (field_control & CACHE_ENTRY_ACCTIME_FC)
1409 url_entry->access_time = entry_info->LastAccessTime;
1410 if (field_control & CACHE_ENTRY_ATTRIBUTE_FC)
1411 url_entry->cache_entry_type = entry_info->CacheEntryType;
1412 if (field_control & CACHE_ENTRY_EXEMPT_DELTA_FC)
1413 url_entry->exempt_delta = entry_info->u.dwExemptDelta;
1414 if (field_control & CACHE_ENTRY_EXPTIME_FC)
1415 file_time_to_dos_date_time(&entry_info->ExpireTime, &url_entry->expire_date, &url_entry->expire_time);
1416 if (field_control & CACHE_ENTRY_HEADERINFO_FC)
1417 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1418 if (field_control & CACHE_ENTRY_HITRATE_FC)
1419 url_entry->hit_rate = entry_info->dwHitRate;
1420 if (field_control & CACHE_ENTRY_MODTIME_FC)
1421 url_entry->modification_time = entry_info->LastModifiedTime;
1422 if (field_control & CACHE_ENTRY_SYNCTIME_FC)
1423 file_time_to_dos_date_time(&entry_info->LastAccessTime, &url_entry->sync_date, &url_entry->sync_time);
1425 return ERROR_SUCCESS;
1428 /***********************************************************************
1429 * urlcache_hash_key (Internal)
1431 * Returns the hash key for a given string
1433 * RETURNS
1434 * hash key for the string
1437 static DWORD urlcache_hash_key(LPCSTR lpszKey)
1439 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1440 * but the algorithm and result are not the same!
1442 static const unsigned char lookupTable[256] =
1444 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1445 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1446 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1447 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1448 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1449 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1450 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1451 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1452 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1453 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1454 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1455 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1456 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1457 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1458 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1459 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1460 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1461 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1462 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1463 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1464 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1465 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1466 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1467 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1468 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1469 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1470 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1471 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1472 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1473 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1474 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1475 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1477 BYTE key[4];
1478 DWORD i;
1480 for (i = 0; i < ARRAY_SIZE(key); i++)
1481 key[i] = lookupTable[(*lpszKey + i) & 0xFF];
1483 for (lpszKey++; *lpszKey; lpszKey++)
1485 for (i = 0; i < ARRAY_SIZE(key); i++)
1486 key[i] = lookupTable[*lpszKey ^ key[i]];
1489 return *(DWORD *)key;
1492 static inline entry_hash_table* urlcache_get_hash_table(const urlcache_header *pHeader, DWORD dwOffset)
1494 if(!dwOffset)
1495 return NULL;
1496 return (entry_hash_table*)((LPBYTE)pHeader + dwOffset);
1499 static BOOL urlcache_find_hash_entry(const urlcache_header *pHeader, LPCSTR lpszUrl, struct hash_entry **ppHashEntry)
1501 /* structure of hash table:
1502 * 448 entries divided into 64 blocks
1503 * each block therefore contains a chain of 7 key/offset pairs
1504 * how position in table is calculated:
1505 * 1. the url is hashed in helper function
1506 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1507 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1509 * note:
1510 * there can be multiple hash tables in the file and the offset to
1511 * the next one is stored in the header of the hash table
1513 DWORD key = urlcache_hash_key(lpszUrl);
1514 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1515 entry_hash_table* pHashEntry;
1516 DWORD id = 0;
1518 key >>= HASHTABLE_FLAG_BITS;
1520 for (pHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1521 pHashEntry; pHashEntry = urlcache_get_hash_table(pHeader, pHashEntry->next))
1523 int i;
1524 if (pHashEntry->id != id++)
1526 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->id, id);
1527 continue;
1529 /* make sure that it is in fact a hash entry */
1530 if (pHashEntry->header.signature != HASH_SIGNATURE)
1532 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->header.signature);
1533 continue;
1536 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1538 struct hash_entry *pHashElement = &pHashEntry->hash_table[offset + i];
1539 if (key == pHashElement->key>>HASHTABLE_FLAG_BITS)
1541 /* FIXME: we should make sure that this is the right element
1542 * before returning and claiming that it is. We can do this
1543 * by doing a simple compare between the URL we were given
1544 * and the URL stored in the entry. However, this assumes
1545 * we know the format of all the entries stored in the
1546 * hash table */
1547 *ppHashEntry = pHashElement;
1548 return TRUE;
1552 return FALSE;
1555 /***********************************************************************
1556 * urlcache_hash_entry_set_flags (Internal)
1558 * Sets special bits in hash key
1560 * RETURNS
1561 * nothing
1564 static void urlcache_hash_entry_set_flags(struct hash_entry *pHashEntry, DWORD dwFlag)
1566 pHashEntry->key = (pHashEntry->key >> HASHTABLE_FLAG_BITS << HASHTABLE_FLAG_BITS) | dwFlag;
1569 /***********************************************************************
1570 * urlcache_hash_entry_delete (Internal)
1572 * Searches all the hash tables in the index for the given URL and
1573 * then if found deletes the entry.
1575 * RETURNS
1576 * TRUE if the entry was found
1577 * FALSE if the entry could not be found
1580 static BOOL urlcache_hash_entry_delete(struct hash_entry *pHashEntry)
1582 pHashEntry->key = HASHTABLE_DEL;
1583 return TRUE;
1586 /***********************************************************************
1587 * urlcache_hash_entry_create (Internal)
1589 * Searches all the hash tables for a free slot based on the offset
1590 * generated from the hash key. If a free slot is found, the offset and
1591 * key are entered into the hash table.
1593 * RETURNS
1594 * ERROR_SUCCESS if the entry was added
1595 * Any other Win32 error code if the entry could not be added
1598 static DWORD urlcache_hash_entry_create(urlcache_header *pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry, DWORD dwFieldType)
1600 /* see urlcache_find_hash_entry for structure of hash tables */
1602 DWORD key = urlcache_hash_key(lpszUrl);
1603 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1604 entry_hash_table* pHashEntry, *pHashPrev = NULL;
1605 DWORD id = 0;
1606 DWORD error;
1608 key = ((key >> HASHTABLE_FLAG_BITS) << HASHTABLE_FLAG_BITS) + dwFieldType;
1610 for (pHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1611 pHashEntry; pHashEntry = urlcache_get_hash_table(pHeader, pHashEntry->next))
1613 int i;
1614 pHashPrev = pHashEntry;
1616 if (pHashEntry->id != id++)
1618 ERR("not right hash table number (%d) expected %d\n", pHashEntry->id, id);
1619 break;
1621 /* make sure that it is in fact a hash entry */
1622 if (pHashEntry->header.signature != HASH_SIGNATURE)
1624 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->header.signature);
1625 break;
1628 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1630 struct hash_entry *pHashElement = &pHashEntry->hash_table[offset + i];
1631 if (pHashElement->key==HASHTABLE_FREE || pHashElement->key==HASHTABLE_DEL) /* if the slot is free */
1633 pHashElement->key = key;
1634 pHashElement->offset = dwOffsetEntry;
1635 return ERROR_SUCCESS;
1639 error = urlcache_create_hash_table(pHeader, pHashPrev, &pHashEntry);
1640 if (error != ERROR_SUCCESS)
1641 return error;
1643 pHashEntry->hash_table[offset].key = key;
1644 pHashEntry->hash_table[offset].offset = dwOffsetEntry;
1645 return ERROR_SUCCESS;
1648 /***********************************************************************
1649 * urlcache_enum_hash_tables (Internal)
1651 * Enumerates the hash tables in a container.
1653 * RETURNS
1654 * TRUE if an entry was found
1655 * FALSE if there are no more tables to enumerate.
1658 static BOOL urlcache_enum_hash_tables(const urlcache_header *pHeader, DWORD *id, entry_hash_table **ppHashEntry)
1660 for (*ppHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1661 *ppHashEntry; *ppHashEntry = urlcache_get_hash_table(pHeader, (*ppHashEntry)->next))
1663 TRACE("looking at hash table number %d\n", (*ppHashEntry)->id);
1664 if ((*ppHashEntry)->id != *id)
1665 continue;
1666 /* make sure that it is in fact a hash entry */
1667 if ((*ppHashEntry)->header.signature != HASH_SIGNATURE)
1669 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->header.signature);
1670 (*id)++;
1671 continue;
1674 TRACE("hash table number %d found\n", *id);
1675 return TRUE;
1677 return FALSE;
1680 /***********************************************************************
1681 * urlcache_enum_hash_table_entries (Internal)
1683 * Enumerates entries in a hash table and returns the next non-free entry.
1685 * RETURNS
1686 * TRUE if an entry was found
1687 * FALSE if the hash table is empty or there are no more entries to
1688 * enumerate.
1691 static BOOL urlcache_enum_hash_table_entries(const urlcache_header *pHeader, const entry_hash_table *pHashEntry,
1692 DWORD * index, const struct hash_entry **ppHashEntry)
1694 for (; *index < HASHTABLE_SIZE ; (*index)++)
1696 if (pHashEntry->hash_table[*index].key==HASHTABLE_FREE || pHashEntry->hash_table[*index].key==HASHTABLE_DEL)
1697 continue;
1699 *ppHashEntry = &pHashEntry->hash_table[*index];
1700 TRACE("entry found %d\n", *index);
1701 return TRUE;
1703 TRACE("no more entries (%d)\n", *index);
1704 return FALSE;
1707 /***********************************************************************
1708 * cache_container_delete_dir (Internal)
1710 * Erase a directory containing an URL cache.
1712 * RETURNS
1713 * TRUE success, FALSE failure/aborted.
1716 static BOOL cache_container_delete_dir(LPCWSTR lpszPath)
1718 DWORD path_len;
1719 WCHAR path[MAX_PATH + 1];
1720 SHFILEOPSTRUCTW shfos;
1721 int ret;
1723 path_len = lstrlenW(lpszPath);
1724 if (path_len >= MAX_PATH)
1725 return FALSE;
1726 lstrcpyW(path, lpszPath);
1727 path[path_len + 1] = 0; /* double-NUL-terminate path */
1729 shfos.hwnd = NULL;
1730 shfos.wFunc = FO_DELETE;
1731 shfos.pFrom = path;
1732 shfos.pTo = NULL;
1733 shfos.fFlags = FOF_NOCONFIRMATION;
1734 shfos.fAnyOperationsAborted = FALSE;
1735 ret = SHFileOperationW(&shfos);
1736 if (ret)
1737 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path), ret);
1738 return !(ret || shfos.fAnyOperationsAborted);
1741 /***********************************************************************
1742 * urlcache_hash_entry_is_locked (Internal)
1744 * Checks if entry is locked. Unlocks it if possible.
1746 static BOOL urlcache_hash_entry_is_locked(struct hash_entry *hash_entry, entry_url *url_entry)
1748 FILETIME cur_time;
1749 ULARGE_INTEGER acc_time, time;
1751 if ((hash_entry->key & ((1<<HASHTABLE_FLAG_BITS)-1)) != HASHTABLE_LOCK)
1752 return FALSE;
1754 GetSystemTimeAsFileTime(&cur_time);
1755 time.u.LowPart = cur_time.dwLowDateTime;
1756 time.u.HighPart = cur_time.dwHighDateTime;
1758 acc_time.u.LowPart = url_entry->access_time.dwLowDateTime;
1759 acc_time.u.HighPart = url_entry->access_time.dwHighDateTime;
1761 time.QuadPart -= acc_time.QuadPart;
1763 /* check if entry was locked for at least a day */
1764 if(time.QuadPart > (ULONGLONG)24*60*60*FILETIME_SECOND) {
1765 urlcache_hash_entry_set_flags(hash_entry, HASHTABLE_URL);
1766 url_entry->use_count = 0;
1767 return FALSE;
1770 return TRUE;
1773 static BOOL urlcache_get_entry_info(const char *url, void *entry_info,
1774 DWORD *size, DWORD flags, BOOL unicode)
1776 urlcache_header *header;
1777 struct hash_entry *hash_entry;
1778 const entry_url *url_entry;
1779 cache_container *container;
1780 DWORD error;
1782 TRACE("(%s, %p, %p, %x, %x)\n", debugstr_a(url), entry_info, size, flags, unicode);
1784 if(flags & ~GET_INSTALLED_ENTRY)
1785 FIXME("ignoring unsupported flags: %x\n", flags);
1787 error = cache_containers_find(url, &container);
1788 if(error != ERROR_SUCCESS) {
1789 SetLastError(error);
1790 return FALSE;
1793 error = cache_container_open_index(container, MIN_BLOCK_NO);
1794 if(error != ERROR_SUCCESS) {
1795 SetLastError(error);
1796 return FALSE;
1799 if(!(header = cache_container_lock_index(container)))
1800 return FALSE;
1802 if(!urlcache_find_hash_entry(header, url, &hash_entry)) {
1803 cache_container_unlock_index(container, header);
1804 WARN("entry %s not found!\n", debugstr_a(url));
1805 SetLastError(ERROR_FILE_NOT_FOUND);
1806 return FALSE;
1809 url_entry = (const entry_url*)((LPBYTE)header + hash_entry->offset);
1810 if(url_entry->header.signature != URL_SIGNATURE) {
1811 cache_container_unlock_index(container, header);
1812 FIXME("Trying to retrieve entry of unknown format %s\n",
1813 debugstr_an((LPCSTR)&url_entry->header.signature, sizeof(DWORD)));
1814 SetLastError(ERROR_FILE_NOT_FOUND);
1815 return FALSE;
1818 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->url_off));
1819 TRACE("Header info: %s\n", debugstr_an((LPCSTR)url_entry +
1820 url_entry->header_info_off, url_entry->header_info_size));
1822 if((flags & GET_INSTALLED_ENTRY) && !(url_entry->cache_entry_type & INSTALLED_CACHE_ENTRY)) {
1823 cache_container_unlock_index(container, header);
1824 SetLastError(ERROR_FILE_NOT_FOUND);
1825 return FALSE;
1828 if(size) {
1829 if(!entry_info)
1830 *size = 0;
1832 error = urlcache_copy_entry(container, header, entry_info, size, url_entry, unicode);
1833 if(error != ERROR_SUCCESS) {
1834 cache_container_unlock_index(container, header);
1835 SetLastError(error);
1836 return FALSE;
1838 if(url_entry->local_name_off)
1839 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->local_name_off));
1842 cache_container_unlock_index(container, header);
1843 return TRUE;
1846 /***********************************************************************
1847 * GetUrlCacheEntryInfoExA (WININET.@)
1850 BOOL WINAPI GetUrlCacheEntryInfoExA(LPCSTR lpszUrl,
1851 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1852 LPDWORD lpdwCacheEntryInfoBufSize, LPSTR lpszReserved,
1853 LPDWORD lpdwReserved, LPVOID lpReserved, DWORD dwFlags)
1855 if(lpszReserved!=NULL || lpdwReserved!=NULL || lpReserved!=NULL) {
1856 ERR("Reserved value was not 0\n");
1857 SetLastError(ERROR_INVALID_PARAMETER);
1858 return FALSE;
1861 return urlcache_get_entry_info(lpszUrl, lpCacheEntryInfo,
1862 lpdwCacheEntryInfoBufSize, dwFlags, FALSE);
1865 /***********************************************************************
1866 * GetUrlCacheEntryInfoA (WININET.@)
1869 BOOL WINAPI GetUrlCacheEntryInfoA(LPCSTR lpszUrlName,
1870 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1871 LPDWORD lpdwCacheEntryInfoBufferSize)
1873 return GetUrlCacheEntryInfoExA(lpszUrlName, lpCacheEntryInfo,
1874 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
1877 static int urlcache_encode_url(const WCHAR *url, char *encoded_url, int encoded_len)
1879 URL_COMPONENTSW uc;
1880 DWORD len, part_len;
1881 WCHAR *punycode;
1883 TRACE("%s\n", debugstr_w(url));
1885 memset(&uc, 0, sizeof(uc));
1886 uc.dwStructSize = sizeof(uc);
1887 uc.dwHostNameLength = 1;
1888 if(!InternetCrackUrlW(url, 0, 0, &uc))
1889 uc.nScheme = INTERNET_SCHEME_UNKNOWN;
1891 if(uc.nScheme!=INTERNET_SCHEME_HTTP && uc.nScheme!=INTERNET_SCHEME_HTTPS)
1892 return WideCharToMultiByte(CP_UTF8, 0, url, -1, encoded_url, encoded_len, NULL, NULL);
1894 len = WideCharToMultiByte(CP_UTF8, 0, url, uc.lpszHostName-url,
1895 encoded_url, encoded_len, NULL, NULL);
1896 if(!len)
1897 return 0;
1898 if(encoded_url)
1899 encoded_len -= len;
1901 part_len = IdnToAscii(0, uc.lpszHostName, uc.dwHostNameLength, NULL, 0);
1902 if(!part_len) {
1903 SetLastError(ERROR_INTERNET_INVALID_URL);
1904 return 0;
1907 punycode = heap_alloc(part_len*sizeof(WCHAR));
1908 if(!punycode)
1909 return 0;
1911 part_len = IdnToAscii(0, uc.lpszHostName, uc.dwHostNameLength, punycode, part_len);
1912 if(!part_len) {
1913 heap_free(punycode);
1914 return 0;
1917 part_len = WideCharToMultiByte(CP_UTF8, 0, punycode, part_len,
1918 encoded_url ? encoded_url+len : NULL, encoded_len, NULL, NULL);
1919 heap_free(punycode);
1920 if(!part_len)
1921 return 0;
1922 if(encoded_url)
1923 encoded_len -= part_len;
1924 len += part_len;
1926 part_len = WideCharToMultiByte(CP_UTF8, 0, uc.lpszHostName+uc.dwHostNameLength,
1927 -1, encoded_url ? encoded_url+len : NULL, encoded_len, NULL, NULL);
1928 if(!part_len)
1929 return 0;
1930 len += part_len;
1932 TRACE("got (%d)%s\n", len, debugstr_a(encoded_url));
1933 return len;
1936 static BOOL urlcache_encode_url_alloc(const WCHAR *url, char **encoded_url)
1938 DWORD encoded_len;
1939 char *ret;
1941 encoded_len = urlcache_encode_url(url, NULL, 0);
1942 if(!encoded_len)
1943 return FALSE;
1945 ret = heap_alloc(encoded_len*sizeof(WCHAR));
1946 if(!ret)
1947 return FALSE;
1949 encoded_len = urlcache_encode_url(url, ret, encoded_len);
1950 if(!encoded_len) {
1951 heap_free(ret);
1952 return FALSE;
1955 *encoded_url = ret;
1956 return TRUE;
1959 /***********************************************************************
1960 * GetUrlCacheEntryInfoExW (WININET.@)
1963 BOOL WINAPI GetUrlCacheEntryInfoExW(LPCWSTR lpszUrl,
1964 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1965 LPDWORD lpdwCacheEntryInfoBufSize, LPWSTR lpszReserved,
1966 LPDWORD lpdwReserved, LPVOID lpReserved, DWORD dwFlags)
1968 char *url;
1969 BOOL ret;
1971 if(lpszReserved!=NULL || lpdwReserved!=NULL || lpReserved!=NULL) {
1972 ERR("Reserved value was not 0\n");
1973 SetLastError(ERROR_INVALID_PARAMETER);
1974 return FALSE;
1977 /* Ignore GET_INSTALLED_ENTRY flag in unicode version of function */
1978 dwFlags &= ~GET_INSTALLED_ENTRY;
1980 if(!urlcache_encode_url_alloc(lpszUrl, &url))
1981 return FALSE;
1983 ret = urlcache_get_entry_info(url, lpCacheEntryInfo,
1984 lpdwCacheEntryInfoBufSize, dwFlags, TRUE);
1985 heap_free(url);
1986 return ret;
1989 /***********************************************************************
1990 * GetUrlCacheEntryInfoW (WININET.@)
1993 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1994 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1995 LPDWORD lpdwCacheEntryInfoBufferSize)
1997 return GetUrlCacheEntryInfoExW(lpszUrl, lpCacheEntryInfo,
1998 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
2001 /***********************************************************************
2002 * SetUrlCacheEntryInfoA (WININET.@)
2004 BOOL WINAPI SetUrlCacheEntryInfoA(LPCSTR lpszUrlName,
2005 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2006 DWORD dwFieldControl)
2008 urlcache_header *pHeader;
2009 struct hash_entry *pHashEntry;
2010 entry_header *pEntry;
2011 cache_container *pContainer;
2012 DWORD error;
2014 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
2016 error = cache_containers_find(lpszUrlName, &pContainer);
2017 if (error != ERROR_SUCCESS)
2019 SetLastError(error);
2020 return FALSE;
2023 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2024 if (error != ERROR_SUCCESS)
2026 SetLastError(error);
2027 return FALSE;
2030 if (!(pHeader = cache_container_lock_index(pContainer)))
2031 return FALSE;
2033 if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
2035 cache_container_unlock_index(pContainer, pHeader);
2036 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
2037 SetLastError(ERROR_FILE_NOT_FOUND);
2038 return FALSE;
2041 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2042 if (pEntry->signature != URL_SIGNATURE)
2044 cache_container_unlock_index(pContainer, pHeader);
2045 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2046 SetLastError(ERROR_FILE_NOT_FOUND);
2047 return FALSE;
2050 urlcache_set_entry_info((entry_url*)pEntry, lpCacheEntryInfo, dwFieldControl);
2052 cache_container_unlock_index(pContainer, pHeader);
2054 return TRUE;
2057 /***********************************************************************
2058 * SetUrlCacheEntryInfoW (WININET.@)
2060 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
2061 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2062 DWORD dwFieldControl)
2064 char *url;
2065 BOOL ret;
2067 if(!urlcache_encode_url_alloc(lpszUrl, &url))
2068 return FALSE;
2070 ret = SetUrlCacheEntryInfoA(url, (INTERNET_CACHE_ENTRY_INFOA*)lpCacheEntryInfo, dwFieldControl);
2071 heap_free(url);
2072 return ret;
2075 static BOOL urlcache_entry_get_file(const char *url, void *entry_info, DWORD *size, BOOL unicode)
2077 urlcache_header *header;
2078 struct hash_entry *hash_entry;
2079 entry_url *url_entry;
2080 cache_container *container;
2081 DWORD error;
2083 TRACE("(%s, %p, %p, %x)\n", debugstr_a(url), entry_info, size, unicode);
2085 if(!url || !size || (!entry_info && *size)) {
2086 SetLastError(ERROR_INVALID_PARAMETER);
2087 return FALSE;
2090 error = cache_containers_find(url, &container);
2091 if(error != ERROR_SUCCESS) {
2092 SetLastError(error);
2093 return FALSE;
2096 error = cache_container_open_index(container, MIN_BLOCK_NO);
2097 if (error != ERROR_SUCCESS) {
2098 SetLastError(error);
2099 return FALSE;
2102 if (!(header = cache_container_lock_index(container)))
2103 return FALSE;
2105 if (!urlcache_find_hash_entry(header, url, &hash_entry)) {
2106 cache_container_unlock_index(container, header);
2107 TRACE("entry %s not found!\n", debugstr_a(url));
2108 SetLastError(ERROR_FILE_NOT_FOUND);
2109 return FALSE;
2112 url_entry = (entry_url*)((LPBYTE)header + hash_entry->offset);
2113 if(url_entry->header.signature != URL_SIGNATURE) {
2114 cache_container_unlock_index(container, header);
2115 FIXME("Trying to retrieve entry of unknown format %s\n",
2116 debugstr_an((LPSTR)&url_entry->header.signature, sizeof(DWORD)));
2117 SetLastError(ERROR_FILE_NOT_FOUND);
2118 return FALSE;
2121 if(!url_entry->local_name_off) {
2122 cache_container_unlock_index(container, header);
2123 SetLastError(ERROR_INVALID_DATA);
2124 return FALSE;
2127 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->url_off));
2128 TRACE("Header info: %s\n", debugstr_an((LPCSTR)url_entry + url_entry->header_info_off,
2129 url_entry->header_info_size));
2131 error = urlcache_copy_entry(container, header, entry_info,
2132 size, url_entry, unicode);
2133 if(error != ERROR_SUCCESS) {
2134 cache_container_unlock_index(container, header);
2135 SetLastError(error);
2136 return FALSE;
2138 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->local_name_off));
2140 url_entry->hit_rate++;
2141 url_entry->use_count++;
2142 urlcache_hash_entry_set_flags(hash_entry, HASHTABLE_LOCK);
2143 GetSystemTimeAsFileTime(&url_entry->access_time);
2145 cache_container_unlock_index(container, header);
2147 return TRUE;
2150 /***********************************************************************
2151 * RetrieveUrlCacheEntryFileA (WININET.@)
2154 BOOL WINAPI RetrieveUrlCacheEntryFileA(LPCSTR lpszUrlName,
2155 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2156 LPDWORD lpdwCacheEntryInfoBufferSize, DWORD dwReserved)
2158 return urlcache_entry_get_file(lpszUrlName, lpCacheEntryInfo,
2159 lpdwCacheEntryInfoBufferSize, FALSE);
2162 /***********************************************************************
2163 * RetrieveUrlCacheEntryFileW (WININET.@)
2166 BOOL WINAPI RetrieveUrlCacheEntryFileW(LPCWSTR lpszUrlName,
2167 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2168 LPDWORD lpdwCacheEntryInfoBufferSize, DWORD dwReserved)
2170 char *url;
2171 BOOL ret;
2173 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
2174 return FALSE;
2176 ret = urlcache_entry_get_file(url, lpCacheEntryInfo,
2177 lpdwCacheEntryInfoBufferSize, TRUE);
2178 heap_free(url);
2179 return ret;
2182 static BOOL urlcache_entry_delete(const cache_container *pContainer,
2183 urlcache_header *pHeader, struct hash_entry *pHashEntry)
2185 entry_header *pEntry;
2186 entry_url * pUrlEntry;
2188 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2189 if (pEntry->signature != URL_SIGNATURE)
2191 FIXME("Trying to delete entry of unknown format %s\n",
2192 debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
2193 SetLastError(ERROR_FILE_NOT_FOUND);
2194 return FALSE;
2197 pUrlEntry = (entry_url *)pEntry;
2198 if(urlcache_hash_entry_is_locked(pHashEntry, pUrlEntry))
2200 TRACE("Trying to delete locked entry\n");
2201 pUrlEntry->cache_entry_type |= PENDING_DELETE_CACHE_ENTRY;
2202 SetLastError(ERROR_SHARING_VIOLATION);
2203 return FALSE;
2206 if(!urlcache_delete_file(pContainer, pHeader, pUrlEntry))
2208 urlcache_entry_free(pHeader, pEntry);
2210 else
2212 /* Add entry to leaked files list */
2213 pUrlEntry->header.signature = LEAK_SIGNATURE;
2214 pUrlEntry->exempt_delta = pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
2215 pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET] = pHashEntry->offset;
2218 urlcache_hash_entry_delete(pHashEntry);
2219 return TRUE;
2222 static HANDLE free_cache_running;
2223 static HANDLE dll_unload_event;
2224 static DWORD WINAPI handle_full_cache_worker(void *param)
2226 FreeUrlCacheSpaceW(NULL, 20, 0);
2227 ReleaseSemaphore(free_cache_running, 1, NULL);
2228 return 0;
2231 static void handle_full_cache(void)
2233 if(WaitForSingleObject(free_cache_running, 0) == WAIT_OBJECT_0) {
2234 if(!QueueUserWorkItem(handle_full_cache_worker, NULL, 0))
2235 ReleaseSemaphore(free_cache_running, 1, NULL);
2239 /* Enumerates entries in cache, allows cache unlocking between calls. */
2240 static BOOL urlcache_next_entry(urlcache_header *header, DWORD *hash_table_off, DWORD *hash_table_entry,
2241 struct hash_entry **hash_entry, entry_header **entry)
2243 entry_hash_table *hashtable_entry;
2245 *hash_entry = NULL;
2246 *entry = NULL;
2248 if(!*hash_table_off) {
2249 *hash_table_off = header->hash_table_off;
2250 *hash_table_entry = 0;
2252 hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2253 }else {
2254 if(*hash_table_off >= header->size) {
2255 *hash_table_off = 0;
2256 return FALSE;
2259 hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2262 if(hashtable_entry->header.signature != HASH_SIGNATURE) {
2263 *hash_table_off = 0;
2264 return FALSE;
2267 while(1) {
2268 if(*hash_table_entry >= HASHTABLE_SIZE) {
2269 *hash_table_off = hashtable_entry->next;
2270 if(!*hash_table_off) {
2271 *hash_table_off = 0;
2272 return FALSE;
2275 hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2276 *hash_table_entry = 0;
2279 if(hashtable_entry->hash_table[*hash_table_entry].key != HASHTABLE_DEL &&
2280 hashtable_entry->hash_table[*hash_table_entry].key != HASHTABLE_FREE) {
2281 *hash_entry = &hashtable_entry->hash_table[*hash_table_entry];
2282 *entry = (entry_header*)((LPBYTE)header + hashtable_entry->hash_table[*hash_table_entry].offset);
2283 (*hash_table_entry)++;
2284 return TRUE;
2287 (*hash_table_entry)++;
2290 *hash_table_off = 0;
2291 return FALSE;
2294 /* Rates an urlcache entry to determine if it can be deleted.
2296 * Score 0 means that entry can safely be removed, the bigger rating
2297 * the smaller chance of entry being removed.
2298 * DWORD_MAX means that entry can't be deleted at all.
2300 * Rating system is currently not fully compatible with native implementation.
2302 static DWORD urlcache_rate_entry(entry_url *url_entry, FILETIME *cur_time)
2304 ULARGE_INTEGER time, access_time;
2305 DWORD rating;
2307 access_time.u.LowPart = url_entry->access_time.dwLowDateTime;
2308 access_time.u.HighPart = url_entry->access_time.dwHighDateTime;
2310 time.u.LowPart = cur_time->dwLowDateTime;
2311 time.u.HighPart = cur_time->dwHighDateTime;
2313 /* Don't touch entries that were added less than 10 minutes ago */
2314 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)10*60*FILETIME_SECOND)
2315 return -1;
2317 if(url_entry->cache_entry_type & STICKY_CACHE_ENTRY)
2318 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)url_entry->exempt_delta*FILETIME_SECOND)
2319 return -1;
2321 time.QuadPart = (time.QuadPart-access_time.QuadPart)/FILETIME_SECOND;
2322 rating = 400*60*60*24/(60*60*24+time.QuadPart);
2324 if(url_entry->hit_rate > 100)
2325 rating += 100;
2326 else
2327 rating += url_entry->hit_rate;
2329 return rating;
2332 static int __cdecl dword_cmp(const void *p1, const void *p2)
2334 return *(const DWORD*)p1 - *(const DWORD*)p2;
2337 /***********************************************************************
2338 * FreeUrlCacheSpaceW (WININET.@)
2340 * Frees up some cache.
2342 * PARAMETERS
2343 * cache_path [I] Which volume to free up from, or NULL if you don't care.
2344 * size [I] Percentage of the cache that should be free.
2345 * filter [I] Which entries can't be deleted (CacheEntryType)
2347 * RETURNS
2348 * TRUE success. FALSE failure.
2350 * IMPLEMENTATION
2351 * This implementation just retrieves the path of the cache directory, and
2352 * deletes its contents from the filesystem. The correct approach would
2353 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
2355 BOOL WINAPI FreeUrlCacheSpaceW(LPCWSTR cache_path, DWORD size, DWORD filter)
2357 cache_container *container;
2358 DWORD path_len, err;
2360 TRACE("(%s, %x, %x)\n", debugstr_w(cache_path), size, filter);
2362 if(size<1 || size>100) {
2363 SetLastError(ERROR_INVALID_PARAMETER);
2364 return FALSE;
2367 if(cache_path) {
2368 path_len = lstrlenW(cache_path);
2369 if(cache_path[path_len-1] == '\\')
2370 path_len--;
2371 }else {
2372 path_len = 0;
2375 if(size==100 && !filter) {
2376 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
2378 /* When cache_path==NULL only clean Temporary Internet Files */
2379 if((!path_len && container->cache_prefix[0]==0) ||
2380 (path_len && !wcsnicmp(container->path, cache_path, path_len) &&
2381 (container->path[path_len]=='\0' || container->path[path_len]=='\\')))
2383 BOOL ret_del;
2385 WaitForSingleObject(container->mutex, INFINITE);
2387 /* unlock, delete, recreate and lock cache */
2388 cache_container_close_index(container);
2389 ret_del = cache_container_delete_dir(container->path);
2390 err = cache_container_open_index(container, MIN_BLOCK_NO);
2392 ReleaseMutex(container->mutex);
2393 if(!ret_del || (err != ERROR_SUCCESS))
2394 return FALSE;
2398 return TRUE;
2401 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
2403 urlcache_header *header;
2404 struct hash_entry *hash_entry;
2405 entry_header *entry;
2406 entry_url *url_entry;
2407 ULONGLONG desired_size, cur_size;
2408 DWORD delete_factor, hash_table_off, hash_table_entry;
2409 DWORD rate[100], rate_no;
2410 FILETIME cur_time;
2412 if((path_len || container->cache_prefix[0]!=0) &&
2413 (!path_len || wcsnicmp(container->path, cache_path, path_len) ||
2414 (container->path[path_len]!='\0' && container->path[path_len]!='\\')))
2415 continue;
2417 err = cache_container_open_index(container, MIN_BLOCK_NO);
2418 if(err != ERROR_SUCCESS)
2419 continue;
2421 header = cache_container_lock_index(container);
2422 if(!header)
2423 continue;
2425 urlcache_clean_leaked_entries(container, header);
2427 desired_size = header->cache_limit.QuadPart*(100-size)/100;
2428 cur_size = header->cache_usage.QuadPart+header->exempt_usage.QuadPart;
2429 if(cur_size <= desired_size)
2430 delete_factor = 0;
2431 else
2432 delete_factor = (cur_size-desired_size)*100/cur_size;
2434 if(!delete_factor) {
2435 cache_container_unlock_index(container, header);
2436 continue;
2439 hash_table_off = 0;
2440 hash_table_entry = 0;
2441 rate_no = 0;
2442 GetSystemTimeAsFileTime(&cur_time);
2443 while(rate_no < ARRAY_SIZE(rate) &&
2444 urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2445 if(entry->signature != URL_SIGNATURE) {
2446 WARN("only url entries are currently supported\n");
2447 continue;
2450 url_entry = (entry_url*)entry;
2451 if(url_entry->cache_entry_type & filter)
2452 continue;
2454 rate[rate_no] = urlcache_rate_entry(url_entry, &cur_time);
2455 if(rate[rate_no] != -1)
2456 rate_no++;
2459 if(!rate_no) {
2460 TRACE("nothing to delete\n");
2461 cache_container_unlock_index(container, header);
2462 continue;
2465 qsort(rate, rate_no, sizeof(DWORD), dword_cmp);
2467 delete_factor = delete_factor*rate_no/100;
2468 delete_factor = rate[delete_factor];
2469 TRACE("deleting files with rating %d or less\n", delete_factor);
2471 hash_table_off = 0;
2472 while(urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2473 if(entry->signature != URL_SIGNATURE)
2474 continue;
2476 url_entry = (entry_url*)entry;
2477 if(url_entry->cache_entry_type & filter)
2478 continue;
2480 if(urlcache_rate_entry(url_entry, &cur_time) <= delete_factor) {
2481 TRACE("deleting file: %s\n", debugstr_a((char*)url_entry+url_entry->local_name_off));
2482 urlcache_entry_delete(container, header, hash_entry);
2484 if(header->cache_usage.QuadPart+header->exempt_usage.QuadPart <= desired_size)
2485 break;
2487 /* Allow other threads to use cache while cleaning */
2488 cache_container_unlock_index(container, header);
2489 if(WaitForSingleObject(dll_unload_event, 0) == WAIT_OBJECT_0) {
2490 TRACE("got dll_unload_event - finishing\n");
2491 return TRUE;
2493 Sleep(0);
2494 header = cache_container_lock_index(container);
2498 TRACE("cache size after cleaning 0x%s/0x%s\n",
2499 wine_dbgstr_longlong(header->cache_usage.QuadPart+header->exempt_usage.QuadPart),
2500 wine_dbgstr_longlong(header->cache_limit.QuadPart));
2501 cache_container_unlock_index(container, header);
2504 return TRUE;
2507 /***********************************************************************
2508 * FreeUrlCacheSpaceA (WININET.@)
2510 * See FreeUrlCacheSpaceW.
2512 BOOL WINAPI FreeUrlCacheSpaceA(LPCSTR lpszCachePath, DWORD dwSize, DWORD dwFilter)
2514 BOOL ret = FALSE;
2515 LPWSTR path = heap_strdupAtoW(lpszCachePath);
2516 if (lpszCachePath == NULL || path != NULL)
2517 ret = FreeUrlCacheSpaceW(path, dwSize, dwFilter);
2518 heap_free(path);
2519 return ret;
2522 /***********************************************************************
2523 * UnlockUrlCacheEntryFileA (WININET.@)
2526 BOOL WINAPI UnlockUrlCacheEntryFileA(LPCSTR lpszUrlName, DWORD dwReserved)
2528 urlcache_header *pHeader;
2529 struct hash_entry *pHashEntry;
2530 entry_header *pEntry;
2531 entry_url * pUrlEntry;
2532 cache_container *pContainer;
2533 DWORD error;
2535 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2537 if (dwReserved)
2539 ERR("dwReserved != 0\n");
2540 SetLastError(ERROR_INVALID_PARAMETER);
2541 return FALSE;
2544 error = cache_containers_find(lpszUrlName, &pContainer);
2545 if (error != ERROR_SUCCESS)
2547 SetLastError(error);
2548 return FALSE;
2551 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2552 if (error != ERROR_SUCCESS)
2554 SetLastError(error);
2555 return FALSE;
2558 if (!(pHeader = cache_container_lock_index(pContainer)))
2559 return FALSE;
2561 if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
2563 cache_container_unlock_index(pContainer, pHeader);
2564 TRACE("entry %s not found!\n", debugstr_a(lpszUrlName));
2565 SetLastError(ERROR_FILE_NOT_FOUND);
2566 return FALSE;
2569 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2570 if (pEntry->signature != URL_SIGNATURE)
2572 cache_container_unlock_index(pContainer, pHeader);
2573 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2574 SetLastError(ERROR_FILE_NOT_FOUND);
2575 return FALSE;
2578 pUrlEntry = (entry_url *)pEntry;
2580 if (pUrlEntry->use_count == 0)
2582 cache_container_unlock_index(pContainer, pHeader);
2583 return FALSE;
2585 pUrlEntry->use_count--;
2586 if (!pUrlEntry->use_count)
2588 urlcache_hash_entry_set_flags(pHashEntry, HASHTABLE_URL);
2589 if (pUrlEntry->cache_entry_type & PENDING_DELETE_CACHE_ENTRY)
2590 urlcache_entry_delete(pContainer, pHeader, pHashEntry);
2593 cache_container_unlock_index(pContainer, pHeader);
2595 return TRUE;
2598 /***********************************************************************
2599 * UnlockUrlCacheEntryFileW (WININET.@)
2602 BOOL WINAPI UnlockUrlCacheEntryFileW(LPCWSTR lpszUrlName, DWORD dwReserved)
2604 char *url;
2605 BOOL ret;
2607 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
2608 return FALSE;
2610 ret = UnlockUrlCacheEntryFileA(url, dwReserved);
2611 heap_free(url);
2612 return ret;
2615 static BOOL urlcache_entry_create(const char *url, const char *ext, WCHAR *full_path)
2617 cache_container *container;
2618 urlcache_header *header;
2619 char file_name[MAX_PATH];
2620 WCHAR extW[MAX_PATH];
2621 BYTE cache_dir;
2622 LONG full_path_len, ext_len = 0;
2623 BOOL generate_name = FALSE;
2624 DWORD error;
2625 HANDLE file;
2626 FILETIME ft;
2627 URL_COMPONENTSA uc;
2628 int i;
2630 TRACE("(%s, %s, %p)\n", debugstr_a(url), debugstr_a(ext), full_path);
2632 memset(&uc, 0, sizeof(uc));
2633 uc.dwStructSize = sizeof(uc);
2634 uc.dwUrlPathLength = 1;
2635 uc.dwExtraInfoLength = 1;
2636 if(!InternetCrackUrlA(url, 0, 0, &uc))
2637 uc.dwUrlPathLength = 0;
2639 if(!uc.dwUrlPathLength) {
2640 file_name[0] = 0;
2641 }else {
2642 char *p, *e;
2644 p = e = uc.lpszUrlPath+uc.dwUrlPathLength;
2645 while(p>uc.lpszUrlPath && *(p-1)!='/' && *(p-1)!='\\' && *(p-1)!='.')
2646 p--;
2647 if(p>uc.lpszUrlPath && *(p-1)=='.') {
2648 e = p-1;
2649 while(p>uc.lpszUrlPath && *(p-1)!='/' && *(p-1)!='\\')
2650 p--;
2653 if(e-p >= MAX_PATH)
2654 e = p+MAX_PATH-1;
2655 memcpy(file_name, p, e-p);
2656 file_name[e-p] = 0;
2658 for(p=file_name; *p; p++) {
2659 switch(*p) {
2660 case '<': case '>':
2661 case ':': case '"':
2662 case '|': case '?':
2663 case '*':
2664 *p = '_'; break;
2665 default: break;
2670 if(!file_name[0])
2671 generate_name = TRUE;
2673 error = cache_containers_find(url, &container);
2674 if(error != ERROR_SUCCESS) {
2675 SetLastError(error);
2676 return FALSE;
2679 error = cache_container_open_index(container, MIN_BLOCK_NO);
2680 if(error != ERROR_SUCCESS) {
2681 SetLastError(error);
2682 return FALSE;
2685 if(!(header = cache_container_lock_index(container)))
2686 return FALSE;
2688 if(header->dirs_no)
2689 cache_dir = (BYTE)(rand() % header->dirs_no);
2690 else
2691 cache_dir = CACHE_CONTAINER_NO_SUBDIR;
2693 full_path_len = MAX_PATH * sizeof(WCHAR);
2694 if(!urlcache_create_file_pathW(container, header, file_name, cache_dir, full_path, &full_path_len, TRUE)) {
2695 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2696 debugstr_a(file_name), full_path_len);
2697 cache_container_unlock_index(container, header);
2698 return FALSE;
2700 full_path_len = full_path_len/sizeof(WCHAR) - 1;
2702 cache_container_unlock_index(container, header);
2704 if(ext) {
2705 WCHAR *p;
2707 extW[0] = '.';
2708 ext_len = MultiByteToWideChar(CP_ACP, 0, ext, -1, extW+1, MAX_PATH-1);
2710 for(p=extW; *p; p++) {
2711 switch(*p) {
2712 case '<': case '>':
2713 case ':': case '"':
2714 case '|': case '?':
2715 case '*':
2716 *p = '_'; break;
2717 default: break;
2720 if(p[-1]==' ' || p[-1]=='.')
2721 p[-1] = '_';
2722 }else {
2723 extW[0] = '\0';
2726 if(!generate_name && full_path_len+5+ext_len>=MAX_PATH) { /* strlen("[255]") = 5 */
2727 full_path_len = MAX_PATH-5-ext_len-1;
2730 for(i=0; i<255 && !generate_name; i++) {
2731 static const WCHAR format[] = {'[','%','u',']','%','s',0};
2733 wsprintfW(full_path+full_path_len, format, i, extW);
2735 TRACE("Trying: %s\n", debugstr_w(full_path));
2736 file = CreateFileW(full_path, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2737 if(file != INVALID_HANDLE_VALUE) {
2738 CloseHandle(file);
2739 return TRUE;
2743 if(full_path_len+8+ext_len >= MAX_PATH)
2744 full_path_len = MAX_PATH-8-ext_len-1;
2746 /* Try to generate random name */
2747 GetSystemTimeAsFileTime(&ft);
2748 lstrcpyW(full_path+full_path_len+8, extW);
2750 for(i=0; i<255; i++) {
2751 int j;
2752 ULONGLONG n = ft.dwHighDateTime;
2753 n <<= 32;
2754 n += ft.dwLowDateTime;
2755 n ^= (ULONGLONG)i<<48;
2757 for(j=0; j<8; j++) {
2758 int r = (n % 36);
2759 n /= 37;
2760 full_path[full_path_len+j] = (r < 10 ? '0' + r : 'A' + r - 10);
2763 TRACE("Trying: %s\n", debugstr_w(full_path));
2764 file = CreateFileW(full_path, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2765 if(file != INVALID_HANDLE_VALUE) {
2766 CloseHandle(file);
2767 return TRUE;
2771 WARN("Could not find a unique filename\n");
2772 return FALSE;
2775 /***********************************************************************
2776 * CreateUrlCacheEntryA (WININET.@)
2779 BOOL WINAPI CreateUrlCacheEntryA(LPCSTR lpszUrlName, DWORD dwExpectedFileSize,
2780 LPCSTR lpszFileExtension, LPSTR lpszFileName, DWORD dwReserved)
2782 WCHAR file_name[MAX_PATH];
2784 if(dwReserved)
2785 FIXME("dwReserved 0x%08x\n", dwReserved);
2787 if(!urlcache_entry_create(lpszUrlName, lpszFileExtension, file_name))
2788 return FALSE;
2790 if(!WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL))
2791 return FALSE;
2792 return TRUE;
2794 /***********************************************************************
2795 * CreateUrlCacheEntryW (WININET.@)
2798 BOOL WINAPI CreateUrlCacheEntryW(LPCWSTR lpszUrlName, DWORD dwExpectedFileSize,
2799 LPCWSTR lpszFileExtension, LPWSTR lpszFileName, DWORD dwReserved)
2801 char *url, *ext = NULL;
2802 BOOL ret;
2804 if(dwReserved)
2805 FIXME("dwReserved 0x%08x\n", dwReserved);
2807 if(lpszFileExtension) {
2808 ext = heap_strdupWtoUTF8(lpszFileExtension);
2809 if(!ext)
2810 return FALSE;
2813 if(!urlcache_encode_url_alloc(lpszUrlName, &url)) {
2814 heap_free(ext);
2815 return FALSE;
2818 ret = urlcache_entry_create(url, ext, lpszFileName);
2819 heap_free(ext);
2820 heap_free(url);
2821 return ret;
2824 static BOOL urlcache_entry_commit(const char *url, const WCHAR *file_name,
2825 FILETIME expire_time, FILETIME modify_time, DWORD entry_type,
2826 BYTE *header_info, DWORD header_size, const char *file_ext,
2827 const char *original_url)
2829 cache_container *container;
2830 urlcache_header *header;
2831 struct hash_entry *hash_entry;
2832 entry_header *entry;
2833 entry_url *url_entry;
2834 DWORD url_entry_offset;
2835 DWORD size = DWORD_ALIGN(sizeof(*url_entry));
2836 DWORD file_name_off = 0;
2837 DWORD header_info_off = 0;
2838 DWORD file_ext_off = 0;
2839 WIN32_FILE_ATTRIBUTE_DATA file_attr;
2840 LARGE_INTEGER file_size;
2841 BYTE dir_id;
2842 char file_name_no_container[MAX_PATH];
2843 char *local_file_name = 0;
2844 DWORD hit_rate = 0;
2845 DWORD exempt_delta = 0;
2846 DWORD error;
2848 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n", debugstr_a(url), debugstr_w(file_name),
2849 entry_type, header_info, header_size, debugstr_a(file_ext), debugstr_a(original_url));
2851 if(entry_type & STICKY_CACHE_ENTRY && !file_name) {
2852 SetLastError(ERROR_INVALID_PARAMETER);
2853 return FALSE;
2855 if(original_url)
2856 WARN(": original_url ignored\n");
2858 memset(&file_attr, 0, sizeof(file_attr));
2859 if(file_name) {
2860 if(!GetFileAttributesExW(file_name, GetFileExInfoStandard, &file_attr))
2861 return FALSE;
2863 file_size.u.LowPart = file_attr.nFileSizeLow;
2864 file_size.u.HighPart = file_attr.nFileSizeHigh;
2866 error = cache_containers_find(url, &container);
2867 if(error != ERROR_SUCCESS) {
2868 SetLastError(error);
2869 return FALSE;
2872 error = cache_container_open_index(container, MIN_BLOCK_NO);
2873 if(error != ERROR_SUCCESS) {
2874 SetLastError(error);
2875 return FALSE;
2878 if(!(header = cache_container_lock_index(container)))
2879 return FALSE;
2881 if(urlcache_find_hash_entry(header, url, &hash_entry)) {
2882 entry_url *url_entry = (entry_url*)((LPBYTE)header + hash_entry->offset);
2884 if(urlcache_hash_entry_is_locked(hash_entry, url_entry)) {
2885 TRACE("Trying to overwrite locked entry\n");
2886 cache_container_unlock_index(container, header);
2887 SetLastError(ERROR_SHARING_VIOLATION);
2888 return FALSE;
2891 hit_rate = url_entry->hit_rate;
2892 exempt_delta = url_entry->exempt_delta;
2893 urlcache_entry_delete(container, header, hash_entry);
2896 if(header->dirs_no)
2897 dir_id = 0;
2898 else
2899 dir_id = CACHE_CONTAINER_NO_SUBDIR;
2901 if(file_name) {
2902 BOOL bFound = FALSE;
2904 if(wcsncmp(file_name, container->path, lstrlenW(container->path))) {
2905 ERR("path %s must begin with cache content path %s\n", debugstr_w(file_name), debugstr_w(container->path));
2906 cache_container_unlock_index(container, header);
2907 SetLastError(ERROR_INVALID_PARAMETER);
2908 return FALSE;
2911 /* skip container path prefix */
2912 file_name += lstrlenW(container->path);
2914 WideCharToMultiByte(CP_ACP, 0, file_name, -1, file_name_no_container, MAX_PATH, NULL, NULL);
2915 local_file_name = file_name_no_container;
2917 if(header->dirs_no) {
2918 for(dir_id = 0; dir_id < header->dirs_no; dir_id++) {
2919 if(!strncmp(header->directory_data[dir_id].name, local_file_name, DIR_LENGTH)) {
2920 bFound = TRUE;
2921 break;
2925 if(!bFound) {
2926 ERR("cache directory not found in path %s\n", debugstr_w(file_name));
2927 cache_container_unlock_index(container, header);
2928 SetLastError(ERROR_INVALID_PARAMETER);
2929 return FALSE;
2932 file_name += DIR_LENGTH + 1;
2933 local_file_name += DIR_LENGTH + 1;
2937 size = DWORD_ALIGN(size + strlen(url) + 1);
2938 if(file_name) {
2939 file_name_off = size;
2940 size = DWORD_ALIGN(size + strlen(local_file_name) + 1);
2942 if(header_info && header_size) {
2943 header_info_off = size;
2944 size = DWORD_ALIGN(size + header_size);
2946 if(file_ext && (file_ext_off = strlen(file_ext))) {
2947 DWORD len = file_ext_off;
2949 file_ext_off = size;
2950 size = DWORD_ALIGN(size + len + 1);
2953 /* round up to next block */
2954 if(size % BLOCKSIZE) {
2955 size -= size % BLOCKSIZE;
2956 size += BLOCKSIZE;
2959 error = urlcache_entry_alloc(header, size / BLOCKSIZE, &entry);
2960 while(error == ERROR_HANDLE_DISK_FULL) {
2961 error = cache_container_clean_index(container, &header);
2962 if(error == ERROR_SUCCESS)
2963 error = urlcache_entry_alloc(header, size / BLOCKSIZE, &entry);
2965 if(error != ERROR_SUCCESS) {
2966 cache_container_unlock_index(container, header);
2967 SetLastError(error);
2968 return FALSE;
2971 /* FindFirstFreeEntry fills in blocks used */
2972 url_entry = (entry_url *)entry;
2973 url_entry_offset = (LPBYTE)url_entry - (LPBYTE)header;
2974 url_entry->header.signature = URL_SIGNATURE;
2975 url_entry->cache_dir = dir_id;
2976 url_entry->cache_entry_type = entry_type | container->default_entry_type;
2977 url_entry->header_info_size = header_size;
2978 if((entry_type & STICKY_CACHE_ENTRY) && !exempt_delta) {
2979 /* Sticky entries have a default exempt time of one day */
2980 exempt_delta = 86400;
2982 url_entry->exempt_delta = exempt_delta;
2983 url_entry->hit_rate = hit_rate+1;
2984 url_entry->file_extension_off = file_ext_off;
2985 url_entry->header_info_off = header_info_off;
2986 url_entry->local_name_off = file_name_off;
2987 url_entry->url_off = DWORD_ALIGN(sizeof(*url_entry));
2988 url_entry->size.QuadPart = file_size.QuadPart;
2989 url_entry->use_count = 0;
2990 GetSystemTimeAsFileTime(&url_entry->access_time);
2991 url_entry->modification_time = modify_time;
2992 file_time_to_dos_date_time(&url_entry->access_time, &url_entry->sync_date, &url_entry->sync_time);
2993 file_time_to_dos_date_time(&expire_time, &url_entry->expire_date, &url_entry->expire_time);
2994 file_time_to_dos_date_time(&file_attr.ftLastWriteTime, &url_entry->write_date, &url_entry->write_time);
2996 /*** Unknowns ***/
2997 url_entry->unk1 = 0;
2998 url_entry->unk2 = 0;
2999 url_entry->unk3 = 0x60;
3000 url_entry->unk4 = 0;
3001 url_entry->unk5 = 0x1010;
3002 url_entry->unk7 = 0;
3003 url_entry->unk8 = 0;
3006 strcpy((LPSTR)url_entry + url_entry->url_off, url);
3007 if(file_name_off)
3008 strcpy((LPSTR)((LPBYTE)url_entry + file_name_off), local_file_name);
3009 if(header_info_off)
3010 memcpy((LPBYTE)url_entry + header_info_off, header_info, header_size);
3011 if(file_ext_off)
3012 strcpy((LPSTR)((LPBYTE)url_entry + file_ext_off), file_ext);
3014 error = urlcache_hash_entry_create(header, url, url_entry_offset, HASHTABLE_URL);
3015 while(error == ERROR_HANDLE_DISK_FULL) {
3016 error = cache_container_clean_index(container, &header);
3017 if(error == ERROR_SUCCESS) {
3018 url_entry = (entry_url *)((LPBYTE)header + url_entry_offset);
3019 error = urlcache_hash_entry_create(header, url,
3020 url_entry_offset, HASHTABLE_URL);
3023 if(error != ERROR_SUCCESS) {
3024 urlcache_entry_free(header, &url_entry->header);
3025 cache_container_unlock_index(container, header);
3026 SetLastError(error);
3027 return FALSE;
3030 if(url_entry->cache_dir < header->dirs_no)
3031 header->directory_data[url_entry->cache_dir].files_no++;
3032 if(entry_type & STICKY_CACHE_ENTRY)
3033 header->exempt_usage.QuadPart += file_size.QuadPart;
3034 else
3035 header->cache_usage.QuadPart += file_size.QuadPart;
3036 if(header->cache_usage.QuadPart+header->exempt_usage.QuadPart > header->cache_limit.QuadPart)
3037 handle_full_cache();
3039 cache_container_unlock_index(container, header);
3040 return TRUE;
3043 /***********************************************************************
3044 * CommitUrlCacheEntryA (WININET.@)
3046 BOOL WINAPI CommitUrlCacheEntryA(LPCSTR lpszUrlName, LPCSTR lpszLocalFileName,
3047 FILETIME ExpireTime, FILETIME LastModifiedTime, DWORD CacheEntryType,
3048 LPBYTE lpHeaderInfo, DWORD dwHeaderSize, LPCSTR lpszFileExtension, LPCSTR lpszOriginalUrl)
3050 WCHAR *file_name = NULL;
3051 BOOL ret;
3053 if(lpszLocalFileName) {
3054 file_name = heap_strdupAtoW(lpszLocalFileName);
3055 if(!file_name)
3056 return FALSE;
3059 ret = urlcache_entry_commit(lpszUrlName, file_name, ExpireTime, LastModifiedTime,
3060 CacheEntryType, lpHeaderInfo, dwHeaderSize, lpszFileExtension, lpszOriginalUrl);
3061 heap_free(file_name);
3062 return ret;
3065 /***********************************************************************
3066 * CommitUrlCacheEntryW (WININET.@)
3068 BOOL WINAPI CommitUrlCacheEntryW(LPCWSTR lpszUrlName, LPCWSTR lpszLocalFileName,
3069 FILETIME ExpireTime, FILETIME LastModifiedTime, DWORD CacheEntryType,
3070 LPWSTR lpHeaderInfo, DWORD dwHeaderSize, LPCWSTR lpszFileExtension, LPCWSTR lpszOriginalUrl)
3072 char *url, *original_url=NULL, *file_ext=NULL, *header_info=NULL;
3073 BOOL ret;
3075 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
3076 return FALSE;
3078 if(lpHeaderInfo) {
3079 header_info = heap_strdupWtoUTF8(lpHeaderInfo);
3080 if(!header_info) {
3081 heap_free(url);
3082 return FALSE;
3084 dwHeaderSize = strlen(header_info);
3087 if(lpszFileExtension) {
3088 file_ext = heap_strdupWtoA(lpszFileExtension);
3089 if(!file_ext) {
3090 heap_free(url);
3091 heap_free(header_info);
3092 return FALSE;
3096 if(lpszOriginalUrl && !urlcache_encode_url_alloc(lpszOriginalUrl, &original_url)) {
3097 heap_free(url);
3098 heap_free(header_info);
3099 heap_free(file_ext);
3100 return FALSE;
3103 ret = urlcache_entry_commit(url, lpszLocalFileName, ExpireTime, LastModifiedTime,
3104 CacheEntryType, (BYTE*)header_info, dwHeaderSize, file_ext, original_url);
3105 heap_free(url);
3106 heap_free(header_info);
3107 heap_free(file_ext);
3108 heap_free(original_url);
3109 return ret;
3112 /***********************************************************************
3113 * ReadUrlCacheEntryStream (WININET.@)
3116 BOOL WINAPI ReadUrlCacheEntryStream(
3117 IN HANDLE hUrlCacheStream,
3118 IN DWORD dwLocation,
3119 IN OUT LPVOID lpBuffer,
3120 IN OUT LPDWORD lpdwLen,
3121 IN DWORD dwReserved
3124 /* Get handle to file from 'stream' */
3125 stream_handle *pStream = (stream_handle*)hUrlCacheStream;
3127 if (dwReserved != 0)
3129 ERR("dwReserved != 0\n");
3130 SetLastError(ERROR_INVALID_PARAMETER);
3131 return FALSE;
3134 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->url, INTERNET_MAX_URL_LENGTH))
3136 SetLastError(ERROR_INVALID_HANDLE);
3137 return FALSE;
3140 if (SetFilePointer(pStream->file, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
3141 return FALSE;
3142 return ReadFile(pStream->file, lpBuffer, *lpdwLen, lpdwLen, NULL);
3145 /***********************************************************************
3146 * RetrieveUrlCacheEntryStreamA (WININET.@)
3149 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(LPCSTR lpszUrlName,
3150 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
3151 LPDWORD lpdwCacheEntryInfoBufferSize, BOOL fRandomRead, DWORD dwReserved)
3153 /* NOTE: this is not the same as the way that the native
3154 * version allocates 'stream' handles. I did it this way
3155 * as it is much easier and no applications should depend
3156 * on this behaviour. (Native version appears to allocate
3157 * indices into a table)
3159 stream_handle *stream;
3160 HANDLE file;
3162 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
3163 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved);
3165 if(!RetrieveUrlCacheEntryFileA(lpszUrlName, lpCacheEntryInfo,
3166 lpdwCacheEntryInfoBufferSize, dwReserved))
3167 return NULL;
3169 file = CreateFileA(lpCacheEntryInfo->lpszLocalFileName, GENERIC_READ, FILE_SHARE_READ,
3170 NULL, OPEN_EXISTING, fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0, NULL);
3171 if(file == INVALID_HANDLE_VALUE) {
3172 UnlockUrlCacheEntryFileA(lpszUrlName, 0);
3173 return NULL;
3176 /* allocate handle storage space */
3177 stream = heap_alloc(sizeof(stream_handle) + strlen(lpszUrlName) * sizeof(CHAR));
3178 if(!stream) {
3179 CloseHandle(file);
3180 UnlockUrlCacheEntryFileA(lpszUrlName, 0);
3181 SetLastError(ERROR_OUTOFMEMORY);
3182 return NULL;
3185 stream->file = file;
3186 strcpy(stream->url, lpszUrlName);
3187 return stream;
3190 /***********************************************************************
3191 * RetrieveUrlCacheEntryStreamW (WININET.@)
3194 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(LPCWSTR lpszUrlName,
3195 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3196 LPDWORD lpdwCacheEntryInfoBufferSize, BOOL fRandomRead, DWORD dwReserved)
3198 DWORD len;
3199 /* NOTE: this is not the same as the way that the native
3200 * version allocates 'stream' handles. I did it this way
3201 * as it is much easier and no applications should depend
3202 * on this behaviour. (Native version appears to allocate
3203 * indices into a table)
3205 stream_handle *stream;
3206 HANDLE file;
3208 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3209 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved);
3211 if(!(len = urlcache_encode_url(lpszUrlName, NULL, 0)))
3212 return NULL;
3214 if(!RetrieveUrlCacheEntryFileW(lpszUrlName, lpCacheEntryInfo,
3215 lpdwCacheEntryInfoBufferSize, dwReserved))
3216 return NULL;
3218 file = CreateFileW(lpCacheEntryInfo->lpszLocalFileName, GENERIC_READ, FILE_SHARE_READ,
3219 NULL, OPEN_EXISTING, fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0, NULL);
3220 if(file == INVALID_HANDLE_VALUE) {
3221 UnlockUrlCacheEntryFileW(lpszUrlName, 0);
3222 return NULL;
3225 /* allocate handle storage space */
3226 stream = heap_alloc(sizeof(stream_handle) + len*sizeof(WCHAR));
3227 if(!stream) {
3228 CloseHandle(file);
3229 UnlockUrlCacheEntryFileW(lpszUrlName, 0);
3230 SetLastError(ERROR_OUTOFMEMORY);
3231 return NULL;
3234 stream->file = file;
3235 if(!urlcache_encode_url(lpszUrlName, stream->url, len)) {
3236 CloseHandle(file);
3237 UnlockUrlCacheEntryFileW(lpszUrlName, 0);
3238 heap_free(stream);
3239 return NULL;
3241 return stream;
3244 /***********************************************************************
3245 * UnlockUrlCacheEntryStream (WININET.@)
3248 BOOL WINAPI UnlockUrlCacheEntryStream(
3249 IN HANDLE hUrlCacheStream,
3250 IN DWORD dwReserved
3253 stream_handle *pStream = (stream_handle*)hUrlCacheStream;
3255 if (dwReserved != 0)
3257 ERR("dwReserved != 0\n");
3258 SetLastError(ERROR_INVALID_PARAMETER);
3259 return FALSE;
3262 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->url, INTERNET_MAX_URL_LENGTH))
3264 SetLastError(ERROR_INVALID_HANDLE);
3265 return FALSE;
3268 if (!UnlockUrlCacheEntryFileA(pStream->url, 0))
3269 return FALSE;
3271 CloseHandle(pStream->file);
3272 heap_free(pStream);
3273 return TRUE;
3277 /***********************************************************************
3278 * DeleteUrlCacheEntryA (WININET.@)
3281 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3283 cache_container *pContainer;
3284 urlcache_header *pHeader;
3285 struct hash_entry *pHashEntry;
3286 DWORD error;
3287 BOOL ret;
3289 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3291 error = cache_containers_find(lpszUrlName, &pContainer);
3292 if (error != ERROR_SUCCESS)
3294 SetLastError(error);
3295 return FALSE;
3298 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3299 if (error != ERROR_SUCCESS)
3301 SetLastError(error);
3302 return FALSE;
3305 if (!(pHeader = cache_container_lock_index(pContainer)))
3306 return FALSE;
3308 if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
3310 cache_container_unlock_index(pContainer, pHeader);
3311 TRACE("entry %s not found!\n", debugstr_a(lpszUrlName));
3312 SetLastError(ERROR_FILE_NOT_FOUND);
3313 return FALSE;
3316 ret = urlcache_entry_delete(pContainer, pHeader, pHashEntry);
3318 cache_container_unlock_index(pContainer, pHeader);
3320 return ret;
3323 /***********************************************************************
3324 * DeleteUrlCacheEntryW (WININET.@)
3327 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3329 char *url;
3330 BOOL ret;
3332 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
3333 return FALSE;
3335 ret = DeleteUrlCacheEntryA(url);
3336 heap_free(url);
3337 return ret;
3340 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3342 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3343 return TRUE;
3346 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3348 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3349 return TRUE;
3352 /***********************************************************************
3353 * CreateCacheContainerA (WININET.@)
3355 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3356 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3358 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3359 d1, d2, d3, d4, d5, d6, d7, d8);
3360 return TRUE;
3363 /***********************************************************************
3364 * CreateCacheContainerW (WININET.@)
3366 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3367 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3369 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3370 d1, d2, d3, d4, d5, d6, d7, d8);
3371 return TRUE;
3374 /***********************************************************************
3375 * FindFirstUrlCacheContainerA (WININET.@)
3377 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3379 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3380 return NULL;
3383 /***********************************************************************
3384 * FindFirstUrlCacheContainerW (WININET.@)
3386 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3388 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3389 return NULL;
3392 /***********************************************************************
3393 * FindNextUrlCacheContainerA (WININET.@)
3395 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3397 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3398 return FALSE;
3401 /***********************************************************************
3402 * FindNextUrlCacheContainerW (WININET.@)
3404 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3406 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3407 return FALSE;
3410 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3411 LPCSTR lpszUrlSearchPattern,
3412 DWORD dwFlags,
3413 DWORD dwFilter,
3414 GROUPID GroupId,
3415 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3416 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3417 LPVOID lpReserved,
3418 LPDWORD pcbReserved2,
3419 LPVOID lpReserved3
3422 FIXME("(%s, 0x%08x, 0x%08x, 0x%s, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3423 dwFlags, dwFilter, wine_dbgstr_longlong(GroupId), lpFirstCacheEntryInfo,
3424 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3425 SetLastError(ERROR_FILE_NOT_FOUND);
3426 return NULL;
3429 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3430 LPCWSTR lpszUrlSearchPattern,
3431 DWORD dwFlags,
3432 DWORD dwFilter,
3433 GROUPID GroupId,
3434 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3435 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3436 LPVOID lpReserved,
3437 LPDWORD pcbReserved2,
3438 LPVOID lpReserved3
3441 FIXME("(%s, 0x%08x, 0x%08x, 0x%s, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3442 dwFlags, dwFilter, wine_dbgstr_longlong(GroupId), lpFirstCacheEntryInfo,
3443 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3444 SetLastError(ERROR_FILE_NOT_FOUND);
3445 return NULL;
3448 /***********************************************************************
3449 * FindFirstUrlCacheEntryA (WININET.@)
3452 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3453 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3455 find_handle *pEntryHandle;
3457 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3459 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3460 if (!pEntryHandle)
3461 return NULL;
3463 pEntryHandle->magic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3464 if (lpszUrlSearchPattern)
3466 pEntryHandle->url_search_pattern = heap_strdupA(lpszUrlSearchPattern);
3467 if (!pEntryHandle->url_search_pattern)
3469 heap_free(pEntryHandle);
3470 return NULL;
3473 else
3474 pEntryHandle->url_search_pattern = NULL;
3475 pEntryHandle->container_idx = 0;
3476 pEntryHandle->hash_table_idx = 0;
3477 pEntryHandle->hash_entry_idx = 0;
3479 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3481 heap_free(pEntryHandle);
3482 return NULL;
3484 return pEntryHandle;
3487 /***********************************************************************
3488 * FindFirstUrlCacheEntryW (WININET.@)
3491 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3492 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3494 find_handle *pEntryHandle;
3496 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3498 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3499 if (!pEntryHandle)
3500 return NULL;
3502 pEntryHandle->magic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3503 if (lpszUrlSearchPattern)
3505 pEntryHandle->url_search_pattern = heap_strdupWtoA(lpszUrlSearchPattern);
3506 if (!pEntryHandle->url_search_pattern)
3508 heap_free(pEntryHandle);
3509 return NULL;
3512 else
3513 pEntryHandle->url_search_pattern = NULL;
3514 pEntryHandle->container_idx = 0;
3515 pEntryHandle->hash_table_idx = 0;
3516 pEntryHandle->hash_entry_idx = 0;
3518 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3520 heap_free(pEntryHandle);
3521 return NULL;
3523 return pEntryHandle;
3526 static BOOL urlcache_find_next_entry(
3527 HANDLE hEnumHandle,
3528 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3529 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3530 BOOL unicode)
3532 find_handle *pEntryHandle = (find_handle*)hEnumHandle;
3533 cache_container *pContainer;
3535 if (pEntryHandle->magic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3537 SetLastError(ERROR_INVALID_HANDLE);
3538 return FALSE;
3541 for (; cache_containers_enum(pEntryHandle->url_search_pattern, pEntryHandle->container_idx, &pContainer);
3542 pEntryHandle->container_idx++, pEntryHandle->hash_table_idx = 0)
3544 urlcache_header *pHeader;
3545 entry_hash_table *pHashTableEntry;
3546 DWORD error;
3548 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3549 if (error != ERROR_SUCCESS)
3551 SetLastError(error);
3552 return FALSE;
3555 if (!(pHeader = cache_container_lock_index(pContainer)))
3556 return FALSE;
3558 for (; urlcache_enum_hash_tables(pHeader, &pEntryHandle->hash_table_idx, &pHashTableEntry);
3559 pEntryHandle->hash_table_idx++, pEntryHandle->hash_entry_idx = 0)
3561 const struct hash_entry *pHashEntry = NULL;
3562 for (; urlcache_enum_hash_table_entries(pHeader, pHashTableEntry, &pEntryHandle->hash_entry_idx, &pHashEntry);
3563 pEntryHandle->hash_entry_idx++)
3565 const entry_url *pUrlEntry;
3566 const entry_header *pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
3568 if (pEntry->signature != URL_SIGNATURE)
3569 continue;
3571 pUrlEntry = (const entry_url *)pEntry;
3572 TRACE("Found URL: %s\n",
3573 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->url_off));
3574 TRACE("Header info: %s\n",
3575 debugstr_an((LPCSTR)pUrlEntry + pUrlEntry->header_info_off,
3576 pUrlEntry->header_info_size));
3578 error = urlcache_copy_entry(
3579 pContainer,
3580 pHeader,
3581 lpNextCacheEntryInfo,
3582 lpdwNextCacheEntryInfoBufferSize,
3583 pUrlEntry,
3584 unicode);
3585 if (error != ERROR_SUCCESS)
3587 cache_container_unlock_index(pContainer, pHeader);
3588 SetLastError(error);
3589 return FALSE;
3591 if(pUrlEntry->local_name_off)
3592 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->local_name_off));
3594 /* increment the current index so that next time the function
3595 * is called the next entry is returned */
3596 pEntryHandle->hash_entry_idx++;
3597 cache_container_unlock_index(pContainer, pHeader);
3598 return TRUE;
3602 cache_container_unlock_index(pContainer, pHeader);
3605 SetLastError(ERROR_NO_MORE_ITEMS);
3606 return FALSE;
3609 /***********************************************************************
3610 * FindNextUrlCacheEntryA (WININET.@)
3612 BOOL WINAPI FindNextUrlCacheEntryA(
3613 HANDLE hEnumHandle,
3614 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3615 LPDWORD lpdwNextCacheEntryInfoBufferSize)
3617 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3619 return urlcache_find_next_entry(hEnumHandle, lpNextCacheEntryInfo,
3620 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
3623 /***********************************************************************
3624 * FindNextUrlCacheEntryW (WININET.@)
3626 BOOL WINAPI FindNextUrlCacheEntryW(
3627 HANDLE hEnumHandle,
3628 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3629 LPDWORD lpdwNextCacheEntryInfoBufferSize
3632 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3634 return urlcache_find_next_entry(hEnumHandle,
3635 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
3636 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
3639 /***********************************************************************
3640 * FindCloseUrlCache (WININET.@)
3642 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3644 find_handle *pEntryHandle = (find_handle*)hEnumHandle;
3646 TRACE("(%p)\n", hEnumHandle);
3648 if (!pEntryHandle || pEntryHandle->magic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3650 SetLastError(ERROR_INVALID_HANDLE);
3651 return FALSE;
3654 pEntryHandle->magic = 0;
3655 heap_free(pEntryHandle->url_search_pattern);
3656 heap_free(pEntryHandle);
3657 return TRUE;
3660 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3661 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3663 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3664 dwSearchCondition, lpGroupId, lpReserved);
3665 return NULL;
3668 BOOL WINAPI FindNextUrlCacheEntryExA(
3669 HANDLE hEnumHandle,
3670 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3671 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3672 LPVOID lpReserved,
3673 LPDWORD pcbReserved2,
3674 LPVOID lpReserved3
3677 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3678 lpReserved, pcbReserved2, lpReserved3);
3679 return FALSE;
3682 BOOL WINAPI FindNextUrlCacheEntryExW(
3683 HANDLE hEnumHandle,
3684 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3685 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3686 LPVOID lpReserved,
3687 LPDWORD pcbReserved2,
3688 LPVOID lpReserved3
3691 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3692 lpReserved, pcbReserved2, lpReserved3);
3693 return FALSE;
3696 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3698 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3699 return FALSE;
3702 /***********************************************************************
3703 * CreateUrlCacheGroup (WININET.@)
3706 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3708 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3709 return FALSE;
3712 /***********************************************************************
3713 * DeleteUrlCacheGroup (WININET.@)
3716 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3718 FIXME("(0x%s, 0x%08x, %p) stub\n",
3719 wine_dbgstr_longlong(GroupId), dwFlags, lpReserved);
3720 return FALSE;
3723 /***********************************************************************
3724 * DeleteWpadCacheForNetworks (WININET.@)
3725 * Undocumented, added in IE8
3727 BOOL WINAPI DeleteWpadCacheForNetworks(DWORD unk1)
3729 FIXME("(%d) stub\n", unk1);
3730 return FALSE;
3733 /***********************************************************************
3734 * SetUrlCacheEntryGroupA (WININET.@)
3737 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR 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_a(lpszUrlName), dwFlags, wine_dbgstr_longlong(GroupId),
3743 pbGroupAttributes, cbGroupAttributes, lpReserved);
3744 SetLastError(ERROR_FILE_NOT_FOUND);
3745 return FALSE;
3748 /***********************************************************************
3749 * SetUrlCacheEntryGroupW (WININET.@)
3752 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3753 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3754 LPVOID lpReserved)
3756 FIXME("(%s, 0x%08x, 0x%s, %p, 0x%08x, %p) stub\n",
3757 debugstr_w(lpszUrlName), dwFlags, wine_dbgstr_longlong(GroupId),
3758 pbGroupAttributes, cbGroupAttributes, lpReserved);
3759 SetLastError(ERROR_FILE_NOT_FOUND);
3760 return FALSE;
3763 static cache_container *find_container(DWORD flags)
3765 cache_container *container;
3767 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
3769 switch (flags & (CACHE_CONFIG_CONTENT_PATHS_FC | CACHE_CONFIG_COOKIES_PATHS_FC | CACHE_CONFIG_HISTORY_PATHS_FC))
3771 case 0:
3772 case CACHE_CONFIG_CONTENT_PATHS_FC:
3773 if (container->default_entry_type == NORMAL_CACHE_ENTRY)
3774 return container;
3775 break;
3777 case CACHE_CONFIG_COOKIES_PATHS_FC:
3778 if (container->default_entry_type == COOKIE_CACHE_ENTRY)
3779 return container;
3780 break;
3782 case CACHE_CONFIG_HISTORY_PATHS_FC:
3783 if (container->default_entry_type == URLHISTORY_CACHE_ENTRY)
3784 return container;
3785 break;
3787 default:
3788 FIXME("flags %08x not handled\n", flags);
3789 break;
3793 return NULL;
3796 /***********************************************************************
3797 * GetUrlCacheConfigInfoW (WININET.@)
3799 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW info, LPDWORD size, DWORD flags)
3801 cache_container *container;
3802 DWORD error;
3804 FIXME("(%p, %p, %x): semi-stub\n", info, size, flags);
3806 if (!info || !(container = find_container(flags)))
3808 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3809 return FALSE;
3812 error = cache_container_open_index(container, MIN_BLOCK_NO);
3813 if (error != ERROR_SUCCESS)
3815 INTERNET_SetLastError(error);
3816 return FALSE;
3819 info->dwContainer = 0;
3820 info->dwQuota = FILE_SIZE(MAX_BLOCK_NO) / 1024;
3821 info->dwReserved4 = 0;
3822 info->fPerUser = TRUE;
3823 info->dwSyncMode = 0;
3824 info->dwNumCachePaths = 1;
3825 info->dwNormalUsage = 0;
3826 info->dwExemptUsage = 0;
3827 info->u.s.dwCacheSize = container->file_size / 1024;
3828 lstrcpynW(info->u.s.CachePath, container->path, MAX_PATH);
3830 cache_container_close_index(container);
3832 TRACE("CachePath %s\n", debugstr_w(info->u.s.CachePath));
3834 return TRUE;
3837 /***********************************************************************
3838 * GetUrlCacheConfigInfoA (WININET.@)
3840 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA info, LPDWORD size, DWORD flags)
3842 INTERNET_CACHE_CONFIG_INFOW infoW;
3844 TRACE("(%p, %p, %x)\n", info, size, flags);
3846 if (!info)
3848 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3849 return FALSE;
3852 infoW.dwStructSize = sizeof(infoW);
3853 if (!GetUrlCacheConfigInfoW(&infoW, size, flags))
3854 return FALSE;
3856 info->dwContainer = infoW.dwContainer;
3857 info->dwQuota = infoW.dwQuota;
3858 info->dwReserved4 = infoW.dwReserved4;
3859 info->fPerUser = infoW.fPerUser;
3860 info->dwSyncMode = infoW.dwSyncMode;
3861 info->dwNumCachePaths = infoW.dwNumCachePaths;
3862 info->dwNormalUsage = infoW.dwNormalUsage;
3863 info->dwExemptUsage = infoW.dwExemptUsage;
3864 info->u.s.dwCacheSize = infoW.u.s.dwCacheSize;
3865 WideCharToMultiByte(CP_ACP, 0, infoW.u.s.CachePath, -1, info->u.s.CachePath, MAX_PATH, NULL, NULL);
3867 return TRUE;
3870 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3871 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3872 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3874 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3875 wine_dbgstr_longlong(gid), dwFlags, dwAttributes, lpGroupInfo,
3876 lpdwGroupInfo, lpReserved);
3877 return FALSE;
3880 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3881 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3882 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3884 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3885 wine_dbgstr_longlong(gid), dwFlags, dwAttributes, lpGroupInfo,
3886 lpdwGroupInfo, lpReserved);
3887 return FALSE;
3890 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3891 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3893 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p) stub\n",
3894 wine_dbgstr_longlong(gid), dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3895 return TRUE;
3898 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3899 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3901 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p) stub\n",
3902 wine_dbgstr_longlong(gid), dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3903 return TRUE;
3906 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3908 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3909 return TRUE;
3912 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3914 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3915 return TRUE;
3918 /***********************************************************************
3919 * DeleteIE3Cache (WININET.@)
3921 * Deletes the files used by the IE3 URL caching system.
3923 * PARAMS
3924 * hWnd [I] A dummy window.
3925 * hInst [I] Instance of process calling the function.
3926 * lpszCmdLine [I] Options used by function.
3927 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3929 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3931 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3932 return 0;
3935 static BOOL urlcache_entry_is_expired(const entry_url *pUrlEntry,
3936 FILETIME *pftLastModified)
3938 BOOL ret;
3939 FILETIME now, expired;
3941 *pftLastModified = pUrlEntry->modification_time;
3942 GetSystemTimeAsFileTime(&now);
3943 dos_date_time_to_file_time(pUrlEntry->expire_date,
3944 pUrlEntry->expire_time, &expired);
3945 /* If the expired time is 0, it's interpreted as not expired */
3946 if (!expired.dwLowDateTime && !expired.dwHighDateTime)
3947 ret = FALSE;
3948 else
3949 ret = CompareFileTime(&expired, &now) < 0;
3950 return ret;
3953 /***********************************************************************
3954 * IsUrlCacheEntryExpiredA (WININET.@)
3956 * PARAMS
3957 * url [I] Url
3958 * dwFlags [I] Unknown
3959 * pftLastModified [O] Last modified time
3961 BOOL WINAPI IsUrlCacheEntryExpiredA(LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified)
3963 urlcache_header *pHeader;
3964 struct hash_entry *pHashEntry;
3965 const entry_header *pEntry;
3966 const entry_url * pUrlEntry;
3967 cache_container *pContainer;
3968 BOOL expired;
3970 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3972 if (!url || !pftLastModified)
3973 return TRUE;
3974 if (dwFlags)
3975 FIXME("unknown flags 0x%08x\n", dwFlags);
3977 /* Any error implies that the URL is expired, i.e. not in the cache */
3978 if (cache_containers_find(url, &pContainer))
3980 memset(pftLastModified, 0, sizeof(*pftLastModified));
3981 return TRUE;
3984 if (cache_container_open_index(pContainer, MIN_BLOCK_NO))
3986 memset(pftLastModified, 0, sizeof(*pftLastModified));
3987 return TRUE;
3990 if (!(pHeader = cache_container_lock_index(pContainer)))
3992 memset(pftLastModified, 0, sizeof(*pftLastModified));
3993 return TRUE;
3996 if (!urlcache_find_hash_entry(pHeader, url, &pHashEntry))
3998 cache_container_unlock_index(pContainer, pHeader);
3999 memset(pftLastModified, 0, sizeof(*pftLastModified));
4000 TRACE("entry %s not found!\n", url);
4001 return TRUE;
4004 pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
4005 if (pEntry->signature != URL_SIGNATURE)
4007 cache_container_unlock_index(pContainer, pHeader);
4008 memset(pftLastModified, 0, sizeof(*pftLastModified));
4009 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
4010 return TRUE;
4013 pUrlEntry = (const entry_url *)pEntry;
4014 expired = urlcache_entry_is_expired(pUrlEntry, pftLastModified);
4016 cache_container_unlock_index(pContainer, pHeader);
4018 return expired;
4021 /***********************************************************************
4022 * IsUrlCacheEntryExpiredW (WININET.@)
4024 * PARAMS
4025 * url [I] Url
4026 * dwFlags [I] Unknown
4027 * pftLastModified [O] Last modified time
4029 BOOL WINAPI IsUrlCacheEntryExpiredW(LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified)
4031 char *encoded_url;
4032 BOOL ret;
4034 if(!urlcache_encode_url_alloc(url, &encoded_url))
4035 return FALSE;
4037 ret = IsUrlCacheEntryExpiredA(encoded_url, dwFlags, pftLastModified);
4038 heap_free(encoded_url);
4039 return ret;
4042 /***********************************************************************
4043 * GetDiskInfoA (WININET.@)
4045 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
4047 BOOL ret;
4048 ULARGE_INTEGER bytes_free, bytes_total;
4050 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
4052 if (!path)
4054 SetLastError(ERROR_INVALID_PARAMETER);
4055 return FALSE;
4058 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
4060 if (cluster_size) *cluster_size = 1;
4061 if (free) *free = bytes_free.QuadPart;
4062 if (total) *total = bytes_total.QuadPart;
4064 return ret;
4067 /***********************************************************************
4068 * RegisterUrlCacheNotification (WININET.@)
4070 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
4072 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
4073 return 0;
4076 /***********************************************************************
4077 * IncrementUrlCacheHeaderData (WININET.@)
4079 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
4081 FIXME("(%u, %p)\n", index, data);
4082 return FALSE;
4085 /***********************************************************************
4086 * RunOnceUrlCache (WININET.@)
4089 DWORD WINAPI RunOnceUrlCache(HWND hwnd, HINSTANCE hinst, LPSTR cmd, int cmdshow)
4091 FIXME("(%p, %p, %s, %d): stub\n", hwnd, hinst, debugstr_a(cmd), cmdshow);
4092 return 0;
4095 BOOL init_urlcache(void)
4097 dll_unload_event = CreateEventW(NULL, FALSE, FALSE, NULL);
4098 if(!dll_unload_event)
4099 return FALSE;
4101 free_cache_running = CreateSemaphoreW(NULL, 1, 1, NULL);
4102 if(!free_cache_running) {
4103 CloseHandle(dll_unload_event);
4104 return FALSE;
4107 cache_containers_init();
4108 return TRUE;
4111 void free_urlcache(void)
4113 SetEvent(dll_unload_event);
4114 WaitForSingleObject(free_cache_running, INFINITE);
4115 ReleaseSemaphore(free_cache_running, 1, NULL);
4116 CloseHandle(free_cache_running);
4117 CloseHandle(dll_unload_event);
4119 cache_containers_free();
4122 /***********************************************************************
4123 * LoadUrlCacheContent (WININET.@)
4125 BOOL WINAPI LoadUrlCacheContent(void)
4127 FIXME("stub!\n");
4128 return FALSE;