winegstreamer: Implement ProcessSample for media sink.
[wine.git] / dlls / wininet / urlcache.c
blob8e43eaae9f3b68f9bc5e014d9f2a18d23f383e89
1 /*
2 * Wininet - Url Cache functions
4 * Copyright 2001,2002 CodeWeavers
5 * Copyright 2003-2008 Robert Shearman
7 * Eric Kohl
8 * Aric Stewart
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #include "ws2tcpip.h"
27 #include <limits.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <time.h>
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winuser.h"
37 #include "wininet.h"
38 #include "winineti.h"
39 #include "winerror.h"
40 #include "winreg.h"
41 #include "shlwapi.h"
42 #include "shlobj.h"
43 #include "shellapi.h"
45 #include "internet.h"
46 #include "wine/debug.h"
48 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
50 static const char urlcache_ver_prefix[] = "WINE URLCache Ver ";
51 static const char urlcache_ver[] = "0.2012001";
53 #define ENTRY_START_OFFSET 0x4000
54 #define DIR_LENGTH 8
55 #define MAX_DIR_NO 0x20
56 #define BLOCKSIZE 128
57 #define HASHTABLE_SIZE 448
58 #define HASHTABLE_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
59 #define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
60 #define ALLOCATION_TABLE_OFFSET 0x250
61 #define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
62 #define MIN_BLOCK_NO 0x80
63 #define MAX_BLOCK_NO (ALLOCATION_TABLE_SIZE * CHAR_BIT)
64 #define FILE_SIZE(blocks) ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
66 #define HASHTABLE_URL 0
67 #define HASHTABLE_DEL 1
68 #define HASHTABLE_LOCK 2
69 #define HASHTABLE_FREE 3
70 #define HASHTABLE_REDR 5
71 #define HASHTABLE_FLAG_BITS 6
73 #define PENDING_DELETE_CACHE_ENTRY 0x00400000
74 #define INSTALLED_CACHE_ENTRY 0x10000000
75 #define GET_INSTALLED_ENTRY 0x200
76 #define CACHE_CONTAINER_NO_SUBDIR 0xFE
78 #define CACHE_HEADER_DATA_ROOT_LEAK_OFFSET 0x16
80 #define FILETIME_SECOND 10000000
82 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
83 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
84 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
85 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
86 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
88 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
90 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
92 typedef struct
94 DWORD signature;
95 DWORD blocks_used; /* number of 128byte blocks used by this entry */
96 } entry_header;
98 typedef struct
100 entry_header header;
101 FILETIME modification_time;
102 FILETIME access_time;
103 WORD expire_date; /* expire date in dos format */
104 WORD expire_time; /* expire time in dos format */
105 DWORD unk1; /* usually zero */
106 ULARGE_INTEGER size; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
107 DWORD unk2; /* usually zero */
108 DWORD exempt_delta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
109 DWORD unk3; /* usually 0x60 */
110 DWORD url_off; /* offset of start of url from start of entry */
111 BYTE cache_dir; /* index of cache directory this url is stored in */
112 BYTE unk4; /* usually zero */
113 WORD unk5; /* usually 0x1010 */
114 DWORD local_name_off; /* offset of start of local filename from start of entry */
115 DWORD cache_entry_type; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
116 DWORD header_info_off; /* offset of start of header info from start of entry */
117 DWORD header_info_size;
118 DWORD file_extension_off; /* offset of start of file extension from start of entry */
119 WORD sync_date; /* last sync date in dos format */
120 WORD sync_time; /* last sync time in dos format */
121 DWORD hit_rate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
122 DWORD use_count; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
123 WORD write_date;
124 WORD write_time;
125 DWORD unk7; /* usually zero */
126 DWORD unk8; /* usually zero */
127 /* packing to dword align start of next field */
128 /* CHAR szSourceUrlName[]; (url) */
129 /* packing to dword align start of next field */
130 /* CHAR szLocalFileName[]; (local file name excluding path) */
131 /* packing to dword align start of next field */
132 /* CHAR szHeaderInfo[]; (header info) */
133 } entry_url;
135 struct hash_entry
137 DWORD key;
138 DWORD offset;
141 typedef struct
143 entry_header header;
144 DWORD next;
145 DWORD id;
146 struct hash_entry hash_table[HASHTABLE_SIZE];
147 } entry_hash_table;
149 typedef struct
151 char signature[28];
152 DWORD size;
153 DWORD hash_table_off;
154 DWORD capacity_in_blocks;
155 DWORD blocks_in_use;
156 DWORD unk1;
157 ULARGE_INTEGER cache_limit;
158 ULARGE_INTEGER cache_usage;
159 ULARGE_INTEGER exempt_usage;
160 DWORD dirs_no;
161 struct _directory_data
163 DWORD files_no;
164 char name[DIR_LENGTH];
165 } directory_data[MAX_DIR_NO];
166 DWORD options[0x21];
167 BYTE allocation_table[ALLOCATION_TABLE_SIZE];
168 } urlcache_header;
170 typedef struct
172 HANDLE file;
173 CHAR url[1];
174 } stream_handle;
176 typedef struct
178 struct list entry; /* part of a list */
179 char *cache_prefix; /* string that has to be prefixed for this container to be used */
180 LPWSTR path; /* path to url container directory */
181 HANDLE mapping; /* handle of file mapping */
182 DWORD file_size; /* size of file when mapping was opened */
183 HANDLE mutex; /* handle of mutex */
184 DWORD default_entry_type;
185 } cache_container;
187 typedef struct
189 DWORD magic;
190 char *url_search_pattern;
191 DWORD container_idx;
192 DWORD hash_table_idx;
193 DWORD hash_entry_idx;
194 } find_handle;
196 /* List of all containers available */
197 static struct list UrlContainers = LIST_INIT(UrlContainers);
199 static inline char *strdupWtoUTF8(const WCHAR *str)
201 char *ret = NULL;
203 if(str) {
204 DWORD size = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);
205 ret = malloc(size);
206 if(ret)
207 WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, size, NULL, NULL);
210 return ret;
213 /***********************************************************************
214 * urlcache_block_is_free (Internal)
216 * Is the specified block number free?
218 * RETURNS
219 * zero if free
220 * non-zero otherwise
223 static inline BYTE urlcache_block_is_free(BYTE *allocation_table, DWORD block_number)
225 BYTE mask = 1 << (block_number%CHAR_BIT);
226 return (allocation_table[block_number/CHAR_BIT] & mask) == 0;
229 /***********************************************************************
230 * urlcache_block_free (Internal)
232 * Marks the specified block as free
234 * CAUTION
235 * this function is not updating used blocks count
237 * RETURNS
238 * nothing
241 static inline void urlcache_block_free(BYTE *allocation_table, DWORD block_number)
243 BYTE mask = ~(1 << (block_number%CHAR_BIT));
244 allocation_table[block_number/CHAR_BIT] &= mask;
247 /***********************************************************************
248 * urlcache_block_alloc (Internal)
250 * Marks the specified block as allocated
252 * CAUTION
253 * this function is not updating used blocks count
255 * RETURNS
256 * nothing
259 static inline void urlcache_block_alloc(BYTE *allocation_table, DWORD block_number)
261 BYTE mask = 1 << (block_number%CHAR_BIT);
262 allocation_table[block_number/CHAR_BIT] |= mask;
265 /***********************************************************************
266 * urlcache_entry_alloc (Internal)
268 * Finds and allocates the first block of free space big enough and
269 * sets entry to point to it.
271 * RETURNS
272 * ERROR_SUCCESS when free memory block was found
273 * Any other Win32 error code if the entry could not be added
276 static DWORD urlcache_entry_alloc(urlcache_header *header, DWORD blocks_needed, entry_header **entry)
278 DWORD block, block_size;
280 for(block=0; block<header->capacity_in_blocks; block+=block_size+1)
282 block_size = 0;
283 while(block_size<blocks_needed && block_size+block<header->capacity_in_blocks
284 && urlcache_block_is_free(header->allocation_table, block+block_size))
285 block_size++;
287 if(block_size == blocks_needed)
289 DWORD index;
291 TRACE("Found free blocks starting at no. %ld (0x%lx)\n", block, ENTRY_START_OFFSET+block*BLOCKSIZE);
293 for(index=0; index<blocks_needed; index++)
294 urlcache_block_alloc(header->allocation_table, block+index);
296 *entry = (entry_header*)((BYTE*)header+ENTRY_START_OFFSET+block*BLOCKSIZE);
297 for(index=0; index<blocks_needed*BLOCKSIZE/sizeof(DWORD); index++)
298 ((DWORD*)*entry)[index] = 0xdeadbeef;
299 (*entry)->blocks_used = blocks_needed;
301 header->blocks_in_use += blocks_needed;
302 return ERROR_SUCCESS;
306 return ERROR_HANDLE_DISK_FULL;
309 /***********************************************************************
310 * urlcache_entry_free (Internal)
312 * Deletes the specified entry and frees the space allocated to it
314 * RETURNS
315 * TRUE if it succeeded
316 * FALSE if it failed
319 static BOOL urlcache_entry_free(urlcache_header *header, entry_header *entry)
321 DWORD start_block, block;
323 /* update allocation table */
324 start_block = ((DWORD)((BYTE*)entry - (BYTE*)header) - ENTRY_START_OFFSET) / BLOCKSIZE;
325 for(block = start_block; block < start_block+entry->blocks_used; block++)
326 urlcache_block_free(header->allocation_table, block);
328 header->blocks_in_use -= entry->blocks_used;
329 return TRUE;
332 /***********************************************************************
333 * urlcache_create_hash_table (Internal)
335 * Creates a new hash table in free space and adds it to the chain of existing
336 * hash tables.
338 * RETURNS
339 * ERROR_SUCCESS if the hash table was created
340 * ERROR_DISK_FULL if the hash table could not be created
343 static DWORD urlcache_create_hash_table(urlcache_header *header, entry_hash_table *hash_table_prev, entry_hash_table **hash_table)
345 DWORD dwOffset, error;
346 int i;
348 if((error = urlcache_entry_alloc(header, 0x20, (entry_header**)hash_table)) != ERROR_SUCCESS)
349 return error;
351 dwOffset = (BYTE*)*hash_table-(BYTE*)header;
353 if(hash_table_prev)
354 hash_table_prev->next = dwOffset;
355 else
356 header->hash_table_off = dwOffset;
358 (*hash_table)->header.signature = HASH_SIGNATURE;
359 (*hash_table)->next = 0;
360 (*hash_table)->id = hash_table_prev ? hash_table_prev->id+1 : 0;
361 for(i = 0; i < HASHTABLE_SIZE; i++) {
362 (*hash_table)->hash_table[i].offset = HASHTABLE_FREE;
363 (*hash_table)->hash_table[i].key = HASHTABLE_FREE;
365 return ERROR_SUCCESS;
368 /***********************************************************************
369 * cache_container_create_object_name (Internal)
371 * Converts a path to a name suitable for use as a Win32 object name.
372 * Replaces '\\' characters in-place with the specified character
373 * (usually '_' or '!')
375 * RETURNS
376 * nothing
379 static void cache_container_create_object_name(LPWSTR lpszPath, WCHAR replace)
381 for (; *lpszPath; lpszPath++)
383 if (*lpszPath == '\\')
384 *lpszPath = replace;
388 /* Caller must hold container lock */
389 static HANDLE cache_container_map_index(HANDLE file, const WCHAR *path, DWORD size, BOOL *validate)
391 WCHAR mapping_name[MAX_PATH];
392 HANDLE mapping;
394 wsprintfW(mapping_name, L"%sindex.dat_%lu", path, size);
395 cache_container_create_object_name(mapping_name, '_');
397 mapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, mapping_name);
398 if(mapping) {
399 if(validate) *validate = FALSE;
400 return mapping;
403 if(validate) *validate = TRUE;
404 return CreateFileMappingW(file, NULL, PAGE_READWRITE, 0, 0, mapping_name);
407 /* Caller must hold container lock */
408 static DWORD cache_container_set_size(cache_container *container, HANDLE file, DWORD blocks_no)
410 DWORD file_size = FILE_SIZE(blocks_no);
411 WCHAR dir_path[MAX_PATH], *dir_name;
412 entry_hash_table *hashtable_entry;
413 urlcache_header *header;
414 HANDLE mapping;
415 FILETIME ft;
416 HKEY key;
417 int i, j;
419 if(SetFilePointer(file, file_size, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
420 return GetLastError();
422 if(!SetEndOfFile(file))
423 return GetLastError();
425 mapping = cache_container_map_index(file, container->path, file_size, NULL);
426 if(!mapping)
427 return GetLastError();
429 header = MapViewOfFile(mapping, FILE_MAP_WRITE, 0, 0, 0);
430 if(!header) {
431 CloseHandle(mapping);
432 return GetLastError();
435 if(blocks_no != MIN_BLOCK_NO) {
436 if(file_size > header->size)
437 memset((char*)header+header->size, 0, file_size-header->size);
438 header->size = file_size;
439 header->capacity_in_blocks = blocks_no;
441 UnmapViewOfFile(header);
442 CloseHandle(container->mapping);
443 container->mapping = mapping;
444 container->file_size = file_size;
445 return ERROR_SUCCESS;
448 memset(header, 0, file_size);
449 /* First set some constants and defaults in the header */
450 memcpy(header->signature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1);
451 memcpy(header->signature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1);
452 header->size = file_size;
453 header->capacity_in_blocks = blocks_no;
454 /* 127MB - taken from default for Windows 2000 */
455 header->cache_limit.QuadPart = 0x07ff5400;
456 /* Copied from a Windows 2000 cache index */
457 header->dirs_no = container->default_entry_type==NORMAL_CACHE_ENTRY ? 4 : 0;
459 /* If the registry has a cache size set, use the registry value */
460 if(RegOpenKeyW(HKEY_CURRENT_USER,
461 L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Cache\\Content", &key) == ERROR_SUCCESS) {
462 DWORD dw, len = sizeof(dw), keytype;
464 if(RegQueryValueExW(key, L"CacheLimit", NULL, &keytype, (BYTE*)&dw, &len) == ERROR_SUCCESS &&
465 keytype == REG_DWORD)
466 header->cache_limit.QuadPart = (ULONGLONG)dw * 1024;
467 RegCloseKey(key);
470 urlcache_create_hash_table(header, NULL, &hashtable_entry);
472 /* Last step - create the directories */
473 lstrcpyW(dir_path, container->path);
474 dir_name = dir_path + lstrlenW(dir_path);
475 dir_name[8] = 0;
477 GetSystemTimeAsFileTime(&ft);
479 for(i=0; i<header->dirs_no; ++i) {
480 header->directory_data[i].files_no = 0;
481 for(j=0;; ++j) {
482 ULONGLONG n = ft.dwHighDateTime;
483 int k;
485 /* Generate a file name to attempt to create.
486 * This algorithm will create what will appear
487 * to be random and unrelated directory names
488 * of up to 9 characters in length.
490 n <<= 32;
491 n += ft.dwLowDateTime;
492 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
494 for(k = 0; k < 8; ++k) {
495 int r = (n % 36);
497 /* Dividing by a prime greater than 36 helps
498 * with the appearance of randomness
500 n /= 37;
502 if(r < 10)
503 dir_name[k] = '0' + r;
504 else
505 dir_name[k] = 'A' + (r - 10);
508 if(CreateDirectoryW(dir_path, 0)) {
509 /* The following is OK because we generated an
510 * 8 character directory name made from characters
511 * [A-Z0-9], which are equivalent for all code
512 * pages and for UTF-16
514 for (k = 0; k < 8; ++k)
515 header->directory_data[i].name[k] = dir_name[k];
516 break;
517 }else if(j >= 255) {
518 /* Give up. The most likely cause of this
519 * is a full disk, but whatever the cause
520 * is, it should be more than apparent that
521 * we won't succeed.
523 UnmapViewOfFile(header);
524 CloseHandle(mapping);
525 return GetLastError();
530 UnmapViewOfFile(header);
531 CloseHandle(container->mapping);
532 container->mapping = mapping;
533 container->file_size = file_size;
534 return ERROR_SUCCESS;
537 static BOOL cache_container_is_valid(urlcache_header *header, DWORD file_size)
539 DWORD allocation_size, count_bits, i;
541 if(file_size < FILE_SIZE(MIN_BLOCK_NO))
542 return FALSE;
544 if(file_size != header->size)
545 return FALSE;
547 if (!memcmp(header->signature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1) &&
548 memcmp(header->signature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1))
549 return FALSE;
551 if(FILE_SIZE(header->capacity_in_blocks) != file_size)
552 return FALSE;
554 allocation_size = 0;
555 for(i=0; i<header->capacity_in_blocks/8; i++) {
556 for(count_bits = header->allocation_table[i]; count_bits!=0; count_bits>>=1) {
557 if(count_bits & 1)
558 allocation_size++;
561 if(allocation_size != header->blocks_in_use)
562 return FALSE;
564 for(; i<ALLOCATION_TABLE_SIZE; i++) {
565 if(header->allocation_table[i])
566 return FALSE;
569 return TRUE;
572 /***********************************************************************
573 * cache_container_open_index (Internal)
575 * Opens the index file and saves mapping handle
577 * RETURNS
578 * ERROR_SUCCESS if succeeded
579 * Any other Win32 error code if failed
582 static DWORD cache_container_open_index(cache_container *container, DWORD blocks_no)
584 HANDLE file;
585 WCHAR index_path[MAX_PATH];
586 DWORD file_size;
587 BOOL validate;
589 WaitForSingleObject(container->mutex, INFINITE);
591 if(container->mapping) {
592 ReleaseMutex(container->mutex);
593 return ERROR_SUCCESS;
596 lstrcpyW(index_path, container->path);
597 lstrcatW(index_path, L"index.dat");
599 file = CreateFileW(index_path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
600 if(file == INVALID_HANDLE_VALUE) {
601 /* Maybe the directory wasn't there? Try to create it */
602 if(CreateDirectoryW(container->path, 0))
603 file = CreateFileW(index_path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
605 if(file == INVALID_HANDLE_VALUE) {
606 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(index_path));
607 ReleaseMutex(container->mutex);
608 return GetLastError();
611 file_size = GetFileSize(file, NULL);
612 if(file_size == INVALID_FILE_SIZE) {
613 CloseHandle(file);
614 ReleaseMutex(container->mutex);
615 return GetLastError();
618 if(blocks_no < MIN_BLOCK_NO)
619 blocks_no = MIN_BLOCK_NO;
620 else if(blocks_no > MAX_BLOCK_NO)
621 blocks_no = MAX_BLOCK_NO;
623 if(file_size < FILE_SIZE(blocks_no)) {
624 DWORD ret = cache_container_set_size(container, file, blocks_no);
625 CloseHandle(file);
626 ReleaseMutex(container->mutex);
627 return ret;
630 container->file_size = file_size;
631 container->mapping = cache_container_map_index(file, container->path, file_size, &validate);
632 CloseHandle(file);
633 if(container->mapping && validate) {
634 urlcache_header *header = MapViewOfFile(container->mapping, FILE_MAP_WRITE, 0, 0, 0);
636 if(header && !cache_container_is_valid(header, file_size)) {
637 WARN("detected old or broken index.dat file\n");
638 UnmapViewOfFile(header);
639 FreeUrlCacheSpaceW(container->path, 100, 0);
640 }else if(header) {
641 UnmapViewOfFile(header);
642 }else {
643 CloseHandle(container->mapping);
644 container->mapping = NULL;
648 if(!container->mapping)
650 ERR("Couldn't create file mapping (error is %ld)\n", GetLastError());
651 ReleaseMutex(container->mutex);
652 return GetLastError();
655 ReleaseMutex(container->mutex);
656 return ERROR_SUCCESS;
659 /***********************************************************************
660 * cache_container_close_index (Internal)
662 * Closes the index
664 * RETURNS
665 * nothing
668 static void cache_container_close_index(cache_container *pContainer)
670 CloseHandle(pContainer->mapping);
671 pContainer->mapping = NULL;
674 static BOOL cache_containers_add(const char *cache_prefix, LPCWSTR path,
675 DWORD default_entry_type, LPWSTR mutex_name)
677 cache_container *pContainer = malloc(sizeof(cache_container));
678 int cache_prefix_len = strlen(cache_prefix);
680 if (!pContainer)
682 return FALSE;
685 pContainer->mapping = NULL;
686 pContainer->file_size = 0;
687 pContainer->default_entry_type = default_entry_type;
689 pContainer->path = wcsdup(path);
690 if (!pContainer->path)
692 free(pContainer);
693 return FALSE;
696 pContainer->cache_prefix = malloc(cache_prefix_len+1);
697 if (!pContainer->cache_prefix)
699 free(pContainer->path);
700 free(pContainer);
701 return FALSE;
704 memcpy(pContainer->cache_prefix, cache_prefix, cache_prefix_len+1);
706 CharLowerW(mutex_name);
707 cache_container_create_object_name(mutex_name, '!');
709 if ((pContainer->mutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
711 ERR("couldn't create mutex (error is %ld)\n", GetLastError());
712 free(pContainer->path);
713 free(pContainer);
714 return FALSE;
717 list_add_head(&UrlContainers, &pContainer->entry);
719 return TRUE;
722 static void cache_container_delete_container(cache_container *pContainer)
724 list_remove(&pContainer->entry);
726 cache_container_close_index(pContainer);
727 CloseHandle(pContainer->mutex);
728 free(pContainer->path);
729 free(pContainer->cache_prefix);
730 free(pContainer);
733 static void cache_containers_init(void)
735 static const struct
737 int nFolder; /* CSIDL_* constant */
738 const WCHAR *shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
739 const char *cache_prefix; /* prefix used to reference the container */
740 DWORD default_entry_type;
741 } DefaultContainerData[] =
743 { CSIDL_INTERNET_CACHE, L"Content.IE5", "", NORMAL_CACHE_ENTRY },
744 { CSIDL_HISTORY, L"History.IE5", "Visited:", URLHISTORY_CACHE_ENTRY },
745 { CSIDL_COOKIES, L"", "Cookie:", COOKIE_CACHE_ENTRY },
747 DWORD i;
749 for (i = 0; i < ARRAY_SIZE(DefaultContainerData); i++)
751 WCHAR wszCachePath[MAX_PATH];
752 WCHAR wszMutexName[MAX_PATH];
753 int path_len, suffix_len;
754 BOOL def_char;
756 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
758 ERR("Couldn't get path for default container %lu\n", i);
759 continue;
761 path_len = lstrlenW(wszCachePath);
762 suffix_len = lstrlenW(DefaultContainerData[i].shpath_suffix);
764 if (path_len + suffix_len + 2 > MAX_PATH)
766 ERR("Path too long\n");
767 continue;
770 wszCachePath[path_len] = '\\';
771 wszCachePath[path_len+1] = 0;
773 lstrcpyW(wszMutexName, wszCachePath);
775 if (suffix_len)
777 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
778 wszCachePath[path_len + suffix_len + 1] = '\\';
779 wszCachePath[path_len + suffix_len + 2] = '\0';
782 if (!WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wszCachePath, path_len,
783 NULL, 0, NULL, &def_char) || def_char)
785 WCHAR tmp[MAX_PATH];
787 /* cannot convert path to ANSI code page */
788 if (!(path_len = GetShortPathNameW(wszCachePath, tmp, MAX_PATH)) ||
789 !WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, tmp, path_len,
790 NULL, 0, NULL, &def_char) || def_char)
791 ERR("Can't create container path accessible by ANSI functions\n");
792 else
793 memcpy(wszCachePath, tmp, (path_len+1)*sizeof(WCHAR));
796 cache_containers_add(DefaultContainerData[i].cache_prefix, wszCachePath,
797 DefaultContainerData[i].default_entry_type, wszMutexName);
801 static void cache_containers_free(void)
803 while(!list_empty(&UrlContainers))
804 cache_container_delete_container(
805 LIST_ENTRY(list_head(&UrlContainers), cache_container, entry)
809 static DWORD cache_containers_find(const char *url, cache_container **ret)
811 cache_container *container;
813 TRACE("searching for prefix for URL: %s\n", debugstr_a(url));
815 if(!url)
816 return ERROR_INVALID_PARAMETER;
818 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
820 int prefix_len = strlen(container->cache_prefix);
822 if(!strncmp(container->cache_prefix, url, prefix_len)) {
823 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
824 *ret = container;
825 return ERROR_SUCCESS;
829 ERR("no container found\n");
830 return ERROR_FILE_NOT_FOUND;
833 static BOOL cache_containers_enum(char *search_pattern, DWORD index, cache_container **ret)
835 DWORD i = 0;
836 cache_container *container;
838 TRACE("searching for prefix: %s\n", debugstr_a(search_pattern));
840 /* non-NULL search pattern only returns one container ever */
841 if (search_pattern && index > 0)
842 return FALSE;
844 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
846 if (search_pattern)
848 if (!strcmp(container->cache_prefix, search_pattern))
850 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
851 *ret = container;
852 return TRUE;
855 else
857 if (i == index)
859 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
860 *ret = container;
861 return TRUE;
864 i++;
866 return FALSE;
869 /***********************************************************************
870 * cache_container_lock_index (Internal)
872 * Locks the index for system-wide exclusive access.
874 * RETURNS
875 * Cache file header if successful
876 * NULL if failed and calls SetLastError.
878 static urlcache_header* cache_container_lock_index(cache_container *pContainer)
880 BYTE index;
881 LPVOID pIndexData;
882 urlcache_header* pHeader;
883 DWORD error;
885 /* acquire mutex */
886 WaitForSingleObject(pContainer->mutex, INFINITE);
888 pIndexData = MapViewOfFile(pContainer->mapping, FILE_MAP_WRITE, 0, 0, 0);
890 if (!pIndexData)
892 ReleaseMutex(pContainer->mutex);
893 ERR("Couldn't MapViewOfFile. Error: %ld\n", GetLastError());
894 return NULL;
896 pHeader = (urlcache_header*)pIndexData;
898 /* file has grown - we need to remap to prevent us getting
899 * access violations when we try and access beyond the end
900 * of the memory mapped file */
901 if (pHeader->size != pContainer->file_size)
903 UnmapViewOfFile( pHeader );
904 cache_container_close_index(pContainer);
905 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
906 if (error != ERROR_SUCCESS)
908 ReleaseMutex(pContainer->mutex);
909 SetLastError(error);
910 return NULL;
912 pIndexData = MapViewOfFile(pContainer->mapping, FILE_MAP_WRITE, 0, 0, 0);
914 if (!pIndexData)
916 ReleaseMutex(pContainer->mutex);
917 ERR("Couldn't MapViewOfFile. Error: %ld\n", GetLastError());
918 return NULL;
920 pHeader = (urlcache_header*)pIndexData;
923 TRACE("Signature: %s, file size: %ld bytes\n", pHeader->signature, pHeader->size);
925 for (index = 0; index < pHeader->dirs_no; index++)
927 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].name);
930 return pHeader;
933 /***********************************************************************
934 * cache_container_unlock_index (Internal)
937 static BOOL cache_container_unlock_index(cache_container *pContainer, urlcache_header *pHeader)
939 /* release mutex */
940 ReleaseMutex(pContainer->mutex);
941 return UnmapViewOfFile(pHeader);
944 /***********************************************************************
945 * urlcache_create_file_pathW (Internal)
947 * Copies the full path to the specified buffer given the local file
948 * name and the index of the directory it is in. Always sets value in
949 * lpBufferSize to the required buffer size (in bytes).
951 * RETURNS
952 * TRUE if the buffer was big enough
953 * FALSE if the buffer was too small
956 static BOOL urlcache_create_file_pathW(
957 const cache_container *pContainer,
958 const urlcache_header *pHeader,
959 LPCSTR szLocalFileName,
960 BYTE Directory,
961 LPWSTR wszPath,
962 LPLONG lpBufferSize,
963 BOOL trunc_name)
965 LONG nRequired;
966 int path_len = lstrlenW(pContainer->path);
967 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
968 if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->dirs_no)
970 *lpBufferSize = 0;
971 return FALSE;
974 nRequired = (path_len + file_name_len) * sizeof(WCHAR);
975 if(Directory != CACHE_CONTAINER_NO_SUBDIR)
976 nRequired += (DIR_LENGTH + 1) * sizeof(WCHAR);
977 if (trunc_name && nRequired >= *lpBufferSize)
978 nRequired = *lpBufferSize;
979 if (nRequired <= *lpBufferSize)
981 int dir_len;
983 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
984 if (Directory != CACHE_CONTAINER_NO_SUBDIR)
986 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].name, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
987 wszPath[dir_len + path_len] = '\\';
988 dir_len++;
990 else
992 dir_len = 0;
994 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len,
995 *lpBufferSize/sizeof(WCHAR)-dir_len-path_len);
996 wszPath[*lpBufferSize/sizeof(WCHAR)-1] = 0;
997 *lpBufferSize = nRequired;
998 return TRUE;
1000 *lpBufferSize = nRequired;
1001 return FALSE;
1004 /***********************************************************************
1005 * urlcache_create_file_pathA (Internal)
1007 * Copies the full path to the specified buffer given the local file
1008 * name and the index of the directory it is in. Always sets value in
1009 * lpBufferSize to the required buffer size.
1011 * RETURNS
1012 * TRUE if the buffer was big enough
1013 * FALSE if the buffer was too small
1016 static BOOL urlcache_create_file_pathA(
1017 const cache_container *pContainer,
1018 const urlcache_header *pHeader,
1019 LPCSTR szLocalFileName,
1020 BYTE Directory,
1021 LPSTR szPath,
1022 LPLONG lpBufferSize)
1024 LONG nRequired;
1025 int path_len, file_name_len, dir_len;
1027 if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->dirs_no)
1029 *lpBufferSize = 0;
1030 return FALSE;
1033 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
1034 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
1035 if (Directory!=CACHE_CONTAINER_NO_SUBDIR)
1036 dir_len = DIR_LENGTH+1;
1037 else
1038 dir_len = 0;
1040 nRequired = (path_len + dir_len + file_name_len) * sizeof(char);
1041 if (nRequired <= *lpBufferSize)
1043 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
1044 if(dir_len) {
1045 memcpy(szPath+path_len, pHeader->directory_data[Directory].name, dir_len-1);
1046 szPath[path_len + dir_len-1] = '\\';
1048 memcpy(szPath + path_len + dir_len, szLocalFileName, file_name_len);
1049 *lpBufferSize = nRequired;
1050 return TRUE;
1052 *lpBufferSize = nRequired;
1053 return FALSE;
1056 /* Just like FileTimeToDosDateTime, except that it also maps the special
1057 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1059 static void file_time_to_dos_date_time(const FILETIME *ft, WORD *fatdate,
1060 WORD *fattime)
1062 if (!ft->dwLowDateTime && !ft->dwHighDateTime)
1063 *fatdate = *fattime = 0;
1064 else
1065 FileTimeToDosDateTime(ft, fatdate, fattime);
1068 /***********************************************************************
1069 * urlcache_delete_file (Internal)
1071 static DWORD urlcache_delete_file(const cache_container *container,
1072 urlcache_header *header, entry_url *url_entry)
1074 WIN32_FILE_ATTRIBUTE_DATA attr;
1075 WCHAR path[MAX_PATH];
1076 LONG path_size = sizeof(path);
1077 DWORD err;
1078 WORD date, time;
1080 if(!url_entry->local_name_off)
1081 goto succ;
1083 if(!urlcache_create_file_pathW(container, header,
1084 (LPCSTR)url_entry+url_entry->local_name_off,
1085 url_entry->cache_dir, path, &path_size, FALSE))
1086 goto succ;
1088 if(!GetFileAttributesExW(path, GetFileExInfoStandard, &attr))
1089 goto succ;
1090 file_time_to_dos_date_time(&attr.ftLastWriteTime, &date, &time);
1091 if(date != url_entry->write_date || time != url_entry->write_time)
1092 goto succ;
1094 err = (DeleteFileW(path) ? ERROR_SUCCESS : GetLastError());
1095 if(err == ERROR_ACCESS_DENIED || err == ERROR_SHARING_VIOLATION)
1096 return err;
1098 succ:
1099 if (url_entry->cache_dir < header->dirs_no)
1101 if (header->directory_data[url_entry->cache_dir].files_no)
1102 header->directory_data[url_entry->cache_dir].files_no--;
1104 if (url_entry->cache_entry_type & STICKY_CACHE_ENTRY)
1106 if (url_entry->size.QuadPart < header->exempt_usage.QuadPart)
1107 header->exempt_usage.QuadPart -= url_entry->size.QuadPart;
1108 else
1109 header->exempt_usage.QuadPart = 0;
1111 else
1113 if (url_entry->size.QuadPart < header->cache_usage.QuadPart)
1114 header->cache_usage.QuadPart -= url_entry->size.QuadPart;
1115 else
1116 header->cache_usage.QuadPart = 0;
1119 return ERROR_SUCCESS;
1122 static BOOL urlcache_clean_leaked_entries(cache_container *container, urlcache_header *header)
1124 DWORD *leak_off;
1125 BOOL freed = FALSE;
1127 leak_off = &header->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
1128 while(*leak_off) {
1129 entry_url *url_entry = (entry_url*)((LPBYTE)header + *leak_off);
1131 if(SUCCEEDED(urlcache_delete_file(container, header, url_entry))) {
1132 *leak_off = url_entry->exempt_delta;
1133 urlcache_entry_free(header, &url_entry->header);
1134 freed = TRUE;
1135 }else {
1136 leak_off = &url_entry->exempt_delta;
1140 return freed;
1143 /***********************************************************************
1144 * cache_container_clean_index (Internal)
1146 * This function is meant to make place in index file by removing leaked
1147 * files entries and resizing the file.
1149 * CAUTION: file view may get mapped to new memory
1151 * RETURNS
1152 * ERROR_SUCCESS when new memory is available
1153 * error code otherwise
1155 static DWORD cache_container_clean_index(cache_container *container, urlcache_header **file_view)
1157 urlcache_header *header = *file_view;
1158 DWORD ret;
1160 TRACE("(%s %s)\n", debugstr_a(container->cache_prefix), debugstr_w(container->path));
1162 if(urlcache_clean_leaked_entries(container, header))
1163 return ERROR_SUCCESS;
1165 if(header->size >= ALLOCATION_TABLE_SIZE*8*BLOCKSIZE + ENTRY_START_OFFSET) {
1166 WARN("index file has maximal size\n");
1167 return ERROR_NOT_ENOUGH_MEMORY;
1170 cache_container_close_index(container);
1171 ret = cache_container_open_index(container, header->capacity_in_blocks*2);
1172 if(ret != ERROR_SUCCESS)
1173 return ret;
1174 header = MapViewOfFile(container->mapping, FILE_MAP_WRITE, 0, 0, 0);
1175 if(!header)
1176 return GetLastError();
1178 UnmapViewOfFile(*file_view);
1179 *file_view = header;
1180 return ERROR_SUCCESS;
1183 /* Just like DosDateTimeToFileTime, except that it also maps the special
1184 * case of a DOS date/time of (0,0) to a filetime of (0,0).
1186 static void dos_date_time_to_file_time(WORD fatdate, WORD fattime,
1187 FILETIME *ft)
1189 if (!fatdate && !fattime)
1190 ft->dwLowDateTime = ft->dwHighDateTime = 0;
1191 else
1192 DosDateTimeToFileTime(fatdate, fattime, ft);
1195 static int urlcache_decode_url(const char *url, WCHAR *decoded_url, int decoded_len)
1197 URL_COMPONENTSA uc;
1198 DWORD len, part_len;
1199 WCHAR *host_name;
1201 memset(&uc, 0, sizeof(uc));
1202 uc.dwStructSize = sizeof(uc);
1203 uc.dwHostNameLength = 1;
1204 if(!InternetCrackUrlA(url, 0, 0, &uc))
1205 uc.nScheme = INTERNET_SCHEME_UNKNOWN;
1207 if(uc.nScheme!=INTERNET_SCHEME_HTTP && uc.nScheme!=INTERNET_SCHEME_HTTPS)
1208 return MultiByteToWideChar(CP_UTF8, 0, url, -1, decoded_url, decoded_len);
1210 if(!decoded_url)
1211 decoded_len = 0;
1213 len = MultiByteToWideChar(CP_UTF8, 0, url, uc.lpszHostName-url, decoded_url, decoded_len);
1214 if(!len)
1215 return 0;
1216 if(decoded_url)
1217 decoded_len -= len;
1219 host_name = malloc(uc.dwHostNameLength * sizeof(WCHAR));
1220 if(!host_name)
1221 return 0;
1222 if(!MultiByteToWideChar(CP_UTF8, 0, uc.lpszHostName, uc.dwHostNameLength,
1223 host_name, uc.dwHostNameLength)) {
1224 free(host_name);
1225 return 0;
1227 part_len = IdnToUnicode(0, host_name, uc.dwHostNameLength,
1228 decoded_url ? decoded_url+len : NULL, decoded_len);
1229 free(host_name);
1230 if(!part_len) {
1231 SetLastError(ERROR_INTERNET_INVALID_URL);
1232 return 0;
1234 len += part_len;
1235 if(decoded_url)
1236 decoded_len -= part_len;
1238 part_len = MultiByteToWideChar(CP_UTF8, 0,
1239 uc.lpszHostName+uc.dwHostNameLength,
1240 -1, decoded_url ? decoded_url+len : NULL, decoded_len);
1241 if(!part_len)
1242 return 0;
1243 len += part_len;
1245 return len;
1248 /***********************************************************************
1249 * urlcache_copy_entry (Internal)
1251 * Copies an entry from the cache index file to the Win32 structure
1253 * RETURNS
1254 * ERROR_SUCCESS if the buffer was big enough
1255 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1258 static DWORD urlcache_copy_entry(cache_container *container, const urlcache_header *header,
1259 INTERNET_CACHE_ENTRY_INFOA *entry_info, DWORD *info_size, const entry_url *url_entry, BOOL unicode)
1261 int url_len;
1262 DWORD size = sizeof(*entry_info);
1264 if(*info_size >= size) {
1265 entry_info->lpHeaderInfo = NULL;
1266 entry_info->lpszFileExtension = NULL;
1267 entry_info->lpszLocalFileName = NULL;
1268 entry_info->lpszSourceUrlName = NULL;
1269 entry_info->CacheEntryType = url_entry->cache_entry_type;
1270 entry_info->dwExemptDelta = url_entry->exempt_delta;
1271 entry_info->dwHeaderInfoSize = url_entry->header_info_size;
1272 entry_info->dwHitRate = url_entry->hit_rate;
1273 entry_info->dwSizeHigh = url_entry->size.u.HighPart;
1274 entry_info->dwSizeLow = url_entry->size.u.LowPart;
1275 entry_info->dwStructSize = sizeof(*entry_info);
1276 entry_info->dwUseCount = url_entry->use_count;
1277 dos_date_time_to_file_time(url_entry->expire_date, url_entry->expire_time, &entry_info->ExpireTime);
1278 entry_info->LastAccessTime = url_entry->access_time;
1279 entry_info->LastModifiedTime = url_entry->modification_time;
1280 dos_date_time_to_file_time(url_entry->sync_date, url_entry->sync_time, &entry_info->LastSyncTime);
1283 if(unicode)
1284 url_len = urlcache_decode_url((const char*)url_entry+url_entry->url_off, NULL, 0);
1285 else
1286 url_len = strlen((LPCSTR)url_entry+url_entry->url_off) + 1;
1287 size += url_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1289 if(*info_size >= size) {
1290 DWORD url_size = url_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1292 entry_info->lpszSourceUrlName = (LPSTR)entry_info+size-url_size;
1293 if(unicode)
1294 urlcache_decode_url((const char*)url_entry+url_entry->url_off, (WCHAR*)entry_info->lpszSourceUrlName, url_len);
1295 else
1296 memcpy(entry_info->lpszSourceUrlName, (LPCSTR)url_entry+url_entry->url_off, url_size);
1299 if(size%4 && size<*info_size)
1300 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1301 size = DWORD_ALIGN(size);
1303 if(url_entry->local_name_off) {
1304 LONG file_name_size;
1305 LPSTR file_name;
1306 file_name = (LPSTR)entry_info+size;
1307 file_name_size = *info_size-size;
1308 if((unicode && urlcache_create_file_pathW(container, header, (LPCSTR)url_entry+url_entry->local_name_off,
1309 url_entry->cache_dir, (LPWSTR)file_name, &file_name_size, FALSE)) ||
1310 (!unicode && urlcache_create_file_pathA(container, header, (LPCSTR)url_entry+url_entry->local_name_off,
1311 url_entry->cache_dir, file_name, &file_name_size))) {
1312 entry_info->lpszLocalFileName = file_name;
1314 size += file_name_size;
1316 if(size%4 && size<*info_size)
1317 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1318 size = DWORD_ALIGN(size);
1321 if(url_entry->header_info_off) {
1322 DWORD header_len;
1324 if(unicode)
1325 header_len = MultiByteToWideChar(CP_UTF8, 0, (const char*)url_entry+url_entry->header_info_off,
1326 url_entry->header_info_size, NULL, 0);
1327 else
1328 header_len = url_entry->header_info_size;
1329 size += header_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1331 if(*info_size >= size) {
1332 DWORD header_size = header_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1333 entry_info->lpHeaderInfo = (LPBYTE)entry_info+size-header_size;
1334 if(unicode)
1335 MultiByteToWideChar(CP_UTF8, 0, (const char*)url_entry+url_entry->header_info_off,
1336 url_entry->header_info_size, (LPWSTR)entry_info->lpHeaderInfo, header_len);
1337 else
1338 memcpy(entry_info->lpHeaderInfo, (LPCSTR)url_entry+url_entry->header_info_off, header_len);
1340 if(size%4 && size<*info_size)
1341 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1342 size = DWORD_ALIGN(size);
1345 if(url_entry->file_extension_off) {
1346 int ext_len;
1348 if(unicode)
1349 ext_len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)url_entry+url_entry->file_extension_off, -1, NULL, 0);
1350 else
1351 ext_len = strlen((LPCSTR)url_entry+url_entry->file_extension_off) + 1;
1352 size += ext_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1354 if(*info_size >= size) {
1355 DWORD ext_size = ext_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1356 entry_info->lpszFileExtension = (LPSTR)entry_info+size-ext_size;
1357 if(unicode)
1358 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)url_entry+url_entry->file_extension_off, -1, (LPWSTR)entry_info->lpszFileExtension, ext_len);
1359 else
1360 memcpy(entry_info->lpszFileExtension, (LPCSTR)url_entry+url_entry->file_extension_off, ext_len*sizeof(CHAR));
1363 if(size%4 && size<*info_size)
1364 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1365 size = DWORD_ALIGN(size);
1368 if(size > *info_size) {
1369 *info_size = size;
1370 return ERROR_INSUFFICIENT_BUFFER;
1372 *info_size = size;
1373 return ERROR_SUCCESS;
1376 /***********************************************************************
1377 * urlcache_set_entry_info (Internal)
1379 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1380 * according to the flags set by field_control.
1382 * RETURNS
1383 * ERROR_SUCCESS if the buffer was big enough
1384 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1387 static DWORD urlcache_set_entry_info(entry_url *url_entry, const INTERNET_CACHE_ENTRY_INFOA *entry_info, DWORD field_control)
1389 if (field_control & CACHE_ENTRY_ACCTIME_FC)
1390 url_entry->access_time = entry_info->LastAccessTime;
1391 if (field_control & CACHE_ENTRY_ATTRIBUTE_FC)
1392 url_entry->cache_entry_type = entry_info->CacheEntryType;
1393 if (field_control & CACHE_ENTRY_EXEMPT_DELTA_FC)
1394 url_entry->exempt_delta = entry_info->dwExemptDelta;
1395 if (field_control & CACHE_ENTRY_EXPTIME_FC)
1396 file_time_to_dos_date_time(&entry_info->ExpireTime, &url_entry->expire_date, &url_entry->expire_time);
1397 if (field_control & CACHE_ENTRY_HEADERINFO_FC)
1398 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1399 if (field_control & CACHE_ENTRY_HITRATE_FC)
1400 url_entry->hit_rate = entry_info->dwHitRate;
1401 if (field_control & CACHE_ENTRY_MODTIME_FC)
1402 url_entry->modification_time = entry_info->LastModifiedTime;
1403 if (field_control & CACHE_ENTRY_SYNCTIME_FC)
1404 file_time_to_dos_date_time(&entry_info->LastAccessTime, &url_entry->sync_date, &url_entry->sync_time);
1406 return ERROR_SUCCESS;
1409 /***********************************************************************
1410 * urlcache_hash_key (Internal)
1412 * Returns the hash key for a given string
1414 * RETURNS
1415 * hash key for the string
1418 static DWORD urlcache_hash_key(LPCSTR lpszKey)
1420 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1421 * but the algorithm and result are not the same!
1423 static const unsigned char lookupTable[256] =
1425 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1426 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1427 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1428 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1429 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1430 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1431 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1432 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1433 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1434 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1435 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1436 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1437 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1438 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1439 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1440 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1441 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1442 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1443 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1444 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1445 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1446 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1447 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1448 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1449 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1450 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1451 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1452 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1453 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1454 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1455 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1456 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1458 BYTE key[4];
1459 DWORD i;
1461 for (i = 0; i < ARRAY_SIZE(key); i++)
1462 key[i] = lookupTable[(*lpszKey + i) & 0xFF];
1464 for (lpszKey++; *lpszKey; lpszKey++)
1466 for (i = 0; i < ARRAY_SIZE(key); i++)
1467 key[i] = lookupTable[*lpszKey ^ key[i]];
1470 return *(DWORD *)key;
1473 static inline entry_hash_table* urlcache_get_hash_table(const urlcache_header *pHeader, DWORD dwOffset)
1475 if(!dwOffset)
1476 return NULL;
1477 return (entry_hash_table*)((LPBYTE)pHeader + dwOffset);
1480 static BOOL urlcache_find_hash_entry(const urlcache_header *pHeader, LPCSTR lpszUrl, struct hash_entry **ppHashEntry)
1482 /* structure of hash table:
1483 * 448 entries divided into 64 blocks
1484 * each block therefore contains a chain of 7 key/offset pairs
1485 * how position in table is calculated:
1486 * 1. the url is hashed in helper function
1487 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1488 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1490 * note:
1491 * there can be multiple hash tables in the file and the offset to
1492 * the next one is stored in the header of the hash table
1494 DWORD key = urlcache_hash_key(lpszUrl);
1495 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1496 entry_hash_table* pHashEntry;
1497 DWORD id = 0;
1499 key >>= HASHTABLE_FLAG_BITS;
1501 for (pHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1502 pHashEntry; pHashEntry = urlcache_get_hash_table(pHeader, pHashEntry->next))
1504 int i;
1505 if (pHashEntry->id != id++)
1507 ERR("Error: not right hash table number (%ld) expected %ld\n", pHashEntry->id, id);
1508 continue;
1510 /* make sure that it is in fact a hash entry */
1511 if (pHashEntry->header.signature != HASH_SIGNATURE)
1513 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->header.signature);
1514 continue;
1517 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1519 struct hash_entry *pHashElement = &pHashEntry->hash_table[offset + i];
1520 if (key == pHashElement->key>>HASHTABLE_FLAG_BITS)
1522 /* FIXME: we should make sure that this is the right element
1523 * before returning and claiming that it is. We can do this
1524 * by doing a simple compare between the URL we were given
1525 * and the URL stored in the entry. However, this assumes
1526 * we know the format of all the entries stored in the
1527 * hash table */
1528 *ppHashEntry = pHashElement;
1529 return TRUE;
1533 return FALSE;
1536 /***********************************************************************
1537 * urlcache_hash_entry_set_flags (Internal)
1539 * Sets special bits in hash key
1541 * RETURNS
1542 * nothing
1545 static void urlcache_hash_entry_set_flags(struct hash_entry *pHashEntry, DWORD dwFlag)
1547 pHashEntry->key = (pHashEntry->key >> HASHTABLE_FLAG_BITS << HASHTABLE_FLAG_BITS) | dwFlag;
1550 /***********************************************************************
1551 * urlcache_hash_entry_delete (Internal)
1553 * Searches all the hash tables in the index for the given URL and
1554 * then if found deletes the entry.
1556 * RETURNS
1557 * TRUE if the entry was found
1558 * FALSE if the entry could not be found
1561 static BOOL urlcache_hash_entry_delete(struct hash_entry *pHashEntry)
1563 pHashEntry->key = HASHTABLE_DEL;
1564 return TRUE;
1567 /***********************************************************************
1568 * urlcache_hash_entry_create (Internal)
1570 * Searches all the hash tables for a free slot based on the offset
1571 * generated from the hash key. If a free slot is found, the offset and
1572 * key are entered into the hash table.
1574 * RETURNS
1575 * ERROR_SUCCESS if the entry was added
1576 * Any other Win32 error code if the entry could not be added
1579 static DWORD urlcache_hash_entry_create(urlcache_header *pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry, DWORD dwFieldType)
1581 /* see urlcache_find_hash_entry for structure of hash tables */
1583 DWORD key = urlcache_hash_key(lpszUrl);
1584 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1585 entry_hash_table* pHashEntry, *pHashPrev = NULL;
1586 DWORD id = 0;
1587 DWORD error;
1589 key = ((key >> HASHTABLE_FLAG_BITS) << HASHTABLE_FLAG_BITS) + dwFieldType;
1591 for (pHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1592 pHashEntry; pHashEntry = urlcache_get_hash_table(pHeader, pHashEntry->next))
1594 int i;
1595 pHashPrev = pHashEntry;
1597 if (pHashEntry->id != id++)
1599 ERR("not right hash table number (%ld) expected %ld\n", pHashEntry->id, id);
1600 break;
1602 /* make sure that it is in fact a hash entry */
1603 if (pHashEntry->header.signature != HASH_SIGNATURE)
1605 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->header.signature);
1606 break;
1609 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1611 struct hash_entry *pHashElement = &pHashEntry->hash_table[offset + i];
1612 if (pHashElement->key==HASHTABLE_FREE || pHashElement->key==HASHTABLE_DEL) /* if the slot is free */
1614 pHashElement->key = key;
1615 pHashElement->offset = dwOffsetEntry;
1616 return ERROR_SUCCESS;
1620 error = urlcache_create_hash_table(pHeader, pHashPrev, &pHashEntry);
1621 if (error != ERROR_SUCCESS)
1622 return error;
1624 pHashEntry->hash_table[offset].key = key;
1625 pHashEntry->hash_table[offset].offset = dwOffsetEntry;
1626 return ERROR_SUCCESS;
1629 /***********************************************************************
1630 * urlcache_enum_hash_tables (Internal)
1632 * Enumerates the hash tables in a container.
1634 * RETURNS
1635 * TRUE if an entry was found
1636 * FALSE if there are no more tables to enumerate.
1639 static BOOL urlcache_enum_hash_tables(const urlcache_header *pHeader, DWORD *id, entry_hash_table **ppHashEntry)
1641 for (*ppHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1642 *ppHashEntry; *ppHashEntry = urlcache_get_hash_table(pHeader, (*ppHashEntry)->next))
1644 TRACE("looking at hash table number %ld\n", (*ppHashEntry)->id);
1645 if ((*ppHashEntry)->id != *id)
1646 continue;
1647 /* make sure that it is in fact a hash entry */
1648 if ((*ppHashEntry)->header.signature != HASH_SIGNATURE)
1650 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->header.signature);
1651 (*id)++;
1652 continue;
1655 TRACE("hash table number %ld found\n", *id);
1656 return TRUE;
1658 return FALSE;
1661 /***********************************************************************
1662 * urlcache_enum_hash_table_entries (Internal)
1664 * Enumerates entries in a hash table and returns the next non-free entry.
1666 * RETURNS
1667 * TRUE if an entry was found
1668 * FALSE if the hash table is empty or there are no more entries to
1669 * enumerate.
1672 static BOOL urlcache_enum_hash_table_entries(const urlcache_header *pHeader, const entry_hash_table *pHashEntry,
1673 DWORD * index, const struct hash_entry **ppHashEntry)
1675 for (; *index < HASHTABLE_SIZE ; (*index)++)
1677 if (pHashEntry->hash_table[*index].key==HASHTABLE_FREE || pHashEntry->hash_table[*index].key==HASHTABLE_DEL)
1678 continue;
1680 *ppHashEntry = &pHashEntry->hash_table[*index];
1681 TRACE("entry found %ld\n", *index);
1682 return TRUE;
1684 TRACE("no more entries (%ld)\n", *index);
1685 return FALSE;
1688 /***********************************************************************
1689 * cache_container_delete_dir (Internal)
1691 * Erase a directory containing an URL cache.
1693 * RETURNS
1694 * TRUE success, FALSE failure/aborted.
1697 static BOOL cache_container_delete_dir(LPCWSTR lpszPath)
1699 DWORD path_len;
1700 WCHAR path[MAX_PATH + 1];
1701 SHFILEOPSTRUCTW shfos;
1702 int ret;
1704 path_len = lstrlenW(lpszPath);
1705 if (path_len >= MAX_PATH)
1706 return FALSE;
1707 lstrcpyW(path, lpszPath);
1708 path[path_len + 1] = 0; /* double-NUL-terminate path */
1710 shfos.hwnd = NULL;
1711 shfos.wFunc = FO_DELETE;
1712 shfos.pFrom = path;
1713 shfos.pTo = NULL;
1714 shfos.fFlags = FOF_NOCONFIRMATION;
1715 shfos.fAnyOperationsAborted = FALSE;
1716 ret = SHFileOperationW(&shfos);
1717 if (ret)
1718 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path), ret);
1719 return !(ret || shfos.fAnyOperationsAborted);
1722 /***********************************************************************
1723 * urlcache_hash_entry_is_locked (Internal)
1725 * Checks if entry is locked. Unlocks it if possible.
1727 static BOOL urlcache_hash_entry_is_locked(struct hash_entry *hash_entry, entry_url *url_entry)
1729 FILETIME cur_time;
1730 ULARGE_INTEGER acc_time, time;
1732 if ((hash_entry->key & ((1<<HASHTABLE_FLAG_BITS)-1)) != HASHTABLE_LOCK)
1733 return FALSE;
1735 GetSystemTimeAsFileTime(&cur_time);
1736 time.u.LowPart = cur_time.dwLowDateTime;
1737 time.u.HighPart = cur_time.dwHighDateTime;
1739 acc_time.u.LowPart = url_entry->access_time.dwLowDateTime;
1740 acc_time.u.HighPart = url_entry->access_time.dwHighDateTime;
1742 time.QuadPart -= acc_time.QuadPart;
1744 /* check if entry was locked for at least a day */
1745 if(time.QuadPart > (ULONGLONG)24*60*60*FILETIME_SECOND) {
1746 urlcache_hash_entry_set_flags(hash_entry, HASHTABLE_URL);
1747 url_entry->use_count = 0;
1748 return FALSE;
1751 return TRUE;
1754 static BOOL urlcache_get_entry_info(const char *url, void *entry_info,
1755 DWORD *size, DWORD flags, BOOL unicode)
1757 urlcache_header *header;
1758 struct hash_entry *hash_entry;
1759 const entry_url *url_entry;
1760 cache_container *container;
1761 DWORD error;
1763 TRACE("(%s, %p, %p, %lx, %x)\n", debugstr_a(url), entry_info, size, flags, unicode);
1765 if(flags & ~GET_INSTALLED_ENTRY)
1766 FIXME("ignoring unsupported flags: %lx\n", flags);
1768 error = cache_containers_find(url, &container);
1769 if(error != ERROR_SUCCESS) {
1770 SetLastError(error);
1771 return FALSE;
1774 error = cache_container_open_index(container, MIN_BLOCK_NO);
1775 if(error != ERROR_SUCCESS) {
1776 SetLastError(error);
1777 return FALSE;
1780 if(!(header = cache_container_lock_index(container)))
1781 return FALSE;
1783 if(!urlcache_find_hash_entry(header, url, &hash_entry)) {
1784 cache_container_unlock_index(container, header);
1785 WARN("entry %s not found!\n", debugstr_a(url));
1786 SetLastError(ERROR_FILE_NOT_FOUND);
1787 return FALSE;
1790 url_entry = (const entry_url*)((LPBYTE)header + hash_entry->offset);
1791 if(url_entry->header.signature != URL_SIGNATURE) {
1792 FIXME("Trying to retrieve entry of unknown format %s\n",
1793 debugstr_an((LPCSTR)&url_entry->header.signature, sizeof(DWORD)));
1794 cache_container_unlock_index(container, header);
1795 SetLastError(ERROR_FILE_NOT_FOUND);
1796 return FALSE;
1799 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->url_off));
1800 TRACE("Header info: %s\n", debugstr_an((LPCSTR)url_entry +
1801 url_entry->header_info_off, url_entry->header_info_size));
1803 if((flags & GET_INSTALLED_ENTRY) && !(url_entry->cache_entry_type & INSTALLED_CACHE_ENTRY)) {
1804 cache_container_unlock_index(container, header);
1805 SetLastError(ERROR_FILE_NOT_FOUND);
1806 return FALSE;
1809 if(size) {
1810 if(!entry_info)
1811 *size = 0;
1813 error = urlcache_copy_entry(container, header, entry_info, size, url_entry, unicode);
1814 if(error != ERROR_SUCCESS) {
1815 cache_container_unlock_index(container, header);
1816 SetLastError(error);
1817 return FALSE;
1819 if(url_entry->local_name_off)
1820 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->local_name_off));
1823 cache_container_unlock_index(container, header);
1824 return TRUE;
1827 /***********************************************************************
1828 * GetUrlCacheEntryInfoExA (WININET.@)
1831 BOOL WINAPI GetUrlCacheEntryInfoExA(LPCSTR lpszUrl,
1832 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1833 LPDWORD lpdwCacheEntryInfoBufSize, LPSTR lpszReserved,
1834 LPDWORD lpdwReserved, LPVOID lpReserved, DWORD dwFlags)
1836 if(lpszReserved!=NULL || lpdwReserved!=NULL || lpReserved!=NULL) {
1837 ERR("Reserved value was not 0\n");
1838 SetLastError(ERROR_INVALID_PARAMETER);
1839 return FALSE;
1842 return urlcache_get_entry_info(lpszUrl, lpCacheEntryInfo,
1843 lpdwCacheEntryInfoBufSize, dwFlags, FALSE);
1846 /***********************************************************************
1847 * GetUrlCacheEntryInfoA (WININET.@)
1850 BOOL WINAPI GetUrlCacheEntryInfoA(LPCSTR lpszUrlName,
1851 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1852 LPDWORD lpdwCacheEntryInfoBufferSize)
1854 return GetUrlCacheEntryInfoExA(lpszUrlName, lpCacheEntryInfo,
1855 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
1858 static int urlcache_encode_url(const WCHAR *url, char *encoded_url, int encoded_len)
1860 URL_COMPONENTSW uc;
1861 DWORD len, part_len;
1862 WCHAR *punycode;
1864 TRACE("%s\n", debugstr_w(url));
1866 memset(&uc, 0, sizeof(uc));
1867 uc.dwStructSize = sizeof(uc);
1868 uc.dwHostNameLength = 1;
1869 if(!InternetCrackUrlW(url, 0, 0, &uc))
1870 uc.nScheme = INTERNET_SCHEME_UNKNOWN;
1872 if(uc.nScheme!=INTERNET_SCHEME_HTTP && uc.nScheme!=INTERNET_SCHEME_HTTPS)
1873 return WideCharToMultiByte(CP_UTF8, 0, url, -1, encoded_url, encoded_len, NULL, NULL);
1875 len = WideCharToMultiByte(CP_UTF8, 0, url, uc.lpszHostName-url,
1876 encoded_url, encoded_len, NULL, NULL);
1877 if(!len)
1878 return 0;
1879 if(encoded_url)
1880 encoded_len -= len;
1882 part_len = IdnToAscii(0, uc.lpszHostName, uc.dwHostNameLength, NULL, 0);
1883 if(!part_len) {
1884 SetLastError(ERROR_INTERNET_INVALID_URL);
1885 return 0;
1888 punycode = malloc(part_len * sizeof(WCHAR));
1889 if(!punycode)
1890 return 0;
1892 part_len = IdnToAscii(0, uc.lpszHostName, uc.dwHostNameLength, punycode, part_len);
1893 if(!part_len) {
1894 free(punycode);
1895 return 0;
1898 part_len = WideCharToMultiByte(CP_UTF8, 0, punycode, part_len,
1899 encoded_url ? encoded_url+len : NULL, encoded_len, NULL, NULL);
1900 free(punycode);
1901 if(!part_len)
1902 return 0;
1903 if(encoded_url)
1904 encoded_len -= part_len;
1905 len += part_len;
1907 part_len = WideCharToMultiByte(CP_UTF8, 0, uc.lpszHostName+uc.dwHostNameLength,
1908 -1, encoded_url ? encoded_url+len : NULL, encoded_len, NULL, NULL);
1909 if(!part_len)
1910 return 0;
1911 len += part_len;
1913 TRACE("got (%ld)%s\n", len, debugstr_a(encoded_url));
1914 return len;
1917 static BOOL urlcache_encode_url_alloc(const WCHAR *url, char **encoded_url)
1919 DWORD encoded_len;
1920 char *ret;
1922 encoded_len = urlcache_encode_url(url, NULL, 0);
1923 if(!encoded_len)
1924 return FALSE;
1926 ret = malloc(encoded_len);
1927 if(!ret)
1928 return FALSE;
1930 encoded_len = urlcache_encode_url(url, ret, encoded_len);
1931 if(!encoded_len) {
1932 free(ret);
1933 return FALSE;
1936 *encoded_url = ret;
1937 return TRUE;
1940 /***********************************************************************
1941 * GetUrlCacheEntryInfoExW (WININET.@)
1944 BOOL WINAPI GetUrlCacheEntryInfoExW(LPCWSTR lpszUrl,
1945 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1946 LPDWORD lpdwCacheEntryInfoBufSize, LPWSTR lpszReserved,
1947 LPDWORD lpdwReserved, LPVOID lpReserved, DWORD dwFlags)
1949 char *url;
1950 BOOL ret;
1952 if(lpszReserved!=NULL || lpdwReserved!=NULL || lpReserved!=NULL) {
1953 ERR("Reserved value was not 0\n");
1954 SetLastError(ERROR_INVALID_PARAMETER);
1955 return FALSE;
1958 /* Ignore GET_INSTALLED_ENTRY flag in unicode version of function */
1959 dwFlags &= ~GET_INSTALLED_ENTRY;
1961 if(!urlcache_encode_url_alloc(lpszUrl, &url))
1962 return FALSE;
1964 ret = urlcache_get_entry_info(url, lpCacheEntryInfo,
1965 lpdwCacheEntryInfoBufSize, dwFlags, TRUE);
1966 free(url);
1967 return ret;
1970 /***********************************************************************
1971 * GetUrlCacheEntryInfoW (WININET.@)
1974 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1975 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1976 LPDWORD lpdwCacheEntryInfoBufferSize)
1978 return GetUrlCacheEntryInfoExW(lpszUrl, lpCacheEntryInfo,
1979 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
1982 /***********************************************************************
1983 * SetUrlCacheEntryInfoA (WININET.@)
1985 BOOL WINAPI SetUrlCacheEntryInfoA(LPCSTR lpszUrlName,
1986 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1987 DWORD dwFieldControl)
1989 urlcache_header *pHeader;
1990 struct hash_entry *pHashEntry;
1991 entry_header *pEntry;
1992 cache_container *pContainer;
1993 DWORD error;
1995 TRACE("(%s, %p, 0x%08lx)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
1997 error = cache_containers_find(lpszUrlName, &pContainer);
1998 if (error != ERROR_SUCCESS)
2000 SetLastError(error);
2001 return FALSE;
2004 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2005 if (error != ERROR_SUCCESS)
2007 SetLastError(error);
2008 return FALSE;
2011 if (!(pHeader = cache_container_lock_index(pContainer)))
2012 return FALSE;
2014 if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
2016 cache_container_unlock_index(pContainer, pHeader);
2017 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
2018 SetLastError(ERROR_FILE_NOT_FOUND);
2019 return FALSE;
2022 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2023 if (pEntry->signature != URL_SIGNATURE)
2025 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2026 cache_container_unlock_index(pContainer, pHeader);
2027 SetLastError(ERROR_FILE_NOT_FOUND);
2028 return FALSE;
2031 urlcache_set_entry_info((entry_url*)pEntry, lpCacheEntryInfo, dwFieldControl);
2033 cache_container_unlock_index(pContainer, pHeader);
2035 return TRUE;
2038 /***********************************************************************
2039 * SetUrlCacheEntryInfoW (WININET.@)
2041 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
2042 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2043 DWORD dwFieldControl)
2045 char *url;
2046 BOOL ret;
2048 if(!urlcache_encode_url_alloc(lpszUrl, &url))
2049 return FALSE;
2051 ret = SetUrlCacheEntryInfoA(url, (INTERNET_CACHE_ENTRY_INFOA*)lpCacheEntryInfo, dwFieldControl);
2052 free(url);
2053 return ret;
2056 static BOOL urlcache_entry_get_file(const char *url, void *entry_info, DWORD *size, BOOL unicode)
2058 urlcache_header *header;
2059 struct hash_entry *hash_entry;
2060 entry_url *url_entry;
2061 cache_container *container;
2062 DWORD error;
2064 TRACE("(%s, %p, %p, %x)\n", debugstr_a(url), entry_info, size, unicode);
2066 if(!url || !size || (!entry_info && *size)) {
2067 SetLastError(ERROR_INVALID_PARAMETER);
2068 return FALSE;
2071 error = cache_containers_find(url, &container);
2072 if(error != ERROR_SUCCESS) {
2073 SetLastError(error);
2074 return FALSE;
2077 error = cache_container_open_index(container, MIN_BLOCK_NO);
2078 if (error != ERROR_SUCCESS) {
2079 SetLastError(error);
2080 return FALSE;
2083 if (!(header = cache_container_lock_index(container)))
2084 return FALSE;
2086 if (!urlcache_find_hash_entry(header, url, &hash_entry)) {
2087 cache_container_unlock_index(container, header);
2088 TRACE("entry %s not found!\n", debugstr_a(url));
2089 SetLastError(ERROR_FILE_NOT_FOUND);
2090 return FALSE;
2093 url_entry = (entry_url*)((LPBYTE)header + hash_entry->offset);
2094 if(url_entry->header.signature != URL_SIGNATURE) {
2095 FIXME("Trying to retrieve entry of unknown format %s\n",
2096 debugstr_an((LPSTR)&url_entry->header.signature, sizeof(DWORD)));
2097 cache_container_unlock_index(container, header);
2098 SetLastError(ERROR_FILE_NOT_FOUND);
2099 return FALSE;
2102 if(!url_entry->local_name_off) {
2103 cache_container_unlock_index(container, header);
2104 SetLastError(ERROR_INVALID_DATA);
2105 return FALSE;
2108 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->url_off));
2109 TRACE("Header info: %s\n", debugstr_an((LPCSTR)url_entry + url_entry->header_info_off,
2110 url_entry->header_info_size));
2112 error = urlcache_copy_entry(container, header, entry_info,
2113 size, url_entry, unicode);
2114 if(error != ERROR_SUCCESS) {
2115 cache_container_unlock_index(container, header);
2116 SetLastError(error);
2117 return FALSE;
2119 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->local_name_off));
2121 url_entry->hit_rate++;
2122 url_entry->use_count++;
2123 urlcache_hash_entry_set_flags(hash_entry, HASHTABLE_LOCK);
2124 GetSystemTimeAsFileTime(&url_entry->access_time);
2126 cache_container_unlock_index(container, header);
2128 return TRUE;
2131 /***********************************************************************
2132 * RetrieveUrlCacheEntryFileA (WININET.@)
2135 BOOL WINAPI RetrieveUrlCacheEntryFileA(LPCSTR lpszUrlName,
2136 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2137 LPDWORD lpdwCacheEntryInfoBufferSize, DWORD dwReserved)
2139 return urlcache_entry_get_file(lpszUrlName, lpCacheEntryInfo,
2140 lpdwCacheEntryInfoBufferSize, FALSE);
2143 /***********************************************************************
2144 * RetrieveUrlCacheEntryFileW (WININET.@)
2147 BOOL WINAPI RetrieveUrlCacheEntryFileW(LPCWSTR lpszUrlName,
2148 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2149 LPDWORD lpdwCacheEntryInfoBufferSize, DWORD dwReserved)
2151 char *url;
2152 BOOL ret;
2154 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
2155 return FALSE;
2157 ret = urlcache_entry_get_file(url, lpCacheEntryInfo,
2158 lpdwCacheEntryInfoBufferSize, TRUE);
2159 free(url);
2160 return ret;
2163 static BOOL urlcache_entry_delete(const cache_container *pContainer,
2164 urlcache_header *pHeader, struct hash_entry *pHashEntry)
2166 entry_header *pEntry;
2167 entry_url * pUrlEntry;
2169 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2170 if (pEntry->signature != URL_SIGNATURE)
2172 FIXME("Trying to delete entry of unknown format %s\n",
2173 debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
2174 SetLastError(ERROR_FILE_NOT_FOUND);
2175 return FALSE;
2178 pUrlEntry = (entry_url *)pEntry;
2179 if(urlcache_hash_entry_is_locked(pHashEntry, pUrlEntry))
2181 TRACE("Trying to delete locked entry\n");
2182 pUrlEntry->cache_entry_type |= PENDING_DELETE_CACHE_ENTRY;
2183 SetLastError(ERROR_SHARING_VIOLATION);
2184 return FALSE;
2187 if(!urlcache_delete_file(pContainer, pHeader, pUrlEntry))
2189 urlcache_entry_free(pHeader, pEntry);
2191 else
2193 /* Add entry to leaked files list */
2194 pUrlEntry->header.signature = LEAK_SIGNATURE;
2195 pUrlEntry->exempt_delta = pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
2196 pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET] = pHashEntry->offset;
2199 urlcache_hash_entry_delete(pHashEntry);
2200 return TRUE;
2203 static HANDLE free_cache_running;
2204 static HANDLE dll_unload_event;
2205 static DWORD WINAPI handle_full_cache_worker(void *param)
2207 FreeUrlCacheSpaceW(NULL, 20, 0);
2208 ReleaseSemaphore(free_cache_running, 1, NULL);
2209 return 0;
2212 static void handle_full_cache(void)
2214 if(WaitForSingleObject(free_cache_running, 0) == WAIT_OBJECT_0) {
2215 if(!QueueUserWorkItem(handle_full_cache_worker, NULL, 0))
2216 ReleaseSemaphore(free_cache_running, 1, NULL);
2220 /* Enumerates entries in cache, allows cache unlocking between calls. */
2221 static BOOL urlcache_next_entry(urlcache_header *header, DWORD *hash_table_off, DWORD *hash_table_entry,
2222 struct hash_entry **hash_entry, entry_header **entry)
2224 entry_hash_table *hashtable_entry;
2226 *hash_entry = NULL;
2227 *entry = NULL;
2229 if(!*hash_table_off) {
2230 *hash_table_off = header->hash_table_off;
2231 *hash_table_entry = 0;
2233 hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2234 }else {
2235 if(*hash_table_off >= header->size) {
2236 *hash_table_off = 0;
2237 return FALSE;
2240 hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2243 if(hashtable_entry->header.signature != HASH_SIGNATURE) {
2244 *hash_table_off = 0;
2245 return FALSE;
2248 while(1) {
2249 if(*hash_table_entry >= HASHTABLE_SIZE) {
2250 *hash_table_off = hashtable_entry->next;
2251 if(!*hash_table_off) {
2252 *hash_table_off = 0;
2253 return FALSE;
2256 hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2257 *hash_table_entry = 0;
2260 if(hashtable_entry->hash_table[*hash_table_entry].key != HASHTABLE_DEL &&
2261 hashtable_entry->hash_table[*hash_table_entry].key != HASHTABLE_FREE) {
2262 *hash_entry = &hashtable_entry->hash_table[*hash_table_entry];
2263 *entry = (entry_header*)((LPBYTE)header + hashtable_entry->hash_table[*hash_table_entry].offset);
2264 (*hash_table_entry)++;
2265 return TRUE;
2268 (*hash_table_entry)++;
2271 *hash_table_off = 0;
2272 return FALSE;
2275 /* Rates an urlcache entry to determine if it can be deleted.
2277 * Score 0 means that entry can safely be removed, the bigger rating
2278 * the smaller chance of entry being removed.
2279 * DWORD_MAX means that entry can't be deleted at all.
2281 * Rating system is currently not fully compatible with native implementation.
2283 static DWORD urlcache_rate_entry(entry_url *url_entry, FILETIME *cur_time)
2285 ULARGE_INTEGER time, access_time;
2286 DWORD rating;
2288 access_time.u.LowPart = url_entry->access_time.dwLowDateTime;
2289 access_time.u.HighPart = url_entry->access_time.dwHighDateTime;
2291 time.u.LowPart = cur_time->dwLowDateTime;
2292 time.u.HighPart = cur_time->dwHighDateTime;
2294 /* Don't touch entries that were added less than 10 minutes ago */
2295 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)10*60*FILETIME_SECOND)
2296 return -1;
2298 if(url_entry->cache_entry_type & STICKY_CACHE_ENTRY)
2299 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)url_entry->exempt_delta*FILETIME_SECOND)
2300 return -1;
2302 time.QuadPart = (time.QuadPart-access_time.QuadPart)/FILETIME_SECOND;
2303 rating = 400*60*60*24/(60*60*24+time.QuadPart);
2305 if(url_entry->hit_rate > 100)
2306 rating += 100;
2307 else
2308 rating += url_entry->hit_rate;
2310 return rating;
2313 static int __cdecl dword_cmp(const void *p1, const void *p2)
2315 return *(const DWORD*)p1 - *(const DWORD*)p2;
2318 /***********************************************************************
2319 * FreeUrlCacheSpaceW (WININET.@)
2321 * Frees up some cache.
2323 * PARAMETERS
2324 * cache_path [I] Which volume to free up from, or NULL if you don't care.
2325 * size [I] Percentage of the cache that should be free.
2326 * filter [I] Which entries can't be deleted (CacheEntryType)
2328 * RETURNS
2329 * TRUE success. FALSE failure.
2331 * IMPLEMENTATION
2332 * This implementation just retrieves the path of the cache directory, and
2333 * deletes its contents from the filesystem. The correct approach would
2334 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
2336 BOOL WINAPI FreeUrlCacheSpaceW(LPCWSTR cache_path, DWORD size, DWORD filter)
2338 cache_container *container;
2339 DWORD path_len, err;
2341 TRACE("(%s, %lx, %lx)\n", debugstr_w(cache_path), size, filter);
2343 if(size<1 || size>100) {
2344 SetLastError(ERROR_INVALID_PARAMETER);
2345 return FALSE;
2348 if(cache_path) {
2349 path_len = lstrlenW(cache_path);
2350 if(cache_path[path_len-1] == '\\')
2351 path_len--;
2352 }else {
2353 path_len = 0;
2356 if(size==100 && !filter) {
2357 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
2359 /* When cache_path==NULL only clean Temporary Internet Files */
2360 if((!path_len && container->cache_prefix[0]==0) ||
2361 (path_len && !wcsnicmp(container->path, cache_path, path_len) &&
2362 (container->path[path_len]=='\0' || container->path[path_len]=='\\')))
2364 BOOL ret_del;
2366 WaitForSingleObject(container->mutex, INFINITE);
2368 /* unlock, delete, recreate and lock cache */
2369 cache_container_close_index(container);
2370 ret_del = cache_container_delete_dir(container->path);
2371 err = cache_container_open_index(container, MIN_BLOCK_NO);
2373 ReleaseMutex(container->mutex);
2374 if(!ret_del || (err != ERROR_SUCCESS))
2375 return FALSE;
2379 return TRUE;
2382 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
2384 urlcache_header *header;
2385 struct hash_entry *hash_entry;
2386 entry_header *entry;
2387 entry_url *url_entry;
2388 ULONGLONG desired_size, cur_size;
2389 DWORD delete_factor, hash_table_off, hash_table_entry;
2390 DWORD rate[100], rate_no;
2391 FILETIME cur_time;
2393 if((path_len || container->cache_prefix[0]!=0) &&
2394 (!path_len || wcsnicmp(container->path, cache_path, path_len) ||
2395 (container->path[path_len]!='\0' && container->path[path_len]!='\\')))
2396 continue;
2398 err = cache_container_open_index(container, MIN_BLOCK_NO);
2399 if(err != ERROR_SUCCESS)
2400 continue;
2402 header = cache_container_lock_index(container);
2403 if(!header)
2404 continue;
2406 urlcache_clean_leaked_entries(container, header);
2408 desired_size = header->cache_limit.QuadPart*(100-size)/100;
2409 cur_size = header->cache_usage.QuadPart+header->exempt_usage.QuadPart;
2410 if(cur_size <= desired_size)
2411 delete_factor = 0;
2412 else
2413 delete_factor = (cur_size-desired_size)*100/cur_size;
2415 if(!delete_factor) {
2416 cache_container_unlock_index(container, header);
2417 continue;
2420 hash_table_off = 0;
2421 hash_table_entry = 0;
2422 rate_no = 0;
2423 GetSystemTimeAsFileTime(&cur_time);
2424 while(rate_no < ARRAY_SIZE(rate) &&
2425 urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2426 if(entry->signature != URL_SIGNATURE) {
2427 WARN("only url entries are currently supported\n");
2428 continue;
2431 url_entry = (entry_url*)entry;
2432 if(url_entry->cache_entry_type & filter)
2433 continue;
2435 rate[rate_no] = urlcache_rate_entry(url_entry, &cur_time);
2436 if(rate[rate_no] != -1)
2437 rate_no++;
2440 if(!rate_no) {
2441 TRACE("nothing to delete\n");
2442 cache_container_unlock_index(container, header);
2443 continue;
2446 qsort(rate, rate_no, sizeof(DWORD), dword_cmp);
2448 delete_factor = delete_factor*rate_no/100;
2449 delete_factor = rate[delete_factor];
2450 TRACE("deleting files with rating %ld or less\n", delete_factor);
2452 hash_table_off = 0;
2453 while(urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2454 if(entry->signature != URL_SIGNATURE)
2455 continue;
2457 url_entry = (entry_url*)entry;
2458 if(url_entry->cache_entry_type & filter)
2459 continue;
2461 if(urlcache_rate_entry(url_entry, &cur_time) <= delete_factor) {
2462 TRACE("deleting file: %s\n", debugstr_a((char*)url_entry+url_entry->local_name_off));
2463 urlcache_entry_delete(container, header, hash_entry);
2465 if(header->cache_usage.QuadPart+header->exempt_usage.QuadPart <= desired_size)
2466 break;
2468 /* Allow other threads to use cache while cleaning */
2469 cache_container_unlock_index(container, header);
2470 if(WaitForSingleObject(dll_unload_event, 0) == WAIT_OBJECT_0) {
2471 TRACE("got dll_unload_event - finishing\n");
2472 return TRUE;
2474 Sleep(0);
2475 header = cache_container_lock_index(container);
2479 TRACE("cache size after cleaning 0x%s/0x%s\n",
2480 wine_dbgstr_longlong(header->cache_usage.QuadPart+header->exempt_usage.QuadPart),
2481 wine_dbgstr_longlong(header->cache_limit.QuadPart));
2482 cache_container_unlock_index(container, header);
2485 return TRUE;
2488 /***********************************************************************
2489 * FreeUrlCacheSpaceA (WININET.@)
2491 * See FreeUrlCacheSpaceW.
2493 BOOL WINAPI FreeUrlCacheSpaceA(LPCSTR lpszCachePath, DWORD dwSize, DWORD dwFilter)
2495 BOOL ret = FALSE;
2496 WCHAR *path = strdupAtoW(lpszCachePath);
2497 if (lpszCachePath == NULL || path != NULL)
2498 ret = FreeUrlCacheSpaceW(path, dwSize, dwFilter);
2499 free(path);
2500 return ret;
2503 /***********************************************************************
2504 * UnlockUrlCacheEntryFileA (WININET.@)
2507 BOOL WINAPI UnlockUrlCacheEntryFileA(LPCSTR lpszUrlName, DWORD dwReserved)
2509 urlcache_header *pHeader;
2510 struct hash_entry *pHashEntry;
2511 entry_header *pEntry;
2512 entry_url * pUrlEntry;
2513 cache_container *pContainer;
2514 DWORD error;
2516 TRACE("(%s, 0x%08lx)\n", debugstr_a(lpszUrlName), dwReserved);
2518 if (dwReserved)
2520 ERR("dwReserved != 0\n");
2521 SetLastError(ERROR_INVALID_PARAMETER);
2522 return FALSE;
2525 error = cache_containers_find(lpszUrlName, &pContainer);
2526 if (error != ERROR_SUCCESS)
2528 SetLastError(error);
2529 return FALSE;
2532 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2533 if (error != ERROR_SUCCESS)
2535 SetLastError(error);
2536 return FALSE;
2539 if (!(pHeader = cache_container_lock_index(pContainer)))
2540 return FALSE;
2542 if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
2544 cache_container_unlock_index(pContainer, pHeader);
2545 TRACE("entry %s not found!\n", debugstr_a(lpszUrlName));
2546 SetLastError(ERROR_FILE_NOT_FOUND);
2547 return FALSE;
2550 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2551 if (pEntry->signature != URL_SIGNATURE)
2553 cache_container_unlock_index(pContainer, pHeader);
2554 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2555 SetLastError(ERROR_FILE_NOT_FOUND);
2556 return FALSE;
2559 pUrlEntry = (entry_url *)pEntry;
2561 if (pUrlEntry->use_count == 0)
2563 cache_container_unlock_index(pContainer, pHeader);
2564 return FALSE;
2566 pUrlEntry->use_count--;
2567 if (!pUrlEntry->use_count)
2569 urlcache_hash_entry_set_flags(pHashEntry, HASHTABLE_URL);
2570 if (pUrlEntry->cache_entry_type & PENDING_DELETE_CACHE_ENTRY)
2571 urlcache_entry_delete(pContainer, pHeader, pHashEntry);
2574 cache_container_unlock_index(pContainer, pHeader);
2576 return TRUE;
2579 /***********************************************************************
2580 * UnlockUrlCacheEntryFileW (WININET.@)
2583 BOOL WINAPI UnlockUrlCacheEntryFileW(LPCWSTR lpszUrlName, DWORD dwReserved)
2585 char *url;
2586 BOOL ret;
2588 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
2589 return FALSE;
2591 ret = UnlockUrlCacheEntryFileA(url, dwReserved);
2592 free(url);
2593 return ret;
2596 static BOOL urlcache_entry_create(const char *url, const char *ext, WCHAR *full_path)
2598 cache_container *container;
2599 urlcache_header *header;
2600 char file_name[MAX_PATH];
2601 WCHAR extW[MAX_PATH];
2602 BYTE cache_dir;
2603 LONG full_path_len, ext_len = 0;
2604 BOOL generate_name = FALSE;
2605 DWORD error;
2606 HANDLE file;
2607 FILETIME ft;
2608 URL_COMPONENTSA uc;
2609 int i;
2611 TRACE("(%s, %s, %p)\n", debugstr_a(url), debugstr_a(ext), full_path);
2613 memset(&uc, 0, sizeof(uc));
2614 uc.dwStructSize = sizeof(uc);
2615 uc.dwUrlPathLength = 1;
2616 uc.dwExtraInfoLength = 1;
2617 if(!InternetCrackUrlA(url, 0, 0, &uc))
2618 uc.dwUrlPathLength = 0;
2620 if(!uc.dwUrlPathLength) {
2621 file_name[0] = 0;
2622 }else {
2623 char *p, *e;
2625 p = e = uc.lpszUrlPath+uc.dwUrlPathLength;
2626 while(p>uc.lpszUrlPath && *(p-1)!='/' && *(p-1)!='\\' && *(p-1)!='.')
2627 p--;
2628 if(p>uc.lpszUrlPath && *(p-1)=='.') {
2629 e = p-1;
2630 while(p>uc.lpszUrlPath && *(p-1)!='/' && *(p-1)!='\\')
2631 p--;
2634 if(e-p >= MAX_PATH)
2635 e = p+MAX_PATH-1;
2636 memcpy(file_name, p, e-p);
2637 file_name[e-p] = 0;
2639 for(p=file_name; *p; p++) {
2640 switch(*p) {
2641 case '<': case '>':
2642 case ':': case '"':
2643 case '|': case '?':
2644 case '*':
2645 *p = '_'; break;
2646 default: break;
2651 if(!file_name[0])
2652 generate_name = TRUE;
2654 error = cache_containers_find(url, &container);
2655 if(error != ERROR_SUCCESS) {
2656 SetLastError(error);
2657 return FALSE;
2660 error = cache_container_open_index(container, MIN_BLOCK_NO);
2661 if(error != ERROR_SUCCESS) {
2662 SetLastError(error);
2663 return FALSE;
2666 if(!(header = cache_container_lock_index(container)))
2667 return FALSE;
2669 if(header->dirs_no)
2670 cache_dir = (BYTE)(rand() % header->dirs_no);
2671 else
2672 cache_dir = CACHE_CONTAINER_NO_SUBDIR;
2674 full_path_len = MAX_PATH * sizeof(WCHAR);
2675 if(!urlcache_create_file_pathW(container, header, file_name, cache_dir, full_path, &full_path_len, TRUE)) {
2676 WARN("Failed to get full path for filename %s, needed %lu bytes.\n",
2677 debugstr_a(file_name), full_path_len);
2678 cache_container_unlock_index(container, header);
2679 return FALSE;
2681 full_path_len = full_path_len/sizeof(WCHAR) - 1;
2683 cache_container_unlock_index(container, header);
2685 if(ext) {
2686 WCHAR *p;
2688 extW[0] = '.';
2689 ext_len = MultiByteToWideChar(CP_ACP, 0, ext, -1, extW+1, MAX_PATH-1);
2691 for(p=extW; *p; p++) {
2692 switch(*p) {
2693 case '<': case '>':
2694 case ':': case '"':
2695 case '|': case '?':
2696 case '*':
2697 *p = '_'; break;
2698 default: break;
2701 if(p[-1]==' ' || p[-1]=='.')
2702 p[-1] = '_';
2703 }else {
2704 extW[0] = '\0';
2707 if(!generate_name && full_path_len+5+ext_len>=MAX_PATH) { /* strlen("[255]") = 5 */
2708 full_path_len = MAX_PATH-5-ext_len-1;
2711 for(i=0; i<255 && !generate_name; i++) {
2712 wsprintfW(full_path+full_path_len, L"[%u]%s", i, extW);
2714 TRACE("Trying: %s\n", debugstr_w(full_path));
2715 file = CreateFileW(full_path, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2716 if(file != INVALID_HANDLE_VALUE) {
2717 CloseHandle(file);
2718 return TRUE;
2722 if(full_path_len+8+ext_len >= MAX_PATH)
2723 full_path_len = MAX_PATH-8-ext_len-1;
2725 /* Try to generate random name */
2726 GetSystemTimeAsFileTime(&ft);
2727 lstrcpyW(full_path+full_path_len+8, extW);
2729 for(i=0; i<255; i++) {
2730 int j;
2731 ULONGLONG n = ft.dwHighDateTime;
2732 n <<= 32;
2733 n += ft.dwLowDateTime;
2734 n ^= (ULONGLONG)i<<48;
2736 for(j=0; j<8; j++) {
2737 int r = (n % 36);
2738 n /= 37;
2739 full_path[full_path_len+j] = (r < 10 ? '0' + r : 'A' + r - 10);
2742 TRACE("Trying: %s\n", debugstr_w(full_path));
2743 file = CreateFileW(full_path, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2744 if(file != INVALID_HANDLE_VALUE) {
2745 CloseHandle(file);
2746 return TRUE;
2750 WARN("Could not find a unique filename\n");
2751 return FALSE;
2754 /***********************************************************************
2755 * CreateUrlCacheEntryA (WININET.@)
2758 BOOL WINAPI CreateUrlCacheEntryA(LPCSTR lpszUrlName, DWORD dwExpectedFileSize,
2759 LPCSTR lpszFileExtension, LPSTR lpszFileName, DWORD dwReserved)
2761 WCHAR file_name[MAX_PATH];
2763 if(dwReserved)
2764 FIXME("dwReserved 0x%08lx\n", dwReserved);
2766 if(!urlcache_entry_create(lpszUrlName, lpszFileExtension, file_name))
2767 return FALSE;
2769 if(!WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL))
2770 return FALSE;
2771 return TRUE;
2773 /***********************************************************************
2774 * CreateUrlCacheEntryW (WININET.@)
2777 BOOL WINAPI CreateUrlCacheEntryW(LPCWSTR lpszUrlName, DWORD dwExpectedFileSize,
2778 LPCWSTR lpszFileExtension, LPWSTR lpszFileName, DWORD dwReserved)
2780 char *url, *ext = NULL;
2781 BOOL ret;
2783 if(dwReserved)
2784 FIXME("dwReserved 0x%08lx\n", dwReserved);
2786 if(lpszFileExtension) {
2787 ext = strdupWtoUTF8(lpszFileExtension);
2788 if(!ext)
2789 return FALSE;
2792 if(!urlcache_encode_url_alloc(lpszUrlName, &url)) {
2793 free(ext);
2794 return FALSE;
2797 ret = urlcache_entry_create(url, ext, lpszFileName);
2798 free(ext);
2799 free(url);
2800 return ret;
2803 static BOOL urlcache_entry_commit(const char *url, const WCHAR *file_name,
2804 FILETIME expire_time, FILETIME modify_time, DWORD entry_type,
2805 BYTE *header_info, DWORD header_size, const char *file_ext,
2806 const char *original_url)
2808 cache_container *container;
2809 urlcache_header *header;
2810 struct hash_entry *hash_entry;
2811 entry_header *entry;
2812 entry_url *url_entry;
2813 DWORD url_entry_offset;
2814 DWORD size = DWORD_ALIGN(sizeof(*url_entry));
2815 DWORD file_name_off = 0;
2816 DWORD header_info_off = 0;
2817 DWORD file_ext_off = 0;
2818 WIN32_FILE_ATTRIBUTE_DATA file_attr;
2819 LARGE_INTEGER file_size;
2820 BYTE dir_id;
2821 char file_name_no_container[MAX_PATH];
2822 char *local_file_name = 0;
2823 DWORD hit_rate = 0;
2824 DWORD exempt_delta = 0;
2825 DWORD error;
2827 TRACE("(%s, %s, ..., ..., %lx, %p, %ld, %s, %s)\n", debugstr_a(url), debugstr_w(file_name),
2828 entry_type, header_info, header_size, debugstr_a(file_ext), debugstr_a(original_url));
2830 if(entry_type & STICKY_CACHE_ENTRY && !file_name) {
2831 SetLastError(ERROR_INVALID_PARAMETER);
2832 return FALSE;
2834 if(original_url)
2835 WARN(": original_url ignored\n");
2837 memset(&file_attr, 0, sizeof(file_attr));
2838 if(file_name) {
2839 if(!GetFileAttributesExW(file_name, GetFileExInfoStandard, &file_attr))
2840 return FALSE;
2842 file_size.u.LowPart = file_attr.nFileSizeLow;
2843 file_size.u.HighPart = file_attr.nFileSizeHigh;
2845 error = cache_containers_find(url, &container);
2846 if(error != ERROR_SUCCESS) {
2847 SetLastError(error);
2848 return FALSE;
2851 error = cache_container_open_index(container, MIN_BLOCK_NO);
2852 if(error != ERROR_SUCCESS) {
2853 SetLastError(error);
2854 return FALSE;
2857 if(!(header = cache_container_lock_index(container)))
2858 return FALSE;
2860 if(urlcache_find_hash_entry(header, url, &hash_entry)) {
2861 entry_url *url_entry = (entry_url*)((LPBYTE)header + hash_entry->offset);
2863 if(urlcache_hash_entry_is_locked(hash_entry, url_entry)) {
2864 TRACE("Trying to overwrite locked entry\n");
2865 cache_container_unlock_index(container, header);
2866 SetLastError(ERROR_SHARING_VIOLATION);
2867 return FALSE;
2870 hit_rate = url_entry->hit_rate;
2871 exempt_delta = url_entry->exempt_delta;
2872 urlcache_entry_delete(container, header, hash_entry);
2875 if(header->dirs_no)
2876 dir_id = 0;
2877 else
2878 dir_id = CACHE_CONTAINER_NO_SUBDIR;
2880 if(file_name) {
2881 BOOL bFound = FALSE;
2883 if(wcsncmp(file_name, container->path, lstrlenW(container->path))) {
2884 ERR("path %s must begin with cache content path %s\n", debugstr_w(file_name), debugstr_w(container->path));
2885 cache_container_unlock_index(container, header);
2886 SetLastError(ERROR_INVALID_PARAMETER);
2887 return FALSE;
2890 /* skip container path prefix */
2891 file_name += lstrlenW(container->path);
2893 WideCharToMultiByte(CP_ACP, 0, file_name, -1, file_name_no_container, MAX_PATH, NULL, NULL);
2894 local_file_name = file_name_no_container;
2896 if(header->dirs_no) {
2897 for(dir_id = 0; dir_id < header->dirs_no; dir_id++) {
2898 if(!strncmp(header->directory_data[dir_id].name, local_file_name, DIR_LENGTH)) {
2899 bFound = TRUE;
2900 break;
2904 if(!bFound) {
2905 ERR("cache directory not found in path %s\n", debugstr_w(file_name));
2906 cache_container_unlock_index(container, header);
2907 SetLastError(ERROR_INVALID_PARAMETER);
2908 return FALSE;
2911 file_name += DIR_LENGTH + 1;
2912 local_file_name += DIR_LENGTH + 1;
2916 size = DWORD_ALIGN(size + strlen(url) + 1);
2917 if(file_name) {
2918 file_name_off = size;
2919 size = DWORD_ALIGN(size + strlen(local_file_name) + 1);
2921 if(header_info && header_size) {
2922 header_info_off = size;
2923 size = DWORD_ALIGN(size + header_size);
2925 if(file_ext && (file_ext_off = strlen(file_ext))) {
2926 DWORD len = file_ext_off;
2928 file_ext_off = size;
2929 size = DWORD_ALIGN(size + len + 1);
2932 /* round up to next block */
2933 if(size % BLOCKSIZE) {
2934 size -= size % BLOCKSIZE;
2935 size += BLOCKSIZE;
2938 error = urlcache_entry_alloc(header, size / BLOCKSIZE, &entry);
2939 while(error == ERROR_HANDLE_DISK_FULL) {
2940 error = cache_container_clean_index(container, &header);
2941 if(error == ERROR_SUCCESS)
2942 error = urlcache_entry_alloc(header, size / BLOCKSIZE, &entry);
2944 if(error != ERROR_SUCCESS) {
2945 cache_container_unlock_index(container, header);
2946 SetLastError(error);
2947 return FALSE;
2950 /* FindFirstFreeEntry fills in blocks used */
2951 url_entry = (entry_url *)entry;
2952 url_entry_offset = (LPBYTE)url_entry - (LPBYTE)header;
2953 url_entry->header.signature = URL_SIGNATURE;
2954 url_entry->cache_dir = dir_id;
2955 url_entry->cache_entry_type = entry_type | container->default_entry_type;
2956 url_entry->header_info_size = header_size;
2957 if((entry_type & STICKY_CACHE_ENTRY) && !exempt_delta) {
2958 /* Sticky entries have a default exempt time of one day */
2959 exempt_delta = 86400;
2961 url_entry->exempt_delta = exempt_delta;
2962 url_entry->hit_rate = hit_rate+1;
2963 url_entry->file_extension_off = file_ext_off;
2964 url_entry->header_info_off = header_info_off;
2965 url_entry->local_name_off = file_name_off;
2966 url_entry->url_off = DWORD_ALIGN(sizeof(*url_entry));
2967 url_entry->size.QuadPart = file_size.QuadPart;
2968 url_entry->use_count = 0;
2969 GetSystemTimeAsFileTime(&url_entry->access_time);
2970 url_entry->modification_time = modify_time;
2971 file_time_to_dos_date_time(&url_entry->access_time, &url_entry->sync_date, &url_entry->sync_time);
2972 file_time_to_dos_date_time(&expire_time, &url_entry->expire_date, &url_entry->expire_time);
2973 file_time_to_dos_date_time(&file_attr.ftLastWriteTime, &url_entry->write_date, &url_entry->write_time);
2975 /*** Unknowns ***/
2976 url_entry->unk1 = 0;
2977 url_entry->unk2 = 0;
2978 url_entry->unk3 = 0x60;
2979 url_entry->unk4 = 0;
2980 url_entry->unk5 = 0x1010;
2981 url_entry->unk7 = 0;
2982 url_entry->unk8 = 0;
2985 strcpy((LPSTR)url_entry + url_entry->url_off, url);
2986 if(file_name_off)
2987 strcpy((LPSTR)((LPBYTE)url_entry + file_name_off), local_file_name);
2988 if(header_info_off)
2989 memcpy((LPBYTE)url_entry + header_info_off, header_info, header_size);
2990 if(file_ext_off)
2991 strcpy((LPSTR)((LPBYTE)url_entry + file_ext_off), file_ext);
2993 error = urlcache_hash_entry_create(header, url, url_entry_offset, HASHTABLE_URL);
2994 while(error == ERROR_HANDLE_DISK_FULL) {
2995 error = cache_container_clean_index(container, &header);
2996 if(error == ERROR_SUCCESS) {
2997 url_entry = (entry_url *)((LPBYTE)header + url_entry_offset);
2998 error = urlcache_hash_entry_create(header, url,
2999 url_entry_offset, HASHTABLE_URL);
3002 if(error != ERROR_SUCCESS) {
3003 urlcache_entry_free(header, &url_entry->header);
3004 cache_container_unlock_index(container, header);
3005 SetLastError(error);
3006 return FALSE;
3009 if(url_entry->cache_dir < header->dirs_no)
3010 header->directory_data[url_entry->cache_dir].files_no++;
3011 if(entry_type & STICKY_CACHE_ENTRY)
3012 header->exempt_usage.QuadPart += file_size.QuadPart;
3013 else
3014 header->cache_usage.QuadPart += file_size.QuadPart;
3015 if(header->cache_usage.QuadPart+header->exempt_usage.QuadPart > header->cache_limit.QuadPart)
3016 handle_full_cache();
3018 cache_container_unlock_index(container, header);
3019 return TRUE;
3022 /***********************************************************************
3023 * CommitUrlCacheEntryA (WININET.@)
3025 BOOL WINAPI CommitUrlCacheEntryA(LPCSTR lpszUrlName, LPCSTR lpszLocalFileName,
3026 FILETIME ExpireTime, FILETIME LastModifiedTime, DWORD CacheEntryType,
3027 LPBYTE lpHeaderInfo, DWORD dwHeaderSize, LPCSTR lpszFileExtension, LPCSTR lpszOriginalUrl)
3029 WCHAR *file_name = NULL;
3030 BOOL ret;
3032 if(lpszLocalFileName) {
3033 file_name = strdupAtoW(lpszLocalFileName);
3034 if(!file_name)
3035 return FALSE;
3038 ret = urlcache_entry_commit(lpszUrlName, file_name, ExpireTime, LastModifiedTime,
3039 CacheEntryType, lpHeaderInfo, dwHeaderSize, lpszFileExtension, lpszOriginalUrl);
3040 free(file_name);
3041 return ret;
3044 /***********************************************************************
3045 * CommitUrlCacheEntryW (WININET.@)
3047 BOOL WINAPI CommitUrlCacheEntryW(LPCWSTR lpszUrlName, LPCWSTR lpszLocalFileName,
3048 FILETIME ExpireTime, FILETIME LastModifiedTime, DWORD CacheEntryType,
3049 LPWSTR lpHeaderInfo, DWORD dwHeaderSize, LPCWSTR lpszFileExtension, LPCWSTR lpszOriginalUrl)
3051 char *url, *original_url=NULL, *file_ext=NULL, *header_info=NULL;
3052 BOOL ret;
3054 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
3055 return FALSE;
3057 if(lpHeaderInfo) {
3058 header_info = strdupWtoUTF8(lpHeaderInfo);
3059 if(!header_info) {
3060 free(url);
3061 return FALSE;
3063 dwHeaderSize = strlen(header_info);
3066 if(lpszFileExtension) {
3067 file_ext = strdupWtoA(lpszFileExtension);
3068 if(!file_ext) {
3069 free(url);
3070 free(header_info);
3071 return FALSE;
3075 if(lpszOriginalUrl && !urlcache_encode_url_alloc(lpszOriginalUrl, &original_url)) {
3076 free(url);
3077 free(header_info);
3078 free(file_ext);
3079 return FALSE;
3082 ret = urlcache_entry_commit(url, lpszLocalFileName, ExpireTime, LastModifiedTime,
3083 CacheEntryType, (BYTE*)header_info, dwHeaderSize, file_ext, original_url);
3084 free(url);
3085 free(header_info);
3086 free(file_ext);
3087 free(original_url);
3088 return ret;
3091 /***********************************************************************
3092 * ReadUrlCacheEntryStream (WININET.@)
3095 BOOL WINAPI ReadUrlCacheEntryStream(
3096 IN HANDLE hUrlCacheStream,
3097 IN DWORD dwLocation,
3098 IN OUT LPVOID lpBuffer,
3099 IN OUT LPDWORD lpdwLen,
3100 IN DWORD dwReserved
3103 /* Get handle to file from 'stream' */
3104 stream_handle *pStream = (stream_handle*)hUrlCacheStream;
3106 if (dwReserved != 0)
3108 ERR("dwReserved != 0\n");
3109 SetLastError(ERROR_INVALID_PARAMETER);
3110 return FALSE;
3113 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->url, INTERNET_MAX_URL_LENGTH))
3115 SetLastError(ERROR_INVALID_HANDLE);
3116 return FALSE;
3119 if (SetFilePointer(pStream->file, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
3120 return FALSE;
3121 return ReadFile(pStream->file, lpBuffer, *lpdwLen, lpdwLen, NULL);
3124 /***********************************************************************
3125 * RetrieveUrlCacheEntryStreamA (WININET.@)
3128 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(LPCSTR lpszUrlName,
3129 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
3130 LPDWORD lpdwCacheEntryInfoBufferSize, BOOL fRandomRead, DWORD dwReserved)
3132 /* NOTE: this is not the same as the way that the native
3133 * version allocates 'stream' handles. I did it this way
3134 * as it is much easier and no applications should depend
3135 * on this behaviour. (Native version appears to allocate
3136 * indices into a table)
3138 stream_handle *stream;
3139 HANDLE file;
3141 TRACE("(%s, %p, %p, %x, 0x%08lx)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
3142 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved);
3144 if(!RetrieveUrlCacheEntryFileA(lpszUrlName, lpCacheEntryInfo,
3145 lpdwCacheEntryInfoBufferSize, dwReserved))
3146 return NULL;
3148 file = CreateFileA(lpCacheEntryInfo->lpszLocalFileName, GENERIC_READ, FILE_SHARE_READ,
3149 NULL, OPEN_EXISTING, fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0, NULL);
3150 if(file == INVALID_HANDLE_VALUE) {
3151 UnlockUrlCacheEntryFileA(lpszUrlName, 0);
3152 return NULL;
3155 /* allocate handle storage space */
3156 stream = malloc(sizeof(stream_handle) + strlen(lpszUrlName) * sizeof(CHAR));
3157 if(!stream) {
3158 CloseHandle(file);
3159 UnlockUrlCacheEntryFileA(lpszUrlName, 0);
3160 SetLastError(ERROR_OUTOFMEMORY);
3161 return NULL;
3164 stream->file = file;
3165 strcpy(stream->url, lpszUrlName);
3166 return stream;
3169 /***********************************************************************
3170 * RetrieveUrlCacheEntryStreamW (WININET.@)
3173 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(LPCWSTR lpszUrlName,
3174 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3175 LPDWORD lpdwCacheEntryInfoBufferSize, BOOL fRandomRead, DWORD dwReserved)
3177 DWORD len;
3178 /* NOTE: this is not the same as the way that the native
3179 * version allocates 'stream' handles. I did it this way
3180 * as it is much easier and no applications should depend
3181 * on this behaviour. (Native version appears to allocate
3182 * indices into a table)
3184 stream_handle *stream;
3185 HANDLE file;
3187 TRACE("(%s, %p, %p, %x, 0x%08lx)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3188 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved);
3190 if(!(len = urlcache_encode_url(lpszUrlName, NULL, 0)))
3191 return NULL;
3193 if(!RetrieveUrlCacheEntryFileW(lpszUrlName, lpCacheEntryInfo,
3194 lpdwCacheEntryInfoBufferSize, dwReserved))
3195 return NULL;
3197 file = CreateFileW(lpCacheEntryInfo->lpszLocalFileName, GENERIC_READ, FILE_SHARE_READ,
3198 NULL, OPEN_EXISTING, fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0, NULL);
3199 if(file == INVALID_HANDLE_VALUE) {
3200 UnlockUrlCacheEntryFileW(lpszUrlName, 0);
3201 return NULL;
3204 /* allocate handle storage space */
3205 stream = malloc(sizeof(stream_handle) + len * sizeof(WCHAR));
3206 if(!stream) {
3207 CloseHandle(file);
3208 UnlockUrlCacheEntryFileW(lpszUrlName, 0);
3209 SetLastError(ERROR_OUTOFMEMORY);
3210 return NULL;
3213 stream->file = file;
3214 if(!urlcache_encode_url(lpszUrlName, stream->url, len)) {
3215 CloseHandle(file);
3216 UnlockUrlCacheEntryFileW(lpszUrlName, 0);
3217 free(stream);
3218 return NULL;
3220 return stream;
3223 /***********************************************************************
3224 * UnlockUrlCacheEntryStream (WININET.@)
3227 BOOL WINAPI UnlockUrlCacheEntryStream(
3228 IN HANDLE hUrlCacheStream,
3229 IN DWORD dwReserved
3232 stream_handle *pStream = (stream_handle*)hUrlCacheStream;
3234 if (dwReserved != 0)
3236 ERR("dwReserved != 0\n");
3237 SetLastError(ERROR_INVALID_PARAMETER);
3238 return FALSE;
3241 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->url, INTERNET_MAX_URL_LENGTH))
3243 SetLastError(ERROR_INVALID_HANDLE);
3244 return FALSE;
3247 if (!UnlockUrlCacheEntryFileA(pStream->url, 0))
3248 return FALSE;
3250 CloseHandle(pStream->file);
3251 free(pStream);
3252 return TRUE;
3256 /***********************************************************************
3257 * DeleteUrlCacheEntryA (WININET.@)
3260 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3262 cache_container *pContainer;
3263 urlcache_header *pHeader;
3264 struct hash_entry *pHashEntry;
3265 DWORD error;
3266 BOOL ret;
3268 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3270 error = cache_containers_find(lpszUrlName, &pContainer);
3271 if (error != ERROR_SUCCESS)
3273 SetLastError(error);
3274 return FALSE;
3277 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3278 if (error != ERROR_SUCCESS)
3280 SetLastError(error);
3281 return FALSE;
3284 if (!(pHeader = cache_container_lock_index(pContainer)))
3285 return FALSE;
3287 if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
3289 cache_container_unlock_index(pContainer, pHeader);
3290 TRACE("entry %s not found!\n", debugstr_a(lpszUrlName));
3291 SetLastError(ERROR_FILE_NOT_FOUND);
3292 return FALSE;
3295 ret = urlcache_entry_delete(pContainer, pHeader, pHashEntry);
3297 cache_container_unlock_index(pContainer, pHeader);
3299 return ret;
3302 /***********************************************************************
3303 * DeleteUrlCacheEntryW (WININET.@)
3306 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3308 char *url;
3309 BOOL ret;
3311 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
3312 return FALSE;
3314 ret = DeleteUrlCacheEntryA(url);
3315 free(url);
3316 return ret;
3319 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3321 FIXME("(0x%08lx, 0x%08lx) stub\n", d1, d2);
3322 return TRUE;
3325 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3327 FIXME("(0x%08lx, 0x%08lx) stub\n", d1, d2);
3328 return TRUE;
3331 /***********************************************************************
3332 * CreateCacheContainerA (WININET.@)
3334 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3335 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3337 FIXME("(0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx) stub\n",
3338 d1, d2, d3, d4, d5, d6, d7, d8);
3339 return TRUE;
3342 /***********************************************************************
3343 * CreateCacheContainerW (WININET.@)
3345 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3346 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3348 FIXME("(0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx, 0x%08lx) stub\n",
3349 d1, d2, d3, d4, d5, d6, d7, d8);
3350 return TRUE;
3353 /***********************************************************************
3354 * FindFirstUrlCacheContainerA (WININET.@)
3356 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3358 FIXME("(%p, %p, %p, 0x%08lx) stub\n", p1, p2, p3, d1 );
3359 return NULL;
3362 /***********************************************************************
3363 * FindFirstUrlCacheContainerW (WININET.@)
3365 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3367 FIXME("(%p, %p, %p, 0x%08lx) stub\n", p1, p2, p3, d1 );
3368 return NULL;
3371 /***********************************************************************
3372 * FindNextUrlCacheContainerA (WININET.@)
3374 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3376 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3377 return FALSE;
3380 /***********************************************************************
3381 * FindNextUrlCacheContainerW (WININET.@)
3383 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3385 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3386 return FALSE;
3389 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3390 LPCSTR lpszUrlSearchPattern,
3391 DWORD dwFlags,
3392 DWORD dwFilter,
3393 GROUPID GroupId,
3394 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3395 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3396 LPVOID lpReserved,
3397 LPDWORD pcbReserved2,
3398 LPVOID lpReserved3
3401 FIXME("(%s, 0x%08lx, 0x%08lx, 0x%s, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3402 dwFlags, dwFilter, wine_dbgstr_longlong(GroupId), lpFirstCacheEntryInfo,
3403 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3404 SetLastError(ERROR_FILE_NOT_FOUND);
3405 return NULL;
3408 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3409 LPCWSTR lpszUrlSearchPattern,
3410 DWORD dwFlags,
3411 DWORD dwFilter,
3412 GROUPID GroupId,
3413 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3414 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3415 LPVOID lpReserved,
3416 LPDWORD pcbReserved2,
3417 LPVOID lpReserved3
3420 FIXME("(%s, 0x%08lx, 0x%08lx, 0x%s, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3421 dwFlags, dwFilter, wine_dbgstr_longlong(GroupId), lpFirstCacheEntryInfo,
3422 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3423 SetLastError(ERROR_FILE_NOT_FOUND);
3424 return NULL;
3427 /***********************************************************************
3428 * FindFirstUrlCacheEntryA (WININET.@)
3431 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3432 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3434 find_handle *pEntryHandle;
3436 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3438 pEntryHandle = malloc(sizeof(*pEntryHandle));
3439 if (!pEntryHandle)
3440 return NULL;
3442 pEntryHandle->magic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3443 if (lpszUrlSearchPattern)
3445 pEntryHandle->url_search_pattern = strdup(lpszUrlSearchPattern);
3446 if (!pEntryHandle->url_search_pattern)
3448 free(pEntryHandle);
3449 return NULL;
3452 else
3453 pEntryHandle->url_search_pattern = NULL;
3454 pEntryHandle->container_idx = 0;
3455 pEntryHandle->hash_table_idx = 0;
3456 pEntryHandle->hash_entry_idx = 0;
3458 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3460 free(pEntryHandle);
3461 return NULL;
3463 return pEntryHandle;
3466 /***********************************************************************
3467 * FindFirstUrlCacheEntryW (WININET.@)
3470 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3471 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3473 find_handle *pEntryHandle;
3475 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3477 pEntryHandle = malloc(sizeof(*pEntryHandle));
3478 if (!pEntryHandle)
3479 return NULL;
3481 pEntryHandle->magic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3482 if (lpszUrlSearchPattern)
3484 pEntryHandle->url_search_pattern = strdupWtoA(lpszUrlSearchPattern);
3485 if (!pEntryHandle->url_search_pattern)
3487 free(pEntryHandle);
3488 return NULL;
3491 else
3492 pEntryHandle->url_search_pattern = NULL;
3493 pEntryHandle->container_idx = 0;
3494 pEntryHandle->hash_table_idx = 0;
3495 pEntryHandle->hash_entry_idx = 0;
3497 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3499 free(pEntryHandle);
3500 return NULL;
3502 return pEntryHandle;
3505 static BOOL urlcache_find_next_entry(
3506 HANDLE hEnumHandle,
3507 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3508 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3509 BOOL unicode)
3511 find_handle *pEntryHandle = (find_handle*)hEnumHandle;
3512 cache_container *pContainer;
3514 if (pEntryHandle->magic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3516 SetLastError(ERROR_INVALID_HANDLE);
3517 return FALSE;
3520 for (; cache_containers_enum(pEntryHandle->url_search_pattern, pEntryHandle->container_idx, &pContainer);
3521 pEntryHandle->container_idx++, pEntryHandle->hash_table_idx = 0)
3523 urlcache_header *pHeader;
3524 entry_hash_table *pHashTableEntry;
3525 DWORD error;
3527 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3528 if (error != ERROR_SUCCESS)
3530 SetLastError(error);
3531 return FALSE;
3534 if (!(pHeader = cache_container_lock_index(pContainer)))
3535 return FALSE;
3537 for (; urlcache_enum_hash_tables(pHeader, &pEntryHandle->hash_table_idx, &pHashTableEntry);
3538 pEntryHandle->hash_table_idx++, pEntryHandle->hash_entry_idx = 0)
3540 const struct hash_entry *pHashEntry = NULL;
3541 for (; urlcache_enum_hash_table_entries(pHeader, pHashTableEntry, &pEntryHandle->hash_entry_idx, &pHashEntry);
3542 pEntryHandle->hash_entry_idx++)
3544 const entry_url *pUrlEntry;
3545 const entry_header *pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
3547 if (pEntry->signature != URL_SIGNATURE)
3548 continue;
3550 pUrlEntry = (const entry_url *)pEntry;
3551 TRACE("Found URL: %s\n",
3552 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->url_off));
3553 TRACE("Header info: %s\n",
3554 debugstr_an((LPCSTR)pUrlEntry + pUrlEntry->header_info_off,
3555 pUrlEntry->header_info_size));
3557 error = urlcache_copy_entry(
3558 pContainer,
3559 pHeader,
3560 lpNextCacheEntryInfo,
3561 lpdwNextCacheEntryInfoBufferSize,
3562 pUrlEntry,
3563 unicode);
3564 if (error != ERROR_SUCCESS)
3566 cache_container_unlock_index(pContainer, pHeader);
3567 SetLastError(error);
3568 return FALSE;
3570 if(pUrlEntry->local_name_off)
3571 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->local_name_off));
3573 /* increment the current index so that next time the function
3574 * is called the next entry is returned */
3575 pEntryHandle->hash_entry_idx++;
3576 cache_container_unlock_index(pContainer, pHeader);
3577 return TRUE;
3581 cache_container_unlock_index(pContainer, pHeader);
3584 SetLastError(ERROR_NO_MORE_ITEMS);
3585 return FALSE;
3588 /***********************************************************************
3589 * FindNextUrlCacheEntryA (WININET.@)
3591 BOOL WINAPI FindNextUrlCacheEntryA(
3592 HANDLE hEnumHandle,
3593 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3594 LPDWORD lpdwNextCacheEntryInfoBufferSize)
3596 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3598 return urlcache_find_next_entry(hEnumHandle, lpNextCacheEntryInfo,
3599 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
3602 /***********************************************************************
3603 * FindNextUrlCacheEntryW (WININET.@)
3605 BOOL WINAPI FindNextUrlCacheEntryW(
3606 HANDLE hEnumHandle,
3607 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3608 LPDWORD lpdwNextCacheEntryInfoBufferSize
3611 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3613 return urlcache_find_next_entry(hEnumHandle,
3614 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
3615 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
3618 /***********************************************************************
3619 * FindCloseUrlCache (WININET.@)
3621 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3623 find_handle *pEntryHandle = (find_handle*)hEnumHandle;
3625 TRACE("(%p)\n", hEnumHandle);
3627 if (!pEntryHandle || pEntryHandle->magic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3629 SetLastError(ERROR_INVALID_HANDLE);
3630 return FALSE;
3633 /* Ensure compiler doesn't optimize out the assignment with 0. */
3634 SecureZeroMemory(&pEntryHandle->magic, sizeof(pEntryHandle->magic));
3635 free(pEntryHandle->url_search_pattern);
3636 free(pEntryHandle);
3637 return TRUE;
3640 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3641 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3643 FIXME("(0x%08lx, 0x%08lx, %p, 0x%08lx, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3644 dwSearchCondition, lpGroupId, lpReserved);
3645 return NULL;
3648 BOOL WINAPI FindNextUrlCacheEntryExA(
3649 HANDLE hEnumHandle,
3650 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3651 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3652 LPVOID lpReserved,
3653 LPDWORD pcbReserved2,
3654 LPVOID lpReserved3
3657 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3658 lpReserved, pcbReserved2, lpReserved3);
3659 return FALSE;
3662 BOOL WINAPI FindNextUrlCacheEntryExW(
3663 HANDLE hEnumHandle,
3664 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3665 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3666 LPVOID lpReserved,
3667 LPDWORD pcbReserved2,
3668 LPVOID lpReserved3
3671 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3672 lpReserved, pcbReserved2, lpReserved3);
3673 return FALSE;
3676 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3678 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3679 return FALSE;
3682 /***********************************************************************
3683 * CreateUrlCacheGroup (WININET.@)
3686 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3688 FIXME("(0x%08lx, %p): stub\n", dwFlags, lpReserved);
3689 return FALSE;
3692 /***********************************************************************
3693 * DeleteUrlCacheGroup (WININET.@)
3696 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3698 FIXME("(0x%s, 0x%08lx, %p) stub\n",
3699 wine_dbgstr_longlong(GroupId), dwFlags, lpReserved);
3700 return FALSE;
3703 /***********************************************************************
3704 * DeleteWpadCacheForNetworks (WININET.@)
3705 * Undocumented, added in IE8
3707 BOOL WINAPI DeleteWpadCacheForNetworks(DWORD unk1)
3709 FIXME("(%ld) stub\n", unk1);
3710 return FALSE;
3713 /***********************************************************************
3714 * SetUrlCacheEntryGroupA (WININET.@)
3717 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3718 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3719 LPVOID lpReserved)
3721 FIXME("(%s, 0x%08lx, 0x%s, %p, 0x%08lx, %p) stub\n",
3722 debugstr_a(lpszUrlName), dwFlags, wine_dbgstr_longlong(GroupId),
3723 pbGroupAttributes, cbGroupAttributes, lpReserved);
3724 SetLastError(ERROR_FILE_NOT_FOUND);
3725 return FALSE;
3728 /***********************************************************************
3729 * SetUrlCacheEntryGroupW (WININET.@)
3732 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3733 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3734 LPVOID lpReserved)
3736 FIXME("(%s, 0x%08lx, 0x%s, %p, 0x%08lx, %p) stub\n",
3737 debugstr_w(lpszUrlName), dwFlags, wine_dbgstr_longlong(GroupId),
3738 pbGroupAttributes, cbGroupAttributes, lpReserved);
3739 SetLastError(ERROR_FILE_NOT_FOUND);
3740 return FALSE;
3743 static cache_container *find_container(DWORD flags)
3745 cache_container *container;
3747 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
3749 switch (flags & (CACHE_CONFIG_CONTENT_PATHS_FC | CACHE_CONFIG_COOKIES_PATHS_FC | CACHE_CONFIG_HISTORY_PATHS_FC))
3751 case 0:
3752 case CACHE_CONFIG_CONTENT_PATHS_FC:
3753 if (container->default_entry_type == NORMAL_CACHE_ENTRY)
3754 return container;
3755 break;
3757 case CACHE_CONFIG_COOKIES_PATHS_FC:
3758 if (container->default_entry_type == COOKIE_CACHE_ENTRY)
3759 return container;
3760 break;
3762 case CACHE_CONFIG_HISTORY_PATHS_FC:
3763 if (container->default_entry_type == URLHISTORY_CACHE_ENTRY)
3764 return container;
3765 break;
3767 default:
3768 FIXME("flags %08lx not handled\n", flags);
3769 break;
3773 return NULL;
3776 /***********************************************************************
3777 * GetUrlCacheConfigInfoW (WININET.@)
3779 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW info, LPDWORD size, DWORD flags)
3781 cache_container *container;
3782 DWORD error;
3784 FIXME("(%p, %p, %lx): semi-stub\n", info, size, flags);
3786 if (!info || !(container = find_container(flags)))
3788 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3789 return FALSE;
3792 error = cache_container_open_index(container, MIN_BLOCK_NO);
3793 if (error != ERROR_SUCCESS)
3795 INTERNET_SetLastError(error);
3796 return FALSE;
3799 info->dwContainer = 0;
3800 info->dwQuota = FILE_SIZE(MAX_BLOCK_NO) / 1024;
3801 info->dwReserved4 = 0;
3802 info->fPerUser = TRUE;
3803 info->dwSyncMode = 0;
3804 info->dwNumCachePaths = 1;
3805 info->dwNormalUsage = 0;
3806 info->dwExemptUsage = 0;
3807 info->dwCacheSize = container->file_size / 1024;
3808 lstrcpynW(info->CachePath, container->path, MAX_PATH);
3810 cache_container_close_index(container);
3812 TRACE("CachePath %s\n", debugstr_w(info->CachePath));
3814 return TRUE;
3817 /***********************************************************************
3818 * GetUrlCacheConfigInfoA (WININET.@)
3820 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA info, LPDWORD size, DWORD flags)
3822 INTERNET_CACHE_CONFIG_INFOW infoW;
3824 TRACE("(%p, %p, %lx)\n", info, size, flags);
3826 if (!info)
3828 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3829 return FALSE;
3832 infoW.dwStructSize = sizeof(infoW);
3833 if (!GetUrlCacheConfigInfoW(&infoW, size, flags))
3834 return FALSE;
3836 info->dwContainer = infoW.dwContainer;
3837 info->dwQuota = infoW.dwQuota;
3838 info->dwReserved4 = infoW.dwReserved4;
3839 info->fPerUser = infoW.fPerUser;
3840 info->dwSyncMode = infoW.dwSyncMode;
3841 info->dwNumCachePaths = infoW.dwNumCachePaths;
3842 info->dwNormalUsage = infoW.dwNormalUsage;
3843 info->dwExemptUsage = infoW.dwExemptUsage;
3844 info->dwCacheSize = infoW.dwCacheSize;
3845 WideCharToMultiByte(CP_ACP, 0, infoW.CachePath, -1, info->CachePath, MAX_PATH, NULL, NULL);
3847 return TRUE;
3850 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3851 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3852 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3854 FIXME("(0x%s, 0x%08lx, 0x%08lx, %p, %p, %p) stub\n",
3855 wine_dbgstr_longlong(gid), dwFlags, dwAttributes, lpGroupInfo,
3856 lpdwGroupInfo, lpReserved);
3857 return FALSE;
3860 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3861 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3862 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3864 FIXME("(0x%s, 0x%08lx, 0x%08lx, %p, %p, %p) stub\n",
3865 wine_dbgstr_longlong(gid), dwFlags, dwAttributes, lpGroupInfo,
3866 lpdwGroupInfo, lpReserved);
3867 return FALSE;
3870 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3871 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3873 FIXME("(0x%s, 0x%08lx, 0x%08lx, %p, %p) stub\n",
3874 wine_dbgstr_longlong(gid), dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3875 return TRUE;
3878 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3879 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3881 FIXME("(0x%s, 0x%08lx, 0x%08lx, %p, %p) stub\n",
3882 wine_dbgstr_longlong(gid), dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3883 return TRUE;
3886 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3888 FIXME("(%p, 0x%08lx) stub\n", lpCacheConfigInfo, dwFieldControl);
3889 return TRUE;
3892 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3894 FIXME("(%p, 0x%08lx) stub\n", lpCacheConfigInfo, dwFieldControl);
3895 return TRUE;
3898 /***********************************************************************
3899 * DeleteIE3Cache (WININET.@)
3901 * Deletes the files used by the IE3 URL caching system.
3903 * PARAMS
3904 * hWnd [I] A dummy window.
3905 * hInst [I] Instance of process calling the function.
3906 * lpszCmdLine [I] Options used by function.
3907 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3909 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3911 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3912 return 0;
3915 static BOOL urlcache_entry_is_expired(const entry_url *pUrlEntry,
3916 FILETIME *pftLastModified)
3918 BOOL ret;
3919 FILETIME now, expired;
3921 *pftLastModified = pUrlEntry->modification_time;
3922 GetSystemTimeAsFileTime(&now);
3923 dos_date_time_to_file_time(pUrlEntry->expire_date,
3924 pUrlEntry->expire_time, &expired);
3925 /* If the expired time is 0, it's interpreted as not expired */
3926 if (!expired.dwLowDateTime && !expired.dwHighDateTime)
3927 ret = FALSE;
3928 else
3929 ret = CompareFileTime(&expired, &now) < 0;
3930 return ret;
3933 /***********************************************************************
3934 * IsUrlCacheEntryExpiredA (WININET.@)
3936 * PARAMS
3937 * url [I] Url
3938 * dwFlags [I] Unknown
3939 * pftLastModified [O] Last modified time
3941 BOOL WINAPI IsUrlCacheEntryExpiredA(LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified)
3943 urlcache_header *pHeader;
3944 struct hash_entry *pHashEntry;
3945 const entry_header *pEntry;
3946 const entry_url * pUrlEntry;
3947 cache_container *pContainer;
3948 BOOL expired;
3950 TRACE("(%s, %08lx, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3952 if (!url || !pftLastModified)
3953 return TRUE;
3954 if (dwFlags)
3955 FIXME("unknown flags 0x%08lx\n", dwFlags);
3957 /* Any error implies that the URL is expired, i.e. not in the cache */
3958 if (cache_containers_find(url, &pContainer))
3960 memset(pftLastModified, 0, sizeof(*pftLastModified));
3961 return TRUE;
3964 if (cache_container_open_index(pContainer, MIN_BLOCK_NO))
3966 memset(pftLastModified, 0, sizeof(*pftLastModified));
3967 return TRUE;
3970 if (!(pHeader = cache_container_lock_index(pContainer)))
3972 memset(pftLastModified, 0, sizeof(*pftLastModified));
3973 return TRUE;
3976 if (!urlcache_find_hash_entry(pHeader, url, &pHashEntry))
3978 cache_container_unlock_index(pContainer, pHeader);
3979 memset(pftLastModified, 0, sizeof(*pftLastModified));
3980 TRACE("entry %s not found!\n", url);
3981 return TRUE;
3984 pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
3985 if (pEntry->signature != URL_SIGNATURE)
3987 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
3988 cache_container_unlock_index(pContainer, pHeader);
3989 memset(pftLastModified, 0, sizeof(*pftLastModified));
3990 return TRUE;
3993 pUrlEntry = (const entry_url *)pEntry;
3994 expired = urlcache_entry_is_expired(pUrlEntry, pftLastModified);
3996 cache_container_unlock_index(pContainer, pHeader);
3998 return expired;
4001 /***********************************************************************
4002 * IsUrlCacheEntryExpiredW (WININET.@)
4004 * PARAMS
4005 * url [I] Url
4006 * dwFlags [I] Unknown
4007 * pftLastModified [O] Last modified time
4009 BOOL WINAPI IsUrlCacheEntryExpiredW(LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified)
4011 char *encoded_url;
4012 BOOL ret;
4014 if(!urlcache_encode_url_alloc(url, &encoded_url))
4015 return FALSE;
4017 ret = IsUrlCacheEntryExpiredA(encoded_url, dwFlags, pftLastModified);
4018 free(encoded_url);
4019 return ret;
4022 /***********************************************************************
4023 * GetDiskInfoA (WININET.@)
4025 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
4027 BOOL ret;
4028 ULARGE_INTEGER bytes_free, bytes_total;
4030 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
4032 if (!path)
4034 SetLastError(ERROR_INVALID_PARAMETER);
4035 return FALSE;
4038 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
4040 if (cluster_size) *cluster_size = 1;
4041 if (free) *free = bytes_free.QuadPart;
4042 if (total) *total = bytes_total.QuadPart;
4044 return ret;
4047 /***********************************************************************
4048 * RegisterUrlCacheNotification (WININET.@)
4050 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
4052 FIXME("(%p %lx %lx %lx %lx %lx)\n", a, b, c, d, e, f);
4053 return 0;
4056 /***********************************************************************
4057 * IncrementUrlCacheHeaderData (WININET.@)
4059 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
4061 FIXME("(%lu, %p)\n", index, data);
4062 return FALSE;
4065 /***********************************************************************
4066 * RunOnceUrlCache (WININET.@)
4069 DWORD WINAPI RunOnceUrlCache(HWND hwnd, HINSTANCE hinst, LPSTR cmd, int cmdshow)
4071 FIXME("(%p, %p, %s, %d): stub\n", hwnd, hinst, debugstr_a(cmd), cmdshow);
4072 return 0;
4075 BOOL init_urlcache(void)
4077 dll_unload_event = CreateEventW(NULL, FALSE, FALSE, NULL);
4078 if(!dll_unload_event)
4079 return FALSE;
4081 free_cache_running = CreateSemaphoreW(NULL, 1, 1, NULL);
4082 if(!free_cache_running) {
4083 CloseHandle(dll_unload_event);
4084 return FALSE;
4087 cache_containers_init();
4088 return TRUE;
4091 void free_urlcache(void)
4093 SetEvent(dll_unload_event);
4094 WaitForSingleObject(free_cache_running, INFINITE);
4095 ReleaseSemaphore(free_cache_running, 1, NULL);
4096 CloseHandle(free_cache_running);
4097 CloseHandle(dll_unload_event);
4099 cache_containers_free();
4102 /***********************************************************************
4103 * LoadUrlCacheContent (WININET.@)
4105 BOOL WINAPI LoadUrlCacheContent(void)
4107 FIXME("stub!\n");
4108 return FALSE;