winemac: Don't unminimize a window for SetFocus().
[wine.git] / dlls / wininet / urlcache.c
blobf0a731b9061728c00dcde7410f0240e1cb1f7273
1 /*
2 * Wininet - Url Cache functions
4 * Copyright 2001,2002 CodeWeavers
5 * Copyright 2003-2008 Robert Shearman
7 * Eric Kohl
8 * Aric Stewart
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public
12 * License as published by the Free Software Foundation; either
13 * version 2.1 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with this library; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
25 #include "config.h"
26 #include "wine/port.h"
28 #define NONAMELESSUNION
29 #define NONAMELESSSTRUCT
31 #if defined(__MINGW32__) || defined (_MSC_VER)
32 #include <ws2tcpip.h>
33 #endif
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <sys/types.h>
40 #ifdef HAVE_SYS_SOCKET_H
41 # include <sys/socket.h>
42 #endif
43 #include <time.h>
45 #include "windef.h"
46 #include "winbase.h"
47 #include "winuser.h"
48 #include "wininet.h"
49 #include "winineti.h"
50 #include "winerror.h"
51 #include "winreg.h"
52 #include "shlwapi.h"
53 #include "shlobj.h"
54 #include "shellapi.h"
56 #include "internet.h"
58 #include "wine/unicode.h"
59 #include "wine/debug.h"
61 WINE_DEFAULT_DEBUG_CHANNEL(wininet);
63 static const char urlcache_ver_prefix[] = "WINE URLCache Ver ";
64 static const char urlcache_ver[] = "0.2012001";
66 #ifndef CHAR_BIT
67 #define CHAR_BIT (8 * sizeof(CHAR))
68 #endif
70 #define ENTRY_START_OFFSET 0x4000
71 #define DIR_LENGTH 8
72 #define MAX_DIR_NO 0x20
73 #define BLOCKSIZE 128
74 #define HASHTABLE_SIZE 448
75 #define HASHTABLE_NUM_ENTRIES 64 /* this needs to be power of 2, that divides HASHTABLE_SIZE */
76 #define HASHTABLE_BLOCKSIZE (HASHTABLE_SIZE / HASHTABLE_NUM_ENTRIES)
77 #define ALLOCATION_TABLE_OFFSET 0x250
78 #define ALLOCATION_TABLE_SIZE (ENTRY_START_OFFSET - ALLOCATION_TABLE_OFFSET)
79 #define MIN_BLOCK_NO 0x80
80 #define MAX_BLOCK_NO (ALLOCATION_TABLE_SIZE * CHAR_BIT)
81 #define FILE_SIZE(blocks) ((blocks) * BLOCKSIZE + ENTRY_START_OFFSET)
83 #define HASHTABLE_URL 0
84 #define HASHTABLE_DEL 1
85 #define HASHTABLE_LOCK 2
86 #define HASHTABLE_FREE 3
87 #define HASHTABLE_REDR 5
88 #define HASHTABLE_FLAG_BITS 6
90 #define PENDING_DELETE_CACHE_ENTRY 0x00400000
91 #define INSTALLED_CACHE_ENTRY 0x10000000
92 #define GET_INSTALLED_ENTRY 0x200
93 #define CACHE_CONTAINER_NO_SUBDIR 0xFE
95 #define CACHE_HEADER_DATA_ROOT_LEAK_OFFSET 0x16
97 #define FILETIME_SECOND 10000000
99 #define DWORD_SIG(a,b,c,d) (a | (b << 8) | (c << 16) | (d << 24))
100 #define URL_SIGNATURE DWORD_SIG('U','R','L',' ')
101 #define REDR_SIGNATURE DWORD_SIG('R','E','D','R')
102 #define LEAK_SIGNATURE DWORD_SIG('L','E','A','K')
103 #define HASH_SIGNATURE DWORD_SIG('H','A','S','H')
105 #define DWORD_ALIGN(x) ( (DWORD)(((DWORD)(x)+sizeof(DWORD)-1)/sizeof(DWORD))*sizeof(DWORD) )
107 #define URLCACHE_FIND_ENTRY_HANDLE_MAGIC 0xF389ABCD
109 typedef struct
111 DWORD signature;
112 DWORD blocks_used; /* number of 128byte blocks used by this entry */
113 } entry_header;
115 typedef struct
117 entry_header header;
118 FILETIME modification_time;
119 FILETIME access_time;
120 WORD expire_date; /* expire date in dos format */
121 WORD expire_time; /* expire time in dos format */
122 DWORD unk1; /* usually zero */
123 ULARGE_INTEGER size; /* see INTERNET_CACHE_ENTRY_INFO::dwSizeLow/High */
124 DWORD unk2; /* usually zero */
125 DWORD exempt_delta; /* see INTERNET_CACHE_ENTRY_INFO::dwExemptDelta */
126 DWORD unk3; /* usually 0x60 */
127 DWORD url_off; /* offset of start of url from start of entry */
128 BYTE cache_dir; /* index of cache directory this url is stored in */
129 BYTE unk4; /* usually zero */
130 WORD unk5; /* usually 0x1010 */
131 DWORD local_name_off; /* offset of start of local filename from start of entry */
132 DWORD cache_entry_type; /* see INTERNET_CACHE_ENTRY_INFO::CacheEntryType */
133 DWORD header_info_off; /* offset of start of header info from start of entry */
134 DWORD header_info_size;
135 DWORD file_extension_off; /* offset of start of file extension from start of entry */
136 WORD sync_date; /* last sync date in dos format */
137 WORD sync_time; /* last sync time in dos format */
138 DWORD hit_rate; /* see INTERNET_CACHE_ENTRY_INFO::dwHitRate */
139 DWORD use_count; /* see INTERNET_CACHE_ENTRY_INFO::dwUseCount */
140 WORD write_date;
141 WORD write_time;
142 DWORD unk7; /* usually zero */
143 DWORD unk8; /* usually zero */
144 /* packing to dword align start of next field */
145 /* CHAR szSourceUrlName[]; (url) */
146 /* packing to dword align start of next field */
147 /* CHAR szLocalFileName[]; (local file name excluding path) */
148 /* packing to dword align start of next field */
149 /* CHAR szHeaderInfo[]; (header info) */
150 } entry_url;
152 struct hash_entry
154 DWORD key;
155 DWORD offset;
158 typedef struct
160 entry_header header;
161 DWORD next;
162 DWORD id;
163 struct hash_entry hash_table[HASHTABLE_SIZE];
164 } entry_hash_table;
166 typedef struct
168 char signature[28];
169 DWORD size;
170 DWORD hash_table_off;
171 DWORD capacity_in_blocks;
172 DWORD blocks_in_use;
173 DWORD unk1;
174 ULARGE_INTEGER cache_limit;
175 ULARGE_INTEGER cache_usage;
176 ULARGE_INTEGER exempt_usage;
177 DWORD dirs_no;
178 struct _directory_data
180 DWORD files_no;
181 char name[DIR_LENGTH];
182 } directory_data[MAX_DIR_NO];
183 DWORD options[0x21];
184 BYTE allocation_table[ALLOCATION_TABLE_SIZE];
185 } urlcache_header;
187 typedef struct
189 HANDLE file;
190 CHAR url[1];
191 } stream_handle;
193 typedef struct
195 struct list entry; /* part of a list */
196 char *cache_prefix; /* string that has to be prefixed for this container to be used */
197 LPWSTR path; /* path to url container directory */
198 HANDLE mapping; /* handle of file mapping */
199 DWORD file_size; /* size of file when mapping was opened */
200 HANDLE mutex; /* handle of mutex */
201 DWORD default_entry_type;
202 } cache_container;
204 typedef struct
206 DWORD magic;
207 char *url_search_pattern;
208 DWORD container_idx;
209 DWORD hash_table_idx;
210 DWORD hash_entry_idx;
211 } find_handle;
213 /* List of all containers available */
214 static struct list UrlContainers = LIST_INIT(UrlContainers);
216 static inline char *heap_strdupWtoUTF8(LPCWSTR str)
218 char *ret = NULL;
220 if(str) {
221 DWORD size = WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL);
222 ret = heap_alloc(size);
223 if(ret)
224 WideCharToMultiByte(CP_UTF8, 0, str, -1, ret, size, NULL, NULL);
227 return ret;
230 /***********************************************************************
231 * urlcache_block_is_free (Internal)
233 * Is the specified block number free?
235 * RETURNS
236 * zero if free
237 * non-zero otherwise
240 static inline BYTE urlcache_block_is_free(BYTE *allocation_table, DWORD block_number)
242 BYTE mask = 1 << (block_number%CHAR_BIT);
243 return (allocation_table[block_number/CHAR_BIT] & mask) == 0;
246 /***********************************************************************
247 * urlcache_block_free (Internal)
249 * Marks the specified block as free
251 * CAUTION
252 * this function is not updating used blocks count
254 * RETURNS
255 * nothing
258 static inline void urlcache_block_free(BYTE *allocation_table, DWORD block_number)
260 BYTE mask = ~(1 << (block_number%CHAR_BIT));
261 allocation_table[block_number/CHAR_BIT] &= mask;
264 /***********************************************************************
265 * urlcache_block_alloc (Internal)
267 * Marks the specified block as allocated
269 * CAUTION
270 * this function is not updating used blocks count
272 * RETURNS
273 * nothing
276 static inline void urlcache_block_alloc(BYTE *allocation_table, DWORD block_number)
278 BYTE mask = 1 << (block_number%CHAR_BIT);
279 allocation_table[block_number/CHAR_BIT] |= mask;
282 /***********************************************************************
283 * urlcache_entry_alloc (Internal)
285 * Finds and allocates the first block of free space big enough and
286 * sets entry to point to it.
288 * RETURNS
289 * ERROR_SUCCESS when free memory block was found
290 * Any other Win32 error code if the entry could not be added
293 static DWORD urlcache_entry_alloc(urlcache_header *header, DWORD blocks_needed, entry_header **entry)
295 DWORD block, block_size;
297 for(block=0; block<header->capacity_in_blocks; block+=block_size+1)
299 block_size = 0;
300 while(block_size<blocks_needed && block_size+block<header->capacity_in_blocks
301 && urlcache_block_is_free(header->allocation_table, block+block_size))
302 block_size++;
304 if(block_size == blocks_needed)
306 DWORD index;
308 TRACE("Found free blocks starting at no. %d (0x%x)\n", block, ENTRY_START_OFFSET+block*BLOCKSIZE);
310 for(index=0; index<blocks_needed; index++)
311 urlcache_block_alloc(header->allocation_table, block+index);
313 *entry = (entry_header*)((BYTE*)header+ENTRY_START_OFFSET+block*BLOCKSIZE);
314 for(index=0; index<blocks_needed*BLOCKSIZE/sizeof(DWORD); index++)
315 ((DWORD*)*entry)[index] = 0xdeadbeef;
316 (*entry)->blocks_used = blocks_needed;
318 header->blocks_in_use += blocks_needed;
319 return ERROR_SUCCESS;
323 return ERROR_HANDLE_DISK_FULL;
326 /***********************************************************************
327 * urlcache_entry_free (Internal)
329 * Deletes the specified entry and frees the space allocated to it
331 * RETURNS
332 * TRUE if it succeeded
333 * FALSE if it failed
336 static BOOL urlcache_entry_free(urlcache_header *header, entry_header *entry)
338 DWORD start_block, block;
340 /* update allocation table */
341 start_block = ((DWORD)((BYTE*)entry - (BYTE*)header) - ENTRY_START_OFFSET) / BLOCKSIZE;
342 for(block = start_block; block < start_block+entry->blocks_used; block++)
343 urlcache_block_free(header->allocation_table, block);
345 header->blocks_in_use -= entry->blocks_used;
346 return TRUE;
349 /***********************************************************************
350 * urlcache_create_hash_table (Internal)
352 * Creates a new hash table in free space and adds it to the chain of existing
353 * hash tables.
355 * RETURNS
356 * ERROR_SUCCESS if the hash table was created
357 * ERROR_DISK_FULL if the hash table could not be created
360 static DWORD urlcache_create_hash_table(urlcache_header *header, entry_hash_table *hash_table_prev, entry_hash_table **hash_table)
362 DWORD dwOffset, error;
363 int i;
365 if((error = urlcache_entry_alloc(header, 0x20, (entry_header**)hash_table)) != ERROR_SUCCESS)
366 return error;
368 dwOffset = (BYTE*)*hash_table-(BYTE*)header;
370 if(hash_table_prev)
371 hash_table_prev->next = dwOffset;
372 else
373 header->hash_table_off = dwOffset;
375 (*hash_table)->header.signature = HASH_SIGNATURE;
376 (*hash_table)->next = 0;
377 (*hash_table)->id = hash_table_prev ? hash_table_prev->id+1 : 0;
378 for(i = 0; i < HASHTABLE_SIZE; i++) {
379 (*hash_table)->hash_table[i].offset = HASHTABLE_FREE;
380 (*hash_table)->hash_table[i].key = HASHTABLE_FREE;
382 return ERROR_SUCCESS;
385 /***********************************************************************
386 * cache_container_create_object_name (Internal)
388 * Converts a path to a name suitable for use as a Win32 object name.
389 * Replaces '\\' characters in-place with the specified character
390 * (usually '_' or '!')
392 * RETURNS
393 * nothing
396 static void cache_container_create_object_name(LPWSTR lpszPath, WCHAR replace)
398 for (; *lpszPath; lpszPath++)
400 if (*lpszPath == '\\')
401 *lpszPath = replace;
405 /* Caller must hold container lock */
406 static HANDLE cache_container_map_index(HANDLE file, const WCHAR *path, DWORD size, BOOL *validate)
408 static const WCHAR mapping_name_format[]
409 = {'%','s','i','n','d','e','x','.','d','a','t','_','%','l','u',0};
410 WCHAR mapping_name[MAX_PATH];
411 HANDLE mapping;
413 wsprintfW(mapping_name, mapping_name_format, path, size);
414 cache_container_create_object_name(mapping_name, '_');
416 mapping = OpenFileMappingW(FILE_MAP_WRITE, FALSE, mapping_name);
417 if(mapping) {
418 if(validate) *validate = FALSE;
419 return mapping;
422 if(validate) *validate = TRUE;
423 return CreateFileMappingW(file, NULL, PAGE_READWRITE, 0, 0, mapping_name);
426 /* Caller must hold container lock */
427 static DWORD cache_container_set_size(cache_container *container, HANDLE file, DWORD blocks_no)
429 static const WCHAR cache_content_key[] = {'S','o','f','t','w','a','r','e','\\',
430 'M','i','c','r','o','s','o','f','t','\\','W','i','n','d','o','w','s','\\',
431 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
432 'I','n','t','e','r','n','e','t',' ','S','e','t','t','i','n','g','s','\\',
433 'C','a','c','h','e','\\','C','o','n','t','e','n','t',0};
434 static const WCHAR cache_limit[] = {'C','a','c','h','e','L','i','m','i','t',0};
436 DWORD file_size = FILE_SIZE(blocks_no);
437 WCHAR dir_path[MAX_PATH], *dir_name;
438 entry_hash_table *hashtable_entry;
439 urlcache_header *header;
440 HANDLE mapping;
441 FILETIME ft;
442 HKEY key;
443 int i, j;
445 if(SetFilePointer(file, file_size, NULL, FILE_BEGIN) == INVALID_SET_FILE_POINTER)
446 return GetLastError();
448 if(!SetEndOfFile(file))
449 return GetLastError();
451 mapping = cache_container_map_index(file, container->path, file_size, NULL);
452 if(!mapping)
453 return GetLastError();
455 header = MapViewOfFile(mapping, FILE_MAP_WRITE, 0, 0, 0);
456 if(!header) {
457 CloseHandle(mapping);
458 return GetLastError();
461 if(blocks_no != MIN_BLOCK_NO) {
462 if(file_size > header->size)
463 memset((char*)header+header->size, 0, file_size-header->size);
464 header->size = file_size;
465 header->capacity_in_blocks = blocks_no;
467 UnmapViewOfFile(header);
468 CloseHandle(container->mapping);
469 container->mapping = mapping;
470 container->file_size = file_size;
471 return ERROR_SUCCESS;
474 memset(header, 0, file_size);
475 /* First set some constants and defaults in the header */
476 memcpy(header->signature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1);
477 memcpy(header->signature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1);
478 header->size = file_size;
479 header->capacity_in_blocks = blocks_no;
480 /* 127MB - taken from default for Windows 2000 */
481 header->cache_limit.QuadPart = 0x07ff5400;
482 /* Copied from a Windows 2000 cache index */
483 header->dirs_no = container->default_entry_type==NORMAL_CACHE_ENTRY ? 4 : 0;
485 /* If the registry has a cache size set, use the registry value */
486 if(RegOpenKeyW(HKEY_CURRENT_USER, cache_content_key, &key) == ERROR_SUCCESS) {
487 DWORD dw, len = sizeof(dw), keytype;
489 if(RegQueryValueExW(key, cache_limit, NULL, &keytype, (BYTE*)&dw, &len) == ERROR_SUCCESS &&
490 keytype == REG_DWORD)
491 header->cache_limit.QuadPart = (ULONGLONG)dw * 1024;
492 RegCloseKey(key);
495 urlcache_create_hash_table(header, NULL, &hashtable_entry);
497 /* Last step - create the directories */
498 strcpyW(dir_path, container->path);
499 dir_name = dir_path + strlenW(dir_path);
500 dir_name[8] = 0;
502 GetSystemTimeAsFileTime(&ft);
504 for(i=0; i<header->dirs_no; ++i) {
505 header->directory_data[i].files_no = 0;
506 for(j=0;; ++j) {
507 ULONGLONG n = ft.dwHighDateTime;
508 int k;
510 /* Generate a file name to attempt to create.
511 * This algorithm will create what will appear
512 * to be random and unrelated directory names
513 * of up to 9 characters in length.
515 n <<= 32;
516 n += ft.dwLowDateTime;
517 n ^= ((ULONGLONG) i << 56) | ((ULONGLONG) j << 48);
519 for(k = 0; k < 8; ++k) {
520 int r = (n % 36);
522 /* Dividing by a prime greater than 36 helps
523 * with the appearance of randomness
525 n /= 37;
527 if(r < 10)
528 dir_name[k] = '0' + r;
529 else
530 dir_name[k] = 'A' + (r - 10);
533 if(CreateDirectoryW(dir_path, 0)) {
534 /* The following is OK because we generated an
535 * 8 character directory name made from characters
536 * [A-Z0-9], which are equivalent for all code
537 * pages and for UTF-16
539 for (k = 0; k < 8; ++k)
540 header->directory_data[i].name[k] = dir_name[k];
541 break;
542 }else if(j >= 255) {
543 /* Give up. The most likely cause of this
544 * is a full disk, but whatever the cause
545 * is, it should be more than apparent that
546 * we won't succeed.
548 UnmapViewOfFile(header);
549 CloseHandle(mapping);
550 return GetLastError();
555 UnmapViewOfFile(header);
556 CloseHandle(container->mapping);
557 container->mapping = mapping;
558 container->file_size = file_size;
559 return ERROR_SUCCESS;
562 static BOOL cache_container_is_valid(urlcache_header *header, DWORD file_size)
564 DWORD allocation_size, count_bits, i;
566 if(file_size < FILE_SIZE(MIN_BLOCK_NO))
567 return FALSE;
569 if(file_size != header->size)
570 return FALSE;
572 if (!memcmp(header->signature, urlcache_ver_prefix, sizeof(urlcache_ver_prefix)-1) &&
573 memcmp(header->signature+sizeof(urlcache_ver_prefix)-1, urlcache_ver, sizeof(urlcache_ver)-1))
574 return FALSE;
576 if(FILE_SIZE(header->capacity_in_blocks) != file_size)
577 return FALSE;
579 allocation_size = 0;
580 for(i=0; i<header->capacity_in_blocks/8; i++) {
581 for(count_bits = header->allocation_table[i]; count_bits!=0; count_bits>>=1) {
582 if(count_bits & 1)
583 allocation_size++;
586 if(allocation_size != header->blocks_in_use)
587 return FALSE;
589 for(; i<ALLOCATION_TABLE_SIZE; i++) {
590 if(header->allocation_table[i])
591 return FALSE;
594 return TRUE;
597 /***********************************************************************
598 * cache_container_open_index (Internal)
600 * Opens the index file and saves mapping handle
602 * RETURNS
603 * ERROR_SUCCESS if succeeded
604 * Any other Win32 error code if failed
607 static DWORD cache_container_open_index(cache_container *container, DWORD blocks_no)
609 static const WCHAR index_dat[] = {'i','n','d','e','x','.','d','a','t',0};
611 HANDLE file;
612 WCHAR index_path[MAX_PATH];
613 DWORD file_size;
614 BOOL validate;
616 WaitForSingleObject(container->mutex, INFINITE);
618 if(container->mapping) {
619 ReleaseMutex(container->mutex);
620 return ERROR_SUCCESS;
623 strcpyW(index_path, container->path);
624 strcatW(index_path, index_dat);
626 file = CreateFileW(index_path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
627 if(file == INVALID_HANDLE_VALUE) {
628 /* Maybe the directory wasn't there? Try to create it */
629 if(CreateDirectoryW(container->path, 0))
630 file = CreateFileW(index_path, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL);
632 if(file == INVALID_HANDLE_VALUE) {
633 TRACE("Could not open or create cache index file \"%s\"\n", debugstr_w(index_path));
634 ReleaseMutex(container->mutex);
635 return GetLastError();
638 file_size = GetFileSize(file, NULL);
639 if(file_size == INVALID_FILE_SIZE) {
640 CloseHandle(file);
641 ReleaseMutex(container->mutex);
642 return GetLastError();
645 if(blocks_no < MIN_BLOCK_NO)
646 blocks_no = MIN_BLOCK_NO;
647 else if(blocks_no > MAX_BLOCK_NO)
648 blocks_no = MAX_BLOCK_NO;
650 if(file_size < FILE_SIZE(blocks_no)) {
651 DWORD ret = cache_container_set_size(container, file, blocks_no);
652 CloseHandle(file);
653 ReleaseMutex(container->mutex);
654 return ret;
657 container->file_size = file_size;
658 container->mapping = cache_container_map_index(file, container->path, file_size, &validate);
659 CloseHandle(file);
660 if(container->mapping && validate) {
661 urlcache_header *header = MapViewOfFile(container->mapping, FILE_MAP_WRITE, 0, 0, 0);
663 if(header && !cache_container_is_valid(header, file_size)) {
664 WARN("detected old or broken index.dat file\n");
665 UnmapViewOfFile(header);
666 FreeUrlCacheSpaceW(container->path, 100, 0);
667 }else if(header) {
668 UnmapViewOfFile(header);
669 }else {
670 CloseHandle(container->mapping);
671 container->mapping = NULL;
675 if(!container->mapping)
677 ERR("Couldn't create file mapping (error is %d)\n", GetLastError());
678 ReleaseMutex(container->mutex);
679 return GetLastError();
682 ReleaseMutex(container->mutex);
683 return ERROR_SUCCESS;
686 /***********************************************************************
687 * cache_container_close_index (Internal)
689 * Closes the index
691 * RETURNS
692 * nothing
695 static void cache_container_close_index(cache_container *pContainer)
697 CloseHandle(pContainer->mapping);
698 pContainer->mapping = NULL;
701 static BOOL cache_containers_add(const char *cache_prefix, LPCWSTR path,
702 DWORD default_entry_type, LPWSTR mutex_name)
704 cache_container *pContainer = heap_alloc(sizeof(cache_container));
705 int cache_prefix_len = strlen(cache_prefix);
707 if (!pContainer)
709 return FALSE;
712 pContainer->mapping = NULL;
713 pContainer->file_size = 0;
714 pContainer->default_entry_type = default_entry_type;
716 pContainer->path = heap_strdupW(path);
717 if (!pContainer->path)
719 heap_free(pContainer);
720 return FALSE;
723 pContainer->cache_prefix = heap_alloc(cache_prefix_len+1);
724 if (!pContainer->cache_prefix)
726 heap_free(pContainer->path);
727 heap_free(pContainer);
728 return FALSE;
731 memcpy(pContainer->cache_prefix, cache_prefix, cache_prefix_len+1);
733 CharLowerW(mutex_name);
734 cache_container_create_object_name(mutex_name, '!');
736 if ((pContainer->mutex = CreateMutexW(NULL, FALSE, mutex_name)) == NULL)
738 ERR("couldn't create mutex (error is %d)\n", GetLastError());
739 heap_free(pContainer->path);
740 heap_free(pContainer);
741 return FALSE;
744 list_add_head(&UrlContainers, &pContainer->entry);
746 return TRUE;
749 static void cache_container_delete_container(cache_container *pContainer)
751 list_remove(&pContainer->entry);
753 cache_container_close_index(pContainer);
754 CloseHandle(pContainer->mutex);
755 heap_free(pContainer->path);
756 heap_free(pContainer->cache_prefix);
757 heap_free(pContainer);
760 static void cache_containers_init(void)
762 static const WCHAR UrlSuffix[] = {'C','o','n','t','e','n','t','.','I','E','5',0};
763 static const WCHAR HistorySuffix[] = {'H','i','s','t','o','r','y','.','I','E','5',0};
764 static const WCHAR CookieSuffix[] = {0};
765 static const struct
767 int nFolder; /* CSIDL_* constant */
768 const WCHAR *shpath_suffix; /* suffix on path returned by SHGetSpecialFolderPath */
769 const char *cache_prefix; /* prefix used to reference the container */
770 DWORD default_entry_type;
771 } DefaultContainerData[] =
773 { CSIDL_INTERNET_CACHE, UrlSuffix, "", NORMAL_CACHE_ENTRY },
774 { CSIDL_HISTORY, HistorySuffix, "Visited:", URLHISTORY_CACHE_ENTRY },
775 { CSIDL_COOKIES, CookieSuffix, "Cookie:", COOKIE_CACHE_ENTRY },
777 DWORD i;
779 for (i = 0; i < sizeof(DefaultContainerData) / sizeof(DefaultContainerData[0]); i++)
781 WCHAR wszCachePath[MAX_PATH];
782 WCHAR wszMutexName[MAX_PATH];
783 int path_len, suffix_len;
784 BOOL def_char;
786 if (!SHGetSpecialFolderPathW(NULL, wszCachePath, DefaultContainerData[i].nFolder, TRUE))
788 ERR("Couldn't get path for default container %u\n", i);
789 continue;
791 path_len = strlenW(wszCachePath);
792 suffix_len = strlenW(DefaultContainerData[i].shpath_suffix);
794 if (path_len + suffix_len + 2 > MAX_PATH)
796 ERR("Path too long\n");
797 continue;
800 wszCachePath[path_len] = '\\';
801 wszCachePath[path_len+1] = 0;
803 strcpyW(wszMutexName, wszCachePath);
805 if (suffix_len)
807 memcpy(wszCachePath + path_len + 1, DefaultContainerData[i].shpath_suffix, (suffix_len + 1) * sizeof(WCHAR));
808 wszCachePath[path_len + suffix_len + 1] = '\\';
809 wszCachePath[path_len + suffix_len + 2] = '\0';
812 if (!WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, wszCachePath, path_len,
813 NULL, 0, NULL, &def_char) || def_char)
815 WCHAR tmp[MAX_PATH];
817 /* cannot convert path to ANSI code page */
818 if (!(path_len = GetShortPathNameW(wszCachePath, tmp, MAX_PATH)) ||
819 !WideCharToMultiByte(CP_ACP, WC_NO_BEST_FIT_CHARS, tmp, path_len,
820 NULL, 0, NULL, &def_char) || def_char)
821 ERR("Can't create container path accessible by ANSI functions\n");
822 else
823 memcpy(wszCachePath, tmp, (path_len+1)*sizeof(WCHAR));
826 cache_containers_add(DefaultContainerData[i].cache_prefix, wszCachePath,
827 DefaultContainerData[i].default_entry_type, wszMutexName);
831 static void cache_containers_free(void)
833 while(!list_empty(&UrlContainers))
834 cache_container_delete_container(
835 LIST_ENTRY(list_head(&UrlContainers), cache_container, entry)
839 static DWORD cache_containers_find(const char *url, cache_container **ret)
841 cache_container *container;
843 TRACE("searching for prefix for URL: %s\n", debugstr_a(url));
845 if(!url)
846 return ERROR_INVALID_PARAMETER;
848 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
850 int prefix_len = strlen(container->cache_prefix);
852 if(!strncmp(container->cache_prefix, url, prefix_len)) {
853 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
854 *ret = container;
855 return ERROR_SUCCESS;
859 ERR("no container found\n");
860 return ERROR_FILE_NOT_FOUND;
863 static BOOL cache_containers_enum(char *search_pattern, DWORD index, cache_container **ret)
865 DWORD i = 0;
866 cache_container *container;
868 TRACE("searching for prefix: %s\n", debugstr_a(search_pattern));
870 /* non-NULL search pattern only returns one container ever */
871 if (search_pattern && index > 0)
872 return FALSE;
874 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
876 if (search_pattern)
878 if (!strcmp(container->cache_prefix, search_pattern))
880 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
881 *ret = container;
882 return TRUE;
885 else
887 if (i == index)
889 TRACE("found container with prefix %s\n", debugstr_a(container->cache_prefix));
890 *ret = container;
891 return TRUE;
894 i++;
896 return FALSE;
899 /***********************************************************************
900 * cache_container_lock_index (Internal)
902 * Locks the index for system-wide exclusive access.
904 * RETURNS
905 * Cache file header if successful
906 * NULL if failed and calls SetLastError.
908 static urlcache_header* cache_container_lock_index(cache_container *pContainer)
910 BYTE index;
911 LPVOID pIndexData;
912 urlcache_header* pHeader;
913 DWORD error;
915 /* acquire mutex */
916 WaitForSingleObject(pContainer->mutex, INFINITE);
918 pIndexData = MapViewOfFile(pContainer->mapping, FILE_MAP_WRITE, 0, 0, 0);
920 if (!pIndexData)
922 ReleaseMutex(pContainer->mutex);
923 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
924 return NULL;
926 pHeader = (urlcache_header*)pIndexData;
928 /* file has grown - we need to remap to prevent us getting
929 * access violations when we try and access beyond the end
930 * of the memory mapped file */
931 if (pHeader->size != pContainer->file_size)
933 UnmapViewOfFile( pHeader );
934 cache_container_close_index(pContainer);
935 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
936 if (error != ERROR_SUCCESS)
938 ReleaseMutex(pContainer->mutex);
939 SetLastError(error);
940 return NULL;
942 pIndexData = MapViewOfFile(pContainer->mapping, FILE_MAP_WRITE, 0, 0, 0);
944 if (!pIndexData)
946 ReleaseMutex(pContainer->mutex);
947 ERR("Couldn't MapViewOfFile. Error: %d\n", GetLastError());
948 return NULL;
950 pHeader = (urlcache_header*)pIndexData;
953 TRACE("Signature: %s, file size: %d bytes\n", pHeader->signature, pHeader->size);
955 for (index = 0; index < pHeader->dirs_no; index++)
957 TRACE("Directory[%d] = \"%.8s\"\n", index, pHeader->directory_data[index].name);
960 return pHeader;
963 /***********************************************************************
964 * cache_container_unlock_index (Internal)
967 static BOOL cache_container_unlock_index(cache_container *pContainer, urlcache_header *pHeader)
969 /* release mutex */
970 ReleaseMutex(pContainer->mutex);
971 return UnmapViewOfFile(pHeader);
974 /***********************************************************************
975 * urlcache_create_file_pathW (Internal)
977 * Copies the full path to the specified buffer given the local file
978 * name and the index of the directory it is in. Always sets value in
979 * lpBufferSize to the required buffer size (in bytes).
981 * RETURNS
982 * TRUE if the buffer was big enough
983 * FALSE if the buffer was too small
986 static BOOL urlcache_create_file_pathW(
987 const cache_container *pContainer,
988 const urlcache_header *pHeader,
989 LPCSTR szLocalFileName,
990 BYTE Directory,
991 LPWSTR wszPath,
992 LPLONG lpBufferSize)
994 LONG nRequired;
995 int path_len = strlenW(pContainer->path);
996 int file_name_len = MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, NULL, 0);
997 if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->dirs_no)
999 *lpBufferSize = 0;
1000 return FALSE;
1003 nRequired = (path_len + file_name_len) * sizeof(WCHAR);
1004 if(Directory != CACHE_CONTAINER_NO_SUBDIR)
1005 nRequired += (DIR_LENGTH + 1) * sizeof(WCHAR);
1006 if (nRequired <= *lpBufferSize)
1008 int dir_len;
1010 memcpy(wszPath, pContainer->path, path_len * sizeof(WCHAR));
1011 if (Directory != CACHE_CONTAINER_NO_SUBDIR)
1013 dir_len = MultiByteToWideChar(CP_ACP, 0, pHeader->directory_data[Directory].name, DIR_LENGTH, wszPath + path_len, DIR_LENGTH);
1014 wszPath[dir_len + path_len] = '\\';
1015 dir_len++;
1017 else
1019 dir_len = 0;
1021 MultiByteToWideChar(CP_ACP, 0, szLocalFileName, -1, wszPath + dir_len + path_len, file_name_len);
1022 *lpBufferSize = nRequired;
1023 return TRUE;
1025 *lpBufferSize = nRequired;
1026 return FALSE;
1029 /***********************************************************************
1030 * urlcache_create_file_pathA (Internal)
1032 * Copies the full path to the specified buffer given the local file
1033 * name and the index of the directory it is in. Always sets value in
1034 * lpBufferSize to the required buffer size.
1036 * RETURNS
1037 * TRUE if the buffer was big enough
1038 * FALSE if the buffer was too small
1041 static BOOL urlcache_create_file_pathA(
1042 const cache_container *pContainer,
1043 const urlcache_header *pHeader,
1044 LPCSTR szLocalFileName,
1045 BYTE Directory,
1046 LPSTR szPath,
1047 LPLONG lpBufferSize)
1049 LONG nRequired;
1050 int path_len, file_name_len, dir_len;
1052 if (Directory!=CACHE_CONTAINER_NO_SUBDIR && Directory>=pHeader->dirs_no)
1054 *lpBufferSize = 0;
1055 return FALSE;
1058 path_len = WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, NULL, 0, NULL, NULL) - 1;
1059 file_name_len = strlen(szLocalFileName) + 1 /* for nul-terminator */;
1060 if (Directory!=CACHE_CONTAINER_NO_SUBDIR)
1061 dir_len = DIR_LENGTH+1;
1062 else
1063 dir_len = 0;
1065 nRequired = (path_len + dir_len + file_name_len) * sizeof(char);
1066 if (nRequired <= *lpBufferSize)
1068 WideCharToMultiByte(CP_ACP, 0, pContainer->path, -1, szPath, path_len, NULL, NULL);
1069 if(dir_len) {
1070 memcpy(szPath+path_len, pHeader->directory_data[Directory].name, dir_len-1);
1071 szPath[path_len + dir_len-1] = '\\';
1073 memcpy(szPath + path_len + dir_len, szLocalFileName, file_name_len);
1074 *lpBufferSize = nRequired;
1075 return TRUE;
1077 *lpBufferSize = nRequired;
1078 return FALSE;
1081 /* Just like FileTimeToDosDateTime, except that it also maps the special
1082 * case of a filetime of (0,0) to a DOS date/time of (0,0).
1084 static void file_time_to_dos_date_time(const FILETIME *ft, WORD *fatdate,
1085 WORD *fattime)
1087 if (!ft->dwLowDateTime && !ft->dwHighDateTime)
1088 *fatdate = *fattime = 0;
1089 else
1090 FileTimeToDosDateTime(ft, fatdate, fattime);
1093 /***********************************************************************
1094 * urlcache_delete_file (Internal)
1096 static DWORD urlcache_delete_file(const cache_container *container,
1097 urlcache_header *header, entry_url *url_entry)
1099 WIN32_FILE_ATTRIBUTE_DATA attr;
1100 WCHAR path[MAX_PATH];
1101 LONG path_size = sizeof(path);
1102 DWORD err;
1103 WORD date, time;
1105 if(!url_entry->local_name_off)
1106 goto succ;
1108 if(!urlcache_create_file_pathW(container, header,
1109 (LPCSTR)url_entry+url_entry->local_name_off,
1110 url_entry->cache_dir, path, &path_size))
1111 goto succ;
1113 if(!GetFileAttributesExW(path, GetFileExInfoStandard, &attr))
1114 goto succ;
1115 file_time_to_dos_date_time(&attr.ftLastWriteTime, &date, &time);
1116 if(date != url_entry->write_date || time != url_entry->write_time)
1117 goto succ;
1119 err = (DeleteFileW(path) ? ERROR_SUCCESS : GetLastError());
1120 if(err == ERROR_ACCESS_DENIED || err == ERROR_SHARING_VIOLATION)
1121 return err;
1123 succ:
1124 if (url_entry->cache_dir < header->dirs_no)
1126 if (header->directory_data[url_entry->cache_dir].files_no)
1127 header->directory_data[url_entry->cache_dir].files_no--;
1129 if (url_entry->cache_entry_type & STICKY_CACHE_ENTRY)
1131 if (url_entry->size.QuadPart < header->exempt_usage.QuadPart)
1132 header->exempt_usage.QuadPart -= url_entry->size.QuadPart;
1133 else
1134 header->exempt_usage.QuadPart = 0;
1136 else
1138 if (url_entry->size.QuadPart < header->cache_usage.QuadPart)
1139 header->cache_usage.QuadPart -= url_entry->size.QuadPart;
1140 else
1141 header->cache_usage.QuadPart = 0;
1144 return ERROR_SUCCESS;
1147 static BOOL urlcache_clean_leaked_entries(cache_container *container, urlcache_header *header)
1149 DWORD *leak_off;
1150 BOOL freed = FALSE;
1152 leak_off = &header->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
1153 while(*leak_off) {
1154 entry_url *url_entry = (entry_url*)((LPBYTE)header + *leak_off);
1156 if(SUCCEEDED(urlcache_delete_file(container, header, url_entry))) {
1157 *leak_off = url_entry->exempt_delta;
1158 urlcache_entry_free(header, &url_entry->header);
1159 freed = TRUE;
1160 }else {
1161 leak_off = &url_entry->exempt_delta;
1165 return freed;
1168 /***********************************************************************
1169 * cache_container_clean_index (Internal)
1171 * This function is meant to make place in index file by removing leaked
1172 * files entries and resizing the file.
1174 * CAUTION: file view may get mapped to new memory
1176 * RETURNS
1177 * ERROR_SUCCESS when new memory is available
1178 * error code otherwise
1180 static DWORD cache_container_clean_index(cache_container *container, urlcache_header **file_view)
1182 urlcache_header *header = *file_view;
1183 DWORD ret;
1185 TRACE("(%s %s)\n", debugstr_a(container->cache_prefix), debugstr_w(container->path));
1187 if(urlcache_clean_leaked_entries(container, header))
1188 return ERROR_SUCCESS;
1190 if(header->size >= ALLOCATION_TABLE_SIZE*8*BLOCKSIZE + ENTRY_START_OFFSET) {
1191 WARN("index file has maximal size\n");
1192 return ERROR_NOT_ENOUGH_MEMORY;
1195 cache_container_close_index(container);
1196 ret = cache_container_open_index(container, header->capacity_in_blocks*2);
1197 if(ret != ERROR_SUCCESS)
1198 return ret;
1199 header = MapViewOfFile(container->mapping, FILE_MAP_WRITE, 0, 0, 0);
1200 if(!header)
1201 return GetLastError();
1203 UnmapViewOfFile(*file_view);
1204 *file_view = header;
1205 return ERROR_SUCCESS;
1208 /* Just like DosDateTimeToFileTime, except that it also maps the special
1209 * case of a DOS date/time of (0,0) to a filetime of (0,0).
1211 static void dos_date_time_to_file_time(WORD fatdate, WORD fattime,
1212 FILETIME *ft)
1214 if (!fatdate && !fattime)
1215 ft->dwLowDateTime = ft->dwHighDateTime = 0;
1216 else
1217 DosDateTimeToFileTime(fatdate, fattime, ft);
1220 static int urlcache_decode_url(const char *url, WCHAR *decoded_url, int decoded_len)
1222 URL_COMPONENTSA uc;
1223 DWORD len, part_len;
1224 WCHAR *host_name;
1226 memset(&uc, 0, sizeof(uc));
1227 uc.dwStructSize = sizeof(uc);
1228 uc.dwHostNameLength = 1;
1229 if(!InternetCrackUrlA(url, 0, 0, &uc))
1230 uc.nScheme = INTERNET_SCHEME_UNKNOWN;
1232 if(uc.nScheme!=INTERNET_SCHEME_HTTP && uc.nScheme!=INTERNET_SCHEME_HTTPS)
1233 return MultiByteToWideChar(CP_UTF8, 0, url, -1, decoded_url, decoded_len);
1235 if(!decoded_url)
1236 decoded_len = 0;
1238 len = MultiByteToWideChar(CP_UTF8, 0, url, uc.lpszHostName-url, decoded_url, decoded_len);
1239 if(!len)
1240 return 0;
1241 if(decoded_url)
1242 decoded_len -= len;
1244 host_name = heap_alloc(uc.dwHostNameLength*sizeof(WCHAR));
1245 if(!host_name)
1246 return 0;
1247 if(!MultiByteToWideChar(CP_UTF8, 0, uc.lpszHostName, uc.dwHostNameLength,
1248 host_name, uc.dwHostNameLength)) {
1249 heap_free(host_name);
1250 return 0;
1252 part_len = IdnToUnicode(0, host_name, uc.dwHostNameLength,
1253 decoded_url ? decoded_url+len : NULL, decoded_len);
1254 heap_free(host_name);
1255 if(!part_len) {
1256 SetLastError(ERROR_INTERNET_INVALID_URL);
1257 return 0;
1259 len += part_len;
1260 if(decoded_url)
1261 decoded_len -= part_len;
1263 part_len = MultiByteToWideChar(CP_UTF8, 0,
1264 uc.lpszHostName+uc.dwHostNameLength,
1265 -1, decoded_url ? decoded_url+len : NULL, decoded_len);
1266 if(!part_len)
1267 return 0;
1268 len += part_len;
1270 return len;
1273 /***********************************************************************
1274 * urlcache_copy_entry (Internal)
1276 * Copies an entry from the cache index file to the Win32 structure
1278 * RETURNS
1279 * ERROR_SUCCESS if the buffer was big enough
1280 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1283 static DWORD urlcache_copy_entry(cache_container *container, const urlcache_header *header,
1284 INTERNET_CACHE_ENTRY_INFOA *entry_info, DWORD *info_size, const entry_url *url_entry, BOOL unicode)
1286 int url_len;
1287 DWORD size = sizeof(*entry_info);
1289 if(*info_size >= size) {
1290 entry_info->lpHeaderInfo = NULL;
1291 entry_info->lpszFileExtension = NULL;
1292 entry_info->lpszLocalFileName = NULL;
1293 entry_info->lpszSourceUrlName = NULL;
1294 entry_info->CacheEntryType = url_entry->cache_entry_type;
1295 entry_info->u.dwExemptDelta = url_entry->exempt_delta;
1296 entry_info->dwHeaderInfoSize = url_entry->header_info_size;
1297 entry_info->dwHitRate = url_entry->hit_rate;
1298 entry_info->dwSizeHigh = url_entry->size.u.HighPart;
1299 entry_info->dwSizeLow = url_entry->size.u.LowPart;
1300 entry_info->dwStructSize = sizeof(*entry_info);
1301 entry_info->dwUseCount = url_entry->use_count;
1302 dos_date_time_to_file_time(url_entry->expire_date, url_entry->expire_time, &entry_info->ExpireTime);
1303 entry_info->LastAccessTime = url_entry->access_time;
1304 entry_info->LastModifiedTime = url_entry->modification_time;
1305 dos_date_time_to_file_time(url_entry->sync_date, url_entry->sync_time, &entry_info->LastSyncTime);
1308 if(size%4 && size<*info_size)
1309 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1310 size = DWORD_ALIGN(size);
1311 if(unicode)
1312 url_len = urlcache_decode_url((const char*)url_entry+url_entry->url_off, NULL, 0);
1313 else
1314 url_len = strlen((LPCSTR)url_entry+url_entry->url_off) + 1;
1315 size += url_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1317 if(*info_size >= size) {
1318 DWORD url_size = url_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1320 entry_info->lpszSourceUrlName = (LPSTR)entry_info+size-url_size;
1321 if(unicode)
1322 urlcache_decode_url((const char*)url_entry+url_entry->url_off, (WCHAR*)entry_info->lpszSourceUrlName, url_len);
1323 else
1324 memcpy(entry_info->lpszSourceUrlName, (LPCSTR)url_entry+url_entry->url_off, url_size);
1327 if(size%4 && size<*info_size)
1328 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1329 size = DWORD_ALIGN(size);
1331 if(url_entry->local_name_off) {
1332 LONG file_name_size;
1333 LPSTR file_name;
1334 file_name = (LPSTR)entry_info+size;
1335 file_name_size = *info_size-size;
1336 if((unicode && urlcache_create_file_pathW(container, header, (LPCSTR)url_entry+url_entry->local_name_off, url_entry->cache_dir, (LPWSTR)file_name, &file_name_size)) ||
1337 (!unicode && urlcache_create_file_pathA(container, header, (LPCSTR)url_entry+url_entry->local_name_off, url_entry->cache_dir, file_name, &file_name_size))) {
1338 entry_info->lpszLocalFileName = file_name;
1340 size += file_name_size;
1342 if(size%4 && size<*info_size)
1343 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1344 size = DWORD_ALIGN(size);
1347 if(url_entry->header_info_off) {
1348 DWORD header_len;
1350 if(unicode)
1351 header_len = MultiByteToWideChar(CP_UTF8, 0, (const char*)url_entry+url_entry->header_info_off,
1352 url_entry->header_info_size, NULL, 0);
1353 else
1354 header_len = url_entry->header_info_size;
1355 size += header_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1357 if(*info_size >= size) {
1358 DWORD header_size = header_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1359 entry_info->lpHeaderInfo = (LPBYTE)entry_info+size-header_size;
1360 if(unicode)
1361 MultiByteToWideChar(CP_UTF8, 0, (const char*)url_entry+url_entry->header_info_off,
1362 url_entry->header_info_size, (LPWSTR)entry_info->lpHeaderInfo, header_len);
1363 else
1364 memcpy(entry_info->lpHeaderInfo, (LPCSTR)url_entry+url_entry->header_info_off, header_len);
1366 if(size%4 && size<*info_size)
1367 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1368 size = DWORD_ALIGN(size);
1371 if(url_entry->file_extension_off) {
1372 int ext_len;
1374 if(unicode)
1375 ext_len = MultiByteToWideChar(CP_ACP, 0, (LPCSTR)url_entry+url_entry->file_extension_off, -1, NULL, 0);
1376 else
1377 ext_len = strlen((LPCSTR)url_entry+url_entry->file_extension_off) + 1;
1378 size += ext_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1380 if(*info_size >= size) {
1381 DWORD ext_size = ext_len * (unicode ? sizeof(WCHAR) : sizeof(CHAR));
1382 entry_info->lpszFileExtension = (LPSTR)entry_info+size-ext_size;
1383 if(unicode)
1384 MultiByteToWideChar(CP_ACP, 0, (LPCSTR)url_entry+url_entry->file_extension_off, -1, (LPWSTR)entry_info->lpszFileExtension, ext_len);
1385 else
1386 memcpy(entry_info->lpszFileExtension, (LPCSTR)url_entry+url_entry->file_extension_off, ext_len*sizeof(CHAR));
1389 if(size%4 && size<*info_size)
1390 ZeroMemory((LPBYTE)entry_info+size, 4-size%4);
1391 size = DWORD_ALIGN(size);
1394 if(size > *info_size) {
1395 *info_size = size;
1396 return ERROR_INSUFFICIENT_BUFFER;
1398 *info_size = size;
1399 return ERROR_SUCCESS;
1402 /***********************************************************************
1403 * urlcache_set_entry_info (Internal)
1405 * Helper for SetUrlCacheEntryInfo{A,W}. Sets fields in URL entry
1406 * according to the flags set by field_control.
1408 * RETURNS
1409 * ERROR_SUCCESS if the buffer was big enough
1410 * ERROR_INSUFFICIENT_BUFFER if the buffer was too small
1413 static DWORD urlcache_set_entry_info(entry_url *url_entry, const INTERNET_CACHE_ENTRY_INFOA *entry_info, DWORD field_control)
1415 if (field_control & CACHE_ENTRY_ACCTIME_FC)
1416 url_entry->access_time = entry_info->LastAccessTime;
1417 if (field_control & CACHE_ENTRY_ATTRIBUTE_FC)
1418 url_entry->cache_entry_type = entry_info->CacheEntryType;
1419 if (field_control & CACHE_ENTRY_EXEMPT_DELTA_FC)
1420 url_entry->exempt_delta = entry_info->u.dwExemptDelta;
1421 if (field_control & CACHE_ENTRY_EXPTIME_FC)
1422 file_time_to_dos_date_time(&entry_info->ExpireTime, &url_entry->expire_date, &url_entry->expire_time);
1423 if (field_control & CACHE_ENTRY_HEADERINFO_FC)
1424 FIXME("CACHE_ENTRY_HEADERINFO_FC unimplemented\n");
1425 if (field_control & CACHE_ENTRY_HITRATE_FC)
1426 url_entry->hit_rate = entry_info->dwHitRate;
1427 if (field_control & CACHE_ENTRY_MODTIME_FC)
1428 url_entry->modification_time = entry_info->LastModifiedTime;
1429 if (field_control & CACHE_ENTRY_SYNCTIME_FC)
1430 file_time_to_dos_date_time(&entry_info->LastAccessTime, &url_entry->sync_date, &url_entry->sync_time);
1432 return ERROR_SUCCESS;
1435 /***********************************************************************
1436 * urlcache_hash_key (Internal)
1438 * Returns the hash key for a given string
1440 * RETURNS
1441 * hash key for the string
1444 static DWORD urlcache_hash_key(LPCSTR lpszKey)
1446 /* NOTE: this uses the same lookup table as SHLWAPI.UrlHash{A,W}
1447 * but the algorithm and result are not the same!
1449 static const unsigned char lookupTable[256] =
1451 0x01, 0x0E, 0x6E, 0x19, 0x61, 0xAE, 0x84, 0x77,
1452 0x8A, 0xAA, 0x7D, 0x76, 0x1B, 0xE9, 0x8C, 0x33,
1453 0x57, 0xC5, 0xB1, 0x6B, 0xEA, 0xA9, 0x38, 0x44,
1454 0x1E, 0x07, 0xAD, 0x49, 0xBC, 0x28, 0x24, 0x41,
1455 0x31, 0xD5, 0x68, 0xBE, 0x39, 0xD3, 0x94, 0xDF,
1456 0x30, 0x73, 0x0F, 0x02, 0x43, 0xBA, 0xD2, 0x1C,
1457 0x0C, 0xB5, 0x67, 0x46, 0x16, 0x3A, 0x4B, 0x4E,
1458 0xB7, 0xA7, 0xEE, 0x9D, 0x7C, 0x93, 0xAC, 0x90,
1459 0xB0, 0xA1, 0x8D, 0x56, 0x3C, 0x42, 0x80, 0x53,
1460 0x9C, 0xF1, 0x4F, 0x2E, 0xA8, 0xC6, 0x29, 0xFE,
1461 0xB2, 0x55, 0xFD, 0xED, 0xFA, 0x9A, 0x85, 0x58,
1462 0x23, 0xCE, 0x5F, 0x74, 0xFC, 0xC0, 0x36, 0xDD,
1463 0x66, 0xDA, 0xFF, 0xF0, 0x52, 0x6A, 0x9E, 0xC9,
1464 0x3D, 0x03, 0x59, 0x09, 0x2A, 0x9B, 0x9F, 0x5D,
1465 0xA6, 0x50, 0x32, 0x22, 0xAF, 0xC3, 0x64, 0x63,
1466 0x1A, 0x96, 0x10, 0x91, 0x04, 0x21, 0x08, 0xBD,
1467 0x79, 0x40, 0x4D, 0x48, 0xD0, 0xF5, 0x82, 0x7A,
1468 0x8F, 0x37, 0x69, 0x86, 0x1D, 0xA4, 0xB9, 0xC2,
1469 0xC1, 0xEF, 0x65, 0xF2, 0x05, 0xAB, 0x7E, 0x0B,
1470 0x4A, 0x3B, 0x89, 0xE4, 0x6C, 0xBF, 0xE8, 0x8B,
1471 0x06, 0x18, 0x51, 0x14, 0x7F, 0x11, 0x5B, 0x5C,
1472 0xFB, 0x97, 0xE1, 0xCF, 0x15, 0x62, 0x71, 0x70,
1473 0x54, 0xE2, 0x12, 0xD6, 0xC7, 0xBB, 0x0D, 0x20,
1474 0x5E, 0xDC, 0xE0, 0xD4, 0xF7, 0xCC, 0xC4, 0x2B,
1475 0xF9, 0xEC, 0x2D, 0xF4, 0x6F, 0xB6, 0x99, 0x88,
1476 0x81, 0x5A, 0xD9, 0xCA, 0x13, 0xA5, 0xE7, 0x47,
1477 0xE6, 0x8E, 0x60, 0xE3, 0x3E, 0xB3, 0xF6, 0x72,
1478 0xA2, 0x35, 0xA0, 0xD7, 0xCD, 0xB4, 0x2F, 0x6D,
1479 0x2C, 0x26, 0x1F, 0x95, 0x87, 0x00, 0xD8, 0x34,
1480 0x3F, 0x17, 0x25, 0x45, 0x27, 0x75, 0x92, 0xB8,
1481 0xA3, 0xC8, 0xDE, 0xEB, 0xF8, 0xF3, 0xDB, 0x0A,
1482 0x98, 0x83, 0x7B, 0xE5, 0xCB, 0x4C, 0x78, 0xD1
1484 BYTE key[4];
1485 DWORD i;
1487 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1488 key[i] = lookupTable[(*lpszKey + i) & 0xFF];
1490 for (lpszKey++; *lpszKey && ((lpszKey[0] != '/') || (lpszKey[1] != 0)); lpszKey++)
1492 for (i = 0; i < sizeof(key) / sizeof(key[0]); i++)
1493 key[i] = lookupTable[*lpszKey ^ key[i]];
1496 return *(DWORD *)key;
1499 static inline entry_hash_table* urlcache_get_hash_table(const urlcache_header *pHeader, DWORD dwOffset)
1501 if(!dwOffset)
1502 return NULL;
1503 return (entry_hash_table*)((LPBYTE)pHeader + dwOffset);
1506 static BOOL urlcache_find_hash_entry(const urlcache_header *pHeader, LPCSTR lpszUrl, struct hash_entry **ppHashEntry)
1508 /* structure of hash table:
1509 * 448 entries divided into 64 blocks
1510 * each block therefore contains a chain of 7 key/offset pairs
1511 * how position in table is calculated:
1512 * 1. the url is hashed in helper function
1513 * 2. the key % HASHTABLE_NUM_ENTRIES is the bucket number
1514 * 3. bucket number * HASHTABLE_BLOCKSIZE is offset of the bucket
1516 * note:
1517 * there can be multiple hash tables in the file and the offset to
1518 * the next one is stored in the header of the hash table
1520 DWORD key = urlcache_hash_key(lpszUrl);
1521 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1522 entry_hash_table* pHashEntry;
1523 DWORD id = 0;
1525 key >>= HASHTABLE_FLAG_BITS;
1527 for (pHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1528 pHashEntry; pHashEntry = urlcache_get_hash_table(pHeader, pHashEntry->next))
1530 int i;
1531 if (pHashEntry->id != id++)
1533 ERR("Error: not right hash table number (%d) expected %d\n", pHashEntry->id, id);
1534 continue;
1536 /* make sure that it is in fact a hash entry */
1537 if (pHashEntry->header.signature != HASH_SIGNATURE)
1539 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->header.signature);
1540 continue;
1543 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1545 struct hash_entry *pHashElement = &pHashEntry->hash_table[offset + i];
1546 if (key == pHashElement->key>>HASHTABLE_FLAG_BITS)
1548 /* FIXME: we should make sure that this is the right element
1549 * before returning and claiming that it is. We can do this
1550 * by doing a simple compare between the URL we were given
1551 * and the URL stored in the entry. However, this assumes
1552 * we know the format of all the entries stored in the
1553 * hash table */
1554 *ppHashEntry = pHashElement;
1555 return TRUE;
1559 return FALSE;
1562 /***********************************************************************
1563 * urlcache_hash_entry_set_flags (Internal)
1565 * Sets special bits in hash key
1567 * RETURNS
1568 * nothing
1571 static void urlcache_hash_entry_set_flags(struct hash_entry *pHashEntry, DWORD dwFlag)
1573 pHashEntry->key = (pHashEntry->key >> HASHTABLE_FLAG_BITS << HASHTABLE_FLAG_BITS) | dwFlag;
1576 /***********************************************************************
1577 * urlcache_hash_entry_delete (Internal)
1579 * Searches all the hash tables in the index for the given URL and
1580 * then if found deletes the entry.
1582 * RETURNS
1583 * TRUE if the entry was found
1584 * FALSE if the entry could not be found
1587 static BOOL urlcache_hash_entry_delete(struct hash_entry *pHashEntry)
1589 pHashEntry->key = HASHTABLE_DEL;
1590 return TRUE;
1593 /***********************************************************************
1594 * urlcache_hash_entry_create (Internal)
1596 * Searches all the hash tables for a free slot based on the offset
1597 * generated from the hash key. If a free slot is found, the offset and
1598 * key are entered into the hash table.
1600 * RETURNS
1601 * ERROR_SUCCESS if the entry was added
1602 * Any other Win32 error code if the entry could not be added
1605 static DWORD urlcache_hash_entry_create(urlcache_header *pHeader, LPCSTR lpszUrl, DWORD dwOffsetEntry, DWORD dwFieldType)
1607 /* see urlcache_find_hash_entry for structure of hash tables */
1609 DWORD key = urlcache_hash_key(lpszUrl);
1610 DWORD offset = (key & (HASHTABLE_NUM_ENTRIES-1)) * HASHTABLE_BLOCKSIZE;
1611 entry_hash_table* pHashEntry, *pHashPrev = NULL;
1612 DWORD id = 0;
1613 DWORD error;
1615 key = ((key >> HASHTABLE_FLAG_BITS) << HASHTABLE_FLAG_BITS) + dwFieldType;
1617 for (pHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1618 pHashEntry; pHashEntry = urlcache_get_hash_table(pHeader, pHashEntry->next))
1620 int i;
1621 pHashPrev = pHashEntry;
1623 if (pHashEntry->id != id++)
1625 ERR("not right hash table number (%d) expected %d\n", pHashEntry->id, id);
1626 break;
1628 /* make sure that it is in fact a hash entry */
1629 if (pHashEntry->header.signature != HASH_SIGNATURE)
1631 ERR("not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&pHashEntry->header.signature);
1632 break;
1635 for (i = 0; i < HASHTABLE_BLOCKSIZE; i++)
1637 struct hash_entry *pHashElement = &pHashEntry->hash_table[offset + i];
1638 if (pHashElement->key==HASHTABLE_FREE || pHashElement->key==HASHTABLE_DEL) /* if the slot is free */
1640 pHashElement->key = key;
1641 pHashElement->offset = dwOffsetEntry;
1642 return ERROR_SUCCESS;
1646 error = urlcache_create_hash_table(pHeader, pHashPrev, &pHashEntry);
1647 if (error != ERROR_SUCCESS)
1648 return error;
1650 pHashEntry->hash_table[offset].key = key;
1651 pHashEntry->hash_table[offset].offset = dwOffsetEntry;
1652 return ERROR_SUCCESS;
1655 /***********************************************************************
1656 * urlcache_enum_hash_tables (Internal)
1658 * Enumerates the hash tables in a container.
1660 * RETURNS
1661 * TRUE if an entry was found
1662 * FALSE if there are no more tables to enumerate.
1665 static BOOL urlcache_enum_hash_tables(const urlcache_header *pHeader, DWORD *id, entry_hash_table **ppHashEntry)
1667 for (*ppHashEntry = urlcache_get_hash_table(pHeader, pHeader->hash_table_off);
1668 *ppHashEntry; *ppHashEntry = urlcache_get_hash_table(pHeader, (*ppHashEntry)->next))
1670 TRACE("looking at hash table number %d\n", (*ppHashEntry)->id);
1671 if ((*ppHashEntry)->id != *id)
1672 continue;
1673 /* make sure that it is in fact a hash entry */
1674 if ((*ppHashEntry)->header.signature != HASH_SIGNATURE)
1676 ERR("Error: not right signature (\"%.4s\") - expected \"HASH\"\n", (LPCSTR)&(*ppHashEntry)->header.signature);
1677 (*id)++;
1678 continue;
1681 TRACE("hash table number %d found\n", *id);
1682 return TRUE;
1684 return FALSE;
1687 /***********************************************************************
1688 * urlcache_enum_hash_table_entries (Internal)
1690 * Enumerates entries in a hash table and returns the next non-free entry.
1692 * RETURNS
1693 * TRUE if an entry was found
1694 * FALSE if the hash table is empty or there are no more entries to
1695 * enumerate.
1698 static BOOL urlcache_enum_hash_table_entries(const urlcache_header *pHeader, const entry_hash_table *pHashEntry,
1699 DWORD * index, const struct hash_entry **ppHashEntry)
1701 for (; *index < HASHTABLE_SIZE ; (*index)++)
1703 if (pHashEntry->hash_table[*index].key==HASHTABLE_FREE || pHashEntry->hash_table[*index].key==HASHTABLE_DEL)
1704 continue;
1706 *ppHashEntry = &pHashEntry->hash_table[*index];
1707 TRACE("entry found %d\n", *index);
1708 return TRUE;
1710 TRACE("no more entries (%d)\n", *index);
1711 return FALSE;
1714 /***********************************************************************
1715 * cache_container_delete_dir (Internal)
1717 * Erase a directory containing an URL cache.
1719 * RETURNS
1720 * TRUE success, FALSE failure/aborted.
1723 static BOOL cache_container_delete_dir(LPCWSTR lpszPath)
1725 DWORD path_len;
1726 WCHAR path[MAX_PATH + 1];
1727 SHFILEOPSTRUCTW shfos;
1728 int ret;
1730 path_len = strlenW(lpszPath);
1731 if (path_len >= MAX_PATH)
1732 return FALSE;
1733 strcpyW(path, lpszPath);
1734 path[path_len + 1] = 0; /* double-NUL-terminate path */
1736 shfos.hwnd = NULL;
1737 shfos.wFunc = FO_DELETE;
1738 shfos.pFrom = path;
1739 shfos.pTo = NULL;
1740 shfos.fFlags = FOF_NOCONFIRMATION;
1741 shfos.fAnyOperationsAborted = FALSE;
1742 ret = SHFileOperationW(&shfos);
1743 if (ret)
1744 ERR("SHFileOperationW on %s returned %i\n", debugstr_w(path), ret);
1745 return !(ret || shfos.fAnyOperationsAborted);
1748 /***********************************************************************
1749 * urlcache_hash_entry_is_locked (Internal)
1751 * Checks if entry is locked. Unlocks it if possible.
1753 static BOOL urlcache_hash_entry_is_locked(struct hash_entry *hash_entry, entry_url *url_entry)
1755 FILETIME cur_time;
1756 ULARGE_INTEGER acc_time, time;
1758 if ((hash_entry->key & ((1<<HASHTABLE_FLAG_BITS)-1)) != HASHTABLE_LOCK)
1759 return FALSE;
1761 GetSystemTimeAsFileTime(&cur_time);
1762 time.u.LowPart = cur_time.dwLowDateTime;
1763 time.u.HighPart = cur_time.dwHighDateTime;
1765 acc_time.u.LowPart = url_entry->access_time.dwLowDateTime;
1766 acc_time.u.HighPart = url_entry->access_time.dwHighDateTime;
1768 time.QuadPart -= acc_time.QuadPart;
1770 /* check if entry was locked for at least a day */
1771 if(time.QuadPart > (ULONGLONG)24*60*60*FILETIME_SECOND) {
1772 urlcache_hash_entry_set_flags(hash_entry, HASHTABLE_URL);
1773 url_entry->use_count = 0;
1774 return FALSE;
1777 return TRUE;
1780 static BOOL urlcache_get_entry_info(const char *url, void *entry_info,
1781 DWORD *size, DWORD flags, BOOL unicode)
1783 urlcache_header *header;
1784 struct hash_entry *hash_entry;
1785 const entry_url *url_entry;
1786 cache_container *container;
1787 DWORD error;
1789 TRACE("(%s, %p, %p, %x, %x)\n", debugstr_a(url), entry_info, size, flags, unicode);
1791 if(flags & ~GET_INSTALLED_ENTRY)
1792 FIXME("ignoring unsupported flags: %x\n", flags);
1794 error = cache_containers_find(url, &container);
1795 if(error != ERROR_SUCCESS) {
1796 SetLastError(error);
1797 return FALSE;
1800 error = cache_container_open_index(container, MIN_BLOCK_NO);
1801 if(error != ERROR_SUCCESS) {
1802 SetLastError(error);
1803 return FALSE;
1806 if(!(header = cache_container_lock_index(container)))
1807 return FALSE;
1809 if(!urlcache_find_hash_entry(header, url, &hash_entry)) {
1810 cache_container_unlock_index(container, header);
1811 WARN("entry %s not found!\n", debugstr_a(url));
1812 SetLastError(ERROR_FILE_NOT_FOUND);
1813 return FALSE;
1816 url_entry = (const entry_url*)((LPBYTE)header + hash_entry->offset);
1817 if(url_entry->header.signature != URL_SIGNATURE) {
1818 cache_container_unlock_index(container, header);
1819 FIXME("Trying to retrieve entry of unknown format %s\n",
1820 debugstr_an((LPCSTR)&url_entry->header.signature, sizeof(DWORD)));
1821 SetLastError(ERROR_FILE_NOT_FOUND);
1822 return FALSE;
1825 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->url_off));
1826 TRACE("Header info: %s\n", debugstr_an((LPCSTR)url_entry +
1827 url_entry->header_info_off, url_entry->header_info_size));
1829 if((flags & GET_INSTALLED_ENTRY) && !(url_entry->cache_entry_type & INSTALLED_CACHE_ENTRY)) {
1830 cache_container_unlock_index(container, header);
1831 SetLastError(ERROR_FILE_NOT_FOUND);
1832 return FALSE;
1835 if(size) {
1836 if(!entry_info)
1837 *size = 0;
1839 error = urlcache_copy_entry(container, header, entry_info, size, url_entry, unicode);
1840 if(error != ERROR_SUCCESS) {
1841 cache_container_unlock_index(container, header);
1842 SetLastError(error);
1843 return FALSE;
1845 if(url_entry->local_name_off)
1846 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->local_name_off));
1849 cache_container_unlock_index(container, header);
1850 return TRUE;
1853 /***********************************************************************
1854 * GetUrlCacheEntryInfoExA (WININET.@)
1857 BOOL WINAPI GetUrlCacheEntryInfoExA(LPCSTR lpszUrl,
1858 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1859 LPDWORD lpdwCacheEntryInfoBufSize, LPSTR lpszReserved,
1860 LPDWORD lpdwReserved, LPVOID lpReserved, DWORD dwFlags)
1862 if(lpszReserved!=NULL || lpdwReserved!=NULL || lpReserved!=NULL) {
1863 ERR("Reserved value was not 0\n");
1864 SetLastError(ERROR_INVALID_PARAMETER);
1865 return FALSE;
1868 return urlcache_get_entry_info(lpszUrl, lpCacheEntryInfo,
1869 lpdwCacheEntryInfoBufSize, dwFlags, FALSE);
1872 /***********************************************************************
1873 * GetUrlCacheEntryInfoA (WININET.@)
1876 BOOL WINAPI GetUrlCacheEntryInfoA(LPCSTR lpszUrlName,
1877 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
1878 LPDWORD lpdwCacheEntryInfoBufferSize)
1880 return GetUrlCacheEntryInfoExA(lpszUrlName, lpCacheEntryInfo,
1881 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
1884 static int urlcache_encode_url(const WCHAR *url, char *encoded_url, int encoded_len)
1886 URL_COMPONENTSW uc;
1887 DWORD len, part_len;
1888 WCHAR *punycode;
1890 TRACE("%s\n", debugstr_w(url));
1892 memset(&uc, 0, sizeof(uc));
1893 uc.dwStructSize = sizeof(uc);
1894 uc.dwHostNameLength = 1;
1895 if(!InternetCrackUrlW(url, 0, 0, &uc))
1896 uc.nScheme = INTERNET_SCHEME_UNKNOWN;
1898 if(uc.nScheme!=INTERNET_SCHEME_HTTP && uc.nScheme!=INTERNET_SCHEME_HTTPS)
1899 return WideCharToMultiByte(CP_UTF8, 0, url, -1, encoded_url, encoded_len, NULL, NULL);
1901 len = WideCharToMultiByte(CP_UTF8, 0, url, uc.lpszHostName-url,
1902 encoded_url, encoded_len, NULL, NULL);
1903 if(!len)
1904 return 0;
1905 if(encoded_url)
1906 encoded_len -= len;
1908 part_len = IdnToAscii(0, uc.lpszHostName, uc.dwHostNameLength, NULL, 0);
1909 if(!part_len) {
1910 SetLastError(ERROR_INTERNET_INVALID_URL);
1911 return 0;
1914 punycode = heap_alloc(part_len*sizeof(WCHAR));
1915 if(!punycode)
1916 return 0;
1918 part_len = IdnToAscii(0, uc.lpszHostName, uc.dwHostNameLength, punycode, part_len);
1919 if(!part_len) {
1920 heap_free(punycode);
1921 return 0;
1924 part_len = WideCharToMultiByte(CP_UTF8, 0, punycode, part_len,
1925 encoded_url ? encoded_url+len : NULL, encoded_len, NULL, NULL);
1926 heap_free(punycode);
1927 if(!part_len)
1928 return 0;
1929 if(encoded_url)
1930 encoded_len -= part_len;
1931 len += part_len;
1933 part_len = WideCharToMultiByte(CP_UTF8, 0, uc.lpszHostName+uc.dwHostNameLength,
1934 -1, encoded_url ? encoded_url+len : NULL, encoded_len, NULL, NULL);
1935 if(!part_len)
1936 return 0;
1937 len += part_len;
1939 TRACE("got (%d)%s\n", len, debugstr_a(encoded_url));
1940 return len;
1943 static BOOL urlcache_encode_url_alloc(const WCHAR *url, char **encoded_url)
1945 DWORD encoded_len;
1946 char *ret;
1948 encoded_len = urlcache_encode_url(url, NULL, 0);
1949 if(!encoded_len)
1950 return FALSE;
1952 ret = heap_alloc(encoded_len*sizeof(WCHAR));
1953 if(!ret)
1954 return FALSE;
1956 encoded_len = urlcache_encode_url(url, ret, encoded_len);
1957 if(!encoded_len) {
1958 heap_free(ret);
1959 return FALSE;
1962 *encoded_url = ret;
1963 return TRUE;
1966 /***********************************************************************
1967 * GetUrlCacheEntryInfoExW (WININET.@)
1970 BOOL WINAPI GetUrlCacheEntryInfoExW(LPCWSTR lpszUrl,
1971 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
1972 LPDWORD lpdwCacheEntryInfoBufSize, LPWSTR lpszReserved,
1973 LPDWORD lpdwReserved, LPVOID lpReserved, DWORD dwFlags)
1975 char *url;
1976 BOOL ret;
1978 if(lpszReserved!=NULL || lpdwReserved!=NULL || lpReserved!=NULL) {
1979 ERR("Reserved value was not 0\n");
1980 SetLastError(ERROR_INVALID_PARAMETER);
1981 return FALSE;
1984 /* Ignore GET_INSTALLED_ENTRY flag in unicode version of function */
1985 dwFlags &= ~GET_INSTALLED_ENTRY;
1987 if(!urlcache_encode_url_alloc(lpszUrl, &url))
1988 return FALSE;
1990 ret = urlcache_get_entry_info(url, lpCacheEntryInfo,
1991 lpdwCacheEntryInfoBufSize, dwFlags, TRUE);
1992 heap_free(url);
1993 return ret;
1996 /***********************************************************************
1997 * GetUrlCacheEntryInfoW (WININET.@)
2000 BOOL WINAPI GetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
2001 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2002 LPDWORD lpdwCacheEntryInfoBufferSize)
2004 return GetUrlCacheEntryInfoExW(lpszUrl, lpCacheEntryInfo,
2005 lpdwCacheEntryInfoBufferSize, NULL, NULL, NULL, 0);
2008 /***********************************************************************
2009 * SetUrlCacheEntryInfoA (WININET.@)
2011 BOOL WINAPI SetUrlCacheEntryInfoA(LPCSTR lpszUrlName,
2012 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2013 DWORD dwFieldControl)
2015 urlcache_header *pHeader;
2016 struct hash_entry *pHashEntry;
2017 entry_header *pEntry;
2018 cache_container *pContainer;
2019 DWORD error;
2021 TRACE("(%s, %p, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo, dwFieldControl);
2023 error = cache_containers_find(lpszUrlName, &pContainer);
2024 if (error != ERROR_SUCCESS)
2026 SetLastError(error);
2027 return FALSE;
2030 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2031 if (error != ERROR_SUCCESS)
2033 SetLastError(error);
2034 return FALSE;
2037 if (!(pHeader = cache_container_lock_index(pContainer)))
2038 return FALSE;
2040 if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
2042 cache_container_unlock_index(pContainer, pHeader);
2043 WARN("entry %s not found!\n", debugstr_a(lpszUrlName));
2044 SetLastError(ERROR_FILE_NOT_FOUND);
2045 return FALSE;
2048 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2049 if (pEntry->signature != URL_SIGNATURE)
2051 cache_container_unlock_index(pContainer, pHeader);
2052 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2053 SetLastError(ERROR_FILE_NOT_FOUND);
2054 return FALSE;
2057 urlcache_set_entry_info((entry_url*)pEntry, lpCacheEntryInfo, dwFieldControl);
2059 cache_container_unlock_index(pContainer, pHeader);
2061 return TRUE;
2064 /***********************************************************************
2065 * SetUrlCacheEntryInfoW (WININET.@)
2067 BOOL WINAPI SetUrlCacheEntryInfoW(LPCWSTR lpszUrl,
2068 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2069 DWORD dwFieldControl)
2071 char *url;
2072 BOOL ret;
2074 if(!urlcache_encode_url_alloc(lpszUrl, &url))
2075 return FALSE;
2077 ret = SetUrlCacheEntryInfoA(url, (INTERNET_CACHE_ENTRY_INFOA*)lpCacheEntryInfo, dwFieldControl);
2078 heap_free(url);
2079 return ret;
2082 static BOOL urlcache_entry_get_file(const char *url, void *entry_info, DWORD *size, BOOL unicode)
2084 urlcache_header *header;
2085 struct hash_entry *hash_entry;
2086 entry_url *url_entry;
2087 cache_container *container;
2088 DWORD error;
2090 TRACE("(%s, %p, %p, %x)\n", debugstr_a(url), entry_info, size, unicode);
2092 if(!url || !size || (!entry_info && *size)) {
2093 SetLastError(ERROR_INVALID_PARAMETER);
2094 return FALSE;
2097 error = cache_containers_find(url, &container);
2098 if(error != ERROR_SUCCESS) {
2099 SetLastError(error);
2100 return FALSE;
2103 error = cache_container_open_index(container, MIN_BLOCK_NO);
2104 if (error != ERROR_SUCCESS) {
2105 SetLastError(error);
2106 return FALSE;
2109 if (!(header = cache_container_lock_index(container)))
2110 return FALSE;
2112 if (!urlcache_find_hash_entry(header, url, &hash_entry)) {
2113 cache_container_unlock_index(container, header);
2114 TRACE("entry %s not found!\n", url);
2115 SetLastError(ERROR_FILE_NOT_FOUND);
2116 return FALSE;
2119 url_entry = (entry_url*)((LPBYTE)header + hash_entry->offset);
2120 if(url_entry->header.signature != URL_SIGNATURE) {
2121 cache_container_unlock_index(container, header);
2122 FIXME("Trying to retrieve entry of unknown format %s\n",
2123 debugstr_an((LPSTR)&url_entry->header.signature, sizeof(DWORD)));
2124 SetLastError(ERROR_FILE_NOT_FOUND);
2125 return FALSE;
2128 if(!url_entry->local_name_off) {
2129 cache_container_unlock_index(container, header);
2130 SetLastError(ERROR_INVALID_DATA);
2131 return FALSE;
2134 TRACE("Found URL: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->url_off));
2135 TRACE("Header info: %s\n", debugstr_an((LPCSTR)url_entry + url_entry->header_info_off,
2136 url_entry->header_info_size));
2138 error = urlcache_copy_entry(container, header, entry_info,
2139 size, url_entry, unicode);
2140 if(error != ERROR_SUCCESS) {
2141 cache_container_unlock_index(container, header);
2142 SetLastError(error);
2143 return FALSE;
2145 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)url_entry + url_entry->local_name_off));
2147 url_entry->hit_rate++;
2148 url_entry->use_count++;
2149 urlcache_hash_entry_set_flags(hash_entry, HASHTABLE_LOCK);
2150 GetSystemTimeAsFileTime(&url_entry->access_time);
2152 cache_container_unlock_index(container, header);
2154 return TRUE;
2157 /***********************************************************************
2158 * RetrieveUrlCacheEntryFileA (WININET.@)
2161 BOOL WINAPI RetrieveUrlCacheEntryFileA(LPCSTR lpszUrlName,
2162 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
2163 LPDWORD lpdwCacheEntryInfoBufferSize, DWORD dwReserved)
2165 return urlcache_entry_get_file(lpszUrlName, lpCacheEntryInfo,
2166 lpdwCacheEntryInfoBufferSize, FALSE);
2169 /***********************************************************************
2170 * RetrieveUrlCacheEntryFileW (WININET.@)
2173 BOOL WINAPI RetrieveUrlCacheEntryFileW(LPCWSTR lpszUrlName,
2174 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
2175 LPDWORD lpdwCacheEntryInfoBufferSize, DWORD dwReserved)
2177 char *url;
2178 BOOL ret;
2180 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
2181 return FALSE;
2183 ret = urlcache_entry_get_file(url, lpCacheEntryInfo,
2184 lpdwCacheEntryInfoBufferSize, TRUE);
2185 heap_free(url);
2186 return ret;
2189 static BOOL urlcache_entry_delete(const cache_container *pContainer,
2190 urlcache_header *pHeader, struct hash_entry *pHashEntry)
2192 entry_header *pEntry;
2193 entry_url * pUrlEntry;
2195 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2196 if (pEntry->signature != URL_SIGNATURE)
2198 FIXME("Trying to delete entry of unknown format %s\n",
2199 debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
2200 SetLastError(ERROR_FILE_NOT_FOUND);
2201 return FALSE;
2204 pUrlEntry = (entry_url *)pEntry;
2205 if(urlcache_hash_entry_is_locked(pHashEntry, pUrlEntry))
2207 TRACE("Trying to delete locked entry\n");
2208 pUrlEntry->cache_entry_type |= PENDING_DELETE_CACHE_ENTRY;
2209 SetLastError(ERROR_SHARING_VIOLATION);
2210 return FALSE;
2213 if(!urlcache_delete_file(pContainer, pHeader, pUrlEntry))
2215 urlcache_entry_free(pHeader, pEntry);
2217 else
2219 /* Add entry to leaked files list */
2220 pUrlEntry->header.signature = LEAK_SIGNATURE;
2221 pUrlEntry->exempt_delta = pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET];
2222 pHeader->options[CACHE_HEADER_DATA_ROOT_LEAK_OFFSET] = pHashEntry->offset;
2225 urlcache_hash_entry_delete(pHashEntry);
2226 return TRUE;
2229 static HANDLE free_cache_running;
2230 static HANDLE dll_unload_event;
2231 static DWORD WINAPI handle_full_cache_worker(void *param)
2233 FreeUrlCacheSpaceW(NULL, 20, 0);
2234 ReleaseSemaphore(free_cache_running, 1, NULL);
2235 return 0;
2238 static void handle_full_cache(void)
2240 if(WaitForSingleObject(free_cache_running, 0) == WAIT_OBJECT_0) {
2241 if(!QueueUserWorkItem(handle_full_cache_worker, NULL, 0))
2242 ReleaseSemaphore(free_cache_running, 1, NULL);
2246 /* Enumerates entries in cache, allows cache unlocking between calls. */
2247 static BOOL urlcache_next_entry(urlcache_header *header, DWORD *hash_table_off, DWORD *hash_table_entry,
2248 struct hash_entry **hash_entry, entry_header **entry)
2250 entry_hash_table *hashtable_entry;
2252 *hash_entry = NULL;
2253 *entry = NULL;
2255 if(!*hash_table_off) {
2256 *hash_table_off = header->hash_table_off;
2257 *hash_table_entry = 0;
2259 hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2260 }else {
2261 if(*hash_table_off >= header->size) {
2262 *hash_table_off = 0;
2263 return FALSE;
2266 hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2269 if(hashtable_entry->header.signature != HASH_SIGNATURE) {
2270 *hash_table_off = 0;
2271 return FALSE;
2274 while(1) {
2275 if(*hash_table_entry >= HASHTABLE_SIZE) {
2276 *hash_table_off = hashtable_entry->next;
2277 if(!*hash_table_off) {
2278 *hash_table_off = 0;
2279 return FALSE;
2282 hashtable_entry = urlcache_get_hash_table(header, *hash_table_off);
2283 *hash_table_entry = 0;
2286 if(hashtable_entry->hash_table[*hash_table_entry].key != HASHTABLE_DEL &&
2287 hashtable_entry->hash_table[*hash_table_entry].key != HASHTABLE_FREE) {
2288 *hash_entry = &hashtable_entry->hash_table[*hash_table_entry];
2289 *entry = (entry_header*)((LPBYTE)header + hashtable_entry->hash_table[*hash_table_entry].offset);
2290 (*hash_table_entry)++;
2291 return TRUE;
2294 (*hash_table_entry)++;
2297 *hash_table_off = 0;
2298 return FALSE;
2301 /* Rates an urlcache entry to determine if it can be deleted.
2303 * Score 0 means that entry can safely be removed, the bigger rating
2304 * the smaller chance of entry being removed.
2305 * DWORD_MAX means that entry can't be deleted at all.
2307 * Rating system is currently not fully compatible with native implementation.
2309 static DWORD urlcache_rate_entry(entry_url *url_entry, FILETIME *cur_time)
2311 ULARGE_INTEGER time, access_time;
2312 DWORD rating;
2314 access_time.u.LowPart = url_entry->access_time.dwLowDateTime;
2315 access_time.u.HighPart = url_entry->access_time.dwHighDateTime;
2317 time.u.LowPart = cur_time->dwLowDateTime;
2318 time.u.HighPart = cur_time->dwHighDateTime;
2320 /* Don't touch entries that were added less than 10 minutes ago */
2321 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)10*60*FILETIME_SECOND)
2322 return -1;
2324 if(url_entry->cache_entry_type & STICKY_CACHE_ENTRY)
2325 if(time.QuadPart < access_time.QuadPart + (ULONGLONG)url_entry->exempt_delta*FILETIME_SECOND)
2326 return -1;
2328 time.QuadPart = (time.QuadPart-access_time.QuadPart)/FILETIME_SECOND;
2329 rating = 400*60*60*24/(60*60*24+time.QuadPart);
2331 if(url_entry->hit_rate > 100)
2332 rating += 100;
2333 else
2334 rating += url_entry->hit_rate;
2336 return rating;
2339 static int dword_cmp(const void *p1, const void *p2)
2341 return *(const DWORD*)p1 - *(const DWORD*)p2;
2344 /***********************************************************************
2345 * FreeUrlCacheSpaceW (WININET.@)
2347 * Frees up some cache.
2349 * PARAMETERS
2350 * cache_path [I] Which volume to free up from, or NULL if you don't care.
2351 * size [I] Percentage of the cache that should be free.
2352 * filter [I] Which entries can't be deleted (CacheEntryType)
2354 * RETURNS
2355 * TRUE success. FALSE failure.
2357 * IMPLEMENTATION
2358 * This implementation just retrieves the path of the cache directory, and
2359 * deletes its contents from the filesystem. The correct approach would
2360 * probably be to implement and use {FindFirst,FindNext,Delete}UrlCacheGroup().
2362 BOOL WINAPI FreeUrlCacheSpaceW(LPCWSTR cache_path, DWORD size, DWORD filter)
2364 cache_container *container;
2365 DWORD path_len, err;
2367 TRACE("(%s, %x, %x)\n", debugstr_w(cache_path), size, filter);
2369 if(size<1 || size>100) {
2370 SetLastError(ERROR_INVALID_PARAMETER);
2371 return FALSE;
2374 if(cache_path) {
2375 path_len = strlenW(cache_path);
2376 if(cache_path[path_len-1] == '\\')
2377 path_len--;
2378 }else {
2379 path_len = 0;
2382 if(size==100 && !filter) {
2383 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
2385 /* When cache_path==NULL only clean Temporary Internet Files */
2386 if((!path_len && container->cache_prefix[0]==0) ||
2387 (path_len && !strncmpiW(container->path, cache_path, path_len) &&
2388 (container->path[path_len]=='\0' || container->path[path_len]=='\\')))
2390 BOOL ret_del;
2392 WaitForSingleObject(container->mutex, INFINITE);
2394 /* unlock, delete, recreate and lock cache */
2395 cache_container_close_index(container);
2396 ret_del = cache_container_delete_dir(container->path);
2397 err = cache_container_open_index(container, MIN_BLOCK_NO);
2399 ReleaseMutex(container->mutex);
2400 if(!ret_del || (err != ERROR_SUCCESS))
2401 return FALSE;
2405 return TRUE;
2408 LIST_FOR_EACH_ENTRY(container, &UrlContainers, cache_container, entry)
2410 urlcache_header *header;
2411 struct hash_entry *hash_entry;
2412 entry_header *entry;
2413 entry_url *url_entry;
2414 ULONGLONG desired_size, cur_size;
2415 DWORD delete_factor, hash_table_off, hash_table_entry;
2416 DWORD rate[100], rate_no;
2417 FILETIME cur_time;
2419 if((path_len || container->cache_prefix[0]!=0) &&
2420 (!path_len || strncmpiW(container->path, cache_path, path_len) ||
2421 (container->path[path_len]!='\0' && container->path[path_len]!='\\')))
2422 continue;
2424 err = cache_container_open_index(container, MIN_BLOCK_NO);
2425 if(err != ERROR_SUCCESS)
2426 continue;
2428 header = cache_container_lock_index(container);
2429 if(!header)
2430 continue;
2432 urlcache_clean_leaked_entries(container, header);
2434 desired_size = header->cache_limit.QuadPart*(100-size)/100;
2435 cur_size = header->cache_usage.QuadPart+header->exempt_usage.QuadPart;
2436 if(cur_size <= desired_size)
2437 delete_factor = 0;
2438 else
2439 delete_factor = (cur_size-desired_size)*100/cur_size;
2441 if(!delete_factor) {
2442 cache_container_unlock_index(container, header);
2443 continue;
2446 hash_table_off = 0;
2447 hash_table_entry = 0;
2448 rate_no = 0;
2449 GetSystemTimeAsFileTime(&cur_time);
2450 while(rate_no<sizeof(rate)/sizeof(*rate) &&
2451 urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2452 if(entry->signature != URL_SIGNATURE) {
2453 WARN("only url entries are currently supported\n");
2454 continue;
2457 url_entry = (entry_url*)entry;
2458 if(url_entry->cache_entry_type & filter)
2459 continue;
2461 rate[rate_no] = urlcache_rate_entry(url_entry, &cur_time);
2462 if(rate[rate_no] != -1)
2463 rate_no++;
2466 if(!rate_no) {
2467 TRACE("nothing to delete\n");
2468 cache_container_unlock_index(container, header);
2469 continue;
2472 qsort(rate, rate_no, sizeof(DWORD), dword_cmp);
2474 delete_factor = delete_factor*rate_no/100;
2475 delete_factor = rate[delete_factor];
2476 TRACE("deleting files with rating %d or less\n", delete_factor);
2478 hash_table_off = 0;
2479 while(urlcache_next_entry(header, &hash_table_off, &hash_table_entry, &hash_entry, &entry)) {
2480 if(entry->signature != URL_SIGNATURE)
2481 continue;
2483 url_entry = (entry_url*)entry;
2484 if(url_entry->cache_entry_type & filter)
2485 continue;
2487 if(urlcache_rate_entry(url_entry, &cur_time) <= delete_factor) {
2488 TRACE("deleting file: %s\n", debugstr_a((char*)url_entry+url_entry->local_name_off));
2489 urlcache_entry_delete(container, header, hash_entry);
2491 if(header->cache_usage.QuadPart+header->exempt_usage.QuadPart <= desired_size)
2492 break;
2494 /* Allow other threads to use cache while cleaning */
2495 cache_container_unlock_index(container, header);
2496 if(WaitForSingleObject(dll_unload_event, 0) == WAIT_OBJECT_0) {
2497 TRACE("got dll_unload_event - finishing\n");
2498 return TRUE;
2500 Sleep(0);
2501 header = cache_container_lock_index(container);
2505 TRACE("cache size after cleaning 0x%s/0x%s\n",
2506 wine_dbgstr_longlong(header->cache_usage.QuadPart+header->exempt_usage.QuadPart),
2507 wine_dbgstr_longlong(header->cache_limit.QuadPart));
2508 cache_container_unlock_index(container, header);
2511 return TRUE;
2514 /***********************************************************************
2515 * FreeUrlCacheSpaceA (WININET.@)
2517 * See FreeUrlCacheSpaceW.
2519 BOOL WINAPI FreeUrlCacheSpaceA(LPCSTR lpszCachePath, DWORD dwSize, DWORD dwFilter)
2521 BOOL ret = FALSE;
2522 LPWSTR path = heap_strdupAtoW(lpszCachePath);
2523 if (lpszCachePath == NULL || path != NULL)
2524 ret = FreeUrlCacheSpaceW(path, dwSize, dwFilter);
2525 heap_free(path);
2526 return ret;
2529 /***********************************************************************
2530 * UnlockUrlCacheEntryFileA (WININET.@)
2533 BOOL WINAPI UnlockUrlCacheEntryFileA(LPCSTR lpszUrlName, DWORD dwReserved)
2535 urlcache_header *pHeader;
2536 struct hash_entry *pHashEntry;
2537 entry_header *pEntry;
2538 entry_url * pUrlEntry;
2539 cache_container *pContainer;
2540 DWORD error;
2542 TRACE("(%s, 0x%08x)\n", debugstr_a(lpszUrlName), dwReserved);
2544 if (dwReserved)
2546 ERR("dwReserved != 0\n");
2547 SetLastError(ERROR_INVALID_PARAMETER);
2548 return FALSE;
2551 error = cache_containers_find(lpszUrlName, &pContainer);
2552 if (error != ERROR_SUCCESS)
2554 SetLastError(error);
2555 return FALSE;
2558 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
2559 if (error != ERROR_SUCCESS)
2561 SetLastError(error);
2562 return FALSE;
2565 if (!(pHeader = cache_container_lock_index(pContainer)))
2566 return FALSE;
2568 if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
2570 cache_container_unlock_index(pContainer, pHeader);
2571 TRACE("entry %s not found!\n", lpszUrlName);
2572 SetLastError(ERROR_FILE_NOT_FOUND);
2573 return FALSE;
2576 pEntry = (entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
2577 if (pEntry->signature != URL_SIGNATURE)
2579 cache_container_unlock_index(pContainer, pHeader);
2580 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPSTR)&pEntry->signature, sizeof(DWORD)));
2581 SetLastError(ERROR_FILE_NOT_FOUND);
2582 return FALSE;
2585 pUrlEntry = (entry_url *)pEntry;
2587 if (pUrlEntry->use_count == 0)
2589 cache_container_unlock_index(pContainer, pHeader);
2590 return FALSE;
2592 pUrlEntry->use_count--;
2593 if (!pUrlEntry->use_count)
2595 urlcache_hash_entry_set_flags(pHashEntry, HASHTABLE_URL);
2596 if (pUrlEntry->cache_entry_type & PENDING_DELETE_CACHE_ENTRY)
2597 urlcache_entry_delete(pContainer, pHeader, pHashEntry);
2600 cache_container_unlock_index(pContainer, pHeader);
2602 return TRUE;
2605 /***********************************************************************
2606 * UnlockUrlCacheEntryFileW (WININET.@)
2609 BOOL WINAPI UnlockUrlCacheEntryFileW(LPCWSTR lpszUrlName, DWORD dwReserved)
2611 char *url;
2612 BOOL ret;
2614 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
2615 return FALSE;
2617 ret = UnlockUrlCacheEntryFileA(url, dwReserved);
2618 heap_free(url);
2619 return ret;
2622 static BOOL urlcache_entry_create(const char *url, const char *ext, WCHAR *full_path)
2624 cache_container *container;
2625 urlcache_header *header;
2626 char file_name[MAX_PATH];
2627 WCHAR extW[MAX_PATH];
2628 BYTE cache_dir;
2629 LONG full_path_len;
2630 BOOL generate_name = FALSE;
2631 DWORD error;
2632 HANDLE file;
2633 FILETIME ft;
2634 URL_COMPONENTSA uc;
2635 int i;
2637 TRACE("(%s, %s, %p)\n", debugstr_a(url), debugstr_a(ext), full_path);
2639 memset(&uc, 0, sizeof(uc));
2640 uc.dwStructSize = sizeof(uc);
2641 uc.dwUrlPathLength = 1;
2642 uc.dwExtraInfoLength = 1;
2643 if(!InternetCrackUrlA(url, 0, 0, &uc))
2644 uc.dwUrlPathLength = 0;
2646 if(!uc.dwUrlPathLength) {
2647 file_name[0] = 0;
2648 }else {
2649 char *p, *e;
2651 p = e = uc.lpszUrlPath+uc.dwUrlPathLength;
2652 while(p>uc.lpszUrlPath && *(p-1)!='/' && *(p-1)!='\\' && *(p-1)!='.')
2653 p--;
2654 if(p>uc.lpszUrlPath && *(p-1)=='.') {
2655 e = p-1;
2656 while(p>uc.lpszUrlPath && *(p-1)!='/' && *(p-1)!='\\')
2657 p--;
2660 memcpy(file_name, p, e-p);
2661 file_name[e-p] = 0;
2663 for(p=file_name; *p; p++) {
2664 switch(*p) {
2665 case '<': case '>':
2666 case ':': case '"':
2667 case '|': case '?':
2668 case '*':
2669 *p = '_'; break;
2670 default: break;
2675 if(!file_name[0])
2676 generate_name = TRUE;
2678 error = cache_containers_find(url, &container);
2679 if(error != ERROR_SUCCESS) {
2680 SetLastError(error);
2681 return FALSE;
2684 error = cache_container_open_index(container, MIN_BLOCK_NO);
2685 if(error != ERROR_SUCCESS) {
2686 SetLastError(error);
2687 return FALSE;
2690 if(!(header = cache_container_lock_index(container)))
2691 return FALSE;
2693 if(header->dirs_no)
2694 cache_dir = (BYTE)(rand() % header->dirs_no);
2695 else
2696 cache_dir = CACHE_CONTAINER_NO_SUBDIR;
2698 full_path_len = MAX_PATH * sizeof(WCHAR);
2699 if(!urlcache_create_file_pathW(container, header, file_name, cache_dir, full_path, &full_path_len)) {
2700 WARN("Failed to get full path for filename %s, needed %u bytes.\n",
2701 debugstr_a(file_name), full_path_len);
2702 cache_container_unlock_index(container, header);
2703 return FALSE;
2705 full_path_len = full_path_len/sizeof(WCHAR) - 1;
2707 cache_container_unlock_index(container, header);
2709 if(ext) {
2710 WCHAR *p;
2712 extW[0] = '.';
2713 MultiByteToWideChar(CP_ACP, 0, ext, -1, extW+1, MAX_PATH-1);
2715 for(p=extW; *p; p++) {
2716 switch(*p) {
2717 case '<': case '>':
2718 case ':': case '"':
2719 case '|': case '?':
2720 case '*':
2721 *p = '_'; break;
2722 default: break;
2725 if(p[-1]==' ' || p[-1]=='.')
2726 p[-1] = '_';
2727 }else {
2728 extW[0] = '\0';
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 /* Try to generate random name */
2745 GetSystemTimeAsFileTime(&ft);
2746 strcpyW(full_path+full_path_len+8, extW);
2748 for(i=0; i<255; i++) {
2749 int j;
2750 ULONGLONG n = ft.dwHighDateTime;
2751 n <<= 32;
2752 n += ft.dwLowDateTime;
2753 n ^= (ULONGLONG)i<<48;
2755 for(j=0; j<8; j++) {
2756 int r = (n % 36);
2757 n /= 37;
2758 full_path[full_path_len+j] = (r < 10 ? '0' + r : 'A' + r - 10);
2761 TRACE("Trying: %s\n", debugstr_w(full_path));
2762 file = CreateFileW(full_path, GENERIC_READ, 0, NULL, CREATE_NEW, 0, NULL);
2763 if(file != INVALID_HANDLE_VALUE) {
2764 CloseHandle(file);
2765 return TRUE;
2769 WARN("Could not find a unique filename\n");
2770 return FALSE;
2773 /***********************************************************************
2774 * CreateUrlCacheEntryA (WININET.@)
2777 BOOL WINAPI CreateUrlCacheEntryA(LPCSTR lpszUrlName, DWORD dwExpectedFileSize,
2778 LPCSTR lpszFileExtension, LPSTR lpszFileName, DWORD dwReserved)
2780 WCHAR file_name[MAX_PATH];
2782 if(dwReserved)
2783 FIXME("dwReserved 0x%08x\n", dwReserved);
2785 if(!urlcache_entry_create(lpszUrlName, lpszFileExtension, file_name))
2786 return FALSE;
2788 if(!WideCharToMultiByte(CP_ACP, 0, file_name, -1, lpszFileName, MAX_PATH, NULL, NULL))
2789 return FALSE;
2790 return TRUE;
2792 /***********************************************************************
2793 * CreateUrlCacheEntryW (WININET.@)
2796 BOOL WINAPI CreateUrlCacheEntryW(LPCWSTR lpszUrlName, DWORD dwExpectedFileSize,
2797 LPCWSTR lpszFileExtension, LPWSTR lpszFileName, DWORD dwReserved)
2799 char *url, *ext = NULL;
2800 BOOL ret;
2802 if(dwReserved)
2803 FIXME("dwReserved 0x%08x\n", dwReserved);
2805 if(lpszFileExtension) {
2806 ext = heap_strdupWtoUTF8(lpszFileExtension);
2807 if(!ext)
2808 return FALSE;
2811 if(!urlcache_encode_url_alloc(lpszUrlName, &url)) {
2812 heap_free(ext);
2813 return FALSE;
2816 ret = urlcache_entry_create(url, ext, lpszFileName);
2817 heap_free(ext);
2818 heap_free(url);
2819 return ret;
2822 static BOOL urlcache_entry_commit(const char *url, const WCHAR *file_name,
2823 FILETIME expire_time, FILETIME modify_time, DWORD entry_type,
2824 BYTE *header_info, DWORD header_size, const char *file_ext,
2825 const char *original_url)
2827 cache_container *container;
2828 urlcache_header *header;
2829 struct hash_entry *hash_entry;
2830 entry_header *entry;
2831 entry_url *url_entry;
2832 DWORD url_entry_offset;
2833 DWORD size = DWORD_ALIGN(sizeof(*url_entry));
2834 DWORD file_name_off = 0;
2835 DWORD header_info_off = 0;
2836 DWORD file_ext_off = 0;
2837 WIN32_FILE_ATTRIBUTE_DATA file_attr;
2838 LARGE_INTEGER file_size;
2839 BYTE dir_id;
2840 char file_name_no_container[MAX_PATH];
2841 char *local_file_name = 0;
2842 DWORD hit_rate = 0;
2843 DWORD exempt_delta = 0;
2844 DWORD error;
2846 TRACE("(%s, %s, ..., ..., %x, %p, %d, %s, %s)\n", debugstr_a(url), debugstr_w(file_name),
2847 entry_type, header_info, header_size, debugstr_a(file_ext), debugstr_a(original_url));
2849 if(entry_type & STICKY_CACHE_ENTRY && !file_name) {
2850 SetLastError(ERROR_INVALID_PARAMETER);
2851 return FALSE;
2853 if(original_url)
2854 WARN(": original_url ignored\n");
2856 memset(&file_attr, 0, sizeof(file_attr));
2857 if(file_name) {
2858 if(!GetFileAttributesExW(file_name, GetFileExInfoStandard, &file_attr))
2859 return FALSE;
2861 file_size.u.LowPart = file_attr.nFileSizeLow;
2862 file_size.u.HighPart = file_attr.nFileSizeHigh;
2864 error = cache_containers_find(url, &container);
2865 if(error != ERROR_SUCCESS) {
2866 SetLastError(error);
2867 return FALSE;
2870 error = cache_container_open_index(container, MIN_BLOCK_NO);
2871 if(error != ERROR_SUCCESS) {
2872 SetLastError(error);
2873 return FALSE;
2876 if(!(header = cache_container_lock_index(container)))
2877 return FALSE;
2879 if(urlcache_find_hash_entry(header, url, &hash_entry)) {
2880 entry_url *url_entry = (entry_url*)((LPBYTE)header + hash_entry->offset);
2882 if(urlcache_hash_entry_is_locked(hash_entry, url_entry)) {
2883 TRACE("Trying to overwrite locked entry\n");
2884 cache_container_unlock_index(container, header);
2885 SetLastError(ERROR_SHARING_VIOLATION);
2886 return FALSE;
2889 hit_rate = url_entry->hit_rate;
2890 exempt_delta = url_entry->exempt_delta;
2891 urlcache_entry_delete(container, header, hash_entry);
2894 if(header->dirs_no)
2895 dir_id = 0;
2896 else
2897 dir_id = CACHE_CONTAINER_NO_SUBDIR;
2899 if(file_name) {
2900 BOOL bFound = FALSE;
2902 if(strncmpW(file_name, container->path, lstrlenW(container->path))) {
2903 ERR("path %s must begin with cache content path %s\n", debugstr_w(file_name), debugstr_w(container->path));
2904 cache_container_unlock_index(container, header);
2905 SetLastError(ERROR_INVALID_PARAMETER);
2906 return FALSE;
2909 /* skip container path prefix */
2910 file_name += lstrlenW(container->path);
2912 WideCharToMultiByte(CP_ACP, 0, file_name, -1, file_name_no_container, MAX_PATH, NULL, NULL);
2913 local_file_name = file_name_no_container;
2915 if(header->dirs_no) {
2916 for(dir_id = 0; dir_id < header->dirs_no; dir_id++) {
2917 if(!strncmp(header->directory_data[dir_id].name, local_file_name, DIR_LENGTH)) {
2918 bFound = TRUE;
2919 break;
2923 if(!bFound) {
2924 ERR("cache directory not found in path %s\n", debugstr_w(file_name));
2925 cache_container_unlock_index(container, header);
2926 SetLastError(ERROR_INVALID_PARAMETER);
2927 return FALSE;
2930 file_name += DIR_LENGTH + 1;
2931 local_file_name += DIR_LENGTH + 1;
2935 size = DWORD_ALIGN(size + strlen(url) + 1);
2936 if(file_name) {
2937 file_name_off = size;
2938 size = DWORD_ALIGN(size + strlen(local_file_name) + 1);
2940 if(header_info && header_size) {
2941 header_info_off = size;
2942 size = DWORD_ALIGN(size + header_size);
2944 if(file_ext && (file_ext_off = strlen(file_ext))) {
2945 DWORD len = file_ext_off;
2947 file_ext_off = size;
2948 size = DWORD_ALIGN(size + len + 1);
2951 /* round up to next block */
2952 if(size % BLOCKSIZE) {
2953 size -= size % BLOCKSIZE;
2954 size += BLOCKSIZE;
2957 error = urlcache_entry_alloc(header, size / BLOCKSIZE, &entry);
2958 while(error == ERROR_HANDLE_DISK_FULL) {
2959 error = cache_container_clean_index(container, &header);
2960 if(error == ERROR_SUCCESS)
2961 error = urlcache_entry_alloc(header, size / BLOCKSIZE, &entry);
2963 if(error != ERROR_SUCCESS) {
2964 cache_container_unlock_index(container, header);
2965 SetLastError(error);
2966 return FALSE;
2969 /* FindFirstFreeEntry fills in blocks used */
2970 url_entry = (entry_url *)entry;
2971 url_entry_offset = (LPBYTE)url_entry - (LPBYTE)header;
2972 url_entry->header.signature = URL_SIGNATURE;
2973 url_entry->cache_dir = dir_id;
2974 url_entry->cache_entry_type = entry_type | container->default_entry_type;
2975 url_entry->header_info_size = header_size;
2976 if((entry_type & STICKY_CACHE_ENTRY) && !exempt_delta) {
2977 /* Sticky entries have a default exempt time of one day */
2978 exempt_delta = 86400;
2980 url_entry->exempt_delta = exempt_delta;
2981 url_entry->hit_rate = hit_rate+1;
2982 url_entry->file_extension_off = file_ext_off;
2983 url_entry->header_info_off = header_info_off;
2984 url_entry->local_name_off = file_name_off;
2985 url_entry->url_off = DWORD_ALIGN(sizeof(*url_entry));
2986 url_entry->size.QuadPart = file_size.QuadPart;
2987 url_entry->use_count = 0;
2988 GetSystemTimeAsFileTime(&url_entry->access_time);
2989 url_entry->modification_time = modify_time;
2990 file_time_to_dos_date_time(&url_entry->access_time, &url_entry->sync_date, &url_entry->sync_time);
2991 file_time_to_dos_date_time(&expire_time, &url_entry->expire_date, &url_entry->expire_time);
2992 file_time_to_dos_date_time(&file_attr.ftLastWriteTime, &url_entry->write_date, &url_entry->write_time);
2994 /*** Unknowns ***/
2995 url_entry->unk1 = 0;
2996 url_entry->unk2 = 0;
2997 url_entry->unk3 = 0x60;
2998 url_entry->unk4 = 0;
2999 url_entry->unk5 = 0x1010;
3000 url_entry->unk7 = 0;
3001 url_entry->unk8 = 0;
3004 strcpy((LPSTR)url_entry + url_entry->url_off, url);
3005 if(file_name_off)
3006 strcpy((LPSTR)((LPBYTE)url_entry + file_name_off), local_file_name);
3007 if(header_info_off)
3008 memcpy((LPBYTE)url_entry + header_info_off, header_info, header_size);
3009 if(file_ext_off)
3010 strcpy((LPSTR)((LPBYTE)url_entry + file_ext_off), file_ext);
3012 error = urlcache_hash_entry_create(header, url, url_entry_offset, HASHTABLE_URL);
3013 while(error == ERROR_HANDLE_DISK_FULL) {
3014 error = cache_container_clean_index(container, &header);
3015 if(error == ERROR_SUCCESS) {
3016 url_entry = (entry_url *)((LPBYTE)header + url_entry_offset);
3017 error = urlcache_hash_entry_create(header, url,
3018 url_entry_offset, HASHTABLE_URL);
3021 if(error != ERROR_SUCCESS) {
3022 urlcache_entry_free(header, &url_entry->header);
3023 cache_container_unlock_index(container, header);
3024 SetLastError(error);
3025 return FALSE;
3028 if(url_entry->cache_dir < header->dirs_no)
3029 header->directory_data[url_entry->cache_dir].files_no++;
3030 if(entry_type & STICKY_CACHE_ENTRY)
3031 header->exempt_usage.QuadPart += file_size.QuadPart;
3032 else
3033 header->cache_usage.QuadPart += file_size.QuadPart;
3034 if(header->cache_usage.QuadPart+header->exempt_usage.QuadPart > header->cache_limit.QuadPart)
3035 handle_full_cache();
3037 cache_container_unlock_index(container, header);
3038 return TRUE;
3041 /***********************************************************************
3042 * CommitUrlCacheEntryA (WININET.@)
3044 BOOL WINAPI CommitUrlCacheEntryA(LPCSTR lpszUrlName, LPCSTR lpszLocalFileName,
3045 FILETIME ExpireTime, FILETIME LastModifiedTime, DWORD CacheEntryType,
3046 LPBYTE lpHeaderInfo, DWORD dwHeaderSize, LPCSTR lpszFileExtension, LPCSTR lpszOriginalUrl)
3048 WCHAR *file_name = NULL;
3049 BOOL ret;
3051 if(lpszLocalFileName) {
3052 file_name = heap_strdupAtoW(lpszLocalFileName);
3053 if(!file_name)
3054 return FALSE;
3057 ret = urlcache_entry_commit(lpszUrlName, file_name, ExpireTime, LastModifiedTime,
3058 CacheEntryType, lpHeaderInfo, dwHeaderSize, lpszFileExtension, lpszOriginalUrl);
3059 heap_free(file_name);
3060 return ret;
3063 /***********************************************************************
3064 * CommitUrlCacheEntryW (WININET.@)
3066 BOOL WINAPI CommitUrlCacheEntryW(LPCWSTR lpszUrlName, LPCWSTR lpszLocalFileName,
3067 FILETIME ExpireTime, FILETIME LastModifiedTime, DWORD CacheEntryType,
3068 LPWSTR lpHeaderInfo, DWORD dwHeaderSize, LPCWSTR lpszFileExtension, LPCWSTR lpszOriginalUrl)
3070 char *url, *original_url=NULL, *file_ext=NULL, *header_info=NULL;
3071 BOOL ret;
3073 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
3074 return FALSE;
3076 if(lpHeaderInfo) {
3077 header_info = heap_strdupWtoUTF8(lpHeaderInfo);
3078 if(!header_info) {
3079 heap_free(url);
3080 return FALSE;
3082 dwHeaderSize = strlen(header_info);
3085 if(lpszFileExtension) {
3086 file_ext = heap_strdupWtoA(lpszFileExtension);
3087 if(!file_ext) {
3088 heap_free(url);
3089 heap_free(header_info);
3090 return FALSE;
3094 if(lpszOriginalUrl && !urlcache_encode_url_alloc(lpszOriginalUrl, &original_url)) {
3095 heap_free(url);
3096 heap_free(header_info);
3097 heap_free(file_ext);
3098 return FALSE;
3101 ret = urlcache_entry_commit(url, lpszLocalFileName, ExpireTime, LastModifiedTime,
3102 CacheEntryType, (BYTE*)header_info, dwHeaderSize, file_ext, original_url);
3103 heap_free(url);
3104 heap_free(header_info);
3105 heap_free(file_ext);
3106 heap_free(original_url);
3107 return ret;
3110 /***********************************************************************
3111 * ReadUrlCacheEntryStream (WININET.@)
3114 BOOL WINAPI ReadUrlCacheEntryStream(
3115 IN HANDLE hUrlCacheStream,
3116 IN DWORD dwLocation,
3117 IN OUT LPVOID lpBuffer,
3118 IN OUT LPDWORD lpdwLen,
3119 IN DWORD dwReserved
3122 /* Get handle to file from 'stream' */
3123 stream_handle *pStream = (stream_handle*)hUrlCacheStream;
3125 if (dwReserved != 0)
3127 ERR("dwReserved != 0\n");
3128 SetLastError(ERROR_INVALID_PARAMETER);
3129 return FALSE;
3132 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->url, INTERNET_MAX_URL_LENGTH))
3134 SetLastError(ERROR_INVALID_HANDLE);
3135 return FALSE;
3138 if (SetFilePointer(pStream->file, dwLocation, NULL, FILE_CURRENT) == INVALID_SET_FILE_POINTER)
3139 return FALSE;
3140 return ReadFile(pStream->file, lpBuffer, *lpdwLen, lpdwLen, NULL);
3143 /***********************************************************************
3144 * RetrieveUrlCacheEntryStreamA (WININET.@)
3147 HANDLE WINAPI RetrieveUrlCacheEntryStreamA(LPCSTR lpszUrlName,
3148 LPINTERNET_CACHE_ENTRY_INFOA lpCacheEntryInfo,
3149 LPDWORD lpdwCacheEntryInfoBufferSize, BOOL fRandomRead, DWORD dwReserved)
3151 /* NOTE: this is not the same as the way that the native
3152 * version allocates 'stream' handles. I did it this way
3153 * as it is much easier and no applications should depend
3154 * on this behaviour. (Native version appears to allocate
3155 * indices into a table)
3157 stream_handle *stream;
3158 HANDLE file;
3160 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_a(lpszUrlName), lpCacheEntryInfo,
3161 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved);
3163 if(!RetrieveUrlCacheEntryFileA(lpszUrlName, lpCacheEntryInfo,
3164 lpdwCacheEntryInfoBufferSize, dwReserved))
3165 return NULL;
3167 file = CreateFileA(lpCacheEntryInfo->lpszLocalFileName, GENERIC_READ, FILE_SHARE_READ,
3168 NULL, OPEN_EXISTING, fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0, NULL);
3169 if(file == INVALID_HANDLE_VALUE) {
3170 UnlockUrlCacheEntryFileA(lpszUrlName, 0);
3171 return NULL;
3174 /* allocate handle storage space */
3175 stream = heap_alloc(sizeof(stream_handle) + strlen(lpszUrlName) * sizeof(CHAR));
3176 if(!stream) {
3177 CloseHandle(file);
3178 UnlockUrlCacheEntryFileA(lpszUrlName, 0);
3179 SetLastError(ERROR_OUTOFMEMORY);
3180 return NULL;
3183 stream->file = file;
3184 strcpy(stream->url, lpszUrlName);
3185 return stream;
3188 /***********************************************************************
3189 * RetrieveUrlCacheEntryStreamW (WININET.@)
3192 HANDLE WINAPI RetrieveUrlCacheEntryStreamW(LPCWSTR lpszUrlName,
3193 LPINTERNET_CACHE_ENTRY_INFOW lpCacheEntryInfo,
3194 LPDWORD lpdwCacheEntryInfoBufferSize, BOOL fRandomRead, DWORD dwReserved)
3196 DWORD len;
3197 /* NOTE: this is not the same as the way that the native
3198 * version allocates 'stream' handles. I did it this way
3199 * as it is much easier and no applications should depend
3200 * on this behaviour. (Native version appears to allocate
3201 * indices into a table)
3203 stream_handle *stream;
3204 HANDLE file;
3206 TRACE("(%s, %p, %p, %x, 0x%08x)\n", debugstr_w(lpszUrlName), lpCacheEntryInfo,
3207 lpdwCacheEntryInfoBufferSize, fRandomRead, dwReserved);
3209 if(!(len = urlcache_encode_url(lpszUrlName, NULL, 0)))
3210 return NULL;
3212 if(!RetrieveUrlCacheEntryFileW(lpszUrlName, lpCacheEntryInfo,
3213 lpdwCacheEntryInfoBufferSize, dwReserved))
3214 return NULL;
3216 file = CreateFileW(lpCacheEntryInfo->lpszLocalFileName, GENERIC_READ, FILE_SHARE_READ,
3217 NULL, OPEN_EXISTING, fRandomRead ? FILE_FLAG_RANDOM_ACCESS : 0, NULL);
3218 if(file == INVALID_HANDLE_VALUE) {
3219 UnlockUrlCacheEntryFileW(lpszUrlName, 0);
3220 return NULL;
3223 /* allocate handle storage space */
3224 stream = heap_alloc(sizeof(stream_handle) + len*sizeof(WCHAR));
3225 if(!stream) {
3226 CloseHandle(file);
3227 UnlockUrlCacheEntryFileW(lpszUrlName, 0);
3228 SetLastError(ERROR_OUTOFMEMORY);
3229 return NULL;
3232 stream->file = file;
3233 if(!urlcache_encode_url(lpszUrlName, stream->url, len)) {
3234 CloseHandle(file);
3235 UnlockUrlCacheEntryFileW(lpszUrlName, 0);
3236 heap_free(stream);
3237 return NULL;
3239 return stream;
3242 /***********************************************************************
3243 * UnlockUrlCacheEntryStream (WININET.@)
3246 BOOL WINAPI UnlockUrlCacheEntryStream(
3247 IN HANDLE hUrlCacheStream,
3248 IN DWORD dwReserved
3251 stream_handle *pStream = (stream_handle*)hUrlCacheStream;
3253 if (dwReserved != 0)
3255 ERR("dwReserved != 0\n");
3256 SetLastError(ERROR_INVALID_PARAMETER);
3257 return FALSE;
3260 if (IsBadReadPtr(pStream, sizeof(*pStream)) || IsBadStringPtrA(pStream->url, INTERNET_MAX_URL_LENGTH))
3262 SetLastError(ERROR_INVALID_HANDLE);
3263 return FALSE;
3266 if (!UnlockUrlCacheEntryFileA(pStream->url, 0))
3267 return FALSE;
3269 CloseHandle(pStream->file);
3270 heap_free(pStream);
3271 return TRUE;
3275 /***********************************************************************
3276 * DeleteUrlCacheEntryA (WININET.@)
3279 BOOL WINAPI DeleteUrlCacheEntryA(LPCSTR lpszUrlName)
3281 cache_container *pContainer;
3282 urlcache_header *pHeader;
3283 struct hash_entry *pHashEntry;
3284 DWORD error;
3285 BOOL ret;
3287 TRACE("(%s)\n", debugstr_a(lpszUrlName));
3289 error = cache_containers_find(lpszUrlName, &pContainer);
3290 if (error != ERROR_SUCCESS)
3292 SetLastError(error);
3293 return FALSE;
3296 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3297 if (error != ERROR_SUCCESS)
3299 SetLastError(error);
3300 return FALSE;
3303 if (!(pHeader = cache_container_lock_index(pContainer)))
3304 return FALSE;
3306 if (!urlcache_find_hash_entry(pHeader, lpszUrlName, &pHashEntry))
3308 cache_container_unlock_index(pContainer, pHeader);
3309 TRACE("entry %s not found!\n", lpszUrlName);
3310 SetLastError(ERROR_FILE_NOT_FOUND);
3311 return FALSE;
3314 ret = urlcache_entry_delete(pContainer, pHeader, pHashEntry);
3316 cache_container_unlock_index(pContainer, pHeader);
3318 return ret;
3321 /***********************************************************************
3322 * DeleteUrlCacheEntryW (WININET.@)
3325 BOOL WINAPI DeleteUrlCacheEntryW(LPCWSTR lpszUrlName)
3327 char *url;
3328 BOOL ret;
3330 if(!urlcache_encode_url_alloc(lpszUrlName, &url))
3331 return FALSE;
3333 ret = DeleteUrlCacheEntryA(url);
3334 heap_free(url);
3335 return ret;
3338 BOOL WINAPI DeleteUrlCacheContainerA(DWORD d1, DWORD d2)
3340 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3341 return TRUE;
3344 BOOL WINAPI DeleteUrlCacheContainerW(DWORD d1, DWORD d2)
3346 FIXME("(0x%08x, 0x%08x) stub\n", d1, d2);
3347 return TRUE;
3350 /***********************************************************************
3351 * CreateCacheContainerA (WININET.@)
3353 BOOL WINAPI CreateUrlCacheContainerA(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3354 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3356 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3357 d1, d2, d3, d4, d5, d6, d7, d8);
3358 return TRUE;
3361 /***********************************************************************
3362 * CreateCacheContainerW (WININET.@)
3364 BOOL WINAPI CreateUrlCacheContainerW(DWORD d1, DWORD d2, DWORD d3, DWORD d4,
3365 DWORD d5, DWORD d6, DWORD d7, DWORD d8)
3367 FIXME("(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x) stub\n",
3368 d1, d2, d3, d4, d5, d6, d7, d8);
3369 return TRUE;
3372 /***********************************************************************
3373 * FindFirstUrlCacheContainerA (WININET.@)
3375 HANDLE WINAPI FindFirstUrlCacheContainerA( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3377 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3378 return NULL;
3381 /***********************************************************************
3382 * FindFirstUrlCacheContainerW (WININET.@)
3384 HANDLE WINAPI FindFirstUrlCacheContainerW( LPVOID p1, LPVOID p2, LPVOID p3, DWORD d1 )
3386 FIXME("(%p, %p, %p, 0x%08x) stub\n", p1, p2, p3, d1 );
3387 return NULL;
3390 /***********************************************************************
3391 * FindNextUrlCacheContainerA (WININET.@)
3393 BOOL WINAPI FindNextUrlCacheContainerA( HANDLE handle, LPVOID p1, LPVOID p2 )
3395 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3396 return FALSE;
3399 /***********************************************************************
3400 * FindNextUrlCacheContainerW (WININET.@)
3402 BOOL WINAPI FindNextUrlCacheContainerW( HANDLE handle, LPVOID p1, LPVOID p2 )
3404 FIXME("(%p, %p, %p) stub\n", handle, p1, p2 );
3405 return FALSE;
3408 HANDLE WINAPI FindFirstUrlCacheEntryExA(
3409 LPCSTR lpszUrlSearchPattern,
3410 DWORD dwFlags,
3411 DWORD dwFilter,
3412 GROUPID GroupId,
3413 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3414 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3415 LPVOID lpReserved,
3416 LPDWORD pcbReserved2,
3417 LPVOID lpReserved3
3420 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_a(lpszUrlSearchPattern),
3421 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3422 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3423 SetLastError(ERROR_FILE_NOT_FOUND);
3424 return NULL;
3427 HANDLE WINAPI FindFirstUrlCacheEntryExW(
3428 LPCWSTR lpszUrlSearchPattern,
3429 DWORD dwFlags,
3430 DWORD dwFilter,
3431 GROUPID GroupId,
3432 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3433 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3434 LPVOID lpReserved,
3435 LPDWORD pcbReserved2,
3436 LPVOID lpReserved3
3439 FIXME("(%s, 0x%08x, 0x%08x, 0x%08x%08x, %p, %p, %p, %p, %p) stub\n", debugstr_w(lpszUrlSearchPattern),
3440 dwFlags, dwFilter, (ULONG)(GroupId >> 32), (ULONG)GroupId, lpFirstCacheEntryInfo,
3441 lpdwFirstCacheEntryInfoBufferSize, lpReserved, pcbReserved2,lpReserved3);
3442 SetLastError(ERROR_FILE_NOT_FOUND);
3443 return NULL;
3446 /***********************************************************************
3447 * FindFirstUrlCacheEntryA (WININET.@)
3450 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryA(LPCSTR lpszUrlSearchPattern,
3451 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3453 find_handle *pEntryHandle;
3455 TRACE("(%s, %p, %p)\n", debugstr_a(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3457 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3458 if (!pEntryHandle)
3459 return NULL;
3461 pEntryHandle->magic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3462 if (lpszUrlSearchPattern)
3464 pEntryHandle->url_search_pattern = heap_strdupA(lpszUrlSearchPattern);
3465 if (!pEntryHandle->url_search_pattern)
3467 heap_free(pEntryHandle);
3468 return NULL;
3471 else
3472 pEntryHandle->url_search_pattern = NULL;
3473 pEntryHandle->container_idx = 0;
3474 pEntryHandle->hash_table_idx = 0;
3475 pEntryHandle->hash_entry_idx = 0;
3477 if (!FindNextUrlCacheEntryA(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3479 heap_free(pEntryHandle);
3480 return NULL;
3482 return pEntryHandle;
3485 /***********************************************************************
3486 * FindFirstUrlCacheEntryW (WININET.@)
3489 INTERNETAPI HANDLE WINAPI FindFirstUrlCacheEntryW(LPCWSTR lpszUrlSearchPattern,
3490 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo, LPDWORD lpdwFirstCacheEntryInfoBufferSize)
3492 find_handle *pEntryHandle;
3494 TRACE("(%s, %p, %p)\n", debugstr_w(lpszUrlSearchPattern), lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize);
3496 pEntryHandle = heap_alloc(sizeof(*pEntryHandle));
3497 if (!pEntryHandle)
3498 return NULL;
3500 pEntryHandle->magic = URLCACHE_FIND_ENTRY_HANDLE_MAGIC;
3501 if (lpszUrlSearchPattern)
3503 pEntryHandle->url_search_pattern = heap_strdupWtoA(lpszUrlSearchPattern);
3504 if (!pEntryHandle->url_search_pattern)
3506 heap_free(pEntryHandle);
3507 return NULL;
3510 else
3511 pEntryHandle->url_search_pattern = NULL;
3512 pEntryHandle->container_idx = 0;
3513 pEntryHandle->hash_table_idx = 0;
3514 pEntryHandle->hash_entry_idx = 0;
3516 if (!FindNextUrlCacheEntryW(pEntryHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize))
3518 heap_free(pEntryHandle);
3519 return NULL;
3521 return pEntryHandle;
3524 static BOOL urlcache_find_next_entry(
3525 HANDLE hEnumHandle,
3526 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3527 LPDWORD lpdwNextCacheEntryInfoBufferSize,
3528 BOOL unicode)
3530 find_handle *pEntryHandle = (find_handle*)hEnumHandle;
3531 cache_container *pContainer;
3533 if (pEntryHandle->magic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3535 SetLastError(ERROR_INVALID_HANDLE);
3536 return FALSE;
3539 for (; cache_containers_enum(pEntryHandle->url_search_pattern, pEntryHandle->container_idx, &pContainer);
3540 pEntryHandle->container_idx++, pEntryHandle->hash_table_idx = 0)
3542 urlcache_header *pHeader;
3543 entry_hash_table *pHashTableEntry;
3544 DWORD error;
3546 error = cache_container_open_index(pContainer, MIN_BLOCK_NO);
3547 if (error != ERROR_SUCCESS)
3549 SetLastError(error);
3550 return FALSE;
3553 if (!(pHeader = cache_container_lock_index(pContainer)))
3554 return FALSE;
3556 for (; urlcache_enum_hash_tables(pHeader, &pEntryHandle->hash_table_idx, &pHashTableEntry);
3557 pEntryHandle->hash_table_idx++, pEntryHandle->hash_entry_idx = 0)
3559 const struct hash_entry *pHashEntry = NULL;
3560 for (; urlcache_enum_hash_table_entries(pHeader, pHashTableEntry, &pEntryHandle->hash_entry_idx, &pHashEntry);
3561 pEntryHandle->hash_entry_idx++)
3563 const entry_url *pUrlEntry;
3564 const entry_header *pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
3566 if (pEntry->signature != URL_SIGNATURE)
3567 continue;
3569 pUrlEntry = (const entry_url *)pEntry;
3570 TRACE("Found URL: %s\n",
3571 debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->url_off));
3572 TRACE("Header info: %s\n",
3573 debugstr_an((LPCSTR)pUrlEntry + pUrlEntry->header_info_off,
3574 pUrlEntry->header_info_size));
3576 error = urlcache_copy_entry(
3577 pContainer,
3578 pHeader,
3579 lpNextCacheEntryInfo,
3580 lpdwNextCacheEntryInfoBufferSize,
3581 pUrlEntry,
3582 unicode);
3583 if (error != ERROR_SUCCESS)
3585 cache_container_unlock_index(pContainer, pHeader);
3586 SetLastError(error);
3587 return FALSE;
3589 if(pUrlEntry->local_name_off)
3590 TRACE("Local File Name: %s\n", debugstr_a((LPCSTR)pUrlEntry + pUrlEntry->local_name_off));
3592 /* increment the current index so that next time the function
3593 * is called the next entry is returned */
3594 pEntryHandle->hash_entry_idx++;
3595 cache_container_unlock_index(pContainer, pHeader);
3596 return TRUE;
3600 cache_container_unlock_index(pContainer, pHeader);
3603 SetLastError(ERROR_NO_MORE_ITEMS);
3604 return FALSE;
3607 /***********************************************************************
3608 * FindNextUrlCacheEntryA (WININET.@)
3610 BOOL WINAPI FindNextUrlCacheEntryA(
3611 HANDLE hEnumHandle,
3612 LPINTERNET_CACHE_ENTRY_INFOA lpNextCacheEntryInfo,
3613 LPDWORD lpdwNextCacheEntryInfoBufferSize)
3615 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3617 return urlcache_find_next_entry(hEnumHandle, lpNextCacheEntryInfo,
3618 lpdwNextCacheEntryInfoBufferSize, FALSE /* not UNICODE */);
3621 /***********************************************************************
3622 * FindNextUrlCacheEntryW (WININET.@)
3624 BOOL WINAPI FindNextUrlCacheEntryW(
3625 HANDLE hEnumHandle,
3626 LPINTERNET_CACHE_ENTRY_INFOW lpNextCacheEntryInfo,
3627 LPDWORD lpdwNextCacheEntryInfoBufferSize
3630 TRACE("(%p, %p, %p)\n", hEnumHandle, lpNextCacheEntryInfo, lpdwNextCacheEntryInfoBufferSize);
3632 return urlcache_find_next_entry(hEnumHandle,
3633 (LPINTERNET_CACHE_ENTRY_INFOA)lpNextCacheEntryInfo,
3634 lpdwNextCacheEntryInfoBufferSize, TRUE /* UNICODE */);
3637 /***********************************************************************
3638 * FindCloseUrlCache (WININET.@)
3640 BOOL WINAPI FindCloseUrlCache(HANDLE hEnumHandle)
3642 find_handle *pEntryHandle = (find_handle*)hEnumHandle;
3644 TRACE("(%p)\n", hEnumHandle);
3646 if (!pEntryHandle || pEntryHandle->magic != URLCACHE_FIND_ENTRY_HANDLE_MAGIC)
3648 SetLastError(ERROR_INVALID_HANDLE);
3649 return FALSE;
3652 pEntryHandle->magic = 0;
3653 heap_free(pEntryHandle->url_search_pattern);
3654 heap_free(pEntryHandle);
3655 return TRUE;
3658 HANDLE WINAPI FindFirstUrlCacheGroup( DWORD dwFlags, DWORD dwFilter, LPVOID lpSearchCondition,
3659 DWORD dwSearchCondition, GROUPID* lpGroupId, LPVOID lpReserved )
3661 FIXME("(0x%08x, 0x%08x, %p, 0x%08x, %p, %p) stub\n", dwFlags, dwFilter, lpSearchCondition,
3662 dwSearchCondition, lpGroupId, lpReserved);
3663 return NULL;
3666 BOOL WINAPI FindNextUrlCacheEntryExA(
3667 HANDLE hEnumHandle,
3668 LPINTERNET_CACHE_ENTRY_INFOA lpFirstCacheEntryInfo,
3669 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3670 LPVOID lpReserved,
3671 LPDWORD pcbReserved2,
3672 LPVOID lpReserved3
3675 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3676 lpReserved, pcbReserved2, lpReserved3);
3677 return FALSE;
3680 BOOL WINAPI FindNextUrlCacheEntryExW(
3681 HANDLE hEnumHandle,
3682 LPINTERNET_CACHE_ENTRY_INFOW lpFirstCacheEntryInfo,
3683 LPDWORD lpdwFirstCacheEntryInfoBufferSize,
3684 LPVOID lpReserved,
3685 LPDWORD pcbReserved2,
3686 LPVOID lpReserved3
3689 FIXME("(%p, %p, %p, %p, %p, %p) stub\n", hEnumHandle, lpFirstCacheEntryInfo, lpdwFirstCacheEntryInfoBufferSize,
3690 lpReserved, pcbReserved2, lpReserved3);
3691 return FALSE;
3694 BOOL WINAPI FindNextUrlCacheGroup( HANDLE hFind, GROUPID* lpGroupId, LPVOID lpReserved )
3696 FIXME("(%p, %p, %p) stub\n", hFind, lpGroupId, lpReserved);
3697 return FALSE;
3700 /***********************************************************************
3701 * CreateUrlCacheGroup (WININET.@)
3704 INTERNETAPI GROUPID WINAPI CreateUrlCacheGroup(DWORD dwFlags, LPVOID lpReserved)
3706 FIXME("(0x%08x, %p): stub\n", dwFlags, lpReserved);
3707 return FALSE;
3710 /***********************************************************************
3711 * DeleteUrlCacheGroup (WININET.@)
3714 BOOL WINAPI DeleteUrlCacheGroup(GROUPID GroupId, DWORD dwFlags, LPVOID lpReserved)
3716 FIXME("(0x%08x%08x, 0x%08x, %p) stub\n",
3717 (ULONG)(GroupId >> 32), (ULONG)GroupId, dwFlags, lpReserved);
3718 return FALSE;
3721 /***********************************************************************
3722 * DeleteWpadCacheForNetworks (WININET.@)
3723 * Undocumented, added in IE8
3725 BOOL WINAPI DeleteWpadCacheForNetworks(DWORD unk1)
3727 FIXME("(%d) stub\n", unk1);
3728 return FALSE;
3731 /***********************************************************************
3732 * SetUrlCacheEntryGroupA (WININET.@)
3735 BOOL WINAPI SetUrlCacheEntryGroupA(LPCSTR lpszUrlName, DWORD dwFlags,
3736 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3737 LPVOID lpReserved)
3739 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3740 debugstr_a(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3741 pbGroupAttributes, cbGroupAttributes, lpReserved);
3742 SetLastError(ERROR_FILE_NOT_FOUND);
3743 return FALSE;
3746 /***********************************************************************
3747 * SetUrlCacheEntryGroupW (WININET.@)
3750 BOOL WINAPI SetUrlCacheEntryGroupW(LPCWSTR lpszUrlName, DWORD dwFlags,
3751 GROUPID GroupId, LPBYTE pbGroupAttributes, DWORD cbGroupAttributes,
3752 LPVOID lpReserved)
3754 FIXME("(%s, 0x%08x, 0x%08x%08x, %p, 0x%08x, %p) stub\n",
3755 debugstr_w(lpszUrlName), dwFlags, (ULONG)(GroupId >> 32), (ULONG)GroupId,
3756 pbGroupAttributes, cbGroupAttributes, lpReserved);
3757 SetLastError(ERROR_FILE_NOT_FOUND);
3758 return FALSE;
3761 /***********************************************************************
3762 * GetUrlCacheConfigInfoW (WININET.@)
3764 BOOL WINAPI GetUrlCacheConfigInfoW(LPINTERNET_CACHE_CONFIG_INFOW CacheInfo, LPDWORD size, DWORD bitmask)
3766 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3767 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3768 return FALSE;
3771 /***********************************************************************
3772 * GetUrlCacheConfigInfoA (WININET.@)
3774 BOOL WINAPI GetUrlCacheConfigInfoA(LPINTERNET_CACHE_CONFIG_INFOA CacheInfo, LPDWORD size, DWORD bitmask)
3776 FIXME("(%p, %p, %x)\n", CacheInfo, size, bitmask);
3777 INTERNET_SetLastError(ERROR_INVALID_PARAMETER);
3778 return FALSE;
3781 BOOL WINAPI GetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3782 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo,
3783 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3785 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3786 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3787 lpdwGroupInfo, lpReserved);
3788 return FALSE;
3791 BOOL WINAPI GetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3792 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo,
3793 LPDWORD lpdwGroupInfo, LPVOID lpReserved )
3795 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p, %p) stub\n",
3796 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo,
3797 lpdwGroupInfo, lpReserved);
3798 return FALSE;
3801 BOOL WINAPI SetUrlCacheGroupAttributeA( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3802 LPINTERNET_CACHE_GROUP_INFOA lpGroupInfo, LPVOID lpReserved )
3804 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3805 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3806 return TRUE;
3809 BOOL WINAPI SetUrlCacheGroupAttributeW( GROUPID gid, DWORD dwFlags, DWORD dwAttributes,
3810 LPINTERNET_CACHE_GROUP_INFOW lpGroupInfo, LPVOID lpReserved )
3812 FIXME("(0x%08x%08x, 0x%08x, 0x%08x, %p, %p) stub\n",
3813 (ULONG)(gid >> 32), (ULONG)gid, dwFlags, dwAttributes, lpGroupInfo, lpReserved);
3814 return TRUE;
3817 BOOL WINAPI SetUrlCacheConfigInfoA( LPINTERNET_CACHE_CONFIG_INFOA lpCacheConfigInfo, DWORD dwFieldControl )
3819 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3820 return TRUE;
3823 BOOL WINAPI SetUrlCacheConfigInfoW( LPINTERNET_CACHE_CONFIG_INFOW lpCacheConfigInfo, DWORD dwFieldControl )
3825 FIXME("(%p, 0x%08x) stub\n", lpCacheConfigInfo, dwFieldControl);
3826 return TRUE;
3829 /***********************************************************************
3830 * DeleteIE3Cache (WININET.@)
3832 * Deletes the files used by the IE3 URL caching system.
3834 * PARAMS
3835 * hWnd [I] A dummy window.
3836 * hInst [I] Instance of process calling the function.
3837 * lpszCmdLine [I] Options used by function.
3838 * nCmdShow [I] The nCmdShow value to use when showing windows created, if any.
3840 DWORD WINAPI DeleteIE3Cache(HWND hWnd, HINSTANCE hInst, LPSTR lpszCmdLine, int nCmdShow)
3842 FIXME("(%p, %p, %s, %d)\n", hWnd, hInst, debugstr_a(lpszCmdLine), nCmdShow);
3843 return 0;
3846 static BOOL urlcache_entry_is_expired(const entry_url *pUrlEntry,
3847 FILETIME *pftLastModified)
3849 BOOL ret;
3850 FILETIME now, expired;
3852 *pftLastModified = pUrlEntry->modification_time;
3853 GetSystemTimeAsFileTime(&now);
3854 dos_date_time_to_file_time(pUrlEntry->expire_date,
3855 pUrlEntry->expire_time, &expired);
3856 /* If the expired time is 0, it's interpreted as not expired */
3857 if (!expired.dwLowDateTime && !expired.dwHighDateTime)
3858 ret = FALSE;
3859 else
3860 ret = CompareFileTime(&expired, &now) < 0;
3861 return ret;
3864 /***********************************************************************
3865 * IsUrlCacheEntryExpiredA (WININET.@)
3867 * PARAMS
3868 * url [I] Url
3869 * dwFlags [I] Unknown
3870 * pftLastModified [O] Last modified time
3872 BOOL WINAPI IsUrlCacheEntryExpiredA(LPCSTR url, DWORD dwFlags, FILETIME* pftLastModified)
3874 urlcache_header *pHeader;
3875 struct hash_entry *pHashEntry;
3876 const entry_header *pEntry;
3877 const entry_url * pUrlEntry;
3878 cache_container *pContainer;
3879 BOOL expired;
3881 TRACE("(%s, %08x, %p)\n", debugstr_a(url), dwFlags, pftLastModified);
3883 if (!url || !pftLastModified)
3884 return TRUE;
3885 if (dwFlags)
3886 FIXME("unknown flags 0x%08x\n", dwFlags);
3888 /* Any error implies that the URL is expired, i.e. not in the cache */
3889 if (cache_containers_find(url, &pContainer))
3891 memset(pftLastModified, 0, sizeof(*pftLastModified));
3892 return TRUE;
3895 if (cache_container_open_index(pContainer, MIN_BLOCK_NO))
3897 memset(pftLastModified, 0, sizeof(*pftLastModified));
3898 return TRUE;
3901 if (!(pHeader = cache_container_lock_index(pContainer)))
3903 memset(pftLastModified, 0, sizeof(*pftLastModified));
3904 return TRUE;
3907 if (!urlcache_find_hash_entry(pHeader, url, &pHashEntry))
3909 cache_container_unlock_index(pContainer, pHeader);
3910 memset(pftLastModified, 0, sizeof(*pftLastModified));
3911 TRACE("entry %s not found!\n", url);
3912 return TRUE;
3915 pEntry = (const entry_header*)((LPBYTE)pHeader + pHashEntry->offset);
3916 if (pEntry->signature != URL_SIGNATURE)
3918 cache_container_unlock_index(pContainer, pHeader);
3919 memset(pftLastModified, 0, sizeof(*pftLastModified));
3920 FIXME("Trying to retrieve entry of unknown format %s\n", debugstr_an((LPCSTR)&pEntry->signature, sizeof(DWORD)));
3921 return TRUE;
3924 pUrlEntry = (const entry_url *)pEntry;
3925 expired = urlcache_entry_is_expired(pUrlEntry, pftLastModified);
3927 cache_container_unlock_index(pContainer, pHeader);
3929 return expired;
3932 /***********************************************************************
3933 * IsUrlCacheEntryExpiredW (WININET.@)
3935 * PARAMS
3936 * url [I] Url
3937 * dwFlags [I] Unknown
3938 * pftLastModified [O] Last modified time
3940 BOOL WINAPI IsUrlCacheEntryExpiredW(LPCWSTR url, DWORD dwFlags, FILETIME* pftLastModified)
3942 char *encoded_url;
3943 BOOL ret;
3945 if(!urlcache_encode_url_alloc(url, &encoded_url))
3946 return FALSE;
3948 ret = IsUrlCacheEntryExpiredA(encoded_url, dwFlags, pftLastModified);
3949 heap_free(encoded_url);
3950 return ret;
3953 /***********************************************************************
3954 * GetDiskInfoA (WININET.@)
3956 BOOL WINAPI GetDiskInfoA(PCSTR path, PDWORD cluster_size, PDWORDLONG free, PDWORDLONG total)
3958 BOOL ret;
3959 ULARGE_INTEGER bytes_free, bytes_total;
3961 TRACE("(%s, %p, %p, %p)\n", debugstr_a(path), cluster_size, free, total);
3963 if (!path)
3965 SetLastError(ERROR_INVALID_PARAMETER);
3966 return FALSE;
3969 if ((ret = GetDiskFreeSpaceExA(path, NULL, &bytes_total, &bytes_free)))
3971 if (cluster_size) *cluster_size = 1;
3972 if (free) *free = bytes_free.QuadPart;
3973 if (total) *total = bytes_total.QuadPart;
3975 return ret;
3978 /***********************************************************************
3979 * RegisterUrlCacheNotification (WININET.@)
3981 DWORD WINAPI RegisterUrlCacheNotification(LPVOID a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
3983 FIXME("(%p %x %x %x %x %x)\n", a, b, c, d, e, f);
3984 return 0;
3987 /***********************************************************************
3988 * IncrementUrlCacheHeaderData (WININET.@)
3990 BOOL WINAPI IncrementUrlCacheHeaderData(DWORD index, LPDWORD data)
3992 FIXME("(%u, %p)\n", index, data);
3993 return FALSE;
3996 /***********************************************************************
3997 * RunOnceUrlCache (WININET.@)
4000 DWORD WINAPI RunOnceUrlCache(HWND hwnd, HINSTANCE hinst, LPSTR cmd, int cmdshow)
4002 FIXME("(%p, %p, %s, %d): stub\n", hwnd, hinst, debugstr_a(cmd), cmdshow);
4003 return 0;
4006 BOOL init_urlcache(void)
4008 dll_unload_event = CreateEventW(NULL, FALSE, FALSE, NULL);
4009 if(!dll_unload_event)
4010 return FALSE;
4012 free_cache_running = CreateSemaphoreW(NULL, 1, 1, NULL);
4013 if(!free_cache_running) {
4014 CloseHandle(dll_unload_event);
4015 return FALSE;
4018 cache_containers_init();
4019 return TRUE;
4022 void free_urlcache(void)
4024 SetEvent(dll_unload_event);
4025 WaitForSingleObject(free_cache_running, INFINITE);
4026 ReleaseSemaphore(free_cache_running, 1, NULL);
4027 CloseHandle(free_cache_running);
4028 CloseHandle(dll_unload_event);
4030 cache_containers_free();
4033 /***********************************************************************
4034 * LoadUrlCacheContent (WININET.@)
4036 BOOL WINAPI LoadUrlCacheContent(void)
4038 FIXME("stub!\n");
4039 return FALSE;