comctl32: Fix a typo in comment.
[wine.git] / dlls / wininet / urlcache.c
blobc7b5f9ecadc96e36b39f8d23703e9f755a0e0ef0
1 /*
2 * Wininet - Url Cache functions
4 * Copyright 2001,2002 CodeWeavers
5 * Copyright 2003-2008 Robert Shearman
7 * Eric Kohl
8 * Aric Stewart
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #define NONAMELESSUNION
27 #include "ws2tcpip.h"
29 #include <stdarg.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <time.h>
35 #include "windef.h"
36 #include "winbase.h"
37 #include "winuser.h"
38 #include "wininet.h"
39 #include "winineti.h"
40 #include "winerror.h"
41 #include "winreg.h"
42 #include "shlwapi.h"
43 #include "shlobj.h"
44 #include "shellapi.h"
46 #include "internet.h"
48 #include "wine/unicode.h"
49 #include "wine/debug.h"
51 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
53 static const char urlcache_ver_prefix[] = "WINE URLCache Ver ";
54 static const char urlcache_ver[] = "0.2012001";
56 #ifndef CHAR_BIT
57 #define CHAR_BIT (8 * sizeof(CHAR))
58 #endif
60 #define ENTRY_START_OFFSET 0x4000
61 #define DIR_LENGTH 8
62 #define MAX_DIR_NO 0x20
63 #define BLOCKSIZE 128
64 #define HASHTABLE_SIZE 448
65 #define HASHTABLE_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
66 #define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
67 #define ALLOCATION_TABLE_OFFSET 0x250
68 #define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
69 #define MIN_BLOCK_NO 0x80
70 #define MAX_BLOCK_NO (ALLOCATION_TABLE_SIZE * CHAR_BIT)
71 #define FILE_SIZE(blocks) ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
73 #define HASHTABLE_URL 0
74 #define HASHTABLE_DEL 1
75 #define HASHTABLE_LOCK 2
76 #define HASHTABLE_FREE 3
77 #define HASHTABLE_REDR 5
78 #define HASHTABLE_FLAG_BITS 6
80 #define PENDING_DELETE_CACHE_ENTRY 0x00400000
81 #define INSTALLED_CACHE_ENTRY 0x10000000
82 #define GET_INSTALLED_ENTRY 0x200
83 #define CACHE_CONTAINER_NO_SUBDIR 0xFE
85 #define CACHE_HEADER_DATA_ROOT_LEAK_OFFSET 0x16
87 #define FILETIME_SECOND 10000000
89 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
90 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
91 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
92 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
93 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
95 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
97 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
99 typedef struct
101 DWORD signature;
102 DWORD blocks_used; /* number of 128byte blocks used by this entry */
103 } entry_header;
105 typedef struct
107 entry_header header;
108 FILETIME modification_time;
109 FILETIME access_time;
110 WORD expire_date; /* expire date in dos format */
111 WORD expire_time; /* expire time in dos format */
112 DWORD unk1; /* usually zero */
113 ULARGE_INTEGER size; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
114 DWORD unk2; /* usually zero */
115 DWORD exempt_delta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
116 DWORD unk3; /* usually 0x60 */
117 DWORD url_off; /* offset of start of url from start of entry */
118 BYTE cache_dir; /* index of cache directory this url is stored in */
119 BYTE unk4; /* usually zero */
120 WORD unk5; /* usually 0x1010 */
121 DWORD local_name_off; /* offset of start of local filename from start of entry */
122 DWORD cache_entry_type; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
123 DWORD header_info_off; /* offset of start of header info from start of entry */
124 DWORD header_info_size;
125 DWORD file_extension_off; /* offset of start of file extension from start of entry */
126 WORD sync_date; /* last sync date in dos format */
127 WORD sync_time; /* last sync time in dos format */
128 DWORD hit_rate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
129 DWORD use_count; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
130 WORD write_date;
131 WORD write_time;
132 DWORD unk7; /* usually zero */
133 DWORD unk8; /* usually zero */
134 /* packing to dword align start of next field */
135 /* CHAR szSourceUrlName[]; (url) */
136 /* packing to dword align start of next field */
137 /* CHAR szLocalFileName[]; (local file name excluding path) */
138 /* packing to dword align start of next field */
139 /* CHAR szHeaderInfo[]; (header info) */
140 } entry_url;
142 struct hash_entry
144 DWORD key;
145 DWORD offset;
148 typedef struct
150 entry_header header;
151 DWORD next;
152 DWORD id;
153 struct hash_entry hash_table[HASHTABLE_SIZE];
154 } entry_hash_table;
156 typedef struct
158 char signature[28];
159 DWORD size;
160 DWORD hash_table_off;
161 DWORD capacity_in_blocks;
162 DWORD blocks_in_use;
163 DWORD unk1;
164 ULARGE_INTEGER cache_limit;
165 ULARGE_INTEGER cache_usage;
166 ULARGE_INTEGER exempt_usage;
167 DWORD dirs_no;
168 struct _directory_data
170 DWORD files_no;
171 char name[DIR_LENGTH];
172 } directory_data[MAX_DIR_NO];
173 DWORD options[0x21];
174 BYTE allocation_table[ALLOCATION_TABLE_SIZE];
175 } urlcache_header;
177 typedef struct
179 HANDLE file;
180 CHAR url[1];
181 } stream_handle;
183 typedef struct
185 struct list entry; /* part of a list */
186 char *cache_prefix; /* string that has to be prefixed for this container to be used */
187 LPWSTR path; /* path to url container directory */
188 HANDLE mapping; /* handle of file mapping */
189 DWORD file_size; /* size of file when mapping was opened */
190 HANDLE mutex; /* handle of mutex */
191 DWORD default_entry_type;
192 } cache_container;
194 typedef struct
196 DWORD magic;
197 char *url_search_pattern;
198 DWORD container_idx;
199 DWORD hash_table_idx;
200 DWORD hash_entry_idx;
201 } find_handle;
203 /* List of all containers available */
204 static struct list UrlContainers = LIST_INIT(UrlContainers);
206 static inline char *heap_strdupWtoUTF8(LPCWSTR str)
208 char *ret = NULL;
210 if(str) {
211 DWORD size = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);
212 ret = heap_alloc(size);
213 if(ret)
214 WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, size, NULL, NULL);
217 return ret;
220 /***********************************************************************
221 * urlcache_block_is_free (Internal)
223 * Is the specified block number free?
225 * RETURNS
226 * zero if free
227 * non-zero otherwise
230 static inline BYTE urlcache_block_is_free(BYTE *allocation_table, DWORD block_number)
232 BYTE mask = 1 << (block_number%CHAR_BIT);
233 return (allocation_table[block_number/CHAR_BIT] & mask) == 0;
236 /***********************************************************************
237 * urlcache_block_free (Internal)
239 * Marks the specified block as free
241 * CAUTION
242 * this function is not updating used blocks count
244 * RETURNS
245 * nothing
248 static inline void urlcache_block_free(BYTE *allocation_table, DWORD block_number)
250 BYTE mask = ~(1 << (block_number%CHAR_BIT));
251 allocation_table[block_number/CHAR_BIT] &= mask;
254 /***********************************************************************
255 * urlcache_block_alloc (Internal)
257 * Marks the specified block as allocated
259 * CAUTION
260 * this function is not updating used blocks count
262 * RETURNS
263 * nothing
266 static inline void urlcache_block_alloc(BYTE *allocation_table, DWORD block_number)
268 BYTE mask = 1 << (block_number%CHAR_BIT);
269 allocation_table[block_number/CHAR_BIT] |= mask;
272 /***********************************************************************
273 * urlcache_entry_alloc (Internal)
275 * Finds and allocates the first block of free space big enough and
276 * sets entry to point to it.
278 * RETURNS
279 * ERROR_SUCCESS when free memory block was found
280 * Any other Win32 error code if the entry could not be added
283 static DWORD urlcache_entry_alloc(urlcache_header *header, DWORD blocks_needed, entry_header **entry)
285 DWORD block, block_size;
287 for(block=0; block<header->capacity_in_blocks; block+=block_size+1)
289 block_size = 0;
290 while(block_size<blocks_needed && block_size+block<header->capacity_in_blocks
291 && urlcache_block_is_free(header->allocation_table, block+block_size))
292 block_size++;
294 if(block_size == blocks_needed)
296 DWORD index;
298 TRACE("Found free blocks starting at no. %d (0x%x)\n", block, ENTRY_START_OFFSET+block*BLOCKSIZE);
300 for(index=0; index<blocks_needed; index++)
301 urlcache_block_alloc(header->allocation_table, block+index);
303 *entry = (entry_header*)((BYTE*)header+ENTRY_START_OFFSET+block*BLOCKSIZE);
304 for(index=0; index<blocks_needed*BLOCKSIZE/sizeof(DWORD); index++)
305 ((DWORD*)*entry)[index] = 0xdeadbeef;
306 (*entry)->blocks_used = blocks_needed;
308 header->blocks_in_use += blocks_needed;
309 return ERROR_SUCCESS;
313 return ERROR_HANDLE_DISK_FULL;
316 /***********************************************************************
317 * urlcache_entry_free (Internal)
319 * Deletes the specified entry and frees the space allocated to it
321 * RETURNS
322 * TRUE if it succeeded
323 * FALSE if it failed
326 static BOOL urlcache_entry_free(urlcache_header *header, entry_header *entry)
328 DWORD start_block, block;
330 /* update allocation table */
331 start_block = ((DWORD)((BYTE*)entry - (BYTE*)header) - ENTRY_START_OFFSET) / BLOCKSIZE;
332 for(block = start_block; block < start_block+entry->blocks_used; block++)
333 urlcache_block_free(header->allocation_table, block);
335 header->blocks_in_use -= entry->blocks_used;
336 return TRUE;
339 /***********************************************************************
340 * urlcache_create_hash_table (Internal)
342 * Creates a new hash table in free space and adds it to the chain of existing
343 * hash tables.
345 * RETURNS
346 * ERROR_SUCCESS if the hash table was created
347 * ERROR_DISK_FULL if the hash table could not be created
350 static DWORD urlcache_create_hash_table(urlcache_header *header, entry_hash_table *hash_table_prev, entry_hash_table **hash_table)
352 DWORD dwOffset, error;
353 int i;
355 if((error = urlcache_entry_alloc(header, 0x20, (entry_header**)hash_table)) != ERROR_SUCCESS)
356 return error;
358 dwOffset = (BYTE*)*hash_table-(BYTE*)header;
360 if(hash_table_prev)
361 hash_table_prev->next = dwOffset;
362 else
363 header->hash_table_off = dwOffset;
365 (*hash_table)->header.signature = HASH_SIGNATURE;
366 (*hash_table)->next = 0;
367 (*hash_table)->id = hash_table_prev ? hash_table_prev->id+1 : 0;
368 for(i = 0; i < HASHTABLE_SIZE; i++) {
369 (*hash_table)->hash_table[i].offset = HASHTABLE_FREE;
370 (*hash_table)->hash_table[i].key = HASHTABLE_FREE;
372 return ERROR_SUCCESS;
375 /***********************************************************************
376 * cache_container_create_object_name (Internal)
378 * Converts a path to a name suitable for use as a Win32 object name.
379 * Replaces '\\' characters in-place with the specified character
380 * (usually '_' or '!')
382 * RETURNS
383 * nothing
386 static void cache_container_create_object_name(LPWSTR lpszPath, WCHAR replace)
388 for (; *lpszPath; lpszPath++)
390 if (*lpszPath == '\\')
391 *lpszPath = replace;
395 /* Caller must hold container lock */
396 static HANDLE cache_container_map_index(HANDLE file, const WCHAR *path, DWORD size, BOOL *validate)
398 static const WCHAR mapping_name_format[]
399 = {'%','s','i','n','d','e','x','.','d','a','t','_','%','l','u',0};
400 WCHAR mapping_name[MAX_PATH];
401 HANDLE mapping;
403 wsprintfW(mapping_name, mapping_name_format, path, size);
404 cache_container_create_object_name(mapping_name, '_');
406 mapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, mapping_name);
407 if(mapping) {
408 if(validate) *validate = FALSE;
409 return mapping;
412 if(validate) *validate = TRUE;
413 return CreateFileMappingW(file, NULL, PAGE_READWRITE, 0, 0, mapping_name);
416 /* Caller must hold container lock */
417 static DWORD cache_container_set_size(cache_container *container, HANDLE file, DWORD blocks_no)
419 static const WCHAR cache_content_key[] = {'S','o','f','t','w','a','r','e','\\',
420 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
421 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
422 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s','\\',
423 'C','a','c','h','e','\\','C','o','n','t','e','n','t',0};
424 static const WCHAR cache_limit[] = {'C','a','c','h','e','L','i','m','i','t',0};
426 DWORD file_size = FILE_SIZE(blocks_no);
427 WCHAR dir_path[MAX_PATH], *dir_name;
428 entry_hash_table *hashtable_entry;
429 urlcache_header *header;
430 HANDLE mapping;
431 FILETIME ft;
432 HKEY key;
433 int i, j;
435 if(SetFilePointer(file, file_size, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
436 return GetLastError();
438 if(!SetEndOfFile(file))
439 return GetLastError();
441 mapping = cache_container_map_index(file, container->path, file_size, NULL);
442 if(!mapping)
443 return GetLastError();
445 header = MapViewOfFile(mapping, FILE_MAP_WRITE, 0, 0, 0);
446 if(!header) {
447 CloseHandle(mapping);
448 return GetLastError();
451 if(blocks_no != MIN_BLOCK_NO) {
452 if(file_size > header->size)
453 memset((char*)header+header->size, 0, file_size-header->size);
454 header->size = file_size;
455 header->capacity_in_blocks = blocks_no;
457 UnmapViewOfFile(header);
458 CloseHandle(container->mapping);
459 container->mapping = mapping;
460 container->file_size = file_size;
461 return ERROR_SUCCESS;
464 memset(header, 0, file_size);
465 /* First set some constants and defaults in the header */
466 memcpy(header->signature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1);
467 memcpy(header->signature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1);
468 header->size = file_size;
469 header->capacity_in_blocks = blocks_no;
470 /* 127MB - taken from default for Windows 2000 */
471 header->cache_limit.QuadPart = 0x07ff5400;
472 /* Copied from a Windows 2000 cache index */
473 header->dirs_no = container->default_entry_type==NORMAL_CACHE_ENTRY ? 4 : 0;
475 /* If the registry has a cache size set, use the registry value */
476 if(RegOpenKeyW(HKEY_CURRENT_USER, cache_content_key, &key) == ERROR_SUCCESS) {
477 DWORD dw, len = sizeof(dw), keytype;
479 if(RegQueryValueExW(key, cache_limit, NULL, &keytype, (BYTE*)&dw, &len) == ERROR_SUCCESS &&
480 keytype == REG_DWORD)
481 header->cache_limit.QuadPart = (ULONGLONG)dw * 1024;
482 RegCloseKey(key);
485 urlcache_create_hash_table(header, NULL, &hashtable_entry);
487 /* Last step - create the directories */
488 strcpyW(dir_path, container->path);
489 dir_name = dir_path + strlenW(dir_path);
490 dir_name[8] = 0;
492 GetSystemTimeAsFileTime(&ft);
494 for(i=0; i<header->dirs_no; ++i) {
495 header->directory_data[i].files_no = 0;
496 for(j=0;; ++j) {
497 ULONGLONG n = ft.dwHighDateTime;
498 int k;
500 /* Generate a file name to attempt to create.
501 * This algorithm will create what will appear
502 * to be random and unrelated directory names
503 * of up to 9 characters in length.
505 n <<= 32;
506 n += ft.dwLowDateTime;
507 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
509 for(k = 0; k < 8; ++k) {
510 int r = (n % 36);
512 /* Dividing by a prime greater than 36 helps
513 * with the appearance of randomness
515 n /= 37;
517 if(r < 10)
518 dir_name[k] = '0' + r;
519 else
520 dir_name[k] = 'A' + (r - 10);
523 if(CreateDirectoryW(dir_path, 0)) {
524 /* The following is OK because we generated an
525 * 8 character directory name made from characters
526 * [A-Z0-9], which are equivalent for all code
527 * pages and for UTF-16
529 for (k = 0; k < 8; ++k)
530 header->directory_data[i].name[k] = dir_name[k];
531 break;
532 }else if(j >= 255) {
533 /* Give up. The most likely cause of this
534 * is a full disk, but whatever the cause
535 * is, it should be more than apparent that
536 * we won't succeed.
538 UnmapViewOfFile(header);
539 CloseHandle(mapping);
540 return GetLastError();
545 UnmapViewOfFile(header);
546 CloseHandle(container->mapping);
547 container->mapping = mapping;
548 container->file_size = file_size;
549 return ERROR_SUCCESS;
552 static BOOL cache_container_is_valid(urlcache_header *header, DWORD file_size)
554 DWORD allocation_size, count_bits, i;
556 if(file_size < FILE_SIZE(MIN_BLOCK_NO))
557 return FALSE;
559 if(file_size != header->size)
560 return FALSE;
562 if (!memcmp(header->signature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1) &&
563 memcmp(header->signature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1))
564 return FALSE;
566 if(FILE_SIZE(header->capacity_in_blocks) != file_size)
567 return FALSE;
569 allocation_size = 0;
570 for(i=0; i<header->capacity_in_blocks/8; i++) {
571 for(count_bits = header->allocation_table[i]; count_bits!=0; count_bits>>=1) {
572 if(count_bits & 1)
573 allocation_size++;
576 if(allocation_size != header->blocks_in_use)
577 return FALSE;
579 for(; i<ALLOCATION_TABLE_SIZE; i++) {
580 if(header->allocation_table[i])
581 return FALSE;
584 return TRUE;
587 /***********************************************************************
588 * cache_container_open_index (Internal)
590 * Opens the index file and saves mapping handle
592 * RETURNS
593 * ERROR_SUCCESS if succeeded
594 * Any other Win32 error code if failed
597 static DWORD cache_container_open_index(cache_container *container, DWORD blocks_no)
599 static const WCHAR index_dat[] = {'i','n','d','e','x','.','d','a','t',0};
601 HANDLE file;
602 WCHAR index_path[MAX_PATH];
603 DWORD file_size;
604 BOOL validate;
606 WaitForSingleObject(container->mutex, INFINITE);
608 if(container->mapping) {
609 ReleaseMutex(container->mutex);
610 return ERROR_SUCCESS;
613 strcpyW(index_path, container->path);
614 strcatW(index_path, index_dat);
616 file = CreateFileW(index_path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
617 if(file == INVALID_HANDLE_VALUE) {
618 /* Maybe the directory wasn't there? Try to create it */
619 if(CreateDirectoryW(container->path, 0))
620 file = CreateFileW(index_path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
622 if(file == INVALID_HANDLE_VALUE) {
623 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(index_path));
624 ReleaseMutex(container->mutex);
625 return GetLastError();
628 file_size = GetFileSize(file, NULL);
629 if(file_size == INVALID_FILE_SIZE) {
630 CloseHandle(file);
631 ReleaseMutex(container->mutex);
632 return GetLastError();
635 if(blocks_no < MIN_BLOCK_NO)
636 blocks_no = MIN_BLOCK_NO;
637 else if(blocks_no > MAX_BLOCK_NO)
638 blocks_no = MAX_BLOCK_NO;
640 if(file_size < FILE_SIZE(blocks_no)) {
641 DWORD ret = cache_container_set_size(container, file, blocks_no);
642 CloseHandle(file);
643 ReleaseMutex(container->mutex);
644 return ret;
647 container->file_size = file_size;
648 container->mapping = cache_container_map_index(file, container->path, file_size, &validate);
649 CloseHandle(file);
650 if(container->mapping && validate) {
651 urlcache_header *header = MapViewOfFile(container->mapping, FILE_MAP_WRITE, 0, 0, 0);
653 if(header && !cache_container_is_valid(header, file_size)) {
654 WARN("detected old or broken index.dat file\n");
655 UnmapViewOfFile(header);
656 FreeUrlCacheSpaceW(container->path, 100, 0);
657 }else if(header) {
658 UnmapViewOfFile(header);
659 }else {
660 CloseHandle(container->mapping);
661 container->mapping = NULL;
665 if(!container->mapping)
667 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
668 ReleaseMutex(container->mutex);
669 return GetLastError();
672 ReleaseMutex(container->mutex);
673 return ERROR_SUCCESS;
676 /***********************************************************************
677 * cache_container_close_index (Internal)
679 * Closes the index
681 * RETURNS
682 * nothing
685 static void cache_container_close_index(cache_container *pContainer)
687 CloseHandle(pContainer->mapping);
688 pContainer->mapping = NULL;
691 static BOOL cache_containers_add(const char *cache_prefix, LPCWSTR path,
692 DWORD default_entry_type, LPWSTR mutex_name)
694 cache_container *pContainer = heap_alloc(sizeof(cache_container));
695 int cache_prefix_len = strlen(cache_prefix);
697 if (!pContainer)
699 return FALSE;
702 pContainer->mapping = NULL;
703 pContainer->file_size = 0;
704 pContainer->default_entry_type = default_entry_type;
706 pContainer->path = heap_strdupW(path);
707 if (!pContainer->path)
709 heap_free(pContainer);
710 return FALSE;
713 pContainer->cache_prefix = heap_alloc(cache_prefix_len+1);
714 if (!pContainer->cache_prefix)
716 heap_free(pContainer->path);
717 heap_free(pContainer);
718 return FALSE;
721 memcpy(pContainer->cache_prefix, cache_prefix, cache_prefix_len+1);
723 CharLowerW(mutex_name);
724 cache_container_create_object_name(mutex_name, '!');
726 if ((pContainer->mutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
728 ERR("couldn't create mutex (error is %d)\n", GetLastError());
729 heap_free(pContainer->path);
730 heap_free(pContainer);
731 return FALSE;
734 list_add_head(&UrlContainers, &pContainer->entry);
736 return TRUE;
739 static void cache_container_delete_container(cache_container *pContainer)
741 list_remove(&pContainer->entry);
743 cache_container_close_index(pContainer);
744 CloseHandle(pContainer->mutex);
745 heap_free(pContainer->path);
746 heap_free(pContainer->cache_prefix);
747 heap_free(pContainer);
750 static void cache_containers_init(void)
752 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
753 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
754 static const WCHAR CookieSuffix[] = {0};
755 static const struct
757 int nFolder; /* CSIDL_* constant */
758 const WCHAR *shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
759 const char *cache_prefix; /* prefix used to reference the container */
760 DWORD default_entry_type;
761 } DefaultContainerData[] =
763 { CSIDL_INTERNET_CACHE, UrlSuffix, "", NORMAL_CACHE_ENTRY },
764 { CSIDL_HISTORY, HistorySuffix, "Visited:", URLHISTORY_CACHE_ENTRY },
765 { CSIDL_COOKIES, CookieSuffix, "Cookie:", COOKIE_CACHE_ENTRY },
767 DWORD i;
769 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
771 WCHAR wszCachePath[MAX_PATH];
772 WCHAR wszMutexName[MAX_PATH];
773 int path_len, suffix_len;
774 BOOL def_char;
776 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
778 ERR("Couldn't get path for default container %u\n", i);
779 continue;
781 path_len = strlenW(wszCachePath);
782 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
784 if (path_len + suffix_len + 2 > MAX_PATH)
786 ERR("Path too long\n");
787 continue;
790 wszCachePath[path_len] = '\\';
791 wszCachePath[path_len+1] = 0;
793 strcpyW(wszMutexName, wszCachePath);
795 if (suffix_len)
797 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
798 wszCachePath[path_len + suffix_len + 1] = '\\';
799 wszCachePath[path_len + suffix_len + 2] = '\0';
802 if (!WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wszCachePath, path_len,
803 NULL, 0, NULL, &def_char) || def_char)
805 WCHAR tmp[MAX_PATH];
807 /* cannot convert path to ANSI code page */
808 if (!(path_len = GetShortPathNameW(wszCachePath, tmp, MAX_PATH)) ||
809 !WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, tmp, path_len,
810 NULL, 0, NULL, &def_char) || def_char)
811 ERR("Can't create container path accessible by ANSI functions\n");
812 else
813 memcpy(wszCachePath, tmp, (path_len+1)*sizeof(WCHAR));
816 cache_containers_add(DefaultContainerData[i].cache_prefix, wszCachePath,
817 DefaultContainerData[i].default_entry_type, wszMutexName);
821 static void cache_containers_free(void)
823 while(!list_empty(&UrlContainers))
824 cache_container_delete_container(
825 LIST_ENTRY(list_head(&UrlContainers), cache_container, entry)
829 static DWORD cache_containers_find(const char *url, cache_container **ret)
831 cache_container *container;
833 TRACE("searching for prefix for URL: %s\n", debugstr_a(url));
835 if(!url)
836 return ERROR_INVALID_PARAMETER;
838 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
840 int prefix_len = strlen(container->cache_prefix);
842 if(!strncmp(container->cache_prefix, url, prefix_len)) {
843 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
844 *ret = container;
845 return ERROR_SUCCESS;
849 ERR("no container found\n");
850 return ERROR_FILE_NOT_FOUND;
853 static BOOL cache_containers_enum(char *search_pattern, DWORD index, cache_container **ret)
855 DWORD i = 0;
856 cache_container *container;
858 TRACE("searching for prefix: %s\n", debugstr_a(search_pattern));
860 /* non-NULL search pattern only returns one container ever */
861 if (search_pattern && index > 0)
862 return FALSE;
864 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
866 if (search_pattern)
868 if (!strcmp(container->cache_prefix, search_pattern))
870 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
871 *ret = container;
872 return TRUE;
875 else
877 if (i == index)
879 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
880 *ret = container;
881 return TRUE;
884 i++;
886 return FALSE;
889 /***********************************************************************
890 * cache_container_lock_index (Internal)
892 * Locks the index for system-wide exclusive access.
894 * RETURNS
895 * Cache file header if successful
896 * NULL if failed and calls SetLastError.
898 static urlcache_header* cache_container_lock_index(cache_container *pContainer)
900 BYTE index;
901 LPVOID pIndexData;
902 urlcache_header* pHeader;
903 DWORD error;
905 /* acquire mutex */
906 WaitForSingleObject(pContainer->mutex, INFINITE);
908 pIndexData = MapViewOfFile(pContainer->mapping, FILE_MAP_WRITE, 0, 0, 0);
910 if (!pIndexData)
912 ReleaseMutex(pContainer->mutex);
913 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
914 return NULL;
916 pHeader = (urlcache_header*)pIndexData;
918 /* file has grown - we need to remap to prevent us getting
919 * access violations when we try and access beyond the end
920 * of the memory mapped file */
921 if (pHeader->size != pContainer->file_size)
923 UnmapViewOfFile( pHeader );
924 cache_container_close_index(pContainer);
925 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
926 if (error != ERROR_SUCCESS)
928 ReleaseMutex(pContainer->mutex);
929 SetLastError(error);
930 return NULL;
932 pIndexData = MapViewOfFile(pContainer->mapping, FILE_MAP_WRITE, 0, 0, 0);
934 if (!pIndexData)
936 ReleaseMutex(pContainer->mutex);
937 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
938 return NULL;
940 pHeader = (urlcache_header*)pIndexData;
943 TRACE("Signature: %s, file size: %d bytes\n", pHeader->signature, pHeader->size);
945 for (index = 0; index < pHeader->dirs_no; index++)
947 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].name);
950 return pHeader;
953 /***********************************************************************
954 * cache_container_unlock_index (Internal)
957 static BOOL cache_container_unlock_index(cache_container *pContainer, urlcache_header *pHeader)
959 /* release mutex */
960 ReleaseMutex(pContainer->mutex);
961 return UnmapViewOfFile(pHeader);
964 /***********************************************************************
965 * urlcache_create_file_pathW (Internal)
967 * Copies the full path to the specified buffer given the local file
968 * name and the index of the directory it is in. Always sets value in
969 * lpBufferSize to the required buffer size (in bytes).
971 * RETURNS
972 * TRUE if the buffer was big enough
973 * FALSE if the buffer was too small
976 static BOOL urlcache_create_file_pathW(
977 const cache_container *pContainer,
978 const urlcache_header *pHeader,
979 LPCSTR szLocalFileName,
980 BYTE Directory,
981 LPWSTR wszPath,
982 LPLONG lpBufferSize,
983 BOOL trunc_name)
985 LONG nRequired;
986 int path_len = strlenW(pContainer->path);
987 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
988 if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->dirs_no)
990 *lpBufferSize = 0;
991 return FALSE;
994 nRequired = (path_len + file_name_len) * sizeof(WCHAR);
995 if(Directory != CACHE_CONTAINER_NO_SUBDIR)
996 nRequired += (DIR_LENGTH + 1) * sizeof(WCHAR);
997 if (trunc_name && nRequired >= *lpBufferSize)
998 nRequired = *lpBufferSize;
999 if (nRequired <= *lpBufferSize)
1001 int dir_len;
1003 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
1004 if (Directory != CACHE_CONTAINER_NO_SUBDIR)
1006 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].name, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
1007 wszPath[dir_len + path_len] = '\\';
1008 dir_len++;
1010 else
1012 dir_len = 0;
1014 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len,
1015 *lpBufferSize/sizeof(WCHAR)-dir_len-path_len);
1016 wszPath[*lpBufferSize/sizeof(WCHAR)-1] = 0;
1017 *lpBufferSize = nRequired;
1018 return TRUE;
1020 *lpBufferSize = nRequired;
1021 return FALSE;
1024 /***********************************************************************
1025 * urlcache_create_file_pathA (Internal)
1027 * Copies the full path to the specified buffer given the local file
1028 * name and the index of the directory it is in. Always sets value in
1029 * lpBufferSize to the required buffer size.
1031 * RETURNS
1032 * TRUE if the buffer was big enough
1033 * FALSE if the buffer was too small
1036 static BOOL urlcache_create_file_pathA(
1037 const cache_container *pContainer,
1038 const urlcache_header *pHeader,
1039 LPCSTR szLocalFileName,
1040 BYTE Directory,
1041 LPSTR szPath,
1042 LPLONG lpBufferSize)
1044 LONG nRequired;
1045 int path_len, file_name_len, dir_len;
1047 if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->dirs_no)
1049 *lpBufferSize = 0;
1050 return FALSE;
1053 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
1054 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
1055 if (Directory!=CACHE_CONTAINER_NO_SUBDIR)
1056 dir_len = DIR_LENGTH+1;
1057 else
1058 dir_len = 0;
1060 nRequired = (path_len + dir_len + file_name_len) * sizeof(char);
1061 if (nRequired <= *lpBufferSize)
1063 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
1064 if(dir_len) {
1065 memcpy(szPath+path_len, pHeader->directory_data[Directory].name, dir_len-1);
1066 szPath[path_len + dir_len-1] = '\\';
1068 memcpy(szPath + path_len + dir_len, szLocalFileName, file_name_len);
1069 *lpBufferSize = nRequired;
1070 return TRUE;
1072 *lpBufferSize = nRequired;
1073 return FALSE;
1076 /* Just like FileTimeToDosDateTime, except that it also maps the special
1077 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1079 static void file_time_to_dos_date_time(const FILETIME *ft, WORD *fatdate,
1080 WORD *fattime)
1082 if (!ft->dwLowDateTime && !ft->dwHighDateTime)
1083 *fatdate = *fattime = 0;
1084 else
1085 FileTimeToDosDateTime(ft, fatdate, fattime);
1088 /***********************************************************************
1089 * urlcache_delete_file (Internal)
1091 static DWORD urlcache_delete_file(const cache_container *container,
1092 urlcache_header *header, entry_url *url_entry)
1094 WIN32_FILE_ATTRIBUTE_DATA attr;
1095 WCHAR path[MAX_PATH];
1096 LONG path_size = sizeof(path);
1097 DWORD err;
1098 WORD date, time;
1100 if(!url_entry->local_name_off)
1101 goto succ;
1103 if(!urlcache_create_file_pathW(container, header,
1104 (LPCSTR)url_entry+url_entry->local_name_off,
1105 url_entry->cache_dir, path, &path_size, FALSE))
1106 goto succ;
1108 if(!GetFileAttributesExW(path, GetFileExInfoStandard, &attr))
1109 goto succ;
1110 file_time_to_dos_date_time(&attr.ftLastWriteTime, &date, &time);
1111 if(date != url_entry->write_date || time != url_entry->write_time)
1112 goto succ;
1114 err = (DeleteFileW(path) ? ERROR_SUCCESS : GetLastError());
1115 if(err == ERROR_ACCESS_DENIED || err == ERROR_SHARING_VIOLATION)
1116 return err;
1118 succ:
1119 if (url_entry->cache_dir < header->dirs_no)
1121 if (header->directory_data[url_entry->cache_dir].files_no)
1122 header->directory_data[url_entry->cache_dir].files_no--;
1124 if (url_entry->cache_entry_type & STICKY_CACHE_ENTRY)
1126 if (url_entry->size.QuadPart < header->exempt_usage.QuadPart)
1127 header->exempt_usage.QuadPart -= url_entry->size.QuadPart;
1128 else
1129 header->exempt_usage.QuadPart = 0;
1131 else
1133 if (url_entry->size.QuadPart < header->cache_usage.QuadPart)
1134 header->cache_usage.QuadPart -= url_entry->size.QuadPart;
1135 else
1136 header->cache_usage.QuadPart = 0;
1139 return ERROR_SUCCESS;
1142 static BOOL urlcache_clean_leaked_entries(cache_container *container, urlcache_header *header)
1144 DWORD *leak_off;
1145 BOOL freed = FALSE;
1147 leak_off = &header->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
1148 while(*leak_off) {
1149 entry_url *url_entry = (entry_url*)((LPBYTE)header + *leak_off);
1151 if(SUCCEEDED(urlcache_delete_file(container, header, url_entry))) {
1152 *leak_off = url_entry->exempt_delta;
1153 urlcache_entry_free(header, &url_entry->header);
1154 freed = TRUE;
1155 }else {
1156 leak_off = &url_entry->exempt_delta;
1160 return freed;
1163 /***********************************************************************
1164 * cache_container_clean_index (Internal)
1166 * This function is meant to make place in index file by removing leaked
1167 * files entries and resizing the file.
1169 * CAUTION: file view may get mapped to new memory
1171 * RETURNS
1172 * ERROR_SUCCESS when new memory is available
1173 * error code otherwise
1175 static DWORD cache_container_clean_index(cache_container *container, urlcache_header **file_view)
1177 urlcache_header *header = *file_view;
1178 DWORD ret;
1180 TRACE("(%s %s)\n", debugstr_a(container->cache_prefix), debugstr_w(container->path));
1182 if(urlcache_clean_leaked_entries(container, header))
1183 return ERROR_SUCCESS;
1185 if(header->size >= ALLOCATION_TABLE_SIZE*8*BLOCKSIZE + ENTRY_START_OFFSET) {
1186 WARN("index file has maximal size\n");
1187 return ERROR_NOT_ENOUGH_MEMORY;
1190 cache_container_close_index(container);
1191 ret = cache_container_open_index(container, header->capacity_in_blocks*2);
1192 if(ret != ERROR_SUCCESS)
1193 return ret;
1194 header = MapViewOfFile(container->mapping, FILE_MAP_WRITE, 0, 0, 0);
1195 if(!header)
1196 return GetLastError();
1198 UnmapViewOfFile(*file_view);
1199 *file_view = header;
1200 return ERROR_SUCCESS;
1203 /* Just like DosDateTimeToFileTime, except that it also maps the special
1204 * case of a DOS date/time of (0,0) to a filetime of (0,0).
1206 static void dos_date_time_to_file_time(WORD fatdate, WORD fattime,
1207 FILETIME *ft)
1209 if (!fatdate && !fattime)
1210 ft->dwLowDateTime = ft->dwHighDateTime = 0;
1211 else
1212 DosDateTimeToFileTime(fatdate, fattime, ft);
1215 static int urlcache_decode_url(const char *url, WCHAR *decoded_url, int decoded_len)
1217 URL_COMPONENTSA uc;
1218 DWORD len, part_len;
1219 WCHAR *host_name;
1221 memset(&uc, 0, sizeof(uc));
1222 uc.dwStructSize = sizeof(uc);
1223 uc.dwHostNameLength = 1;
1224 if(!InternetCrackUrlA(url, 0, 0, &uc))
1225 uc.nScheme = INTERNET_SCHEME_UNKNOWN;
1227 if(uc.nScheme!=INTERNET_SCHEME_HTTP && uc.nScheme!=INTERNET_SCHEME_HTTPS)
1228 return MultiByteToWideChar(CP_UTF8, 0, url, -1, decoded_url, decoded_len);
1230 if(!decoded_url)
1231 decoded_len = 0;
1233 len = MultiByteToWideChar(CP_UTF8, 0, url, uc.lpszHostName-url, decoded_url, decoded_len);
1234 if(!len)
1235 return 0;
1236 if(decoded_url)
1237 decoded_len -= len;
1239 host_name = heap_alloc(uc.dwHostNameLength*sizeof(WCHAR));
1240 if(!host_name)
1241 return 0;
1242 if(!MultiByteToWideChar(CP_UTF8, 0, uc.lpszHostName, uc.dwHostNameLength,
1243 host_name, uc.dwHostNameLength)) {
1244 heap_free(host_name);
1245 return 0;
1247 part_len = IdnToUnicode(0, host_name, uc.dwHostNameLength,
1248 decoded_url ? decoded_url+len : NULL, decoded_len);
1249 heap_free(host_name);
1250 if(!part_len) {
1251 SetLastError(ERROR_INTERNET_INVALID_URL);
1252 return 0;
1254 len += part_len;
1255 if(decoded_url)
1256 decoded_len -= part_len;
1258 part_len = MultiByteToWideChar(CP_UTF8, 0,
1259 uc.lpszHostName+uc.dwHostNameLength,
1260 -1, decoded_url ? decoded_url+len : NULL, decoded_len);
1261 if(!part_len)
1262 return 0;
1263 len += part_len;
1265 return len;
1268 /***********************************************************************
1269 * urlcache_copy_entry (Internal)
1271 * Copies an entry from the cache index file to the Win32 structure
1273 * RETURNS
1274 * ERROR_SUCCESS if the buffer was big enough
1275 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1278 static DWORD urlcache_copy_entry(cache_container *container, const urlcache_header *header,
1279 INTERNET_CACHE_ENTRY_INFOA *entry_info, DWORD *info_size, const entry_url *url_entry, BOOL unicode)
1281 int url_len;
1282 DWORD size = sizeof(*entry_info);
1284 if(*info_size >= size) {
1285 entry_info->lpHeaderInfo = NULL;
1286 entry_info->lpszFileExtension = NULL;
1287 entry_info->lpszLocalFileName = NULL;
1288 entry_info->lpszSourceUrlName = NULL;
1289 entry_info->CacheEntryType = url_entry->cache_entry_type;
1290 entry_info->u.dwExemptDelta = url_entry->exempt_delta;
1291 entry_info->dwHeaderInfoSize = url_entry->header_info_size;
1292 entry_info->dwHitRate = url_entry->hit_rate;
1293 entry_info->dwSizeHigh = url_entry->size.u.HighPart;
1294 entry_info->dwSizeLow = url_entry->size.u.LowPart;
1295 entry_info->dwStructSize = sizeof(*entry_info);
1296 entry_info->dwUseCount = url_entry->use_count;
1297 dos_date_time_to_file_time(url_entry->expire_date, url_entry->expire_time, &entry_info->ExpireTime);
1298 entry_info->LastAccessTime = url_entry->access_time;
1299 entry_info->LastModifiedTime = url_entry->modification_time;
1300 dos_date_time_to_file_time(url_entry->sync_date, url_entry->sync_time, &entry_info->LastSyncTime);
1303 if(unicode)
1304 url_len = urlcache_decode_url((const char*)url_entry+url_entry->url_off, NULL, 0);
1305 else
1306 url_len = strlen((LPCSTR)url_entry+url_entry->url_off) + 1;
1307 size += url_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1309 if(*info_size >= size) {
1310 DWORD url_size = url_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1312 entry_info->lpszSourceUrlName = (LPSTR)entry_info+size-url_size;
1313 if(unicode)
1314 urlcache_decode_url((const char*)url_entry+url_entry->url_off, (WCHAR*)entry_info->lpszSourceUrlName, url_len);
1315 else
1316 memcpy(entry_info->lpszSourceUrlName, (LPCSTR)url_entry+url_entry->url_off, url_size);
1319 if(size%4 && size<*info_size)
1320 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1321 size = DWORD_ALIGN(size);
1323 if(url_entry->local_name_off) {
1324 LONG file_name_size;
1325 LPSTR file_name;
1326 file_name = (LPSTR)entry_info+size;
1327 file_name_size = *info_size-size;
1328 if((unicode && urlcache_create_file_pathW(container, header, (LPCSTR)url_entry+url_entry->local_name_off,
1329 url_entry->cache_dir, (LPWSTR)file_name, &file_name_size, FALSE)) ||
1330 (!unicode && urlcache_create_file_pathA(container, header, (LPCSTR)url_entry+url_entry->local_name_off,
1331 url_entry->cache_dir, file_name, &file_name_size))) {
1332 entry_info->lpszLocalFileName = file_name;
1334 size += file_name_size;
1336 if(size%4 && size<*info_size)
1337 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1338 size = DWORD_ALIGN(size);
1341 if(url_entry->header_info_off) {
1342 DWORD header_len;
1344 if(unicode)
1345 header_len = MultiByteToWideChar(CP_UTF8, 0, (const char*)url_entry+url_entry->header_info_off,
1346 url_entry->header_info_size, NULL, 0);
1347 else
1348 header_len = url_entry->header_info_size;
1349 size += header_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1351 if(*info_size >= size) {
1352 DWORD header_size = header_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1353 entry_info->lpHeaderInfo = (LPBYTE)entry_info+size-header_size;
1354 if(unicode)
1355 MultiByteToWideChar(CP_UTF8, 0, (const char*)url_entry+url_entry->header_info_off,
1356 url_entry->header_info_size, (LPWSTR)entry_info->lpHeaderInfo, header_len);
1357 else
1358 memcpy(entry_info->lpHeaderInfo, (LPCSTR)url_entry+url_entry->header_info_off, header_len);
1360 if(size%4 && size<*info_size)
1361 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1362 size = DWORD_ALIGN(size);
1365 if(url_entry->file_extension_off) {
1366 int ext_len;
1368 if(unicode)
1369 ext_len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)url_entry+url_entry->file_extension_off, -1, NULL, 0);
1370 else
1371 ext_len = strlen((LPCSTR)url_entry+url_entry->file_extension_off) + 1;
1372 size += ext_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1374 if(*info_size >= size) {
1375 DWORD ext_size = ext_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1376 entry_info->lpszFileExtension = (LPSTR)entry_info+size-ext_size;
1377 if(unicode)
1378 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)url_entry+url_entry->file_extension_off, -1, (LPWSTR)entry_info->lpszFileExtension, ext_len);
1379 else
1380 memcpy(entry_info->lpszFileExtension, (LPCSTR)url_entry+url_entry->file_extension_off, ext_len*sizeof(CHAR));
1383 if(size%4 && size<*info_size)
1384 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1385 size = DWORD_ALIGN(size);
1388 if(size > *info_size) {
1389 *info_size = size;
1390 return ERROR_INSUFFICIENT_BUFFER;
1392 *info_size = size;
1393 return ERROR_SUCCESS;
1396 /***********************************************************************
1397 * urlcache_set_entry_info (Internal)
1399 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1400 * according to the flags set by field_control.
1402 * RETURNS
1403 * ERROR_SUCCESS if the buffer was big enough
1404 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1407 static DWORD urlcache_set_entry_info(entry_url *url_entry, const INTERNET_CACHE_ENTRY_INFOA *entry_info, DWORD field_control)
1409 if (field_control & CACHE_ENTRY_ACCTIME_FC)
1410 url_entry->access_time = entry_info->LastAccessTime;
1411 if (field_control & CACHE_ENTRY_ATTRIBUTE_FC)
1412 url_entry->cache_entry_type = entry_info->CacheEntryType;
1413 if (field_control & CACHE_ENTRY_EXEMPT_DELTA_FC)
1414 url_entry->exempt_delta = entry_info->u.dwExemptDelta;
1415 if (field_control & CACHE_ENTRY_EXPTIME_FC)
1416 file_time_to_dos_date_time(&entry_info->ExpireTime, &url_entry->expire_date, &url_entry->expire_time);
1417 if (field_control & CACHE_ENTRY_HEADERINFO_FC)
1418 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1419 if (field_control & CACHE_ENTRY_HITRATE_FC)
1420 url_entry->hit_rate = entry_info->dwHitRate;
1421 if (field_control & CACHE_ENTRY_MODTIME_FC)
1422 url_entry->modification_time = entry_info->LastModifiedTime;
1423 if (field_control & CACHE_ENTRY_SYNCTIME_FC)
1424 file_time_to_dos_date_time(&entry_info->LastAccessTime, &url_entry->sync_date, &url_entry->sync_time);
1426 return ERROR_SUCCESS;
1429 /***********************************************************************
1430 * urlcache_hash_key (Internal)
1432 * Returns the hash key for a given string
1434 * RETURNS
1435 * hash key for the string
1438 static DWORD urlcache_hash_key(LPCSTR lpszKey)
1440 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1441 * but the algorithm and result are not the same!
1443 static const unsigned char lookupTable[256] =
1445 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1446 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1447 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1448 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1449 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1450 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1451 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1452 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1453 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1454 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1455 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1456 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1457 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1458 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1459 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1460 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1461 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1462 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1463 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1464 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1465 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1466 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1467 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1468 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1469 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1470 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1471 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1472 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1473 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1474 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1475 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1476 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1478 BYTE key[4];
1479 DWORD i;
1481 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1482 key[i] = lookupTable[(*lpszKey + i) & 0xFF];
1484 for (lpszKey++; *lpszKey; lpszKey++)
1486 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1487 key[i] = lookupTable[*lpszKey ^ key[i]];
1490 return *(DWORD *)key;
1493 static inline entry_hash_table* urlcache_get_hash_table(const urlcache_header *pHeader, DWORD dwOffset)
1495 if(!dwOffset)
1496 return NULL;
1497 return (entry_hash_table*)((LPBYTE)pHeader + dwOffset);
1500 static BOOL urlcache_find_hash_entry(const urlcache_header *pHeader, LPCSTR lpszUrl, struct hash_entry **ppHashEntry)
1502 /* structure of hash table:
1503 * 448 entries divided into 64 blocks
1504 * each block therefore contains a chain of 7 key/offset pairs
1505 * how position in table is calculated:
1506 * 1. the url is hashed in helper function
1507 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1508 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1510 * note:
1511 * there can be multiple hash tables in the file and the offset to
1512 * the next one is stored in the header of the hash table
1514 DWORD key = urlcache_hash_key(lpszUrl);
1515 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1516 entry_hash_table* pHashEntry;
1517 DWORD id = 0;
1519 key >>= HASHTABLE_FLAG_BITS;
1521 for (pHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1522 pHashEntry; pHashEntry = urlcache_get_hash_table(pHeader, pHashEntry->next))
1524 int i;
1525 if (pHashEntry->id != id++)
1527 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->id, id);
1528 continue;
1530 /* make sure that it is in fact a hash entry */
1531 if (pHashEntry->header.signature != HASH_SIGNATURE)
1533 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->header.signature);
1534 continue;
1537 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1539 struct hash_entry *pHashElement = &pHashEntry->hash_table[offset + i];
1540 if (key == pHashElement->key>>HASHTABLE_FLAG_BITS)
1542 /* FIXME: we should make sure that this is the right element
1543 * before returning and claiming that it is. We can do this
1544 * by doing a simple compare between the URL we were given
1545 * and the URL stored in the entry. However, this assumes
1546 * we know the format of all the entries stored in the
1547 * hash table */
1548 *ppHashEntry = pHashElement;
1549 return TRUE;
1553 return FALSE;
1556 /***********************************************************************
1557 * urlcache_hash_entry_set_flags (Internal)
1559 * Sets special bits in hash key
1561 * RETURNS
1562 * nothing
1565 static void urlcache_hash_entry_set_flags(struct hash_entry *pHashEntry, DWORD dwFlag)
1567 pHashEntry->key = (pHashEntry->key >> HASHTABLE_FLAG_BITS << HASHTABLE_FLAG_BITS) | dwFlag;
1570 /***********************************************************************
1571 * urlcache_hash_entry_delete (Internal)
1573 * Searches all the hash tables in the index for the given URL and
1574 * then if found deletes the entry.
1576 * RETURNS
1577 * TRUE if the entry was found
1578 * FALSE if the entry could not be found
1581 static BOOL urlcache_hash_entry_delete(struct hash_entry *pHashEntry)
1583 pHashEntry->key = HASHTABLE_DEL;
1584 return TRUE;
1587 /***********************************************************************
1588 * urlcache_hash_entry_create (Internal)
1590 * Searches all the hash tables for a free slot based on the offset
1591 * generated from the hash key. If a free slot is found, the offset and
1592 * key are entered into the hash table.
1594 * RETURNS
1595 * ERROR_SUCCESS if the entry was added
1596 * Any other Win32 error code if the entry could not be added
1599 static DWORD urlcache_hash_entry_create(urlcache_header *pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry, DWORD dwFieldType)
1601 /* see urlcache_find_hash_entry for structure of hash tables */
1603 DWORD key = urlcache_hash_key(lpszUrl);
1604 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1605 entry_hash_table* pHashEntry, *pHashPrev = NULL;
1606 DWORD id = 0;
1607 DWORD error;
1609 key = ((key >> HASHTABLE_FLAG_BITS) << HASHTABLE_FLAG_BITS) + dwFieldType;
1611 for (pHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1612 pHashEntry; pHashEntry = urlcache_get_hash_table(pHeader, pHashEntry->next))
1614 int i;
1615 pHashPrev = pHashEntry;
1617 if (pHashEntry->id != id++)
1619 ERR("not right hash table number (%d) expected %d\n", pHashEntry->id, id);
1620 break;
1622 /* make sure that it is in fact a hash entry */
1623 if (pHashEntry->header.signature != HASH_SIGNATURE)
1625 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->header.signature);
1626 break;
1629 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1631 struct hash_entry *pHashElement = &pHashEntry->hash_table[offset + i];
1632 if (pHashElement->key==HASHTABLE_FREE || pHashElement->key==HASHTABLE_DEL) /* if the slot is free */
1634 pHashElement->key = key;
1635 pHashElement->offset = dwOffsetEntry;
1636 return ERROR_SUCCESS;
1640 error = urlcache_create_hash_table(pHeader, pHashPrev, &pHashEntry);
1641 if (error != ERROR_SUCCESS)
1642 return error;
1644 pHashEntry->hash_table[offset].key = key;
1645 pHashEntry->hash_table[offset].offset = dwOffsetEntry;
1646 return ERROR_SUCCESS;
1649 /***********************************************************************
1650 * urlcache_enum_hash_tables (Internal)
1652 * Enumerates the hash tables in a container.
1654 * RETURNS
1655 * TRUE if an entry was found
1656 * FALSE if there are no more tables to enumerate.
1659 static BOOL urlcache_enum_hash_tables(const urlcache_header *pHeader, DWORD *id, entry_hash_table **ppHashEntry)
1661 for (*ppHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1662 *ppHashEntry; *ppHashEntry = urlcache_get_hash_table(pHeader, (*ppHashEntry)->next))
1664 TRACE("looking at hash table number %d\n", (*ppHashEntry)->id);
1665 if ((*ppHashEntry)->id != *id)
1666 continue;
1667 /* make sure that it is in fact a hash entry */
1668 if ((*ppHashEntry)->header.signature != HASH_SIGNATURE)
1670 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->header.signature);
1671 (*id)++;
1672 continue;
1675 TRACE("hash table number %d found\n", *id);
1676 return TRUE;
1678 return FALSE;
1681 /***********************************************************************
1682 * urlcache_enum_hash_table_entries (Internal)
1684 * Enumerates entries in a hash table and returns the next non-free entry.
1686 * RETURNS
1687 * TRUE if an entry was found
1688 * FALSE if the hash table is empty or there are no more entries to
1689 * enumerate.
1692 static BOOL urlcache_enum_hash_table_entries(const urlcache_header *pHeader, const entry_hash_table *pHashEntry,
1693 DWORD * index, const struct hash_entry **ppHashEntry)
1695 for (; *index < HASHTABLE_SIZE ; (*index)++)
1697 if (pHashEntry->hash_table[*index].key==HASHTABLE_FREE || pHashEntry->hash_table[*index].key==HASHTABLE_DEL)
1698 continue;
1700 *ppHashEntry = &pHashEntry->hash_table[*index];
1701 TRACE("entry found %d\n", *index);
1702 return TRUE;
1704 TRACE("no more entries (%d)\n", *index);
1705 return FALSE;
1708 /***********************************************************************
1709 * cache_container_delete_dir (Internal)
1711 * Erase a directory containing an URL cache.
1713 * RETURNS
1714 * TRUE success, FALSE failure/aborted.
1717 static BOOL cache_container_delete_dir(LPCWSTR lpszPath)
1719 DWORD path_len;
1720 WCHAR path[MAX_PATH + 1];
1721 SHFILEOPSTRUCTW shfos;
1722 int ret;
1724 path_len = strlenW(lpszPath);
1725 if (path_len >= MAX_PATH)
1726 return FALSE;
1727 strcpyW(path, lpszPath);
1728 path[path_len + 1] = 0; /* double-NUL-terminate path */
1730 shfos.hwnd = NULL;
1731 shfos.wFunc = FO_DELETE;
1732 shfos.pFrom = path;
1733 shfos.pTo = NULL;
1734 shfos.fFlags = FOF_NOCONFIRMATION;
1735 shfos.fAnyOperationsAborted = FALSE;
1736 ret = SHFileOperationW(&shfos);
1737 if (ret)
1738 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path), ret);
1739 return !(ret || shfos.fAnyOperationsAborted);
1742 /***********************************************************************
1743 * urlcache_hash_entry_is_locked (Internal)
1745 * Checks if entry is locked. Unlocks it if possible.
1747 static BOOL urlcache_hash_entry_is_locked(struct hash_entry *hash_entry, entry_url *url_entry)
1749 FILETIME cur_time;
1750 ULARGE_INTEGER acc_time, time;
1752 if ((hash_entry->key & ((1<<HASHTABLE_FLAG_BITS)-1)) != HASHTABLE_LOCK)
1753 return FALSE;
1755 GetSystemTimeAsFileTime(&cur_time);
1756 time.u.LowPart = cur_time.dwLowDateTime;
1757 time.u.HighPart = cur_time.dwHighDateTime;
1759 acc_time.u.LowPart = url_entry->access_time.dwLowDateTime;
1760 acc_time.u.HighPart = url_entry->access_time.dwHighDateTime;
1762 time.QuadPart -= acc_time.QuadPart;
1764 /* check if entry was locked for at least a day */
1765 if(time.QuadPart > (ULONGLONG)24*60*60*FILETIME_SECOND) {
1766 urlcache_hash_entry_set_flags(hash_entry, HASHTABLE_URL);
1767 url_entry->use_count = 0;
1768 return FALSE;
1771 return TRUE;
1774 static BOOL urlcache_get_entry_info(const char *url, void *entry_info,
1775 DWORD *size, DWORD flags, BOOL unicode)
1777 urlcache_header *header;
1778 struct hash_entry *hash_entry;
1779 const entry_url *url_entry;
1780 cache_container *container;
1781 DWORD error;
1783 TRACE("(%s, %p, %p, %x, %x)\n", debugstr_a(url), entry_info, size, flags, unicode);
1785 if(flags & ~GET_INSTALLED_ENTRY)
1786 FIXME("ignoring unsupported flags: %x\n", flags);
1788 error = cache_containers_find(url, &container);
1789 if(error != ERROR_SUCCESS) {
1790 SetLastError(error);
1791 return FALSE;
1794 error = cache_container_open_index(container, MIN_BLOCK_NO);
1795 if(error != ERROR_SUCCESS) {
1796 SetLastError(error);
1797 return FALSE;
1800 if(!(header = cache_container_lock_index(container)))
1801 return FALSE;
1803 if(!urlcache_find_hash_entry(header, url, &hash_entry)) {
1804 cache_container_unlock_index(container, header);
1805 WARN("entry %s not found!\n", debugstr_a(url));
1806 SetLastError(ERROR_FILE_NOT_FOUND);
1807 return FALSE;
1810 url_entry = (const entry_url*)((LPBYTE)header + hash_entry->offset);
1811 if(url_entry->header.signature != URL_SIGNATURE) {
1812 cache_container_unlock_index(container, header);
1813 FIXME("Trying to retrieve entry of unknown format %s\n",
1814 debugstr_an((LPCSTR)&url_entry->header.signature, sizeof(DWORD)));
1815 SetLastError(ERROR_FILE_NOT_FOUND);
1816 return FALSE;
1819 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->url_off));
1820 TRACE("Header info: %s\n", debugstr_an((LPCSTR)url_entry +
1821 url_entry->header_info_off, url_entry->header_info_size));
1823 if((flags & GET_INSTALLED_ENTRY) && !(url_entry->cache_entry_type & INSTALLED_CACHE_ENTRY)) {
1824 cache_container_unlock_index(container, header);
1825 SetLastError(ERROR_FILE_NOT_FOUND);
1826 return FALSE;
1829 if(size) {
1830 if(!entry_info)
1831 *size = 0;
1833 error = urlcache_copy_entry(container, header, entry_info, size, url_entry, unicode);
1834 if(error != ERROR_SUCCESS) {
1835 cache_container_unlock_index(container, header);
1836 SetLastError(error);
1837 return FALSE;
1839 if(url_entry->local_name_off)
1840 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->local_name_off));
1843 cache_container_unlock_index(container, header);
1844 return TRUE;
1847 /***********************************************************************
1848 * GetUrlCacheEntryInfoExA (WININET.@)
1851 BOOL WINAPI GetUrlCacheEntryInfoExA(LPCSTR lpszUrl,
1852 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1853 LPDWORD lpdwCacheEntryInfoBufSize, LPSTR lpszReserved,
1854 LPDWORD lpdwReserved, LPVOID lpReserved, DWORD dwFlags)
1856 if(lpszReserved!=NULL || lpdwReserved!=NULL || lpReserved!=NULL) {
1857 ERR("Reserved value was not 0\n");
1858 SetLastError(ERROR_INVALID_PARAMETER);
1859 return FALSE;
1862 return urlcache_get_entry_info(lpszUrl, lpCacheEntryInfo,
1863 lpdwCacheEntryInfoBufSize, dwFlags, FALSE);
1866 /***********************************************************************
1867 * GetUrlCacheEntryInfoA (WININET.@)
1870 BOOL WINAPI GetUrlCacheEntryInfoA(LPCSTR lpszUrlName,
1871 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1872 LPDWORD lpdwCacheEntryInfoBufferSize)
1874 return GetUrlCacheEntryInfoExA(lpszUrlName, lpCacheEntryInfo,
1875 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
1878 static int urlcache_encode_url(const WCHAR *url, char *encoded_url, int encoded_len)
1880 URL_COMPONENTSW uc;
1881 DWORD len, part_len;
1882 WCHAR *punycode;
1884 TRACE("%s\n", debugstr_w(url));
1886 memset(&uc, 0, sizeof(uc));
1887 uc.dwStructSize = sizeof(uc);
1888 uc.dwHostNameLength = 1;
1889 if(!InternetCrackUrlW(url, 0, 0, &uc))
1890 uc.nScheme = INTERNET_SCHEME_UNKNOWN;
1892 if(uc.nScheme!=INTERNET_SCHEME_HTTP && uc.nScheme!=INTERNET_SCHEME_HTTPS)
1893 return WideCharToMultiByte(CP_UTF8, 0, url, -1, encoded_url, encoded_len, NULL, NULL);
1895 len = WideCharToMultiByte(CP_UTF8, 0, url, uc.lpszHostName-url,
1896 encoded_url, encoded_len, NULL, NULL);
1897 if(!len)
1898 return 0;
1899 if(encoded_url)
1900 encoded_len -= len;
1902 part_len = IdnToAscii(0, uc.lpszHostName, uc.dwHostNameLength, NULL, 0);
1903 if(!part_len) {
1904 SetLastError(ERROR_INTERNET_INVALID_URL);
1905 return 0;
1908 punycode = heap_alloc(part_len*sizeof(WCHAR));
1909 if(!punycode)
1910 return 0;
1912 part_len = IdnToAscii(0, uc.lpszHostName, uc.dwHostNameLength, punycode, part_len);
1913 if(!part_len) {
1914 heap_free(punycode);
1915 return 0;
1918 part_len = WideCharToMultiByte(CP_UTF8, 0, punycode, part_len,
1919 encoded_url ? encoded_url+len : NULL, encoded_len, NULL, NULL);
1920 heap_free(punycode);
1921 if(!part_len)
1922 return 0;
1923 if(encoded_url)
1924 encoded_len -= part_len;
1925 len += part_len;
1927 part_len = WideCharToMultiByte(CP_UTF8, 0, uc.lpszHostName+uc.dwHostNameLength,
1928 -1, encoded_url ? encoded_url+len : NULL, encoded_len, NULL, NULL);
1929 if(!part_len)
1930 return 0;
1931 len += part_len;
1933 TRACE("got (%d)%s\n", len, debugstr_a(encoded_url));
1934 return len;
1937 static BOOL urlcache_encode_url_alloc(const WCHAR *url, char **encoded_url)
1939 DWORD encoded_len;
1940 char *ret;
1942 encoded_len = urlcache_encode_url(url, NULL, 0);
1943 if(!encoded_len)
1944 return FALSE;
1946 ret = heap_alloc(encoded_len*sizeof(WCHAR));
1947 if(!ret)
1948 return FALSE;
1950 encoded_len = urlcache_encode_url(url, ret, encoded_len);
1951 if(!encoded_len) {
1952 heap_free(ret);
1953 return FALSE;
1956 *encoded_url = ret;
1957 return TRUE;
1960 /***********************************************************************
1961 * GetUrlCacheEntryInfoExW (WININET.@)
1964 BOOL WINAPI GetUrlCacheEntryInfoExW(LPCWSTR lpszUrl,
1965 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1966 LPDWORD lpdwCacheEntryInfoBufSize, LPWSTR lpszReserved,
1967 LPDWORD lpdwReserved, LPVOID lpReserved, DWORD dwFlags)
1969 char *url;
1970 BOOL ret;
1972 if(lpszReserved!=NULL || lpdwReserved!=NULL || lpReserved!=NULL) {
1973 ERR("Reserved value was not 0\n");
1974 SetLastError(ERROR_INVALID_PARAMETER);
1975 return FALSE;
1978 /* Ignore GET_INSTALLED_ENTRY flag in unicode version of function */
1979 dwFlags &= ~GET_INSTALLED_ENTRY;
1981 if(!urlcache_encode_url_alloc(lpszUrl, &url))
1982 return FALSE;
1984 ret = urlcache_get_entry_info(url, lpCacheEntryInfo,
1985 lpdwCacheEntryInfoBufSize, dwFlags, TRUE);
1986 heap_free(url);
1987 return ret;
1990 /***********************************************************************
1991 * GetUrlCacheEntryInfoW (WININET.@)
1994 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
1995 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1996 LPDWORD lpdwCacheEntryInfoBufferSize)
1998 return GetUrlCacheEntryInfoExW(lpszUrl, lpCacheEntryInfo,
1999 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
2002 /***********************************************************************
2003 * SetUrlCacheEntryInfoA (WININET.@)
2005 BOOL WINAPI SetUrlCacheEntryInfoA(LPCSTR lpszUrlName,
2006 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2007 DWORD dwFieldControl)
2009 urlcache_header *pHeader;
2010 struct hash_entry *pHashEntry;
2011 entry_header *pEntry;
2012 cache_container *pContainer;
2013 DWORD error;
2015 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
2017 error = cache_containers_find(lpszUrlName, &pContainer);
2018 if (error != ERROR_SUCCESS)
2020 SetLastError(error);
2021 return FALSE;
2024 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2025 if (error != ERROR_SUCCESS)
2027 SetLastError(error);
2028 return FALSE;
2031 if (!(pHeader = cache_container_lock_index(pContainer)))
2032 return FALSE;
2034 if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
2036 cache_container_unlock_index(pContainer, pHeader);
2037 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
2038 SetLastError(ERROR_FILE_NOT_FOUND);
2039 return FALSE;
2042 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2043 if (pEntry->signature != URL_SIGNATURE)
2045 cache_container_unlock_index(pContainer, pHeader);
2046 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2047 SetLastError(ERROR_FILE_NOT_FOUND);
2048 return FALSE;
2051 urlcache_set_entry_info((entry_url*)pEntry, lpCacheEntryInfo, dwFieldControl);
2053 cache_container_unlock_index(pContainer, pHeader);
2055 return TRUE;
2058 /***********************************************************************
2059 * SetUrlCacheEntryInfoW (WININET.@)
2061 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
2062 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2063 DWORD dwFieldControl)
2065 char *url;
2066 BOOL ret;
2068 if(!urlcache_encode_url_alloc(lpszUrl, &url))
2069 return FALSE;
2071 ret = SetUrlCacheEntryInfoA(url, (INTERNET_CACHE_ENTRY_INFOA*)lpCacheEntryInfo, dwFieldControl);
2072 heap_free(url);
2073 return ret;
2076 static BOOL urlcache_entry_get_file(const char *url, void *entry_info, DWORD *size, BOOL unicode)
2078 urlcache_header *header;
2079 struct hash_entry *hash_entry;
2080 entry_url *url_entry;
2081 cache_container *container;
2082 DWORD error;
2084 TRACE("(%s, %p, %p, %x)\n", debugstr_a(url), entry_info, size, unicode);
2086 if(!url || !size || (!entry_info && *size)) {
2087 SetLastError(ERROR_INVALID_PARAMETER);
2088 return FALSE;
2091 error = cache_containers_find(url, &container);
2092 if(error != ERROR_SUCCESS) {
2093 SetLastError(error);
2094 return FALSE;
2097 error = cache_container_open_index(container, MIN_BLOCK_NO);
2098 if (error != ERROR_SUCCESS) {
2099 SetLastError(error);
2100 return FALSE;
2103 if (!(header = cache_container_lock_index(container)))
2104 return FALSE;
2106 if (!urlcache_find_hash_entry(header, url, &hash_entry)) {
2107 cache_container_unlock_index(container, header);
2108 TRACE("entry %s not found!\n", debugstr_a(url));
2109 SetLastError(ERROR_FILE_NOT_FOUND);
2110 return FALSE;
2113 url_entry = (entry_url*)((LPBYTE)header + hash_entry->offset);
2114 if(url_entry->header.signature != URL_SIGNATURE) {
2115 cache_container_unlock_index(container, header);
2116 FIXME("Trying to retrieve entry of unknown format %s\n",
2117 debugstr_an((LPSTR)&url_entry->header.signature, sizeof(DWORD)));
2118 SetLastError(ERROR_FILE_NOT_FOUND);
2119 return FALSE;
2122 if(!url_entry->local_name_off) {
2123 cache_container_unlock_index(container, header);
2124 SetLastError(ERROR_INVALID_DATA);
2125 return FALSE;
2128 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->url_off));
2129 TRACE("Header info: %s\n", debugstr_an((LPCSTR)url_entry + url_entry->header_info_off,
2130 url_entry->header_info_size));
2132 error = urlcache_copy_entry(container, header, entry_info,
2133 size, url_entry, unicode);
2134 if(error != ERROR_SUCCESS) {
2135 cache_container_unlock_index(container, header);
2136 SetLastError(error);
2137 return FALSE;
2139 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->local_name_off));
2141 url_entry->hit_rate++;
2142 url_entry->use_count++;
2143 urlcache_hash_entry_set_flags(hash_entry, HASHTABLE_LOCK);
2144 GetSystemTimeAsFileTime(&url_entry->access_time);
2146 cache_container_unlock_index(container, header);
2148 return TRUE;
2151 /***********************************************************************
2152 * RetrieveUrlCacheEntryFileA (WININET.@)
2155 BOOL WINAPI RetrieveUrlCacheEntryFileA(LPCSTR lpszUrlName,
2156 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2157 LPDWORD lpdwCacheEntryInfoBufferSize, DWORD dwReserved)
2159 return urlcache_entry_get_file(lpszUrlName, lpCacheEntryInfo,
2160 lpdwCacheEntryInfoBufferSize, FALSE);
2163 /***********************************************************************
2164 * RetrieveUrlCacheEntryFileW (WININET.@)
2167 BOOL WINAPI RetrieveUrlCacheEntryFileW(LPCWSTR lpszUrlName,
2168 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2169 LPDWORD lpdwCacheEntryInfoBufferSize, DWORD dwReserved)
2171 char *url;
2172 BOOL ret;
2174 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
2175 return FALSE;
2177 ret = urlcache_entry_get_file(url, lpCacheEntryInfo,
2178 lpdwCacheEntryInfoBufferSize, TRUE);
2179 heap_free(url);
2180 return ret;
2183 static BOOL urlcache_entry_delete(const cache_container *pContainer,
2184 urlcache_header *pHeader, struct hash_entry *pHashEntry)
2186 entry_header *pEntry;
2187 entry_url * pUrlEntry;
2189 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2190 if (pEntry->signature != URL_SIGNATURE)
2192 FIXME("Trying to delete entry of unknown format %s\n",
2193 debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
2194 SetLastError(ERROR_FILE_NOT_FOUND);
2195 return FALSE;
2198 pUrlEntry = (entry_url *)pEntry;
2199 if(urlcache_hash_entry_is_locked(pHashEntry, pUrlEntry))
2201 TRACE("Trying to delete locked entry\n");
2202 pUrlEntry->cache_entry_type |= PENDING_DELETE_CACHE_ENTRY;
2203 SetLastError(ERROR_SHARING_VIOLATION);
2204 return FALSE;
2207 if(!urlcache_delete_file(pContainer, pHeader, pUrlEntry))
2209 urlcache_entry_free(pHeader, pEntry);
2211 else
2213 /* Add entry to leaked files list */
2214 pUrlEntry->header.signature = LEAK_SIGNATURE;
2215 pUrlEntry->exempt_delta = pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
2216 pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET] = pHashEntry->offset;
2219 urlcache_hash_entry_delete(pHashEntry);
2220 return TRUE;
2223 static HANDLE free_cache_running;
2224 static HANDLE dll_unload_event;
2225 static DWORD WINAPI handle_full_cache_worker(void *param)
2227 FreeUrlCacheSpaceW(NULL, 20, 0);
2228 ReleaseSemaphore(free_cache_running, 1, NULL);
2229 return 0;
2232 static void handle_full_cache(void)
2234 if(WaitForSingleObject(free_cache_running, 0) == WAIT_OBJECT_0) {
2235 if(!QueueUserWorkItem(handle_full_cache_worker, NULL, 0))
2236 ReleaseSemaphore(free_cache_running, 1, NULL);
2240 /* Enumerates entries in cache, allows cache unlocking between calls. */
2241 static BOOL urlcache_next_entry(urlcache_header *header, DWORD *hash_table_off, DWORD *hash_table_entry,
2242 struct hash_entry **hash_entry, entry_header **entry)
2244 entry_hash_table *hashtable_entry;
2246 *hash_entry = NULL;
2247 *entry = NULL;
2249 if(!*hash_table_off) {
2250 *hash_table_off = header->hash_table_off;
2251 *hash_table_entry = 0;
2253 hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2254 }else {
2255 if(*hash_table_off >= header->size) {
2256 *hash_table_off = 0;
2257 return FALSE;
2260 hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2263 if(hashtable_entry->header.signature != HASH_SIGNATURE) {
2264 *hash_table_off = 0;
2265 return FALSE;
2268 while(1) {
2269 if(*hash_table_entry >= HASHTABLE_SIZE) {
2270 *hash_table_off = hashtable_entry->next;
2271 if(!*hash_table_off) {
2272 *hash_table_off = 0;
2273 return FALSE;
2276 hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2277 *hash_table_entry = 0;
2280 if(hashtable_entry->hash_table[*hash_table_entry].key != HASHTABLE_DEL &&
2281 hashtable_entry->hash_table[*hash_table_entry].key != HASHTABLE_FREE) {
2282 *hash_entry = &hashtable_entry->hash_table[*hash_table_entry];
2283 *entry = (entry_header*)((LPBYTE)header + hashtable_entry->hash_table[*hash_table_entry].offset);
2284 (*hash_table_entry)++;
2285 return TRUE;
2288 (*hash_table_entry)++;
2291 *hash_table_off = 0;
2292 return FALSE;
2295 /* Rates an urlcache entry to determine if it can be deleted.
2297 * Score 0 means that entry can safely be removed, the bigger rating
2298 * the smaller chance of entry being removed.
2299 * DWORD_MAX means that entry can't be deleted at all.
2301 * Rating system is currently not fully compatible with native implementation.
2303 static DWORD urlcache_rate_entry(entry_url *url_entry, FILETIME *cur_time)
2305 ULARGE_INTEGER time, access_time;
2306 DWORD rating;
2308 access_time.u.LowPart = url_entry->access_time.dwLowDateTime;
2309 access_time.u.HighPart = url_entry->access_time.dwHighDateTime;
2311 time.u.LowPart = cur_time->dwLowDateTime;
2312 time.u.HighPart = cur_time->dwHighDateTime;
2314 /* Don't touch entries that were added less than 10 minutes ago */
2315 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)10*60*FILETIME_SECOND)
2316 return -1;
2318 if(url_entry->cache_entry_type & STICKY_CACHE_ENTRY)
2319 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)url_entry->exempt_delta*FILETIME_SECOND)
2320 return -1;
2322 time.QuadPart = (time.QuadPart-access_time.QuadPart)/FILETIME_SECOND;
2323 rating = 400*60*60*24/(60*60*24+time.QuadPart);
2325 if(url_entry->hit_rate > 100)
2326 rating += 100;
2327 else
2328 rating += url_entry->hit_rate;
2330 return rating;
2333 static int dword_cmp(const void *p1, const void *p2)
2335 return *(const DWORD*)p1 - *(const DWORD*)p2;
2338 /***********************************************************************
2339 * FreeUrlCacheSpaceW (WININET.@)
2341 * Frees up some cache.
2343 * PARAMETERS
2344 * cache_path [I] Which volume to free up from, or NULL if you don't care.
2345 * size [I] Percentage of the cache that should be free.
2346 * filter [I] Which entries can't be deleted (CacheEntryType)
2348 * RETURNS
2349 * TRUE success. FALSE failure.
2351 * IMPLEMENTATION
2352 * This implementation just retrieves the path of the cache directory, and
2353 * deletes its contents from the filesystem. The correct approach would
2354 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
2356 BOOL WINAPI FreeUrlCacheSpaceW(LPCWSTR cache_path, DWORD size, DWORD filter)
2358 cache_container *container;
2359 DWORD path_len, err;
2361 TRACE("(%s, %x, %x)\n", debugstr_w(cache_path), size, filter);
2363 if(size<1 || size>100) {
2364 SetLastError(ERROR_INVALID_PARAMETER);
2365 return FALSE;
2368 if(cache_path) {
2369 path_len = strlenW(cache_path);
2370 if(cache_path[path_len-1] == '\\')
2371 path_len--;
2372 }else {
2373 path_len = 0;
2376 if(size==100 && !filter) {
2377 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
2379 /* When cache_path==NULL only clean Temporary Internet Files */
2380 if((!path_len && container->cache_prefix[0]==0) ||
2381 (path_len && !strncmpiW(container->path, cache_path, path_len) &&
2382 (container->path[path_len]=='\0' || container->path[path_len]=='\\')))
2384 BOOL ret_del;
2386 WaitForSingleObject(container->mutex, INFINITE);
2388 /* unlock, delete, recreate and lock cache */
2389 cache_container_close_index(container);
2390 ret_del = cache_container_delete_dir(container->path);
2391 err = cache_container_open_index(container, MIN_BLOCK_NO);
2393 ReleaseMutex(container->mutex);
2394 if(!ret_del || (err != ERROR_SUCCESS))
2395 return FALSE;
2399 return TRUE;
2402 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
2404 urlcache_header *header;
2405 struct hash_entry *hash_entry;
2406 entry_header *entry;
2407 entry_url *url_entry;
2408 ULONGLONG desired_size, cur_size;
2409 DWORD delete_factor, hash_table_off, hash_table_entry;
2410 DWORD rate[100], rate_no;
2411 FILETIME cur_time;
2413 if((path_len || container->cache_prefix[0]!=0) &&
2414 (!path_len || strncmpiW(container->path, cache_path, path_len) ||
2415 (container->path[path_len]!='\0' && container->path[path_len]!='\\')))
2416 continue;
2418 err = cache_container_open_index(container, MIN_BLOCK_NO);
2419 if(err != ERROR_SUCCESS)
2420 continue;
2422 header = cache_container_lock_index(container);
2423 if(!header)
2424 continue;
2426 urlcache_clean_leaked_entries(container, header);
2428 desired_size = header->cache_limit.QuadPart*(100-size)/100;
2429 cur_size = header->cache_usage.QuadPart+header->exempt_usage.QuadPart;
2430 if(cur_size <= desired_size)
2431 delete_factor = 0;
2432 else
2433 delete_factor = (cur_size-desired_size)*100/cur_size;
2435 if(!delete_factor) {
2436 cache_container_unlock_index(container, header);
2437 continue;
2440 hash_table_off = 0;
2441 hash_table_entry = 0;
2442 rate_no = 0;
2443 GetSystemTimeAsFileTime(&cur_time);
2444 while(rate_no<sizeof(rate)/sizeof(*rate) &&
2445 urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2446 if(entry->signature != URL_SIGNATURE) {
2447 WARN("only url entries are currently supported\n");
2448 continue;
2451 url_entry = (entry_url*)entry;
2452 if(url_entry->cache_entry_type & filter)
2453 continue;
2455 rate[rate_no] = urlcache_rate_entry(url_entry, &cur_time);
2456 if(rate[rate_no] != -1)
2457 rate_no++;
2460 if(!rate_no) {
2461 TRACE("nothing to delete\n");
2462 cache_container_unlock_index(container, header);
2463 continue;
2466 qsort(rate, rate_no, sizeof(DWORD), dword_cmp);
2468 delete_factor = delete_factor*rate_no/100;
2469 delete_factor = rate[delete_factor];
2470 TRACE("deleting files with rating %d or less\n", delete_factor);
2472 hash_table_off = 0;
2473 while(urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2474 if(entry->signature != URL_SIGNATURE)
2475 continue;
2477 url_entry = (entry_url*)entry;
2478 if(url_entry->cache_entry_type & filter)
2479 continue;
2481 if(urlcache_rate_entry(url_entry, &cur_time) <= delete_factor) {
2482 TRACE("deleting file: %s\n", debugstr_a((char*)url_entry+url_entry->local_name_off));
2483 urlcache_entry_delete(container, header, hash_entry);
2485 if(header->cache_usage.QuadPart+header->exempt_usage.QuadPart <= desired_size)
2486 break;
2488 /* Allow other threads to use cache while cleaning */
2489 cache_container_unlock_index(container, header);
2490 if(WaitForSingleObject(dll_unload_event, 0) == WAIT_OBJECT_0) {
2491 TRACE("got dll_unload_event - finishing\n");
2492 return TRUE;
2494 Sleep(0);
2495 header = cache_container_lock_index(container);
2499 TRACE("cache size after cleaning 0x%s/0x%s\n",
2500 wine_dbgstr_longlong(header->cache_usage.QuadPart+header->exempt_usage.QuadPart),
2501 wine_dbgstr_longlong(header->cache_limit.QuadPart));
2502 cache_container_unlock_index(container, header);
2505 return TRUE;
2508 /***********************************************************************
2509 * FreeUrlCacheSpaceA (WININET.@)
2511 * See FreeUrlCacheSpaceW.
2513 BOOL WINAPI FreeUrlCacheSpaceA(LPCSTR lpszCachePath, DWORD dwSize, DWORD dwFilter)
2515 BOOL ret = FALSE;
2516 LPWSTR path = heap_strdupAtoW(lpszCachePath);
2517 if (lpszCachePath == NULL || path != NULL)
2518 ret = FreeUrlCacheSpaceW(path, dwSize, dwFilter);
2519 heap_free(path);
2520 return ret;
2523 /***********************************************************************
2524 * UnlockUrlCacheEntryFileA (WININET.@)
2527 BOOL WINAPI UnlockUrlCacheEntryFileA(LPCSTR lpszUrlName, DWORD dwReserved)
2529 urlcache_header *pHeader;
2530 struct hash_entry *pHashEntry;
2531 entry_header *pEntry;
2532 entry_url * pUrlEntry;
2533 cache_container *pContainer;
2534 DWORD error;
2536 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2538 if (dwReserved)
2540 ERR("dwReserved != 0\n");
2541 SetLastError(ERROR_INVALID_PARAMETER);
2542 return FALSE;
2545 error = cache_containers_find(lpszUrlName, &pContainer);
2546 if (error != ERROR_SUCCESS)
2548 SetLastError(error);
2549 return FALSE;
2552 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2553 if (error != ERROR_SUCCESS)
2555 SetLastError(error);
2556 return FALSE;
2559 if (!(pHeader = cache_container_lock_index(pContainer)))
2560 return FALSE;
2562 if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
2564 cache_container_unlock_index(pContainer, pHeader);
2565 TRACE("entry %s not found!\n", debugstr_a(lpszUrlName));
2566 SetLastError(ERROR_FILE_NOT_FOUND);
2567 return FALSE;
2570 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2571 if (pEntry->signature != URL_SIGNATURE)
2573 cache_container_unlock_index(pContainer, pHeader);
2574 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2575 SetLastError(ERROR_FILE_NOT_FOUND);
2576 return FALSE;
2579 pUrlEntry = (entry_url *)pEntry;
2581 if (pUrlEntry->use_count == 0)
2583 cache_container_unlock_index(pContainer, pHeader);
2584 return FALSE;
2586 pUrlEntry->use_count--;
2587 if (!pUrlEntry->use_count)
2589 urlcache_hash_entry_set_flags(pHashEntry, HASHTABLE_URL);
2590 if (pUrlEntry->cache_entry_type & PENDING_DELETE_CACHE_ENTRY)
2591 urlcache_entry_delete(pContainer, pHeader, pHashEntry);
2594 cache_container_unlock_index(pContainer, pHeader);
2596 return TRUE;
2599 /***********************************************************************
2600 * UnlockUrlCacheEntryFileW (WININET.@)
2603 BOOL WINAPI UnlockUrlCacheEntryFileW(LPCWSTR lpszUrlName, DWORD dwReserved)
2605 char *url;
2606 BOOL ret;
2608 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
2609 return FALSE;
2611 ret = UnlockUrlCacheEntryFileA(url, dwReserved);
2612 heap_free(url);
2613 return ret;
2616 static BOOL urlcache_entry_create(const char *url, const char *ext, WCHAR *full_path)
2618 cache_container *container;
2619 urlcache_header *header;
2620 char file_name[MAX_PATH];
2621 WCHAR extW[MAX_PATH];
2622 BYTE cache_dir;
2623 LONG full_path_len, ext_len = 0;
2624 BOOL generate_name = FALSE;
2625 DWORD error;
2626 HANDLE file;
2627 FILETIME ft;
2628 URL_COMPONENTSA uc;
2629 int i;
2631 TRACE("(%s, %s, %p)\n", debugstr_a(url), debugstr_a(ext), full_path);
2633 memset(&uc, 0, sizeof(uc));
2634 uc.dwStructSize = sizeof(uc);
2635 uc.dwUrlPathLength = 1;
2636 uc.dwExtraInfoLength = 1;
2637 if(!InternetCrackUrlA(url, 0, 0, &uc))
2638 uc.dwUrlPathLength = 0;
2640 if(!uc.dwUrlPathLength) {
2641 file_name[0] = 0;
2642 }else {
2643 char *p, *e;
2645 p = e = uc.lpszUrlPath+uc.dwUrlPathLength;
2646 while(p>uc.lpszUrlPath && *(p-1)!='/' && *(p-1)!='\\' && *(p-1)!='.')
2647 p--;
2648 if(p>uc.lpszUrlPath && *(p-1)=='.') {
2649 e = p-1;
2650 while(p>uc.lpszUrlPath && *(p-1)!='/' && *(p-1)!='\\')
2651 p--;
2654 if(e-p >= MAX_PATH)
2655 e = p+MAX_PATH-1;
2656 memcpy(file_name, p, e-p);
2657 file_name[e-p] = 0;
2659 for(p=file_name; *p; p++) {
2660 switch(*p) {
2661 case '<': case '>':
2662 case ':': case '"':
2663 case '|': case '?':
2664 case '*':
2665 *p = '_'; break;
2666 default: break;
2671 if(!file_name[0])
2672 generate_name = TRUE;
2674 error = cache_containers_find(url, &container);
2675 if(error != ERROR_SUCCESS) {
2676 SetLastError(error);
2677 return FALSE;
2680 error = cache_container_open_index(container, MIN_BLOCK_NO);
2681 if(error != ERROR_SUCCESS) {
2682 SetLastError(error);
2683 return FALSE;
2686 if(!(header = cache_container_lock_index(container)))
2687 return FALSE;
2689 if(header->dirs_no)
2690 cache_dir = (BYTE)(rand() % header->dirs_no);
2691 else
2692 cache_dir = CACHE_CONTAINER_NO_SUBDIR;
2694 full_path_len = MAX_PATH * sizeof(WCHAR);
2695 if(!urlcache_create_file_pathW(container, header, file_name, cache_dir, full_path, &full_path_len, TRUE)) {
2696 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2697 debugstr_a(file_name), full_path_len);
2698 cache_container_unlock_index(container, header);
2699 return FALSE;
2701 full_path_len = full_path_len/sizeof(WCHAR) - 1;
2703 cache_container_unlock_index(container, header);
2705 if(ext) {
2706 WCHAR *p;
2708 extW[0] = '.';
2709 ext_len = MultiByteToWideChar(CP_ACP, 0, ext, -1, extW+1, MAX_PATH-1);
2711 for(p=extW; *p; p++) {
2712 switch(*p) {
2713 case '<': case '>':
2714 case ':': case '"':
2715 case '|': case '?':
2716 case '*':
2717 *p = '_'; break;
2718 default: break;
2721 if(p[-1]==' ' || p[-1]=='.')
2722 p[-1] = '_';
2723 }else {
2724 extW[0] = '\0';
2727 if(!generate_name && full_path_len+5+ext_len>=MAX_PATH) { /* strlen("[255]") = 5 */
2728 full_path_len = MAX_PATH-5-ext_len-1;
2731 for(i=0; i<255 && !generate_name; i++) {
2732 static const WCHAR format[] = {'[','%','u',']','%','s',0};
2734 wsprintfW(full_path+full_path_len, format, i, extW);
2736 TRACE("Trying: %s\n", debugstr_w(full_path));
2737 file = CreateFileW(full_path, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2738 if(file != INVALID_HANDLE_VALUE) {
2739 CloseHandle(file);
2740 return TRUE;
2744 if(full_path_len+8+ext_len >= MAX_PATH)
2745 full_path_len = MAX_PATH-8-ext_len-1;
2747 /* Try to generate random name */
2748 GetSystemTimeAsFileTime(&ft);
2749 strcpyW(full_path+full_path_len+8, extW);
2751 for(i=0; i<255; i++) {
2752 int j;
2753 ULONGLONG n = ft.dwHighDateTime;
2754 n <<= 32;
2755 n += ft.dwLowDateTime;
2756 n ^= (ULONGLONG)i<<48;
2758 for(j=0; j<8; j++) {
2759 int r = (n % 36);
2760 n /= 37;
2761 full_path[full_path_len+j] = (r < 10 ? '0' + r : 'A' + r - 10);
2764 TRACE("Trying: %s\n", debugstr_w(full_path));
2765 file = CreateFileW(full_path, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2766 if(file != INVALID_HANDLE_VALUE) {
2767 CloseHandle(file);
2768 return TRUE;
2772 WARN("Could not find a unique filename\n");
2773 return FALSE;
2776 /***********************************************************************
2777 * CreateUrlCacheEntryA (WININET.@)
2780 BOOL WINAPI CreateUrlCacheEntryA(LPCSTR lpszUrlName, DWORD dwExpectedFileSize,
2781 LPCSTR lpszFileExtension, LPSTR lpszFileName, DWORD dwReserved)
2783 WCHAR file_name[MAX_PATH];
2785 if(dwReserved)
2786 FIXME("dwReserved 0x%08x\n", dwReserved);
2788 if(!urlcache_entry_create(lpszUrlName, lpszFileExtension, file_name))
2789 return FALSE;
2791 if(!WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL))
2792 return FALSE;
2793 return TRUE;
2795 /***********************************************************************
2796 * CreateUrlCacheEntryW (WININET.@)
2799 BOOL WINAPI CreateUrlCacheEntryW(LPCWSTR lpszUrlName, DWORD dwExpectedFileSize,
2800 LPCWSTR lpszFileExtension, LPWSTR lpszFileName, DWORD dwReserved)
2802 char *url, *ext = NULL;
2803 BOOL ret;
2805 if(dwReserved)
2806 FIXME("dwReserved 0x%08x\n", dwReserved);
2808 if(lpszFileExtension) {
2809 ext = heap_strdupWtoUTF8(lpszFileExtension);
2810 if(!ext)
2811 return FALSE;
2814 if(!urlcache_encode_url_alloc(lpszUrlName, &url)) {
2815 heap_free(ext);
2816 return FALSE;
2819 ret = urlcache_entry_create(url, ext, lpszFileName);
2820 heap_free(ext);
2821 heap_free(url);
2822 return ret;
2825 static BOOL urlcache_entry_commit(const char *url, const WCHAR *file_name,
2826 FILETIME expire_time, FILETIME modify_time, DWORD entry_type,
2827 BYTE *header_info, DWORD header_size, const char *file_ext,
2828 const char *original_url)
2830 cache_container *container;
2831 urlcache_header *header;
2832 struct hash_entry *hash_entry;
2833 entry_header *entry;
2834 entry_url *url_entry;
2835 DWORD url_entry_offset;
2836 DWORD size = DWORD_ALIGN(sizeof(*url_entry));
2837 DWORD file_name_off = 0;
2838 DWORD header_info_off = 0;
2839 DWORD file_ext_off = 0;
2840 WIN32_FILE_ATTRIBUTE_DATA file_attr;
2841 LARGE_INTEGER file_size;
2842 BYTE dir_id;
2843 char file_name_no_container[MAX_PATH];
2844 char *local_file_name = 0;
2845 DWORD hit_rate = 0;
2846 DWORD exempt_delta = 0;
2847 DWORD error;
2849 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n", debugstr_a(url), debugstr_w(file_name),
2850 entry_type, header_info, header_size, debugstr_a(file_ext), debugstr_a(original_url));
2852 if(entry_type & STICKY_CACHE_ENTRY && !file_name) {
2853 SetLastError(ERROR_INVALID_PARAMETER);
2854 return FALSE;
2856 if(original_url)
2857 WARN(": original_url ignored\n");
2859 memset(&file_attr, 0, sizeof(file_attr));
2860 if(file_name) {
2861 if(!GetFileAttributesExW(file_name, GetFileExInfoStandard, &file_attr))
2862 return FALSE;
2864 file_size.u.LowPart = file_attr.nFileSizeLow;
2865 file_size.u.HighPart = file_attr.nFileSizeHigh;
2867 error = cache_containers_find(url, &container);
2868 if(error != ERROR_SUCCESS) {
2869 SetLastError(error);
2870 return FALSE;
2873 error = cache_container_open_index(container, MIN_BLOCK_NO);
2874 if(error != ERROR_SUCCESS) {
2875 SetLastError(error);
2876 return FALSE;
2879 if(!(header = cache_container_lock_index(container)))
2880 return FALSE;
2882 if(urlcache_find_hash_entry(header, url, &hash_entry)) {
2883 entry_url *url_entry = (entry_url*)((LPBYTE)header + hash_entry->offset);
2885 if(urlcache_hash_entry_is_locked(hash_entry, url_entry)) {
2886 TRACE("Trying to overwrite locked entry\n");
2887 cache_container_unlock_index(container, header);
2888 SetLastError(ERROR_SHARING_VIOLATION);
2889 return FALSE;
2892 hit_rate = url_entry->hit_rate;
2893 exempt_delta = url_entry->exempt_delta;
2894 urlcache_entry_delete(container, header, hash_entry);
2897 if(header->dirs_no)
2898 dir_id = 0;
2899 else
2900 dir_id = CACHE_CONTAINER_NO_SUBDIR;
2902 if(file_name) {
2903 BOOL bFound = FALSE;
2905 if(strncmpW(file_name, container->path, lstrlenW(container->path))) {
2906 ERR("path %s must begin with cache content path %s\n", debugstr_w(file_name), debugstr_w(container->path));
2907 cache_container_unlock_index(container, header);
2908 SetLastError(ERROR_INVALID_PARAMETER);
2909 return FALSE;
2912 /* skip container path prefix */
2913 file_name += lstrlenW(container->path);
2915 WideCharToMultiByte(CP_ACP, 0, file_name, -1, file_name_no_container, MAX_PATH, NULL, NULL);
2916 local_file_name = file_name_no_container;
2918 if(header->dirs_no) {
2919 for(dir_id = 0; dir_id < header->dirs_no; dir_id++) {
2920 if(!strncmp(header->directory_data[dir_id].name, local_file_name, DIR_LENGTH)) {
2921 bFound = TRUE;
2922 break;
2926 if(!bFound) {
2927 ERR("cache directory not found in path %s\n", debugstr_w(file_name));
2928 cache_container_unlock_index(container, header);
2929 SetLastError(ERROR_INVALID_PARAMETER);
2930 return FALSE;
2933 file_name += DIR_LENGTH + 1;
2934 local_file_name += DIR_LENGTH + 1;
2938 size = DWORD_ALIGN(size + strlen(url) + 1);
2939 if(file_name) {
2940 file_name_off = size;
2941 size = DWORD_ALIGN(size + strlen(local_file_name) + 1);
2943 if(header_info && header_size) {
2944 header_info_off = size;
2945 size = DWORD_ALIGN(size + header_size);
2947 if(file_ext && (file_ext_off = strlen(file_ext))) {
2948 DWORD len = file_ext_off;
2950 file_ext_off = size;
2951 size = DWORD_ALIGN(size + len + 1);
2954 /* round up to next block */
2955 if(size % BLOCKSIZE) {
2956 size -= size % BLOCKSIZE;
2957 size += BLOCKSIZE;
2960 error = urlcache_entry_alloc(header, size / BLOCKSIZE, &entry);
2961 while(error == ERROR_HANDLE_DISK_FULL) {
2962 error = cache_container_clean_index(container, &header);
2963 if(error == ERROR_SUCCESS)
2964 error = urlcache_entry_alloc(header, size / BLOCKSIZE, &entry);
2966 if(error != ERROR_SUCCESS) {
2967 cache_container_unlock_index(container, header);
2968 SetLastError(error);
2969 return FALSE;
2972 /* FindFirstFreeEntry fills in blocks used */
2973 url_entry = (entry_url *)entry;
2974 url_entry_offset = (LPBYTE)url_entry - (LPBYTE)header;
2975 url_entry->header.signature = URL_SIGNATURE;
2976 url_entry->cache_dir = dir_id;
2977 url_entry->cache_entry_type = entry_type | container->default_entry_type;
2978 url_entry->header_info_size = header_size;
2979 if((entry_type & STICKY_CACHE_ENTRY) && !exempt_delta) {
2980 /* Sticky entries have a default exempt time of one day */
2981 exempt_delta = 86400;
2983 url_entry->exempt_delta = exempt_delta;
2984 url_entry->hit_rate = hit_rate+1;
2985 url_entry->file_extension_off = file_ext_off;
2986 url_entry->header_info_off = header_info_off;
2987 url_entry->local_name_off = file_name_off;
2988 url_entry->url_off = DWORD_ALIGN(sizeof(*url_entry));
2989 url_entry->size.QuadPart = file_size.QuadPart;
2990 url_entry->use_count = 0;
2991 GetSystemTimeAsFileTime(&url_entry->access_time);
2992 url_entry->modification_time = modify_time;
2993 file_time_to_dos_date_time(&url_entry->access_time, &url_entry->sync_date, &url_entry->sync_time);
2994 file_time_to_dos_date_time(&expire_time, &url_entry->expire_date, &url_entry->expire_time);
2995 file_time_to_dos_date_time(&file_attr.ftLastWriteTime, &url_entry->write_date, &url_entry->write_time);
2997 /*** Unknowns ***/
2998 url_entry->unk1 = 0;
2999 url_entry->unk2 = 0;
3000 url_entry->unk3 = 0x60;
3001 url_entry->unk4 = 0;
3002 url_entry->unk5 = 0x1010;
3003 url_entry->unk7 = 0;
3004 url_entry->unk8 = 0;
3007 strcpy((LPSTR)url_entry + url_entry->url_off, url);
3008 if(file_name_off)
3009 strcpy((LPSTR)((LPBYTE)url_entry + file_name_off), local_file_name);
3010 if(header_info_off)
3011 memcpy((LPBYTE)url_entry + header_info_off, header_info, header_size);
3012 if(file_ext_off)
3013 strcpy((LPSTR)((LPBYTE)url_entry + file_ext_off), file_ext);
3015 error = urlcache_hash_entry_create(header, url, url_entry_offset, HASHTABLE_URL);
3016 while(error == ERROR_HANDLE_DISK_FULL) {
3017 error = cache_container_clean_index(container, &header);
3018 if(error == ERROR_SUCCESS) {
3019 url_entry = (entry_url *)((LPBYTE)header + url_entry_offset);
3020 error = urlcache_hash_entry_create(header, url,
3021 url_entry_offset, HASHTABLE_URL);
3024 if(error != ERROR_SUCCESS) {
3025 urlcache_entry_free(header, &url_entry->header);
3026 cache_container_unlock_index(container, header);
3027 SetLastError(error);
3028 return FALSE;
3031 if(url_entry->cache_dir < header->dirs_no)
3032 header->directory_data[url_entry->cache_dir].files_no++;
3033 if(entry_type & STICKY_CACHE_ENTRY)
3034 header->exempt_usage.QuadPart += file_size.QuadPart;
3035 else
3036 header->cache_usage.QuadPart += file_size.QuadPart;
3037 if(header->cache_usage.QuadPart+header->exempt_usage.QuadPart > header->cache_limit.QuadPart)
3038 handle_full_cache();
3040 cache_container_unlock_index(container, header);
3041 return TRUE;
3044 /***********************************************************************
3045 * CommitUrlCacheEntryA (WININET.@)
3047 BOOL WINAPI CommitUrlCacheEntryA(LPCSTR lpszUrlName, LPCSTR lpszLocalFileName,
3048 FILETIME ExpireTime, FILETIME LastModifiedTime, DWORD CacheEntryType,
3049 LPBYTE lpHeaderInfo, DWORD dwHeaderSize, LPCSTR lpszFileExtension, LPCSTR lpszOriginalUrl)
3051 WCHAR *file_name = NULL;
3052 BOOL ret;
3054 if(lpszLocalFileName) {
3055 file_name = heap_strdupAtoW(lpszLocalFileName);
3056 if(!file_name)
3057 return FALSE;
3060 ret = urlcache_entry_commit(lpszUrlName, file_name, ExpireTime, LastModifiedTime,
3061 CacheEntryType, lpHeaderInfo, dwHeaderSize, lpszFileExtension, lpszOriginalUrl);
3062 heap_free(file_name);
3063 return ret;
3066 /***********************************************************************
3067 * CommitUrlCacheEntryW (WININET.@)
3069 BOOL WINAPI CommitUrlCacheEntryW(LPCWSTR lpszUrlName, LPCWSTR lpszLocalFileName,
3070 FILETIME ExpireTime, FILETIME LastModifiedTime, DWORD CacheEntryType,
3071 LPWSTR lpHeaderInfo, DWORD dwHeaderSize, LPCWSTR lpszFileExtension, LPCWSTR lpszOriginalUrl)
3073 char *url, *original_url=NULL, *file_ext=NULL, *header_info=NULL;
3074 BOOL ret;
3076 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
3077 return FALSE;
3079 if(lpHeaderInfo) {
3080 header_info = heap_strdupWtoUTF8(lpHeaderInfo);
3081 if(!header_info) {
3082 heap_free(url);
3083 return FALSE;
3085 dwHeaderSize = strlen(header_info);
3088 if(lpszFileExtension) {
3089 file_ext = heap_strdupWtoA(lpszFileExtension);
3090 if(!file_ext) {
3091 heap_free(url);
3092 heap_free(header_info);
3093 return FALSE;
3097 if(lpszOriginalUrl && !urlcache_encode_url_alloc(lpszOriginalUrl, &original_url)) {
3098 heap_free(url);
3099 heap_free(header_info);
3100 heap_free(file_ext);
3101 return FALSE;
3104 ret = urlcache_entry_commit(url, lpszLocalFileName, ExpireTime, LastModifiedTime,
3105 CacheEntryType, (BYTE*)header_info, dwHeaderSize, file_ext, original_url);
3106 heap_free(url);
3107 heap_free(header_info);
3108 heap_free(file_ext);
3109 heap_free(original_url);
3110 return ret;
3113 /***********************************************************************
3114 * ReadUrlCacheEntryStream (WININET.@)
3117 BOOL WINAPI ReadUrlCacheEntryStream(
3118 IN HANDLE hUrlCacheStream,
3119 IN DWORD dwLocation,
3120 IN OUT LPVOID lpBuffer,
3121 IN OUT LPDWORD lpdwLen,
3122 IN DWORD dwReserved
3125 /* Get handle to file from 'stream' */
3126 stream_handle *pStream = (stream_handle*)hUrlCacheStream;
3128 if (dwReserved != 0)
3130 ERR("dwReserved != 0\n");
3131 SetLastError(ERROR_INVALID_PARAMETER);
3132 return FALSE;
3135 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->url, INTERNET_MAX_URL_LENGTH))
3137 SetLastError(ERROR_INVALID_HANDLE);
3138 return FALSE;
3141 if (SetFilePointer(pStream->file, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
3142 return FALSE;
3143 return ReadFile(pStream->file, lpBuffer, *lpdwLen, lpdwLen, NULL);
3146 /***********************************************************************
3147 * RetrieveUrlCacheEntryStreamA (WININET.@)
3150 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(LPCSTR lpszUrlName,
3151 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
3152 LPDWORD lpdwCacheEntryInfoBufferSize, BOOL fRandomRead, DWORD dwReserved)
3154 /* NOTE: this is not the same as the way that the native
3155 * version allocates 'stream' handles. I did it this way
3156 * as it is much easier and no applications should depend
3157 * on this behaviour. (Native version appears to allocate
3158 * indices into a table)
3160 stream_handle *stream;
3161 HANDLE file;
3163 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
3164 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved);
3166 if(!RetrieveUrlCacheEntryFileA(lpszUrlName, lpCacheEntryInfo,
3167 lpdwCacheEntryInfoBufferSize, dwReserved))
3168 return NULL;
3170 file = CreateFileA(lpCacheEntryInfo->lpszLocalFileName, GENERIC_READ, FILE_SHARE_READ,
3171 NULL, OPEN_EXISTING, fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0, NULL);
3172 if(file == INVALID_HANDLE_VALUE) {
3173 UnlockUrlCacheEntryFileA(lpszUrlName, 0);
3174 return NULL;
3177 /* allocate handle storage space */
3178 stream = heap_alloc(sizeof(stream_handle) + strlen(lpszUrlName) * sizeof(CHAR));
3179 if(!stream) {
3180 CloseHandle(file);
3181 UnlockUrlCacheEntryFileA(lpszUrlName, 0);
3182 SetLastError(ERROR_OUTOFMEMORY);
3183 return NULL;
3186 stream->file = file;
3187 strcpy(stream->url, lpszUrlName);
3188 return stream;
3191 /***********************************************************************
3192 * RetrieveUrlCacheEntryStreamW (WININET.@)
3195 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(LPCWSTR lpszUrlName,
3196 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3197 LPDWORD lpdwCacheEntryInfoBufferSize, BOOL fRandomRead, DWORD dwReserved)
3199 DWORD len;
3200 /* NOTE: this is not the same as the way that the native
3201 * version allocates 'stream' handles. I did it this way
3202 * as it is much easier and no applications should depend
3203 * on this behaviour. (Native version appears to allocate
3204 * indices into a table)
3206 stream_handle *stream;
3207 HANDLE file;
3209 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3210 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved);
3212 if(!(len = urlcache_encode_url(lpszUrlName, NULL, 0)))
3213 return NULL;
3215 if(!RetrieveUrlCacheEntryFileW(lpszUrlName, lpCacheEntryInfo,
3216 lpdwCacheEntryInfoBufferSize, dwReserved))
3217 return NULL;
3219 file = CreateFileW(lpCacheEntryInfo->lpszLocalFileName, GENERIC_READ, FILE_SHARE_READ,
3220 NULL, OPEN_EXISTING, fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0, NULL);
3221 if(file == INVALID_HANDLE_VALUE) {
3222 UnlockUrlCacheEntryFileW(lpszUrlName, 0);
3223 return NULL;
3226 /* allocate handle storage space */
3227 stream = heap_alloc(sizeof(stream_handle) + len*sizeof(WCHAR));
3228 if(!stream) {
3229 CloseHandle(file);
3230 UnlockUrlCacheEntryFileW(lpszUrlName, 0);
3231 SetLastError(ERROR_OUTOFMEMORY);
3232 return NULL;
3235 stream->file = file;
3236 if(!urlcache_encode_url(lpszUrlName, stream->url, len)) {
3237 CloseHandle(file);
3238 UnlockUrlCacheEntryFileW(lpszUrlName, 0);
3239 heap_free(stream);
3240 return NULL;
3242 return stream;
3245 /***********************************************************************
3246 * UnlockUrlCacheEntryStream (WININET.@)
3249 BOOL WINAPI UnlockUrlCacheEntryStream(
3250 IN HANDLE hUrlCacheStream,
3251 IN DWORD dwReserved
3254 stream_handle *pStream = (stream_handle*)hUrlCacheStream;
3256 if (dwReserved != 0)
3258 ERR("dwReserved != 0\n");
3259 SetLastError(ERROR_INVALID_PARAMETER);
3260 return FALSE;
3263 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->url, INTERNET_MAX_URL_LENGTH))
3265 SetLastError(ERROR_INVALID_HANDLE);
3266 return FALSE;
3269 if (!UnlockUrlCacheEntryFileA(pStream->url, 0))
3270 return FALSE;
3272 CloseHandle(pStream->file);
3273 heap_free(pStream);
3274 return TRUE;
3278 /***********************************************************************
3279 * DeleteUrlCacheEntryA (WININET.@)
3282 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3284 cache_container *pContainer;
3285 urlcache_header *pHeader;
3286 struct hash_entry *pHashEntry;
3287 DWORD error;
3288 BOOL ret;
3290 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3292 error = cache_containers_find(lpszUrlName, &pContainer);
3293 if (error != ERROR_SUCCESS)
3295 SetLastError(error);
3296 return FALSE;
3299 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3300 if (error != ERROR_SUCCESS)
3302 SetLastError(error);
3303 return FALSE;
3306 if (!(pHeader = cache_container_lock_index(pContainer)))
3307 return FALSE;
3309 if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
3311 cache_container_unlock_index(pContainer, pHeader);
3312 TRACE("entry %s not found!\n", debugstr_a(lpszUrlName));
3313 SetLastError(ERROR_FILE_NOT_FOUND);
3314 return FALSE;
3317 ret = urlcache_entry_delete(pContainer, pHeader, pHashEntry);
3319 cache_container_unlock_index(pContainer, pHeader);
3321 return ret;
3324 /***********************************************************************
3325 * DeleteUrlCacheEntryW (WININET.@)
3328 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3330 char *url;
3331 BOOL ret;
3333 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
3334 return FALSE;
3336 ret = DeleteUrlCacheEntryA(url);
3337 heap_free(url);
3338 return ret;
3341 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3343 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3344 return TRUE;
3347 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3349 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3350 return TRUE;
3353 /***********************************************************************
3354 * CreateCacheContainerA (WININET.@)
3356 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3357 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3359 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3360 d1, d2, d3, d4, d5, d6, d7, d8);
3361 return TRUE;
3364 /***********************************************************************
3365 * CreateCacheContainerW (WININET.@)
3367 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3368 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3370 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3371 d1, d2, d3, d4, d5, d6, d7, d8);
3372 return TRUE;
3375 /***********************************************************************
3376 * FindFirstUrlCacheContainerA (WININET.@)
3378 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3380 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3381 return NULL;
3384 /***********************************************************************
3385 * FindFirstUrlCacheContainerW (WININET.@)
3387 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3389 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3390 return NULL;
3393 /***********************************************************************
3394 * FindNextUrlCacheContainerA (WININET.@)
3396 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3398 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3399 return FALSE;
3402 /***********************************************************************
3403 * FindNextUrlCacheContainerW (WININET.@)
3405 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3407 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3408 return FALSE;
3411 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3412 LPCSTR lpszUrlSearchPattern,
3413 DWORD dwFlags,
3414 DWORD dwFilter,
3415 GROUPID GroupId,
3416 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3417 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3418 LPVOID lpReserved,
3419 LPDWORD pcbReserved2,
3420 LPVOID lpReserved3
3423 FIXME("(%s, 0x%08x, 0x%08x, 0x%s, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3424 dwFlags, dwFilter, wine_dbgstr_longlong(GroupId), lpFirstCacheEntryInfo,
3425 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3426 SetLastError(ERROR_FILE_NOT_FOUND);
3427 return NULL;
3430 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3431 LPCWSTR lpszUrlSearchPattern,
3432 DWORD dwFlags,
3433 DWORD dwFilter,
3434 GROUPID GroupId,
3435 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3436 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3437 LPVOID lpReserved,
3438 LPDWORD pcbReserved2,
3439 LPVOID lpReserved3
3442 FIXME("(%s, 0x%08x, 0x%08x, 0x%s, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3443 dwFlags, dwFilter, wine_dbgstr_longlong(GroupId), lpFirstCacheEntryInfo,
3444 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3445 SetLastError(ERROR_FILE_NOT_FOUND);
3446 return NULL;
3449 /***********************************************************************
3450 * FindFirstUrlCacheEntryA (WININET.@)
3453 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3454 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3456 find_handle *pEntryHandle;
3458 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3460 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3461 if (!pEntryHandle)
3462 return NULL;
3464 pEntryHandle->magic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3465 if (lpszUrlSearchPattern)
3467 pEntryHandle->url_search_pattern = heap_strdupA(lpszUrlSearchPattern);
3468 if (!pEntryHandle->url_search_pattern)
3470 heap_free(pEntryHandle);
3471 return NULL;
3474 else
3475 pEntryHandle->url_search_pattern = NULL;
3476 pEntryHandle->container_idx = 0;
3477 pEntryHandle->hash_table_idx = 0;
3478 pEntryHandle->hash_entry_idx = 0;
3480 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3482 heap_free(pEntryHandle);
3483 return NULL;
3485 return pEntryHandle;
3488 /***********************************************************************
3489 * FindFirstUrlCacheEntryW (WININET.@)
3492 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3493 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3495 find_handle *pEntryHandle;
3497 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3499 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3500 if (!pEntryHandle)
3501 return NULL;
3503 pEntryHandle->magic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3504 if (lpszUrlSearchPattern)
3506 pEntryHandle->url_search_pattern = heap_strdupWtoA(lpszUrlSearchPattern);
3507 if (!pEntryHandle->url_search_pattern)
3509 heap_free(pEntryHandle);
3510 return NULL;
3513 else
3514 pEntryHandle->url_search_pattern = NULL;
3515 pEntryHandle->container_idx = 0;
3516 pEntryHandle->hash_table_idx = 0;
3517 pEntryHandle->hash_entry_idx = 0;
3519 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3521 heap_free(pEntryHandle);
3522 return NULL;
3524 return pEntryHandle;
3527 static BOOL urlcache_find_next_entry(
3528 HANDLE hEnumHandle,
3529 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3530 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3531 BOOL unicode)
3533 find_handle *pEntryHandle = (find_handle*)hEnumHandle;
3534 cache_container *pContainer;
3536 if (pEntryHandle->magic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3538 SetLastError(ERROR_INVALID_HANDLE);
3539 return FALSE;
3542 for (; cache_containers_enum(pEntryHandle->url_search_pattern, pEntryHandle->container_idx, &pContainer);
3543 pEntryHandle->container_idx++, pEntryHandle->hash_table_idx = 0)
3545 urlcache_header *pHeader;
3546 entry_hash_table *pHashTableEntry;
3547 DWORD error;
3549 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3550 if (error != ERROR_SUCCESS)
3552 SetLastError(error);
3553 return FALSE;
3556 if (!(pHeader = cache_container_lock_index(pContainer)))
3557 return FALSE;
3559 for (; urlcache_enum_hash_tables(pHeader, &pEntryHandle->hash_table_idx, &pHashTableEntry);
3560 pEntryHandle->hash_table_idx++, pEntryHandle->hash_entry_idx = 0)
3562 const struct hash_entry *pHashEntry = NULL;
3563 for (; urlcache_enum_hash_table_entries(pHeader, pHashTableEntry, &pEntryHandle->hash_entry_idx, &pHashEntry);
3564 pEntryHandle->hash_entry_idx++)
3566 const entry_url *pUrlEntry;
3567 const entry_header *pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
3569 if (pEntry->signature != URL_SIGNATURE)
3570 continue;
3572 pUrlEntry = (const entry_url *)pEntry;
3573 TRACE("Found URL: %s\n",
3574 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->url_off));
3575 TRACE("Header info: %s\n",
3576 debugstr_an((LPCSTR)pUrlEntry + pUrlEntry->header_info_off,
3577 pUrlEntry->header_info_size));
3579 error = urlcache_copy_entry(
3580 pContainer,
3581 pHeader,
3582 lpNextCacheEntryInfo,
3583 lpdwNextCacheEntryInfoBufferSize,
3584 pUrlEntry,
3585 unicode);
3586 if (error != ERROR_SUCCESS)
3588 cache_container_unlock_index(pContainer, pHeader);
3589 SetLastError(error);
3590 return FALSE;
3592 if(pUrlEntry->local_name_off)
3593 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->local_name_off));
3595 /* increment the current index so that next time the function
3596 * is called the next entry is returned */
3597 pEntryHandle->hash_entry_idx++;
3598 cache_container_unlock_index(pContainer, pHeader);
3599 return TRUE;
3603 cache_container_unlock_index(pContainer, pHeader);
3606 SetLastError(ERROR_NO_MORE_ITEMS);
3607 return FALSE;
3610 /***********************************************************************
3611 * FindNextUrlCacheEntryA (WININET.@)
3613 BOOL WINAPI FindNextUrlCacheEntryA(
3614 HANDLE hEnumHandle,
3615 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3616 LPDWORD lpdwNextCacheEntryInfoBufferSize)
3618 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3620 return urlcache_find_next_entry(hEnumHandle, lpNextCacheEntryInfo,
3621 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
3624 /***********************************************************************
3625 * FindNextUrlCacheEntryW (WININET.@)
3627 BOOL WINAPI FindNextUrlCacheEntryW(
3628 HANDLE hEnumHandle,
3629 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3630 LPDWORD lpdwNextCacheEntryInfoBufferSize
3633 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3635 return urlcache_find_next_entry(hEnumHandle,
3636 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
3637 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
3640 /***********************************************************************
3641 * FindCloseUrlCache (WININET.@)
3643 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3645 find_handle *pEntryHandle = (find_handle*)hEnumHandle;
3647 TRACE("(%p)\n", hEnumHandle);
3649 if (!pEntryHandle || pEntryHandle->magic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3651 SetLastError(ERROR_INVALID_HANDLE);
3652 return FALSE;
3655 pEntryHandle->magic = 0;
3656 heap_free(pEntryHandle->url_search_pattern);
3657 heap_free(pEntryHandle);
3658 return TRUE;
3661 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3662 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3664 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3665 dwSearchCondition, lpGroupId, lpReserved);
3666 return NULL;
3669 BOOL WINAPI FindNextUrlCacheEntryExA(
3670 HANDLE hEnumHandle,
3671 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3672 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3673 LPVOID lpReserved,
3674 LPDWORD pcbReserved2,
3675 LPVOID lpReserved3
3678 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3679 lpReserved, pcbReserved2, lpReserved3);
3680 return FALSE;
3683 BOOL WINAPI FindNextUrlCacheEntryExW(
3684 HANDLE hEnumHandle,
3685 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3686 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3687 LPVOID lpReserved,
3688 LPDWORD pcbReserved2,
3689 LPVOID lpReserved3
3692 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3693 lpReserved, pcbReserved2, lpReserved3);
3694 return FALSE;
3697 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3699 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3700 return FALSE;
3703 /***********************************************************************
3704 * CreateUrlCacheGroup (WININET.@)
3707 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3709 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3710 return FALSE;
3713 /***********************************************************************
3714 * DeleteUrlCacheGroup (WININET.@)
3717 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3719 FIXME("(0x%s, 0x%08x, %p) stub\n",
3720 wine_dbgstr_longlong(GroupId), dwFlags, lpReserved);
3721 return FALSE;
3724 /***********************************************************************
3725 * DeleteWpadCacheForNetworks (WININET.@)
3726 * Undocumented, added in IE8
3728 BOOL WINAPI DeleteWpadCacheForNetworks(DWORD unk1)
3730 FIXME("(%d) stub\n", unk1);
3731 return FALSE;
3734 /***********************************************************************
3735 * SetUrlCacheEntryGroupA (WININET.@)
3738 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3739 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3740 LPVOID lpReserved)
3742 FIXME("(%s, 0x%08x, 0x%s, %p, 0x%08x, %p) stub\n",
3743 debugstr_a(lpszUrlName), dwFlags, wine_dbgstr_longlong(GroupId),
3744 pbGroupAttributes, cbGroupAttributes, lpReserved);
3745 SetLastError(ERROR_FILE_NOT_FOUND);
3746 return FALSE;
3749 /***********************************************************************
3750 * SetUrlCacheEntryGroupW (WININET.@)
3753 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3754 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3755 LPVOID lpReserved)
3757 FIXME("(%s, 0x%08x, 0x%s, %p, 0x%08x, %p) stub\n",
3758 debugstr_w(lpszUrlName), dwFlags, wine_dbgstr_longlong(GroupId),
3759 pbGroupAttributes, cbGroupAttributes, lpReserved);
3760 SetLastError(ERROR_FILE_NOT_FOUND);
3761 return FALSE;
3764 /***********************************************************************
3765 * GetUrlCacheConfigInfoW (WININET.@)
3767 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3769 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3770 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3771 return FALSE;
3774 /***********************************************************************
3775 * GetUrlCacheConfigInfoA (WININET.@)
3777 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3779 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3780 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3781 return FALSE;
3784 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3785 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3786 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3788 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3789 wine_dbgstr_longlong(gid), dwFlags, dwAttributes, lpGroupInfo,
3790 lpdwGroupInfo, lpReserved);
3791 return FALSE;
3794 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3795 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3796 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3798 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3799 wine_dbgstr_longlong(gid), dwFlags, dwAttributes, lpGroupInfo,
3800 lpdwGroupInfo, lpReserved);
3801 return FALSE;
3804 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3805 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3807 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p) stub\n",
3808 wine_dbgstr_longlong(gid), dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3809 return TRUE;
3812 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3813 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3815 FIXME("(0x%s, 0x%08x, 0x%08x, %p, %p) stub\n",
3816 wine_dbgstr_longlong(gid), dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3817 return TRUE;
3820 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3822 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3823 return TRUE;
3826 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3828 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3829 return TRUE;
3832 /***********************************************************************
3833 * DeleteIE3Cache (WININET.@)
3835 * Deletes the files used by the IE3 URL caching system.
3837 * PARAMS
3838 * hWnd [I] A dummy window.
3839 * hInst [I] Instance of process calling the function.
3840 * lpszCmdLine [I] Options used by function.
3841 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3843 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3845 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3846 return 0;
3849 static BOOL urlcache_entry_is_expired(const entry_url *pUrlEntry,
3850 FILETIME *pftLastModified)
3852 BOOL ret;
3853 FILETIME now, expired;
3855 *pftLastModified = pUrlEntry->modification_time;
3856 GetSystemTimeAsFileTime(&now);
3857 dos_date_time_to_file_time(pUrlEntry->expire_date,
3858 pUrlEntry->expire_time, &expired);
3859 /* If the expired time is 0, it's interpreted as not expired */
3860 if (!expired.dwLowDateTime && !expired.dwHighDateTime)
3861 ret = FALSE;
3862 else
3863 ret = CompareFileTime(&expired, &now) < 0;
3864 return ret;
3867 /***********************************************************************
3868 * IsUrlCacheEntryExpiredA (WININET.@)
3870 * PARAMS
3871 * url [I] Url
3872 * dwFlags [I] Unknown
3873 * pftLastModified [O] Last modified time
3875 BOOL WINAPI IsUrlCacheEntryExpiredA(LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified)
3877 urlcache_header *pHeader;
3878 struct hash_entry *pHashEntry;
3879 const entry_header *pEntry;
3880 const entry_url * pUrlEntry;
3881 cache_container *pContainer;
3882 BOOL expired;
3884 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3886 if (!url || !pftLastModified)
3887 return TRUE;
3888 if (dwFlags)
3889 FIXME("unknown flags 0x%08x\n", dwFlags);
3891 /* Any error implies that the URL is expired, i.e. not in the cache */
3892 if (cache_containers_find(url, &pContainer))
3894 memset(pftLastModified, 0, sizeof(*pftLastModified));
3895 return TRUE;
3898 if (cache_container_open_index(pContainer, MIN_BLOCK_NO))
3900 memset(pftLastModified, 0, sizeof(*pftLastModified));
3901 return TRUE;
3904 if (!(pHeader = cache_container_lock_index(pContainer)))
3906 memset(pftLastModified, 0, sizeof(*pftLastModified));
3907 return TRUE;
3910 if (!urlcache_find_hash_entry(pHeader, url, &pHashEntry))
3912 cache_container_unlock_index(pContainer, pHeader);
3913 memset(pftLastModified, 0, sizeof(*pftLastModified));
3914 TRACE("entry %s not found!\n", url);
3915 return TRUE;
3918 pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
3919 if (pEntry->signature != URL_SIGNATURE)
3921 cache_container_unlock_index(pContainer, pHeader);
3922 memset(pftLastModified, 0, sizeof(*pftLastModified));
3923 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
3924 return TRUE;
3927 pUrlEntry = (const entry_url *)pEntry;
3928 expired = urlcache_entry_is_expired(pUrlEntry, pftLastModified);
3930 cache_container_unlock_index(pContainer, pHeader);
3932 return expired;
3935 /***********************************************************************
3936 * IsUrlCacheEntryExpiredW (WININET.@)
3938 * PARAMS
3939 * url [I] Url
3940 * dwFlags [I] Unknown
3941 * pftLastModified [O] Last modified time
3943 BOOL WINAPI IsUrlCacheEntryExpiredW(LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified)
3945 char *encoded_url;
3946 BOOL ret;
3948 if(!urlcache_encode_url_alloc(url, &encoded_url))
3949 return FALSE;
3951 ret = IsUrlCacheEntryExpiredA(encoded_url, dwFlags, pftLastModified);
3952 heap_free(encoded_url);
3953 return ret;
3956 /***********************************************************************
3957 * GetDiskInfoA (WININET.@)
3959 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
3961 BOOL ret;
3962 ULARGE_INTEGER bytes_free, bytes_total;
3964 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
3966 if (!path)
3968 SetLastError(ERROR_INVALID_PARAMETER);
3969 return FALSE;
3972 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
3974 if (cluster_size) *cluster_size = 1;
3975 if (free) *free = bytes_free.QuadPart;
3976 if (total) *total = bytes_total.QuadPart;
3978 return ret;
3981 /***********************************************************************
3982 * RegisterUrlCacheNotification (WININET.@)
3984 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
3986 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
3987 return 0;
3990 /***********************************************************************
3991 * IncrementUrlCacheHeaderData (WININET.@)
3993 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
3995 FIXME("(%u, %p)\n", index, data);
3996 return FALSE;
3999 /***********************************************************************
4000 * RunOnceUrlCache (WININET.@)
4003 DWORD WINAPI RunOnceUrlCache(HWND hwnd, HINSTANCE hinst, LPSTR cmd, int cmdshow)
4005 FIXME("(%p, %p, %s, %d): stub\n", hwnd, hinst, debugstr_a(cmd), cmdshow);
4006 return 0;
4009 BOOL init_urlcache(void)
4011 dll_unload_event = CreateEventW(NULL, FALSE, FALSE, NULL);
4012 if(!dll_unload_event)
4013 return FALSE;
4015 free_cache_running = CreateSemaphoreW(NULL, 1, 1, NULL);
4016 if(!free_cache_running) {
4017 CloseHandle(dll_unload_event);
4018 return FALSE;
4021 cache_containers_init();
4022 return TRUE;
4025 void free_urlcache(void)
4027 SetEvent(dll_unload_event);
4028 WaitForSingleObject(free_cache_running, INFINITE);
4029 ReleaseSemaphore(free_cache_running, 1, NULL);
4030 CloseHandle(free_cache_running);
4031 CloseHandle(dll_unload_event);
4033 cache_containers_free();
4036 /***********************************************************************
4037 * LoadUrlCacheContent (WININET.@)
4039 BOOL WINAPI LoadUrlCacheContent(void)
4041 FIXME("stub!\n");
4042 return FALSE;